rs-activeadmin-searchable_select 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.actrc +20 -0
- data/.claude/commands/fix-tests.md +203 -0
- data/.github/workflows/ci.yml +170 -0
- data/.github/workflows/npm-publish.yml +47 -0
- data/.gitignore +27 -0
- data/.npmignore +58 -0
- data/.rspec +2 -0
- data/.rubocop.yml +67 -0
- data/.yardopts +2 -0
- data/AGENTS.md +39 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +24 -0
- data/CLAUDE.md +104 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +366 -0
- data/LICENSE.txt +25 -0
- data/README.md +439 -0
- data/Rakefile +4 -0
- data/bin/rspec +17 -0
- data/config/database.yml +16 -0
- data/config.ru +28 -0
- data/docs/activeadmin-4-detailed-reference.md +932 -0
- data/docs/activeadmin-4-gem-migration-guide.md +313 -0
- data/docs/combustion.md +213 -0
- data/docs/for-next-session.md +199 -0
- data/docs/guide-update-your-app.md +213 -0
- data/docs/propshaft-readme.md +121 -0
- data/docs/propshaft-upgrade.md +267 -0
- data/docs/rails-7-asset-pipeline.md +279 -0
- data/docs/setup-activeadmin-app.md +552 -0
- data/docs/setup-activeadmin-gem.md +535 -0
- data/docs/upload-system.md +225 -0
- data/gemfiles/rails_7.x_active_admin_4.x.gemfile +11 -0
- data/gemfiles/rails_7.x_active_admin_4.x.gemfile.lock +371 -0
- data/gemfiles/rails_8.x_active_admin_4.x.gemfile +11 -0
- data/gemfiles/rails_8.x_active_admin_4.x.gemfile.lock +366 -0
- data/lefthook.yml +17 -0
- data/lib/activeadmin/inputs/filters/searchable_select_input.rb +13 -0
- data/lib/activeadmin/inputs/searchable_select_input.rb +11 -0
- data/lib/activeadmin/searchable_select/engine.rb +17 -0
- data/lib/activeadmin/searchable_select/option_collection.rb +119 -0
- data/lib/activeadmin/searchable_select/resource_dsl_extension.rb +56 -0
- data/lib/activeadmin/searchable_select/resource_extension.rb +10 -0
- data/lib/activeadmin/searchable_select/select_input_extension.rb +159 -0
- data/lib/activeadmin/searchable_select/version.rb +5 -0
- data/lib/activeadmin/searchable_select.rb +20 -0
- data/lib/activeadmin-searchable_select.rb +4 -0
- data/lib/generators/active_admin/searchable_select/install/install_generator.rb +217 -0
- data/package-lock.json +18 -0
- data/package.json +45 -0
- data/rs-activeadmin-searchable_select.gemspec +38 -0
- data/sonar-project.properties +25 -0
- data/spec/features/ajax_params_spec.rb +31 -0
- data/spec/features/end_to_end_spec.rb +227 -0
- data/spec/features/filter_input_spec.rb +137 -0
- data/spec/features/form_input_spec.rb +122 -0
- data/spec/features/inline_ajax_setting_spec.rb +26 -0
- data/spec/features/input_errors_spec.rb +76 -0
- data/spec/features/input_html_options_spec.rb +30 -0
- data/spec/features/options_dsl_spec.rb +220 -0
- data/spec/features/production_build_spec.rb +108 -0
- data/spec/internal/app/admin/categories.rb +26 -0
- data/spec/internal/app/admin/dashboard.rb +29 -0
- data/spec/internal/app/admin/option_types.rb +19 -0
- data/spec/internal/app/admin/option_values.rb +30 -0
- data/spec/internal/app/admin/posts.rb +27 -0
- data/spec/internal/app/admin/products.rb +22 -0
- data/spec/internal/app/admin/rgb_colors.rb +25 -0
- data/spec/internal/app/admin/tag_names.rb +21 -0
- data/spec/internal/app/admin/test_ajax_params_category.rb +10 -0
- data/spec/internal/app/admin/test_ajax_params_post.rb +20 -0
- data/spec/internal/app/admin/test_form_post_class.rb +7 -0
- data/spec/internal/app/admin/test_form_post_custom.rb +11 -0
- data/spec/internal/app/admin/test_form_post_resource.rb +11 -0
- data/spec/internal/app/admin/test_form_post_resource_custom.rb +12 -0
- data/spec/internal/app/admin/test_inline_ajax_post.rb +9 -0
- data/spec/internal/app/admin/test_input_html_post.rb +11 -0
- data/spec/internal/app/admin/test_posts_display_text.rb +9 -0
- data/spec/internal/app/admin/test_posts_filter.rb +9 -0
- data/spec/internal/app/admin/test_posts_named.rb +9 -0
- data/spec/internal/app/admin/test_posts_pagination.rb +9 -0
- data/spec/internal/app/admin/test_posts_payload_lambda.rb +11 -0
- data/spec/internal/app/admin/test_posts_payload_proc.rb +9 -0
- data/spec/internal/app/admin/test_posts_scope_lambda.rb +8 -0
- data/spec/internal/app/admin/test_posts_scope_params.rb +8 -0
- data/spec/internal/app/admin/test_posts_scope_user.rb +8 -0
- data/spec/internal/app/admin/test_posts_text_attr.rb +5 -0
- data/spec/internal/app/admin/users.rb +23 -0
- data/spec/internal/app/admin/variants.rb +31 -0
- data/spec/internal/app/assets/config/manifest.js +1 -0
- data/spec/internal/app/assets/javascripts/active_admin.js +2 -0
- data/spec/internal/app/assets/javascripts/searchable_select_test.js +2 -0
- data/spec/internal/app/controllers/application_controller.rb +5 -0
- data/spec/internal/app/css/active_admin_source.css +81 -0
- data/spec/internal/app/js/active_admin.js +17 -0
- data/spec/internal/app/models/article.rb +12 -0
- data/spec/internal/app/models/category.rb +12 -0
- data/spec/internal/app/models/internal/tag_name.rb +14 -0
- data/spec/internal/app/models/internal_tag_name.rb +11 -0
- data/spec/internal/app/models/option_type.rb +12 -0
- data/spec/internal/app/models/option_value.rb +4 -0
- data/spec/internal/app/models/post.rb +15 -0
- data/spec/internal/app/models/product.rb +12 -0
- data/spec/internal/app/models/rgb_color.rb +16 -0
- data/spec/internal/app/models/user.rb +12 -0
- data/spec/internal/app/models/variant.rb +12 -0
- data/spec/internal/build_activeadmin_css.js +115 -0
- data/spec/internal/config/database.yml +7 -0
- data/spec/internal/config/environment.rb +48 -0
- data/spec/internal/config/initializers/active_admin.rb +53 -0
- data/spec/internal/config/initializers/assets.rb +9 -0
- data/spec/internal/config/initializers/searchable_select.rb +6 -0
- data/spec/internal/config/routes.rb +4 -0
- data/spec/internal/config.ru +4 -0
- data/spec/internal/db/schema.rb +63 -0
- data/spec/internal/db/seeds.rb +88 -0
- data/spec/internal/esbuild.config.js +30 -0
- data/spec/internal/inject-jquery.js +4 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/package/LICENSE.txt +25 -0
- data/spec/internal/package/README.md +439 -0
- data/spec/internal/package/package.json +45 -0
- data/spec/internal/package/src/index.js +1 -0
- data/spec/internal/package/src/searchable_select/init.js +1 -0
- data/spec/internal/package/src/searchable_select.css +1 -0
- data/spec/internal/package/src/searchable_select.scss +1 -0
- data/spec/internal/package-lock.json +1385 -0
- data/spec/internal/package.json +26 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/internal/spec/internal/app/css/active_admin_source.css +38 -0
- data/spec/internal/spec/internal/log/test.log +0 -0
- data/spec/internal/tailwind-active_admin.config.js +53 -0
- data/spec/rails_helper.rb +86 -0
- data/spec/spec_helper.rb +137 -0
- data/spec/support/active_admin_helpers.rb +17 -0
- data/spec/support/capybara.rb +8 -0
- data/spec/support/models.rb +88 -0
- data/spec/support/pluck_polyfill.rb +12 -0
- data/spec/support/reset_settings.rb +5 -0
- data/src/index.js +77 -0
- data/src/searchable_select/init.js +58 -0
- data/src/searchable_select.css +5 -0
- data/src/searchable_select.css.map +1 -0
- metadata +405 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/models'
|
4
|
+
require 'support/capybara'
|
5
|
+
|
6
|
+
RSpec.describe 'end to end', type: :feature, js: true do
|
7
|
+
context 'class name without namespaces' do
|
8
|
+
# Using static admin/posts.rb and admin/categories.rb files
|
9
|
+
|
10
|
+
describe 'index page with searchable select filter' do
|
11
|
+
it 'loads filter input options' do
|
12
|
+
Category.create(name: 'Music')
|
13
|
+
Category.create(name: 'Travel')
|
14
|
+
|
15
|
+
visit '/admin/posts'
|
16
|
+
|
17
|
+
expand_select_box
|
18
|
+
wait_for_ajax
|
19
|
+
|
20
|
+
expect(select_box_items).to eq(%w[Music Travel])
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'allows filtering options by term' do
|
24
|
+
Category.create(name: 'Music')
|
25
|
+
Category.create(name: 'Travel')
|
26
|
+
|
27
|
+
visit '/admin/posts'
|
28
|
+
|
29
|
+
expand_select_box
|
30
|
+
enter_search_term('T')
|
31
|
+
wait_for_ajax
|
32
|
+
|
33
|
+
expect(select_box_items).to eq(%w[Travel])
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'loads more items when scrolling down' do
|
37
|
+
15.times { |i| Category.create(name: "Category #{i}") }
|
38
|
+
visit '/admin/posts'
|
39
|
+
|
40
|
+
expand_select_box
|
41
|
+
wait_for_ajax
|
42
|
+
scroll_select_box_list
|
43
|
+
wait_for_ajax
|
44
|
+
|
45
|
+
expect(select_box_items.size).to eq(15)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'class name with namespace' do
|
51
|
+
# Using static admin/colors.rb and admin/tag_names.rb files
|
52
|
+
|
53
|
+
describe 'index page with searchable select filter' do
|
54
|
+
it 'loads filter input options' do
|
55
|
+
RgbColor.create(code: '#eac112', description: 'Orange')
|
56
|
+
RgbColor.create(code: '#19bf25', description: 'Green')
|
57
|
+
|
58
|
+
visit '/admin/tag_names'
|
59
|
+
|
60
|
+
expand_select_box
|
61
|
+
wait_for_ajax
|
62
|
+
|
63
|
+
expect(select_box_items).to eq(['#eac112 - Orange', '#19bf25 - Green'])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'class with nested belongs_to association',
|
69
|
+
skip: 'Nested routes issue with belongs_to in test environment' do
|
70
|
+
# Using static admin files: option_types.rb, products.rb, option_values.rb, variants.rb
|
71
|
+
|
72
|
+
describe 'new page with searchable select filter' do
|
73
|
+
it 'loads filter input options' do
|
74
|
+
option_type = OptionType.create(name: 'Color')
|
75
|
+
ot = OptionType.create(name: 'Size')
|
76
|
+
OptionValue.create(value: 'Black', option_type: option_type)
|
77
|
+
OptionValue.create(value: 'Orange', option_type: option_type)
|
78
|
+
OptionValue.create(value: 'M', option_type: ot)
|
79
|
+
product = Product.create(name: 'Cap', option_type: option_type)
|
80
|
+
|
81
|
+
visit "/admin/products/#{product.id}/variants/new"
|
82
|
+
|
83
|
+
# Debug: Check page loaded
|
84
|
+
puts "Page title: #{page.title}"
|
85
|
+
puts "Current path: #{page.current_path}"
|
86
|
+
|
87
|
+
# Check for error messages
|
88
|
+
error_on_page = page.has_css?('h1', text: 'Action Controller: Exception caught')
|
89
|
+
if error_on_page || page.title.include?('Exception')
|
90
|
+
puts 'ERROR ON PAGE!'
|
91
|
+
puts "Error heading: #{begin
|
92
|
+
find('h1').text
|
93
|
+
rescue StandardError
|
94
|
+
'Could not find h1'
|
95
|
+
end}"
|
96
|
+
puts "Error message: #{begin
|
97
|
+
find('pre').text
|
98
|
+
rescue StandardError
|
99
|
+
'Could not find pre'
|
100
|
+
end}"
|
101
|
+
# Try to get the full body for debugging
|
102
|
+
puts page.body[0..2000] if page.body.length < 2001
|
103
|
+
end
|
104
|
+
|
105
|
+
# Save screenshot for debugging (commented out for CI)
|
106
|
+
# begin
|
107
|
+
# page.save_screenshot('/tmp/variant_new_page.png')
|
108
|
+
# rescue StandardError
|
109
|
+
# nil
|
110
|
+
# end
|
111
|
+
|
112
|
+
# Check for any select elements
|
113
|
+
puts "Has any select elements: #{page.has_css?('select', visible: :all)}"
|
114
|
+
puts "Has form elements: #{page.has_css?('form')}"
|
115
|
+
|
116
|
+
# Debug: Check if searchable select input exists
|
117
|
+
searchable_select_css = '.searchable-select-input'
|
118
|
+
expect(page).to have_css(searchable_select_css, wait: 5)
|
119
|
+
|
120
|
+
# Debug: Check what Select2-related elements are present
|
121
|
+
puts "Page HTML includes .select2-container: #{page.has_css?('.select2-container',
|
122
|
+
wait: 2)}"
|
123
|
+
puts "Page HTML includes .select2: #{page.has_css?('.select2', wait: 2)}"
|
124
|
+
has_searchable = page.has_css?(searchable_select_css)
|
125
|
+
puts "Page HTML includes searchable-select-input: #{has_searchable}"
|
126
|
+
|
127
|
+
# Debug: Print the actual HTML around the select
|
128
|
+
if page.has_css?(searchable_select_css)
|
129
|
+
select_element = find(searchable_select_css, visible: :all)
|
130
|
+
parent_html = begin
|
131
|
+
select_element.find(:xpath,
|
132
|
+
'..')['outerHTML'][0..500]
|
133
|
+
rescue StandardError
|
134
|
+
'Could not get parent HTML'
|
135
|
+
end
|
136
|
+
puts "Parent element HTML: #{parent_html}..."
|
137
|
+
end
|
138
|
+
|
139
|
+
expand_select_box
|
140
|
+
wait_for_ajax
|
141
|
+
|
142
|
+
expect(select_box_items).to eq(%w[Black Orange])
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'allows filtering options by term' do
|
146
|
+
option_type = OptionType.create(name: 'Color')
|
147
|
+
ot = OptionType.create(name: 'Size')
|
148
|
+
OptionValue.create(value: 'Black', option_type: option_type)
|
149
|
+
OptionValue.create(value: 'Orange', option_type: option_type)
|
150
|
+
OptionValue.create(value: 'M', option_type: ot)
|
151
|
+
product = Product.create(name: 'Cap', option_type: option_type)
|
152
|
+
|
153
|
+
visit "/admin/products/#{product.id}/variants/new"
|
154
|
+
|
155
|
+
expand_select_box
|
156
|
+
enter_search_term('O')
|
157
|
+
wait_for_ajax
|
158
|
+
|
159
|
+
expect(select_box_items).to eq(%w[Orange])
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'loads more items when scrolling down' do
|
163
|
+
option_type = OptionType.create(name: 'Color')
|
164
|
+
15.times { |i| OptionValue.create(value: "Black #{i}", option_type: option_type) }
|
165
|
+
product = Product.create(name: 'Cap', option_type: option_type)
|
166
|
+
|
167
|
+
visit "/admin/products/#{product.id}/variants/new"
|
168
|
+
|
169
|
+
expand_select_box
|
170
|
+
wait_for_ajax
|
171
|
+
scroll_select_box_list
|
172
|
+
wait_for_ajax
|
173
|
+
|
174
|
+
expect(select_box_items.size).to eq(15)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'edit page with searchable select filter' do
|
179
|
+
it 'preselects item' do
|
180
|
+
option_type = OptionType.create(name: 'Color')
|
181
|
+
ot = OptionType.create(name: 'Size')
|
182
|
+
option_value = OptionValue.create(value: 'Black', option_type: option_type)
|
183
|
+
OptionValue.create(value: 'Orange', option_type: option_type)
|
184
|
+
OptionValue.create(value: 'M', option_type: ot)
|
185
|
+
product = Product.create(name: 'Cap', option_type: option_type)
|
186
|
+
variant = Variant.create(product: product, option_value: option_value)
|
187
|
+
|
188
|
+
visit "/admin/products/#{product.id}/variants/#{variant.id}/edit"
|
189
|
+
|
190
|
+
expect(select_box_selected_item_text).to eq('Black')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def expand_select_box
|
196
|
+
# Just click the first select2 container on the page
|
197
|
+
# This works for both filter forms and regular forms
|
198
|
+
find('.select2-container', match: :first).click
|
199
|
+
end
|
200
|
+
|
201
|
+
def enter_search_term(term)
|
202
|
+
find('.select2-dropdown input').send_keys(term)
|
203
|
+
end
|
204
|
+
|
205
|
+
def scroll_select_box_list
|
206
|
+
page.execute_script '$(".select2-container ul").scrollTop(1000)'
|
207
|
+
end
|
208
|
+
|
209
|
+
def select_box_items
|
210
|
+
all('.select2-dropdown li').map(&:text)
|
211
|
+
end
|
212
|
+
|
213
|
+
def select_box_selected_item_text
|
214
|
+
find('.select2-selection').text
|
215
|
+
end
|
216
|
+
|
217
|
+
def wait_for_ajax
|
218
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
219
|
+
sleep 0.1
|
220
|
+
loop until finished_all_ajax_requests?
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def finished_all_ajax_requests?
|
225
|
+
page.evaluate_script('jQuery.active').zero?
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/models'
|
4
|
+
require 'support/capybara'
|
5
|
+
require 'support/active_admin_helpers'
|
6
|
+
|
7
|
+
RSpec.describe 'filter input', type: :request do
|
8
|
+
# Test the existing static configuration (category and user filters with ajax: true)
|
9
|
+
describe 'existing ajax-enabled searchable select filters' do
|
10
|
+
it 'renders select input with searchable-select-input css class' do
|
11
|
+
get '/admin/posts'
|
12
|
+
|
13
|
+
expect(response.body).to have_selector('select.searchable-select-input')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sets data-ajax-url attribute for ajax-enabled filters' do
|
17
|
+
get '/admin/posts'
|
18
|
+
|
19
|
+
expect(response.body).to have_selector('.searchable-select-input[data-ajax-url]')
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'renders selected option for current value' do
|
23
|
+
category = Category.create!(name: 'Travel')
|
24
|
+
|
25
|
+
get "/admin/posts?q[category_id_eq]=#{category.id}"
|
26
|
+
|
27
|
+
expect(response.body).to have_selector('.searchable-select-input option[selected]',
|
28
|
+
text: 'Travel')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not render options statically for ajax-enabled filters' do
|
32
|
+
Category.create!(name: 'Travel')
|
33
|
+
|
34
|
+
get '/admin/posts'
|
35
|
+
|
36
|
+
# Ajax-enabled filters should not render options inline
|
37
|
+
expect(response.body).not_to have_selector('.searchable-select-input option', text: 'Travel')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Test specific behaviors of the searchable select functionality
|
42
|
+
describe 'ajax endpoint behavior' do
|
43
|
+
it 'generates correct ajax URLs for category filter' do
|
44
|
+
get '/admin/posts'
|
45
|
+
|
46
|
+
expect(response.body).to include('data-ajax-url="/admin/categories/all_options?"')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'generates correct ajax URLs for user filter' do
|
50
|
+
get '/admin/posts'
|
51
|
+
|
52
|
+
expect(response.body).to include('data-ajax-url="/admin/users/all_options?"')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'includes empty "Any" option by default' do
|
56
|
+
get '/admin/posts'
|
57
|
+
|
58
|
+
expect(response.body).to have_selector('.searchable-select-input option[value=""]',
|
59
|
+
text: 'Any')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'selected option rendering' do
|
64
|
+
it 'renders selected category option when filtering' do
|
65
|
+
category = Category.create!(name: 'Travel')
|
66
|
+
|
67
|
+
get "/admin/posts?q[category_id_eq]=#{category.id}"
|
68
|
+
|
69
|
+
expect(response.body).to have_selector('.searchable-select-input option[selected]',
|
70
|
+
text: 'Travel')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'renders selected user option when filtering' do
|
74
|
+
user = User.create!(name: 'John Doe')
|
75
|
+
|
76
|
+
get "/admin/posts?q[user_id_eq]=#{user.id}"
|
77
|
+
|
78
|
+
expect(response.body).to have_selector('.searchable-select-input option[selected]',
|
79
|
+
text: 'John Doe')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'edge cases and error handling' do
|
84
|
+
it 'handles non-existent category ID gracefully' do
|
85
|
+
get '/admin/posts?q[category_id_eq]=99999'
|
86
|
+
|
87
|
+
# Should not render selected option for non-existent ID
|
88
|
+
expect(response.body).not_to have_selector('.searchable-select-input option[selected]')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'handles multiple filter values correctly' do
|
92
|
+
category1 = Category.create!(name: 'Travel')
|
93
|
+
category2 = Category.create!(name: 'Leisure')
|
94
|
+
|
95
|
+
get "/admin/posts?q[category_id_in][]=#{category1.id}&q[category_id_in][]=#{category2.id}"
|
96
|
+
|
97
|
+
# For 'in' queries, it should handle multiple values
|
98
|
+
# Note: The exact behavior depends on how the input handles 'in' vs 'eq' predicates
|
99
|
+
expect(response.status).to eq(200)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'custom searchable_select_options configurations' do
|
104
|
+
it 'uses the configured text_attribute for display' do
|
105
|
+
category = Category.create!(name: 'Travel')
|
106
|
+
|
107
|
+
get "/admin/posts?q[category_id_eq]=#{category.id}"
|
108
|
+
|
109
|
+
# Should display using the name attribute as configured in the static admin
|
110
|
+
expect(response.body).to have_selector('.searchable-select-input option[selected]',
|
111
|
+
text: 'Travel')
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'uses the correct scope configuration' do
|
115
|
+
# Create a category but test that the scope is working
|
116
|
+
Category.create!(name: 'Travel')
|
117
|
+
|
118
|
+
get '/admin/posts'
|
119
|
+
|
120
|
+
# Should not render options statically because it's ajax-enabled
|
121
|
+
expect(response.body).not_to have_selector('.searchable-select-input option', text: 'Travel')
|
122
|
+
# But should have the ajax URL
|
123
|
+
url_matcher = '/admin/categories/all_options'
|
124
|
+
expect(response.body).to have_selector(
|
125
|
+
".searchable-select-input[data-ajax-url*='#{url_matcher}']"
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'supports custom collection name through admin configuration' do
|
130
|
+
# Test that the custom searchable_select_options configuration works
|
131
|
+
get '/admin/posts'
|
132
|
+
|
133
|
+
# Verify that the custom endpoint is accessible (tests the custom collection)
|
134
|
+
expect { get '/admin/categories/custom_options' }.not_to raise_error
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/models'
|
4
|
+
require 'support/capybara'
|
5
|
+
|
6
|
+
ADMIN_POSTS_PATH = '/admin/posts'.freeze
|
7
|
+
|
8
|
+
RSpec.describe 'form input', type: :request do
|
9
|
+
# The static admin/posts.rb file already configures searchable_select with ajax: true
|
10
|
+
# So we'll test with that configuration
|
11
|
+
describe 'with ajax option (from static admin file)' do
|
12
|
+
before(:each) do
|
13
|
+
ActiveAdmin::SearchableSelect.inline_ajax_options = true
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
ActiveAdmin::SearchableSelect.inline_ajax_options = false
|
18
|
+
end
|
19
|
+
it 'renders select input with searchable-select-input css class' do
|
20
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
21
|
+
expect(response.body).to have_selector('select.searchable-select-input')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'renders options statically' do
|
25
|
+
Category.create!(name: 'Travel')
|
26
|
+
Category.create!(name: 'Music')
|
27
|
+
|
28
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
29
|
+
|
30
|
+
expect(response.body).to have_selector('.searchable-select-input option', text: 'Travel')
|
31
|
+
expect(response.body).to have_selector('.searchable-select-input option', text: 'Music')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not set data-ajax-url attribute' do
|
35
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
36
|
+
|
37
|
+
expect(response.body).not_to have_selector('.searchable-select-input[data-ajax-url]')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
shared_examples 'renders ajax based searchable select input' do
|
42
|
+
it 'renders select input with searchable-select-input css class' do
|
43
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
44
|
+
|
45
|
+
expect(response.body).to have_selector('select.searchable-select-input')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'does not render options statically' do
|
49
|
+
Category.create!(name: 'Travel')
|
50
|
+
|
51
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
52
|
+
|
53
|
+
expect(response.body).not_to have_selector('.searchable-select-input option', text: 'Travel')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'sets data-ajax-url attribute' do
|
57
|
+
get "#{ADMIN_POSTS_PATH}/new"
|
58
|
+
|
59
|
+
expect(response.body).to have_selector('.searchable-select-input[data-ajax-url]')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'renders selected option for current value' do
|
63
|
+
category = Category.create!(name: 'Travel')
|
64
|
+
post = Post.create!(title: 'A post', category: category)
|
65
|
+
|
66
|
+
get "#{ADMIN_POSTS_PATH}/#{post.id}/edit"
|
67
|
+
|
68
|
+
expect(response.body).to have_selector('.searchable-select-input option[selected]',
|
69
|
+
text: 'Travel')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'with ajax option set to true' do
|
74
|
+
# Using static admin/posts.rb which already has ajax: true configured
|
75
|
+
include_examples 'renders ajax based searchable select input'
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'with options collection name passed in ajax option' do
|
79
|
+
# Using static TestFormPostCustom admin and categories.rb which has 'custom' collection
|
80
|
+
include_examples 'renders ajax based searchable select input' do
|
81
|
+
let(:admin_path_prefix) { 'test_form_post_customs' }
|
82
|
+
|
83
|
+
def get(path)
|
84
|
+
path = path.sub(ADMIN_POSTS_PATH, '/admin/test_form_post_customs')
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'with options resource passed in ajax option' do
|
91
|
+
# Using static TestFormPostResource admin
|
92
|
+
include_examples 'renders ajax based searchable select input' do
|
93
|
+
let(:admin_path_prefix) { 'test_form_post_resources' }
|
94
|
+
|
95
|
+
def get(path)
|
96
|
+
path = path.sub(ADMIN_POSTS_PATH, '/admin/test_form_post_resources')
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'with options resource and collection name passed in ajax option' do
|
103
|
+
# Using static TestFormPostResourceCustom admin and categories.rb which has 'custom' collection
|
104
|
+
include_examples 'renders ajax based searchable select input' do
|
105
|
+
let(:admin_path_prefix) { 'test_form_post_resource_customs' }
|
106
|
+
|
107
|
+
def get(path)
|
108
|
+
path = path.sub(ADMIN_POSTS_PATH, '/admin/test_form_post_resource_customs')
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'with custom class attribute' do
|
115
|
+
# Using static TestFormPostClass admin
|
116
|
+
it 'adds searchable-select-input css class' do
|
117
|
+
get '/admin/test_form_post_classes/new'
|
118
|
+
|
119
|
+
expect(response.body).to have_selector('select.custom.searchable-select-input')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/models'
|
4
|
+
require 'support/capybara'
|
5
|
+
|
6
|
+
RSpec.describe 'inline_ajax_options setting', type: :request do
|
7
|
+
describe 'when ajax option set to true ' do
|
8
|
+
# Using static TestInlineAjaxPost and Category admins
|
9
|
+
|
10
|
+
it 'renders all options statically' do
|
11
|
+
Category.create!(name: 'Travel')
|
12
|
+
Category.create!(name: 'Music')
|
13
|
+
Category.create!(name: 'Cooking')
|
14
|
+
|
15
|
+
ActiveAdmin::SearchableSelect.inline_ajax_options = true
|
16
|
+
get '/admin/test_inline_ajax_posts/new'
|
17
|
+
|
18
|
+
expect(response.body).to have_selector('.searchable-select-input option',
|
19
|
+
text: 'Travel')
|
20
|
+
expect(response.body).to have_selector('.searchable-select-input option',
|
21
|
+
text: 'Music')
|
22
|
+
expect(response.body).to have_selector('.searchable-select-input option',
|
23
|
+
text: 'Cooking')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/models'
|
4
|
+
require 'support/capybara'
|
5
|
+
require 'support/active_admin_helpers'
|
6
|
+
|
7
|
+
RSpec.describe 'searchable select', type: :feature do
|
8
|
+
it 'shows helpful error message if ajax resource cannot be auto detected' do
|
9
|
+
ActiveAdminHelpers.setup do
|
10
|
+
# Use a unique admin resource name to avoid conflicts
|
11
|
+
ActiveAdmin.register(Post, as: 'TestErrorPost1') do
|
12
|
+
menu false # Hide from menu to avoid conflicts
|
13
|
+
|
14
|
+
# Try to use a filter with incorrect naming that can't be auto-detected
|
15
|
+
filter(:custom_category_id_eq,
|
16
|
+
as: :searchable_select,
|
17
|
+
ajax: true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
visit '/admin/test_error_post1s'
|
22
|
+
expect(page).to have_content('Cannot auto detect resource')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'shows helpful error message if named option collection does not exist' do
|
26
|
+
ActiveAdminHelpers.setup do
|
27
|
+
# Register Post that tries to use non-existent 'nonexistent_collection' collection
|
28
|
+
ActiveAdmin.register(Post, as: 'TestErrorPost2') do
|
29
|
+
menu false # Hide from menu to avoid conflicts
|
30
|
+
|
31
|
+
filter(:category,
|
32
|
+
as: :searchable_select,
|
33
|
+
ajax: {
|
34
|
+
resource: Category,
|
35
|
+
collection_name: 'nonexistent_collection' # This collection doesn't exist
|
36
|
+
})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
visit '/admin/test_error_post2s'
|
41
|
+
expect(page).to have_content(
|
42
|
+
"No option collection named 'nonexistent_collection' defined in 'Category' admin."
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'shows helpful error message if ajax resource does not have an admin' do
|
47
|
+
# Create a model that doesn't have an admin page
|
48
|
+
unless defined?(NonAdminModel)
|
49
|
+
Object.const_set('NonAdminModel', Class.new(ActiveRecord::Base))
|
50
|
+
NonAdminModel.table_name = 'categories' # Use existing table
|
51
|
+
NonAdminModel.class_eval do
|
52
|
+
def self.ransackable_attributes(_auth_object = nil)
|
53
|
+
['name']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
ActiveAdminHelpers.setup do
|
59
|
+
# Register Post with a filter that references a model without an admin
|
60
|
+
ActiveAdmin.register(Post, as: 'TestErrorPost3') do
|
61
|
+
menu false # Hide from menu to avoid conflicts
|
62
|
+
|
63
|
+
filter(:category,
|
64
|
+
as: :searchable_select,
|
65
|
+
ajax: {
|
66
|
+
resource: NonAdminModel
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
visit '/admin/test_error_post3s'
|
72
|
+
expect(page).to have_content("No admin found for 'NonAdminModel'")
|
73
|
+
ensure
|
74
|
+
Object.send(:remove_const, 'NonAdminModel') if defined?(NonAdminModel)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
require 'support/capybara'
|
4
|
+
|
5
|
+
RSpec.describe 'input_html options issue', type: :request do
|
6
|
+
it 'should pass input_html class option to searchable select element' do
|
7
|
+
# Using static TestInputHtmlPost and Category admins
|
8
|
+
get '/admin/test_input_html_posts/new'
|
9
|
+
|
10
|
+
# Parse the HTML response
|
11
|
+
doc = Nokogiri::HTML(response.body)
|
12
|
+
|
13
|
+
# Find the category select element by its ID
|
14
|
+
category_select = doc.css('#post_category_id').first
|
15
|
+
|
16
|
+
# Check if the element has the expected classes
|
17
|
+
unless category_select
|
18
|
+
raise "Category select element with ID 'post_category_id' not found in response"
|
19
|
+
end
|
20
|
+
|
21
|
+
classes = category_select['class'].to_s.split
|
22
|
+
|
23
|
+
# The actual assertions
|
24
|
+
expect(category_select).not_to be_nil, 'Category select element should exist'
|
25
|
+
expect(classes).to include('searchable-select-input'),
|
26
|
+
"Select element should have 'searchable-select-input' class"
|
27
|
+
expect(classes).to include('custom-class'),
|
28
|
+
"Select element should have 'custom-class' from input_html option"
|
29
|
+
end
|
30
|
+
end
|