I have a Select2 element on a page that loads results via ajax. Would like to test this with capybara/rspec (using the poltergeist driver), but since the Select2 element actually starts out as a hidden field, and then populates a <ul>
once results are processed, none of the normal select
, fill_in
or choose
helpers will work.
What I have now is something like
it "should have a search field for events" do
click_link "Select an Event" # this works as expected
within('.select2-container') do # works
find('.select2-search input').set(event.description[0,5]) # won't work!
find(:xpath, "li[text()='#{event.description}']").click
end
click_button "Search"
page.should have_content("Displaying all 2 photos")
end
line 4 above, apparently capybara can find the element but the value isn't changed and Select2 never makes its ajax call. (can't use normal fill_in
there because the search field element has no label, id, or name attributes)
I created a helper that you can use with Cucumber, it doesn't have a sleep
so it's faster than the methods above. This also works if you use the createSearchChoice
provided by Select2.
Put it in features/support/select2_helper.rb
module Select2Helper
def select2(value, attrs)
first("#s2id_#{attrs[:from]}").click
find(".select2-input").set(value)
within ".select2-result" do
find("span", text: value).click
end
end
end
World(Select2Helper)
Use it like this:
Then(/^I select2 "([^"]*)" from "([^"]*)"$/) do |value, select_name|
select2 value, from: select_name
end
Tested with Rails 4.1.1 and Cucumber 1.3.15.
Stop scratching your head, do this and it will work
select "option_name_here", :from => "Id Of Your select field"
I tried every module and every damn thing I could and finally just did this without any helper and such things by a simple line of code.
For multiple select2 components on your page, I've created the following helper methods that work for me:
def select2(id, value)
page.execute_script %Q{
i = $('#s2id_#{id} .select2-input');
i.trigger('keydown').val('#{value}').trigger('keyup');
}
sleep 2
find('div.select2-result-label').click
end
def remove_all_from_select2(id)
page.execute_script %Q{
$('#s2id_#{id} .select2-choices a').click();
}
end
def remove_from_select2(id, value)
page.execute_script %Q{
$('#s2id_#{id} .select2-choices div:contains("#{value}")').closest('li').find('a').click();
}
end
I was able to solve this by using page.execute_script
to manually hack the needed value and keyup
event into the search field.
it "should have a search field for events" do
click_link "Select an Event"
page.execute_script("i = $('.select2-container input').first();
i.val('#{event.description[0,4]}').trigger('keyup');");
sleep 2
find('.select2-results li:first-child').click
click_button "Search"
page.should have_content("Displaying all 2 photos")
end
For those struggling with Select2 multiple:
Strangely I found that select_tag with multiple:true seems keen to generate html for each entry like:
<div class="select2-result-label">
<span class="select2-match"></span>
value
</div>
rather than
<div class="select2-result-label">
<span class="select2-match">value</span>
</div>
Unhelpfully putting the contents outside of the span.
In order to test it with capybara I have two methods:
# for most select2s
def select2_select(value, id)
# This methods requires @javascript in the Scenario
first("#s2id_#{id}").click
find(".select2-input").set(value)
within ".select2-result" do
if !find("span").text.empty?
find("span", text: value).click
elsif !find("div").text.empty?
find("div", text: value).click
else
fail 'neither span nor div within this select2, did you get the id of the select2 right?'
end
end
end
# for select_tag select2s which are multiple:true
def select2_select_multiple(select_these, id)
# This methods requires @javascript in the Scenario
[select_these].flatten.each do | value |
first("#s2id_#{id}").click
found = false
within("#select2-drop") do
all('li.select2-result').each do | result |
unless found
if result.text == value
result.click
found = true
end
end
end
end
end
end
The latter being too hacky for my liking, so I use the first where possible and hope to replace the latter eventually.
HAML used:
= select_tag "some_id", options_for_select(['hello','world']), multiple: true, include_blank: true
js:
$("#some_id").select2();
Inspired from this answer and one at Capybara select2 helper I came up with what seems robust for multiple select2 fields of both single-select and multi-select on the same page:
def select2(value, attrs)
s2c = first("#s2id_#{attrs[:from]}")
(s2c.first(".select2-choice") || s2c.find(".select2-choices")).click
find(:xpath, "//body").all("input.select2-input")[-1].set(value)
page.execute_script(%|$("input.select2-input:visible").keyup();|)
drop_container = ".select2-results"
find(:xpath, "//body").all("#{drop_container} li", text: value)[-1].click
end
This works for me on a dropdown style select2 with a remote data source:
def select2(value, from:)
execute_script("$('##{from}').select2('open')")
find(".select2-search__field").set(value)
find(".select2-results li", text: /#{value}/).click
end
from
must be the ID of the regular select tag that the select2 is built from.
I have two remote select2s. I search for a string in first one and according to the result found, second one gets filled. After trying capybara-select2 gem and all solutions listed here and there I had to come up with my own solution cause none of them worked for searching. In my case what I select is not important so did not try to select a result label with a specific value. Hope this helps someone in my situation.
Given(/^I have selected "(.*?)" category$/) do |arg1|
page.find(:css, ".select2-choice.select2-default").click
page.find(:css, "#s2id_autogen1_search").set "Dip Boya"
sleep 2
page.all(:css, '.select2-result-label')[1].click
end
Given(/^I have selected "(.*?)" variation$/) do |arg1|
page.find(:css, ".select2-choice.select2-default").click
page.all(:css, '.select2-result-label')[1].click
end
Simple answer:
page.find('.select2-drop-active .select2-input').set('foo')
page.find('.select2-drop-active .select2-input').native.send_keys(:return)
Hey I am not sure that anybody still cares but I just had this issue as well and I found an easy way to deal with it.
First off, I do not add the class to each drop-down select but rather I do it globally like so:
class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
def input_html_classes
super.push('chosen-select')
end
end
So when I started to have issues with capybara not being able to use the select2 drop-down my fix was to only use the global push if not in test environment:
class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
unless Rails.env.test?
def input_html_classes
super.push('chosen-select')
end
end
end
I used to use the capybara-select2 gem but it seems as though it is no longer being maintained :(
def select2(id, value)
find("##{id} ~ span.select2").click
within ".select2-results" do
find("li", text: value).click
end
end
Here's a solution for a more recent version of select2 - specifically the select2-rails (4.0.0) gem:
Place it in features/support/feature_helpers.rb
, and then include it in your spec_helper.rb
file with config.include FeatureHelpers
in your RSpec.configure do |config|
block.
module FeatureHelpers
def fill_in_select2(container_selector, with: '')
page.execute_script %Q{
i = $('#{container_selector} .select2-search__field');
i.trigger('keydown').val('#{with}').trigger('keyup');
}
sleep 2
find('.select2-results__option--highlighted').click
end
end
Then use fill_in_select2
just as you would use fill_in
in Capybara, but include the class or id of a container element with the appropriate prefix symbol. (e.g. fill_in_select2 '.actions_list', with: 'bob@testmaster1000.com'
).
Note: this selects the first element in the dropdown after inputting text.
Tested in Rails 4.2.5.2, Capybara 2.7.1, and Capybara-Webkit 1.11.1
After spending longer on this than I'd like, I finally got it working by building on Brandon McInnis' answer using a xpath selector.
module Select2Helper
def fill_in_select2(container_selector, with: '')
page.execute_script %Q{
i = $('#{container_selector} .select2-search__field');
i.trigger('keydown').val('#{with}').trigger('keyup');
}
sleep 2
find(:xpath, "//body").all("#{container_selector} li", text: with)[-1].click
end
end
For select2 4.0 in combination with capybara-webkit, I tried the execute_script-based solutions but they didn't work because capybara couldn't find the resulting 'li' element. I finally settled on this simple solution based on send_keys. Because I use a remote data source, I added a small sleep to allow the results to appear:
module Select2Helpers
def fill_in_select2(field_name, with: '')
field = find("##{field_name}")
parent = field.find(:xpath, './/..')
ui = parent.find('.select2-search__field')
ui.send_keys(with)
sleep 0.5
ui.send_keys(:enter)
sleep 0.1
end
end
Note that you may need to update to the latest capybara-webkit (1.14.0) to make this work.
https://github.com/goodwill/capybara-select2 with js: true
specified worked fine for me. Cheers
Here is what works for me, even with select2 AJAX autocomplete:
find('#user_id').sibling('span.select2').click
find('.select2-search__field').set('ABCDEF')
find('.select2-results li', text: 'ABCDEF').click
You can use capybara-select-2 gem which will do all the work for you our of the box. Just pass search: true
to the helper options to make an Ajax call:
select2 'Marriage', from: 'Event', search: true
来源:https://stackoverflow.com/questions/12771436/how-to-test-a-select2-element-with-capybara-dsl