-
-
Save thijsc/1391107 to your computer and use it in GitHub Desktop.
def select_from_chosen(item_text, options) | |
field = find_field(options[:from]) | |
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{item_text}')\").val()") | |
page.execute_script("$('##{field[:id]}').val('#{option_value}')") | |
end |
Great work. I've taken a different route to this. Rather than change the hidden select then do the update I've chosen to simulate user interaction. I've tested it with Webkit and Selenium and it works happily. You can check it out here: https://gist.github.com/4102026
Thank you for this snippet! Awesome.
Yeeees!! :-)
You can also overwrite Capybara's own select method and incorporate the above changes.
This way you would be able to target both regular select elements as well as chosen ones.
def select(value, options={})
if options.has_key?(:from)
element = find(:select, options[:from]).find(:option, value)
if element.visible?
element.select_option
else
field = find_field(options[:from])
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{value}')\").val()")
page.execute_script("$('##{field[:id]}').val('#{option_value}')")
page.execute_script("$('##{field[:id]}').trigger('liszt:updated').trigger('change')")
end
else
find(:option, value).select_option
end
end
@egjiri, I'm not sure if this is from some other code, but I'm finding that
element = find(:select, options[:from]).find(:option, value)
fails because the select is disabled and won't be returned by the Capybara finder. Did you not encounter this?
Calling this before using your select helped me:
def enable_element(id)
element = find(:xpath, "//*[@id='#{id}']")
if element["disabled"]
page.execute_script("$('##{id}').removeAttr('disabled')")
end
end
Anyone didn't work with Poltergeist (Phantom.js) :(
@deepj it works for me (poltergeist-1.1.0)
Actually I had to update the select list through the command given by @rheaton.
page.execute_script("$('##{field[:id]}').trigger('liszt:updated').trigger('change')")
Thanks so much for this. I used @rheaton 's snippet, and it ran beautifully.
If using @rheaton's snippet with Capybara 2.1, you will need to pass visible: false
to find_field since Chosen hides the field.
+1 to @jgarber for this point. Just wasted days of my life looking for a fix:
field = find_field(options[:from], visible: false)
Fixes the failing tests.
I know it's been a long time, but it seems these two statements you added for supporting from a multiple-select,
page.execute_script("value = ['#{option_value}']\; if ($('##{field[:id]}').val()) {$.merge(value, $('##{field[:id]}').val())}")
option_value = page.evaluate_script("value")
seem to break functionality for a single select if there is a default value selected. Thought I would mention it here since this is still an active thread.
For capybara 2, visible: true is the default, so this doesn't work as is. Changed the first line to:
field = find_field(options[:from], visible: false)
+1 for pduey's comment I also had to add visible: false
Here is an updated version of @Macrow 's chosen_select.rb
# Support for multiple selects (just call select_from_chosen as many times as required):
module ChosenSelect
def select_from_chosen(item_text, options)
field = find_field(options[:from], visible: false)
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{item_text}')\").val()")
page.execute_script("value = ['#{option_value}']\; if ($('##{field[:id]}').val()) {$.merge(value, $('##{field[:id]}').val())}")
option_value = page.evaluate_script("value")
page.execute_script("$('##{field[:id]}').val(#{option_value})")
page.execute_script("$('##{field[:id]}').trigger('chosen:updated')")
end
end
RSpec.configure do |config|
config.include ChosenSelect
end
Thanks @michaelbrawn!!!!
I'm using cucumber-1.3.16 and rspec-3.0.3 and I've got a problem with
RSpec.configure do |config|
config.include ChosenSelect
end
When I run cucumber I get a "undefined method `configure' for RSpec:Module (NoMethodError)".
I've replaced it with "World(ChosenSelect)" and it works!!!! this is how the complete module code looks like:
# Support for multiple selects (just call select_from_chosen as many times as required):
module ChosenSelect
def select_from_chosen(item_text, options)
field = find_field(options[:from], visible: false)
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{item_text}')\").val()")
page.execute_script("value = ['#{option_value}']\; if ($('##{field[:id]}').val()) {$.merge(value, $('##{field[:id]}').val())}")
option_value = page.evaluate_script("value")
page.execute_script("$('##{field[:id]}').val(#{option_value})")
page.execute_script("$('##{field[:id]}').trigger('chosen:updated')")
end
end
World(ChosenSelect)
Can confirm this works like a charm. Thanks a lot! 👯
(RSpec 3, Capybara 2.4.1)
Thanking you!!!
When to use last one with Ember.js got a problem: it doens't update binded values. So just add .change()
at the end and it will work as expected.
# Support for multiple selects (just call select_from_chosen as many times as required):
module ChosenSelect
def select_from_chosen(item_text, options)
field = find_field(options[:from], visible: false)
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{item_text}')\").val()")
page.execute_script("value = ['#{option_value}']\; if ($('##{field[:id]}').val()) {$.merge(value, $('##{field[:id]}').val())}")
option_value = page.evaluate_script("value")
page.execute_script("$('##{field[:id]}').val(#{option_value})")
page.execute_script("$('##{field[:id]}').trigger('chosen:updated')")
page.execute_script("$('##{field[:id]}').change()")
end
end
World(ChosenSelect)
Without needing to call evaluate_script, this also works with Capybara 2.4.4, Capybara-webkit 1.4.1 and Rspec 3.2.2, using Chosen 1.4.1:
find("#field_id_chosen").trigger("mousedown")
find("#field_id_chosen ul.chosen-results li", :text => "Item Text").trigger("mouseup")
For those of you using rails who need to escape javascript when setting option_value
the first time.
include ActionView::Helpers::JavaScriptHelper
in the Module
/Class
where this method lives, and change
option_value = page.evaluate_script("$(\"##{field[:id]} option:contains('#{item_text}')\").val()")
to
option_value = page.evaluate_script("$('##{field[:id]} option:contains(\"#{j item_text}\")').val()")
As an aside, if your select box is prepopulated, and you want to CHANGE the value, you must clear the value of the select box before calling select_from_chosen
, or it will attempt to select both values.
It only works for a single selection of chosen dropdown but not on grouped options chosen select
Hey guys,
The implementation of @flexybiz above seems to work fine, however if, for whatever reason, I run select_from_chosen
two or more times on the same field, this will select multiple options even though my chosen is configured to be a standard select, not a multiple one.
My workaround was creating deselect_from_chosen
.
So in case you guys need to deselect fields in your tests, here's how I did it:
def deselect_from_chosen(field)
field = find_field(field, visible: false)
page.execute_script("$('##{field[:id]}').val('').trigger('change').trigger('chosen:updated')")
end
Funny to find this gist again 4 years later with so much discussion :-). I just needed this again, this one based on the snippet by @corporealfunk works well in Selenium:
def select_from_chosen(item_text, options)
field = find_field(options[:from], :visible => false)
find("##{field[:id]}_chosen").click
find("##{field[:id]}_chosen ul.chosen-results li", :text => item_text).click
end
For deselecting a specific value :
def deselect_from_chosen(item_text, options)
field = find_field(options[:from], visible: false)
page.execute_script("$(\"##{field[:id]} option:contains('#{item_text}')\").removeAttr('selected')")
page.execute_script("$('##{field[:id]}').trigger('chosen:updated')")
end
Here's another deselect function that's more in-line with @thejsc's approach. I like this approach because it triggers the change
hook.
def deselect_from_chosen(item_text, options)
field = find_field(options[:from], visible: false)
find("##{field[:id]}_chosen ul.chosen-choices li.search-choice", :text => item_text).find("a.search-choice-close").click
end
For anyone whos interested in just selecting whichever option is first without having to know the text
def select_first_from_chosen(from)
field = find_field(from, visible: false)
find("##{field[:id]}_chosen").click
first("##{field[:id]}_chosen ul.chosen-results li").click
end
Use like this: select_first_from_chosen('post[name]')
To guarantee an exact match rather than a partial match aka - find 'OR' and not match 'MORE'
replace
the line
find("##{field[:id]}_chosen ul.chosen-results li", text: item_text).click
with
find("##{field[:id]}_chosen ul.chosen-results li", text: /\A#{Regexp.quote(item_text)}\z/).click
Awesome. Loving this. Thanks for that.