diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..ac4824a7
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,97 @@
+name: CI
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:13
+ env:
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: ransack_demo_test
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ ports:
+ - 5432:5432
+
+ env:
+ RAILS_ENV: test
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/ransack_demo_test
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: '3.2.3'
+ bundler-cache: true
+
+ - name: Setup Database
+ run: |
+ bundle exec rails db:create
+ bundle exec rails db:migrate
+ bundle exec rails db:seed
+
+ - name: Run Tests
+ run: |
+ bundle exec rails test
+
+ - name: Run RuboCop
+ run: |
+ bundle exec rubocop --format github
+
+ system-test:
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:13
+ env:
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: ransack_demo_test
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ ports:
+ - 5432:5432
+
+ env:
+ RAILS_ENV: test
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/ransack_demo_test
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: '3.2.3'
+ bundler-cache: true
+
+ - name: Setup Database
+ run: |
+ bundle exec rails db:create
+ bundle exec rails db:migrate
+ bundle exec rails db:seed
+
+ - name: Install Chrome dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y google-chrome-stable
+
+ - name: Run System Tests
+ run: |
+ bundle exec rails test:system
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 923b6976..c898e58c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.bundle
+vendor/bundle
db/*.sqlite3
log/*.log
tmp/
diff --git a/.ruby-version b/.ruby-version
index f9892605..b347b11e 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.4.4
+3.2.3
diff --git a/Gemfile b/Gemfile
index 660da2f7..926f2c02 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,20 +2,24 @@ source "https://rubygems.org"
ruby file: ".ruby-version"
-gem "rails", "~> 7.1.0"
+gem "rails", "~> 8.0.0"
# Use PostgreSQL as the database for Active Record
gem "pg", "~> 1.0"
# Use Puma as the app server
-gem "puma", "~> 5.6"
+gem "puma", "~> 6.0"
+# Use Tailwind CSS for styling
+gem "tailwindcss-rails", "~> 2.0"
+# Use Importmap for managing JavaScript dependencies
+gem "importmap-rails", "~> 2.0"
+# Turbo provides partial page replacement and forms without full page reloads
+gem "turbo-rails", "~> 2.0"
+# Stimulus provides reactive behavior for JavaScript
+gem "stimulus-rails", "~> 1.0"
# Use SCSS for stylesheets
-gem "sass-rails", "~> 5.0"
-# Use Uglifier as compressor for JavaScript assets
-gem "uglifier", ">= 1.3.0"
+gem "sass-rails", "~> 6.0"
# Use jquery as the JavaScript library
gem "jquery-rails"
-# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
-# gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
# gem 'jbuilder', '~> 2.5'
# Use ActiveModel has_secure_password
@@ -27,6 +31,9 @@ gem "faker"
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem "byebug", platform: :mri
+ # System testing gems
+ gem "selenium-webdriver"
+ gem "webdrivers"
end
group :development do
diff --git a/Gemfile.lock b/Gemfile.lock
index 9a20bd44..b84e563f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,83 +1,77 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.1.5.1)
- actionpack (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ actioncable (8.0.3)
+ actionpack (= 8.0.3)
+ activesupport (= 8.0.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
- actionmailbox (7.1.5.1)
- actionpack (= 7.1.5.1)
- activejob (= 7.1.5.1)
- activerecord (= 7.1.5.1)
- activestorage (= 7.1.5.1)
- activesupport (= 7.1.5.1)
- mail (>= 2.7.1)
- net-imap
- net-pop
- net-smtp
- actionmailer (7.1.5.1)
- actionpack (= 7.1.5.1)
- actionview (= 7.1.5.1)
- activejob (= 7.1.5.1)
- activesupport (= 7.1.5.1)
- mail (~> 2.5, >= 2.5.4)
- net-imap
- net-pop
- net-smtp
+ actionmailbox (8.0.3)
+ actionpack (= 8.0.3)
+ activejob (= 8.0.3)
+ activerecord (= 8.0.3)
+ activestorage (= 8.0.3)
+ activesupport (= 8.0.3)
+ mail (>= 2.8.0)
+ actionmailer (8.0.3)
+ actionpack (= 8.0.3)
+ actionview (= 8.0.3)
+ activejob (= 8.0.3)
+ activesupport (= 8.0.3)
+ mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
- actionpack (7.1.5.1)
- actionview (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ actionpack (8.0.3)
+ actionview (= 8.0.3)
+ activesupport (= 8.0.3)
nokogiri (>= 1.8.5)
- racc
rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
- actiontext (7.1.5.1)
- actionpack (= 7.1.5.1)
- activerecord (= 7.1.5.1)
- activestorage (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ useragent (~> 0.16)
+ actiontext (8.0.3)
+ actionpack (= 8.0.3)
+ activerecord (= 8.0.3)
+ activestorage (= 8.0.3)
+ activesupport (= 8.0.3)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.1.5.1)
- activesupport (= 7.1.5.1)
+ actionview (8.0.3)
+ activesupport (= 8.0.3)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
- activejob (7.1.5.1)
- activesupport (= 7.1.5.1)
+ activejob (8.0.3)
+ activesupport (= 8.0.3)
globalid (>= 0.3.6)
- activemodel (7.1.5.1)
- activesupport (= 7.1.5.1)
- activerecord (7.1.5.1)
- activemodel (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ activemodel (8.0.3)
+ activesupport (= 8.0.3)
+ activerecord (8.0.3)
+ activemodel (= 8.0.3)
+ activesupport (= 8.0.3)
timeout (>= 0.4.0)
- activestorage (7.1.5.1)
- actionpack (= 7.1.5.1)
- activejob (= 7.1.5.1)
- activerecord (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ activestorage (8.0.3)
+ actionpack (= 8.0.3)
+ activejob (= 8.0.3)
+ activerecord (= 8.0.3)
+ activesupport (= 8.0.3)
marcel (~> 1.0)
- activesupport (7.1.5.1)
+ activesupport (8.0.3)
base64
benchmark (>= 0.3)
bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
+ concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
- mutex_m
securerandom (>= 0.3)
- tzinfo (~> 2.0)
+ tzinfo (~> 2.0, >= 2.0.5)
+ uri (>= 0.13.1)
ast (2.4.3)
base64 (0.2.0)
benchmark (0.4.0)
@@ -92,7 +86,6 @@ GEM
drb (2.2.1)
erb (5.0.1)
erubi (1.12.0)
- execjs (2.8.1)
factory_bot (6.5.1)
activesupport (>= 6.1.0)
faker (2.20.0)
@@ -102,6 +95,10 @@ GEM
activesupport (>= 5.0)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
+ importmap-rails (2.2.2)
+ actionpack (>= 6.0.0)
+ activesupport (>= 6.0.0)
+ railties (>= 6.0.0)
io-console (0.8.0)
irb (1.15.2)
pp (>= 0.6.0)
@@ -130,7 +127,6 @@ GEM
mini_mime (1.1.2)
mini_portile2 (2.8.9)
minitest (5.25.5)
- mutex_m (0.3.0)
net-imap (0.3.9)
date
net-protocol
@@ -156,7 +152,7 @@ GEM
psych (5.2.6)
date
stringio
- puma (5.6.9)
+ puma (6.6.1)
nio4r (~> 2.0)
racc (1.8.1)
rack (2.2.14)
@@ -167,20 +163,20 @@ GEM
rackup (1.0.1)
rack (< 3)
webrick
- rails (7.1.5.1)
- actioncable (= 7.1.5.1)
- actionmailbox (= 7.1.5.1)
- actionmailer (= 7.1.5.1)
- actionpack (= 7.1.5.1)
- actiontext (= 7.1.5.1)
- actionview (= 7.1.5.1)
- activejob (= 7.1.5.1)
- activemodel (= 7.1.5.1)
- activerecord (= 7.1.5.1)
- activestorage (= 7.1.5.1)
- activesupport (= 7.1.5.1)
+ rails (8.0.3)
+ actioncable (= 8.0.3)
+ actionmailbox (= 8.0.3)
+ actionmailer (= 8.0.3)
+ actionpack (= 8.0.3)
+ actiontext (= 8.0.3)
+ actionview (= 8.0.3)
+ activejob (= 8.0.3)
+ activemodel (= 8.0.3)
+ activerecord (= 8.0.3)
+ activestorage (= 8.0.3)
+ activesupport (= 8.0.3)
bundler (>= 1.15.0)
- railties (= 7.1.5.1)
+ railties (= 8.0.3)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
@@ -188,13 +184,14 @@ GEM
rails-html-sanitizer (1.6.1)
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
- railties (7.1.5.1)
- actionpack (= 7.1.5.1)
- activesupport (= 7.1.5.1)
- irb
+ railties (8.0.3)
+ actionpack (= 8.0.3)
+ activesupport (= 8.0.3)
+ irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
+ tsort (>= 0.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.2.1)
@@ -226,17 +223,16 @@ GEM
parser (>= 3.3.7.2)
prism (~> 1.4)
ruby-progressbar (1.13.0)
- sass (3.7.4)
- sass-listen (~> 4.0.0)
- sass-listen (4.0.0)
- rb-fsevent (~> 0.9, >= 0.9.4)
- rb-inotify (~> 0.9, >= 0.9.7)
- sass-rails (5.1.0)
- railties (>= 5.2.0)
- sass (~> 3.1)
- sprockets (>= 2.8, < 4.0)
- sprockets-rails (>= 2.0, < 4.0)
- tilt (>= 1.1, < 3)
+ sass-rails (6.0.0)
+ sassc-rails (~> 2.1, >= 2.1.1)
+ sassc (2.4.0)
+ ffi (~> 1.9)
+ sassc-rails (2.1.2)
+ railties (>= 4.0.0)
+ sassc (>= 2.0)
+ sprockets (> 3.0)
+ sprockets-rails
+ tilt
securerandom (0.4.1)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
@@ -245,17 +241,27 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
+ stimulus-rails (1.3.4)
+ railties (>= 6.0.0)
stringio (3.1.7)
+ tailwindcss-rails (2.7.9)
+ railties (>= 7.0.0)
+ tailwindcss-rails (2.7.9-x86_64-linux)
+ railties (>= 7.0.0)
thor (1.3.0)
tilt (2.0.10)
timeout (0.4.3)
+ tsort (0.2.0)
+ turbo-rails (2.0.16)
+ actionpack (>= 7.1.0)
+ railties (>= 7.1.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
- uglifier (4.2.0)
- execjs (>= 0.3.0, < 3)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
+ uri (1.0.3)
+ useragent (0.16.11)
web-console (4.2.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
@@ -269,24 +275,28 @@ GEM
PLATFORMS
ruby
+ x86_64-linux-gnu
DEPENDENCIES
byebug
factory_bot
faker
+ importmap-rails (~> 2.0)
jquery-rails
listen (~> 3.0.5)
pg (~> 1.0)
- puma (~> 5.6)
- rails (~> 7.1.0)
+ puma (~> 6.0)
+ rails (~> 8.0.0)
ransack
rubocop (~> 1.29)
- sass-rails (~> 5.0)
- uglifier (>= 1.3.0)
+ sass-rails (~> 6.0)
+ stimulus-rails (~> 1.0)
+ tailwindcss-rails (~> 2.0)
+ turbo-rails (~> 2.0)
web-console
RUBY VERSION
- ruby 3.4.4p34
+ ruby 3.2.3p157
BUNDLED WITH
2.6.9
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 4f880601..704476bb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -23,7 +23,8 @@ def setup_search_form(builder)
end
def button_to_remove_fields
- tag.button "Remove", class: "remove_fields btn"
+ tag.button "Remove",
+ class: "remove_fields inline-flex items-center px-3 py-1 border border-red-300 shadow-sm text-xs font-medium rounded text-red-700 bg-white hover:bg-red-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
end
def button_to_add_fields(f, type)
@@ -32,12 +33,16 @@ def button_to_add_fields(f, type)
render(name, f: builder)
end
- tag.button button_label[type], class: "add_fields btn", 'data-field-type': type,
+ tag.button button_label[type],
+ class: "add_fields inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500",
+ 'data-field-type': type,
'data-content': "#{fields}"
end
def button_to_nest_fields(type)
- tag.button button_label[type], class: "nest_fields btn", 'data-field-type': type
+ tag.button button_label[type],
+ class: "nest_fields inline-flex items-center px-3 py-1 border border-purple-300 shadow-sm text-xs font-medium rounded text-purple-700 bg-purple-50 hover:bg-purple-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500",
+ 'data-field-type': type
end
def button_label
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 3b42a17a..4ca632a5 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -10,9 +10,11 @@ def action
def link_to_toggle_search_modes
if action_name == "advanced_search"
- link_to("Go to Simple mode", users_path)
+ link_to("← Simple Search", users_path,
+ class: "inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500")
else
- link_to("Go to Advanced mode", advanced_search_users_path)
+ link_to("Advanced Search →", advanced_search_users_path,
+ class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500")
end
end
@@ -51,9 +53,11 @@ def value_fields
end
def display_distinct_label_and_check_box
- tag.section do
- check_box_tag(:distinct, "1", user_wants_distinct_results?, class: :cbx) +
- label_tag(:distinct, "Return distinct records")
+ tag.div(class: "flex items-center space-x-2") do
+ check_box_tag(:distinct, "1", user_wants_distinct_results?,
+ class: "h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded") +
+ label_tag(:distinct, "Return distinct records",
+ class: "text-sm font-medium text-gray-700")
end
end
@@ -75,22 +79,26 @@ def display_results_header(count)
def display_sort_column_headers(search)
user_column_headers.reduce(String.new) do |string, field|
- string << (tag.th sort_link(search, field, method: action))
+ string << (tag.th sort_link(search, field, method: action),
+ class: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100")
end +
post_title_header_labels.reduce(String.new) do |str, i|
- str << (tag.th "Post #{i} title")
+ str << (tag.th "Post #{i} title",
+ class: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider")
end
end
def display_search_results(objects)
objects.limit(results_limit).reduce(String.new) do |string, object|
- string << (tag.tr display_search_results_row(object))
+ string << (tag.tr display_search_results_row(object),
+ class: "hover:bg-gray-50")
end
end
def display_search_results_row(object)
user_column_fields.reduce(String.new) do |string, field|
- string << (tag.td object.send(field))
+ string << (tag.td object.send(field),
+ class: "px-6 py-4 whitespace-nowrap text-sm text-gray-900")
end
.html_safe +
display_user_posts(object.posts)
@@ -98,7 +106,8 @@ def display_search_results_row(object)
def display_user_posts(posts)
posts.reduce(String.new) do |string, post|
- string << (tag.td truncate(post.title, length: post_title_length))
+ string << (tag.td truncate(post.title, length: post_title_length),
+ class: "px-6 py-4 whitespace-nowrap text-sm text-gray-500")
end
.html_safe
end
diff --git a/app/javascript/application.js b/app/javascript/application.js
new file mode 100644
index 00000000..4974c13d
--- /dev/null
+++ b/app/javascript/application.js
@@ -0,0 +1,10 @@
+// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
+import "@hotwired/turbo-rails"
+import "controllers"
+import "jquery"
+
+// Make jQuery available globally
+window.$ = window.jQuery = jQuery
+
+// Import search functionality
+import "./search"
\ No newline at end of file
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
new file mode 100644
index 00000000..cad9feb6
--- /dev/null
+++ b/app/javascript/controllers/application.js
@@ -0,0 +1,9 @@
+import { Application } from "@hotwired/stimulus"
+
+const application = Application.start()
+
+// Configure Stimulus development experience
+application.debug = false
+window.Stimulus = application
+
+export { application }
\ No newline at end of file
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
new file mode 100644
index 00000000..2a17d193
--- /dev/null
+++ b/app/javascript/controllers/index.js
@@ -0,0 +1,7 @@
+// Import and register all your controllers from the importmap under controllers/*
+
+import { application } from "controllers/application"
+
+// Eager load all controllers defined in the import map under controllers/**/*_controller
+import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
+eagerLoadControllersFrom("controllers", application)
\ No newline at end of file
diff --git a/app/javascript/search.js b/app/javascript/search.js
new file mode 100644
index 00000000..44540d66
--- /dev/null
+++ b/app/javascript/search.js
@@ -0,0 +1,31 @@
+class Search {
+ constructor(templates = {}) {
+ this.templates = templates;
+ }
+
+ remove_fields(button) {
+ return $(button).closest('.fields').remove();
+ }
+
+ add_fields(button, type, content) {
+ const new_id = new Date().getTime();
+ const regexp = new RegExp('new_' + type, 'g');
+ return $(button).before(content.replace(regexp, new_id));
+ }
+
+ nest_fields(button, type) {
+ const new_id = new Date().getTime();
+ const id_regexp = new RegExp('new_' + type, 'g');
+ const template = this.templates[type];
+ const object_name = $(button).closest('.fields').attr('data-object-name');
+ const sanitized_object_name = object_name.replace(/\]\[|[^-a-zA-Z0-9:.]/g, '_').replace(/_$/, '');
+ let updated_template = template.replace(/new_object_name\[/g, object_name + "[");
+ updated_template = updated_template.replace(/new_object_name_/, sanitized_object_name + '_');
+ return $(button).before(updated_template.replace(id_regexp, new_id));
+ }
+}
+
+// Make Search available globally
+window.Search = Search;
+
+export default Search;
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 78fefb6d..282f4da2 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -1,21 +1,48 @@
-<%= tag.html do %>
- <%= tag.head do %>
- <%= tag.title 'RansackDemo' %>
- <%= stylesheet_link_tag 'application' %>
- <%= javascript_include_tag 'application' %>
+
+
+ Ransack Demo
+
<%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+ <%= javascript_importmap_tags %>
+
+
+
+
- <% end %>
+
- <%= tag.body do %>
+
+
+
- <%= yield %>
+
+
+ <%= yield %>
+
- <%= tag.footer app_info %>
- <% end %>
-<% end %>
+
+
+
+
diff --git a/app/views/users/_results.erb b/app/views/users/_results.erb
index 81e75699..3495ee0e 100644
--- a/app/views/users/_results.erb
+++ b/app/views/users/_results.erb
@@ -1,6 +1,47 @@
-<%= display_query_sql(@users) %>
-<%= tag.h2 display_results_header(@users.size) %>
-<%= tag.table do %>
- <%= tag.thead display_sort_column_headers(@search), escape: false %>
- <%= tag.tbody display_search_results(@users), escape: false %>
+<% if @users.present? %>
+
+
+
+
Generated SQL Query
+
+
+
+
<%= display_query_sql(@users) %>
+
+
+
+
+
+
+
<%= display_results_header(@users.size) %>
+
+
+
+
+
+
+
+ <%= display_sort_column_headers(@search), escape: false %>
+
+
+ <%= display_search_results(@users), escape: false %>
+
+
+
+
+
+<% else %>
+
+
+
+
No users found
+
Try adjusting your search criteria.
+
+
<% end %>
diff --git a/app/views/users/advanced_search.erb b/app/views/users/advanced_search.erb
index 1913d994..946c7919 100644
--- a/app/views/users/advanced_search.erb
+++ b/app/views/users/advanced_search.erb
@@ -1,32 +1,75 @@
-<%= tag.h1 'Advanced User Search' %>
-<%= tag.header link_to_toggle_search_modes %>
+
+
+
+
+
Advanced User Search
+
Build complex search queries with custom conditions and sorting
+
+
+ <%= link_to_toggle_search_modes %>
+
+
-<%= search_form_for(
- @search,
- url: advanced_search_users_path,
- html: { method: :post }
- ) do |f| %>
+
+
+
+ <%= search_form_for(
+ @search,
+ url: advanced_search_users_path,
+ html: { method: :post, class: "space-y-8" }
+ ) do |f| %>
+
+ <% setup_search_form(f) %>
- <% setup_search_form(f) %>
+
+
+
+
Sorting Options
+ <%= button_to_add_fields(f, :sort) %>
+
+
+
+ <%= f.sort_fields do |s| %>
+
+ <%= render 'sort_fields', f: s %>
+
+ <% end %>
+
+
- <%= tag.fieldset do %>
- <%= tag.legend 'Sorting' %>
- <%= f.sort_fields do |s| %>
- <%= render 'sort_fields', f: s %>
- <% end %>
- <%= button_to_add_fields(f, :sort) %>
- <% end %>
+
+
+
+
Search Conditions
+ <%= button_to_add_fields(f, :grouping) %>
+
+
+
+ <%= f.grouping_fields do |g| %>
+
+ <%= render 'grouping_fields', f: g %>
+
+ <% end %>
+
+
- <%= tag.fieldset do %>
- <%= tag.legend 'Condition Groups' %>
- <%= f.grouping_fields do |g| %>
- <%= render 'grouping_fields', f: g %>
- <% end %>
- <%= button_to_add_fields(f, :grouping) %>
- <% end %>
+
+
+
+ <%= display_distinct_label_and_check_box %>
+
+
+
+ <%= f.submit "Execute Search",
+ class: "inline-flex items-center px-6 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
+
+
+ <% end %>
+
+
- <%= display_distinct_label_and_check_box %>
- <%= f.submit class: 'btn btn--invert' %>
-<% end %>
-
-<%= render 'results' %>
+
+ <%= render 'results' %>
+
diff --git a/app/views/users/index.erb b/app/views/users/index.erb
index 9b9d6bb8..875d8f8c 100644
--- a/app/views/users/index.erb
+++ b/app/views/users/index.erb
@@ -1,39 +1,75 @@
-<%= tag.h1 'Search Users' %>
-<%= tag.header link_to_toggle_search_modes %>
+
+
+
+
+
Search Users
+
Find users by name, email, or their posts
+
+
+ <%= link_to_toggle_search_modes %>
+
+
-<%= search_form_for @search do |f| %>
+
+
+
+ <%= search_form_for @search, local: true, class: "space-y-6" do |f| %>
+
+
+
+
User Information
+
+
+ <%= f.label :first_name_or_last_name_cont, "Name", class: "block text-sm font-medium text-gray-700 mb-1" %>
+ <%= f.text_field :first_name_or_last_name_cont,
+ class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm",
+ placeholder: "Search by first or last name" %>
+
+
+ <%= f.label :email_cont, "Email", class: "block text-sm font-medium text-gray-700 mb-1" %>
+ <%= f.text_field :email_cont,
+ class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm",
+ placeholder: "Search by email address" %>
+
+
+
- <%= tag.fieldset do %>
- <%= tag.legend 'User' %>
- <%= tag.ul do %>
- <%= tag.li do %>
- <%= f.label :first_name_or_last_name_cont %>
- <%= f.text_field :first_name_or_last_name_cont %>
- <% end %>
- <%= tag.li do %>
- <%= f.label :email_cont %>
- <%= f.text_field :email_cont %>
- <% end %>
- <% end %>
- <% end %>
+
+
+
User's Posts
+
+
+ <%= f.label :posts_title_cont, "Post Title", class: "block text-sm font-medium text-gray-700 mb-1" %>
+ <%= f.text_field :posts_title_cont,
+ class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm",
+ placeholder: "Search by post title" %>
+
+
+ <%= f.label :other_posts_title_cont, "Other Posts Title", class: "block text-sm font-medium text-gray-700 mb-1" %>
+ <%= f.text_field :other_posts_title_cont,
+ class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm",
+ placeholder: "Search by other post titles" %>
+
+
+
- <%= tag.fieldset do %>
- <%= tag.legend "User's Posts" %>
- <%= tag.ul do %>
- <%= tag.li do %>
- <%= f.label :posts_title_cont %>
- <%= f.text_field :posts_title_cont %>
+
+
+
+ <%= display_distinct_label_and_check_box %>
+
+
+
+ <%= f.submit "Search Users",
+ class: "inline-flex items-center px-6 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
+
+
<% end %>
+
+
- <%= tag.li do %>
- <%= f.label :other_posts_title_cont %>
- <%= f.text_field :other_posts_title_cont %>
- <% end %>
- <% end %>
- <% end %>
-
- <%= display_distinct_label_and_check_box %>
- <%= f.submit class: 'btn btn--invert' %>
-<% end %>
-
-<%= render 'results' %>
+
+ <%= render 'results' %>
+
diff --git a/config/importmap.rb b/config/importmap.rb
new file mode 100644
index 00000000..9f14511f
--- /dev/null
+++ b/config/importmap.rb
@@ -0,0 +1,8 @@
+# Pin npm packages by running ./bin/importmap
+
+pin "application", preload: true
+pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.7.1/dist/jquery.js"
+pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.2/dist/stimulus.js"
+pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
+pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
+pin_all_from "app/javascript/controllers", under: "controllers"
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
new file mode 100644
index 00000000..9c50e1c8
--- /dev/null
+++ b/test/application_system_test_case.rb
@@ -0,0 +1,5 @@
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
+end
\ No newline at end of file
diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml
index 78577cd2..f4a3beac 100644
--- a/test/fixtures/posts.yml
+++ b/test/fixtures/posts.yml
@@ -1,11 +1,21 @@
# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
-one:
- user:
- title: MyString
- body: MyText
-
-two:
- user:
- title: MyString
- body: MyText
+ruby_basics:
+ user: john
+ title: Ruby Basics
+ body: An introduction to Ruby programming language
+
+rails_intro:
+ user: jane
+ title: Rails Introduction
+ body: Getting started with Ruby on Rails framework
+
+advanced_tips:
+ user: john
+ title: Advanced Tips
+ body: Advanced techniques for Ruby and Rails development
+
+design_patterns:
+ user: bob
+ title: Design Patterns
+ body: Common design patterns in Ruby applications
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index f3af21e5..1247e936 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -1,13 +1,19 @@
# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
-one:
- first_name: MyString
- last_name: MyString
- email: MyString
+john:
+ first_name: John
+ last_name: Doe
+ email: john.doe@example.com
password_digest: MyString
-two:
- first_name: MyString
- last_name: MyString
- email: MyString
+jane:
+ first_name: Jane
+ last_name: Smith
+ email: jane.smith@example.com
+ password_digest: MyString
+
+bob:
+ first_name: Bob
+ last_name: Johnson
+ email: bob.johnson@example.com
password_digest: MyString
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
index eee25744..a367205d 100644
--- a/test/functional/users_controller_test.rb
+++ b/test/functional/users_controller_test.rb
@@ -4,10 +4,75 @@ class UsersControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
+ assert_not_nil assigns(:search)
+ assert_not_nil assigns(:users)
end
test "should get advanced search" do
get :advanced_search
assert_response :success
+ assert_not_nil assigns(:search)
+ assert_not_nil assigns(:users)
+ assert assigns(:search).groupings.any?, "Should have at least one grouping"
+ end
+
+ test "should handle search with name parameter" do
+ get :index, params: { q: { first_name_or_last_name_cont: "John" } }
+ assert_response :success
+ assert_not_nil assigns(:users)
+ # The search should work and return results
+ assert assigns(:users).count >= 0
+ end
+
+ test "should handle search with email parameter" do
+ get :index, params: { q: { email_cont: "example.com" } }
+ assert_response :success
+ assert_not_nil assigns(:users)
+ end
+
+ test "should handle search with posts title parameter" do
+ get :index, params: { q: { posts_title_cont: "Ruby" } }
+ assert_response :success
+ assert_not_nil assigns(:users)
+ end
+
+ test "should handle distinct parameter" do
+ get :index, params: { distinct: "1", q: { first_name_cont: "John" } }
+ assert_response :success
+ assert_not_nil assigns(:users)
+ end
+
+ test "should handle advanced search with complex parameters" do
+ post :advanced_search, params: {
+ q: {
+ groupings: {
+ "0" => {
+ conditions: {
+ "0" => {
+ a: { "0" => { name: "first_name" } },
+ p: "cont",
+ v: { "0" => { value: "John" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ assert_response :success
+ assert_not_nil assigns(:search)
+ assert_not_nil assigns(:users)
+ end
+
+ test "should handle empty search parameters gracefully" do
+ get :index, params: { q: {} }
+ assert_response :success
+ assert_not_nil assigns(:users)
+ end
+
+ test "should handle POST to advanced search" do
+ post :advanced_search
+ assert_response :success
+ assert_not_nil assigns(:search)
+ assert_not_nil assigns(:users)
end
end
diff --git a/test/integration/users_integration_test.rb b/test/integration/users_integration_test.rb
new file mode 100644
index 00000000..7cdc21d8
--- /dev/null
+++ b/test/integration/users_integration_test.rb
@@ -0,0 +1,97 @@
+require "test_helper"
+
+class UsersIntegrationTest < ActionDispatch::IntegrationTest
+ test "can access index page" do
+ get users_path
+ assert_response :success
+ assert_select "h1", "Search Users"
+ end
+
+ test "can access advanced search page" do
+ get advanced_search_users_path
+ assert_response :success
+ assert_select "h1", "Advanced User Search"
+ end
+
+ test "can post to advanced search" do
+ post advanced_search_users_path
+ assert_response :success
+ assert_select "h1", "Advanced User Search"
+ end
+
+ test "search with parameters works" do
+ get users_path, params: { q: { first_name_cont: "John" } }
+ assert_response :success
+ assert_select "table" # Should show results table
+ end
+
+ test "search with distinct parameter works" do
+ get users_path, params: {
+ q: { first_name_cont: "John" },
+ distinct: "1"
+ }
+ assert_response :success
+ assert_select "table"
+ end
+
+ test "advanced search with complex parameters works" do
+ post advanced_search_users_path, params: {
+ q: {
+ groupings: {
+ "0" => {
+ conditions: {
+ "0" => {
+ a: { "0" => { name: "first_name" } },
+ p: "cont",
+ v: { "0" => { value: "John" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ assert_response :success
+ assert_select "table"
+ end
+
+ test "root path redirects to users index" do
+ get root_path
+ assert_response :success
+ assert_select "h1", "Search Users"
+ end
+
+ test "page contains required elements" do
+ get users_path
+ assert_response :success
+
+ # Check for form elements
+ assert_select "form"
+ assert_select "input[type=text]", minimum: 2
+ assert_select "input[type=checkbox]"
+ assert_select "input[type=submit]"
+
+ # Check for navigation
+ assert_select "a", text: /Advanced Search/
+
+ # Check for footer
+ assert_select "footer"
+ assert_select "a[href*='github.com']"
+ end
+
+ test "advanced search page contains required elements" do
+ get advanced_search_users_path
+ assert_response :success
+
+ # Check for form elements
+ assert_select "form"
+ assert_select "input[type=submit]"
+ assert_select "input[type=checkbox]"
+
+ # Check for navigation
+ assert_select "a", text: /Simple Search/
+
+ # Check for dynamic sections
+ assert_select "button", text: /Add Sort/
+ assert_select "button", text: /Add Condition Group/
+ end
+end
\ No newline at end of file
diff --git a/test/system/users_test.rb b/test/system/users_test.rb
new file mode 100644
index 00000000..432779a7
--- /dev/null
+++ b/test/system/users_test.rb
@@ -0,0 +1,93 @@
+require "application_system_test_case"
+
+class UsersTest < ApplicationSystemTestCase
+ test "visiting the index" do
+ visit users_url
+
+ assert_selector "h1", text: "Search Users"
+ assert_selector "form"
+ assert_selector "input[placeholder*='Search by first or last name']"
+ assert_selector "input[placeholder*='Search by email address']"
+ end
+
+ test "performing a simple search" do
+ visit users_url
+
+ # Search by name
+ fill_in "Search by first or last name", with: "John"
+ click_on "Search Users"
+
+ # Should stay on the same page and show results
+ assert_current_path users_path
+ assert_selector "table" # Results table should be present
+ end
+
+ test "navigating to advanced search" do
+ visit users_url
+
+ click_on "Advanced Search"
+
+ assert_current_path advanced_search_users_path
+ assert_selector "h1", text: "Advanced User Search"
+ assert_selector ".bg-green-50", text: "Sorting Options"
+ assert_selector ".bg-purple-50", text: "Search Conditions"
+ end
+
+ test "performing an advanced search" do
+ visit advanced_search_users_path
+
+ # Should have default condition groups
+ assert_selector ".bg-white.rounded-md", minimum: 1
+
+ # Execute search
+ click_on "Execute Search"
+
+ # Should show results
+ assert_selector "table" # Results table should be present
+ end
+
+ test "toggling between search modes" do
+ # Start at simple search
+ visit users_url
+ assert_selector "h1", text: "Search Users"
+
+ # Go to advanced search
+ click_on "Advanced Search"
+ assert_selector "h1", text: "Advanced User Search"
+
+ # Go back to simple search
+ click_on "Simple Search"
+ assert_selector "h1", text: "Search Users"
+ end
+
+ test "distinct checkbox functionality" do
+ visit users_url
+
+ # Checkbox should be present
+ assert_selector "input[type=checkbox]"
+ assert_text "Return distinct records"
+
+ # Check the box and submit
+ check "Return distinct records"
+ click_on "Search Users"
+
+ # Should stay on the same page
+ assert_current_path users_path
+ end
+
+ test "page layout and styling" do
+ visit users_url
+
+ # Check for modern layout elements
+ assert_selector "header.bg-white.shadow"
+ assert_selector "main.max-w-7xl"
+ assert_selector "footer.bg-white.border-t"
+
+ # Check for TailwindCSS classes in form
+ assert_selector ".bg-gray-50.rounded-lg" # User Information section
+ assert_selector ".bg-blue-50.rounded-lg" # User's Posts section
+
+ # Check for modern button styling
+ assert_selector ".bg-indigo-600", text: "Advanced Search"
+ end
+end
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 0ef9c58b..e5bd2435 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -3,6 +3,9 @@
require "rails/test_help"
class ActiveSupport::TestCase
+ # Run tests in parallel with specified workers
+ parallelize(workers: :number_of_processors)
+
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
@@ -10,4 +13,21 @@ class ActiveSupport::TestCase
fixtures :all
# Add more helper methods to be used by all tests here...
+
+ private
+
+ # Helper method to simulate controller action name for helper tests
+ def stub_action_name(name)
+ controller = UsersController.new
+ controller.action_name = name
+ @controller = controller
+ end
+end
+
+class ActionController::TestCase
+ include Devise::Test::ControllerHelpers if defined?(Devise)
+end
+
+class ActionDispatch::IntegrationTest
+ include Devise::Test::IntegrationHelpers if defined?(Devise)
end
diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb
new file mode 100644
index 00000000..40c25c36
--- /dev/null
+++ b/test/unit/helpers/application_helper_test.rb
@@ -0,0 +1,44 @@
+require "test_helper"
+
+class ApplicationHelperTest < ActionView::TestCase
+ include ApplicationHelper
+
+ test "button_to_remove_fields generates correct button" do
+ button = button_to_remove_fields
+ assert_includes button, "Remove"
+ assert_includes button, "remove_fields"
+ assert_includes button, "border-red-300"
+ assert_includes button, "text-red-700"
+ end
+
+ test "button_to_nest_fields generates correct button" do
+ button = button_to_nest_fields(:grouping)
+ assert_includes button, "Add Condition Group"
+ assert_includes button, "nest_fields"
+ assert_includes button, "border-purple-300"
+ assert_includes button, "text-purple-700"
+ assert_includes button, 'data-field-type="grouping"'
+ end
+
+ test "button_label returns correct labels" do
+ labels = button_label
+ assert_equal "Add Value", labels[:value]
+ assert_equal "Add Condition", labels[:condition]
+ assert_equal "Add Sort", labels[:sort]
+ assert_equal "Add Condition Group", labels[:grouping]
+ end
+
+ test "app_info contains correct information" do
+ info = app_info
+ assert_includes info, "Ransack demo app"
+ assert_includes info, "Rails"
+ assert_includes info, "Ruby"
+ assert_includes info, "Source code for this demo available on GitHub"
+ end
+
+ test "source_code_link returns correct link" do
+ link = source_code_link
+ assert_includes link, "Source code for this demo available on GitHub"
+ assert_includes link, "https://github.com/activerecord-hackery/ransack_demo"
+ end
+end
\ No newline at end of file
diff --git a/test/unit/helpers/users_helper_test.rb b/test/unit/helpers/users_helper_test.rb
index 774d33fe..e452dc51 100644
--- a/test/unit/helpers/users_helper_test.rb
+++ b/test/unit/helpers/users_helper_test.rb
@@ -1,4 +1,78 @@
require "test_helper"
class UsersHelperTest < ActionView::TestCase
+ include UsersHelper
+
+ def setup
+ @controller = UsersController.new
+ @controller.action_name = "index"
+ end
+
+ test "link_to_toggle_search_modes shows advanced link for index" do
+ @controller.action_name = "index"
+ link = link_to_toggle_search_modes
+ assert_includes link, "Advanced Search"
+ assert_includes link, "bg-indigo-600"
+ end
+
+ test "link_to_toggle_search_modes shows simple link for advanced_search" do
+ @controller.action_name = "advanced_search"
+ link = link_to_toggle_search_modes
+ assert_includes link, "Simple Search"
+ assert_includes link, "border-gray-300"
+ end
+
+ test "action returns correct method" do
+ @controller.action_name = "index"
+ assert_equal :get, action
+
+ @controller.action_name = "advanced_search"
+ assert_equal :post, action
+ end
+
+ test "display_distinct_label_and_check_box renders correctly" do
+ result = display_distinct_label_and_check_box
+ assert_includes result, 'type="checkbox"'
+ assert_includes result, "Return distinct records"
+ assert_includes result, "text-indigo-600"
+ end
+
+ test "user_wants_distinct_results? works with params" do
+ params[:distinct] = "1"
+ assert user_wants_distinct_results?
+
+ params[:distinct] = "0"
+ assert_not user_wants_distinct_results?
+
+ params[:distinct] = nil
+ assert_not user_wants_distinct_results?
+ end
+
+ test "display_results_header handles different counts" do
+ header = display_results_header(5)
+ assert_equal "Your 5 results", header
+
+ header = display_results_header(1)
+ assert_equal "Your 1 result", header
+
+ header = display_results_header(15)
+ assert_equal "Your first 10 results out of 15 total", header
+ end
+
+ test "display_query_sql formats SQL correctly" do
+ users = User.limit(5)
+ result = display_query_sql(users)
+ assert_includes result, "SQL:"
+ assert_includes result, users.to_sql
+ end
+
+ private
+
+ def params
+ @params ||= {}
+ end
+
+ def action_name
+ @controller.action_name
+ end
end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 5c07f490..0e6e55ad 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -1,7 +1,74 @@
require "test_helper"
class UserTest < ActiveSupport::TestCase
- # test "the truth" do
- # assert true
- # end
+ def setup
+ @user = users(:john)
+ end
+
+ test "should have valid fixtures" do
+ assert @user.valid?
+ assert_equal "John", @user.first_name
+ assert_equal "Doe", @user.last_name
+ assert_equal "john.doe@example.com", @user.email
+ end
+
+ test "should have associations" do
+ assert_respond_to @user, :posts
+ assert_respond_to @user, :other_posts
+ assert_respond_to @user, :comments
+ assert_respond_to @user, :roles
+ end
+
+ test "should format datetime correctly" do
+ assert_match %r{\d{2}/\d{2}/\d{2} \d{2}:\d{2}}, @user.created
+ assert_match %r{\d{2}/\d{2}/\d{2} \d{2}:\d{2}}, @user.updated
+ end
+
+ test "should have ransackable attributes" do
+ ransackable_attrs = User.ransackable_attributes
+ assert_includes ransackable_attrs, "first_name"
+ assert_includes ransackable_attrs, "last_name"
+ assert_includes ransackable_attrs, "email"
+ assert_includes ransackable_attrs, "full_name"
+ assert_not_includes ransackable_attrs, "password_digest"
+ end
+
+ test "should have ransortable attributes" do
+ ransortable_attrs = User.ransortable_attributes
+ assert_includes ransortable_attrs, "first_name"
+ assert_includes ransortable_attrs, "last_name"
+ assert_includes ransortable_attrs, "email"
+ assert_not_includes ransortable_attrs, "password_digest"
+ end
+
+ test "should have ransackable associations" do
+ ransackable_assocs = User.ransackable_associations
+ assert_includes ransackable_assocs, "posts"
+ assert_includes ransackable_assocs, "other_posts"
+ end
+
+ test "should search by full name using ransacker" do
+ search = User.ransack(full_name_cont: "John Doe")
+ results = search.result
+ assert_includes results, @user
+ end
+
+ test "should search by first or last name" do
+ search = User.ransack(first_name_or_last_name_cont: "John")
+ results = search.result
+ assert_includes results, @user
+ end
+
+ test "should search by email" do
+ search = User.ransack(email_cont: "john.doe")
+ results = search.result
+ assert_includes results, @user
+ end
+
+ test "should get postgres version" do
+ version = User.postgres_version
+ assert_not_nil version
+ assert_kind_of String, version
+ assert version.length > 0
+ end
end