Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Select item from chosen js select with Capybara and Selenium
def select_from_chosen(item_text, options)
field_id = find_field(options[:from])[:id]
within "##{field_id}_chzn" do
find('a.chzn-single').click
input = find("div.chzn-search input").native
input.send_keys(item_text)
input.send_key(:return)
end
end
@sjtipton

This comment has been minimized.

Copy link

@sjtipton sjtipton commented Jan 13, 2012

Thank you SOOOOO much for this!! #winning

@jaryl

This comment has been minimized.

Copy link

@jaryl jaryl commented Feb 5, 2012

Hey, I am getting this error: undefined method `send_keys' for "30":String (NoMethodError)

I am guessing input should not be a string, but apparently that's what it is being populated with the find. I am using:

capybara 1.1.2
capybara-webkit 0.9.0
chosen-rails 0.9.7

Any idea what's the problem?

Edit: I realized that send keys and native was a selenium thing, so I converted it to work with capybara-webkit.

@thijsc

This comment has been minimized.

Copy link
Owner Author

@thijsc thijsc commented Feb 6, 2012

Haven't tested this in capybara-webkit, it does work in Selenium. I'd love to see the gist you came up with for capybara-webkit!

@jaryl

This comment has been minimized.

Copy link

@jaryl jaryl commented Feb 6, 2012

I just forked it, it's working great for me (capybara-webkit's awesome): https://gist.github.com/1750858

@sidane

This comment has been minimized.

Copy link

@sidane sidane commented Feb 6, 2012

Just what I was looking for - thanks!

@PrototypeAlex

This comment has been minimized.

Copy link

@PrototypeAlex PrototypeAlex commented Feb 21, 2012

If anyone has problems with the multi-select occasionally not working, try adding a .click on the input before the send_keys command. The input needs to be selected before text can be added to it.

Cheers for the code though, apart from the above problem it works like a charm.

@thijsc

This comment has been minimized.

Copy link
Owner Author

@thijsc thijsc commented Feb 22, 2012

I've posted a new version that consistently works well for us on both selenium and capybara-webkit. Kudos to @jeffkreeftmeijer for writing it. It works for both single and multi selects.

@rheaton

This comment has been minimized.

Copy link

@rheaton rheaton commented Mar 2, 2012

To make sure to get all the correct events firing, add the last line:

  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}')")
    page.execute_script("$('##{field[:id]}').trigger('liszt:updated').trigger('change')")
  end
@tjmcewan

This comment has been minimized.

Copy link

@tjmcewan tjmcewan commented May 4, 2012

Support for multiple selects (just call select_from_chosen as many times as required):

    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("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('liszt:updated').trigger('change')")
    end
@Macrow

This comment has been minimized.

Copy link

@Macrow Macrow commented May 14, 2012

How can I use this function? (select_from_chosen)
I means How can I fill with the options params? find('#select_id') ? or somethings else?
I've test a lot, but still can't find the answer.
Thanks!

@rheaton

This comment has been minimized.

Copy link

@rheaton rheaton commented May 14, 2012

It behaves similarly to capybara's select method. You put the text of the option you want selected as the first argument and the text of the label of the select (or the id, as it just uses find_field) in the options.

select "Alabama", :from => "State"

But if the field is a chosen field:

select "Alabama", :from => "State"
@Macrow

This comment has been minimized.

Copy link

@Macrow Macrow commented May 15, 2012

Thanks for helping, rheaton, but it doesn't work for me. :(

Here's the details:

In haml view files:

= f.select :categories_id_equals, @categories, { include_blank: true }, { class: 'chzn-select-deselect', 'data-placeholder' => 'Please Select' }

this view generate id for the select is: search_categories_id_equals , the select have no label text.

In spec/support/chosen_select.rb , this file have already included in spec_helper.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])
    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('liszt:updated').trigger('change')")
  end  
end

RSpec.configure do |config|
  config.include ChosenSelect
end

In spec codes, I use the function like this, and I'm sure there have one 'news' option in chosen:

select_from_chosen('news', 'search_categories_id_equals')

But the Rspec complains "can't convert Symbol into Integer"
can't convert Symbol into Integer

Failure/Error: select_from_chosen('news', 'search_categories_id_equals')
TypeError:
  can't convert Symbol into Integer
  # ./spec/support/chosen.rb:4:in `[]' ### -> field = find_field(options[:from])

What mistakes I've made?
Thanks again.

@sjtipton

This comment has been minimized.

Copy link

@sjtipton sjtipton commented May 15, 2012

@Macrow, for the 2nd argument to select_from_chosen, you need to use the appropriate key for options:

select_from_chosen('news', from: 'search_categories_id_equals')

What is happening is that since you did not provide the key, it is trying take the value you passed in ('search_categories_id_equals') as a symbol and is unable to convert it to an integer.

@Macrow

This comment has been minimized.

Copy link

@Macrow Macrow commented May 15, 2012

It works!!!

Thank you.

@sjtipton

This comment has been minimized.

Copy link

@sjtipton sjtipton commented May 15, 2012

Awesome. Glad to help!

@atomical

This comment has been minimized.

Copy link

@atomical atomical commented Oct 17, 2012

Awesome. Loving this. Thanks for that.

@michael-harrison

This comment has been minimized.

Copy link

@michael-harrison michael-harrison commented Nov 18, 2012

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

@kelso

This comment has been minimized.

Copy link

@kelso kelso commented Jan 17, 2013

Thank you for this snippet! Awesome.

@bitterloa

This comment has been minimized.

Copy link

@bitterloa bitterloa commented Feb 1, 2013

Yeeees!! :-)

@egjiri

This comment has been minimized.

Copy link

@egjiri egjiri commented Feb 14, 2013

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
@bbuchalter

This comment has been minimized.

Copy link

@bbuchalter bbuchalter commented Feb 26, 2013

@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?

@bbuchalter

This comment has been minimized.

Copy link

@bbuchalter bbuchalter commented Feb 26, 2013

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
@deepj

This comment has been minimized.

Copy link

@deepj deepj commented Mar 7, 2013

Anyone didn't work with Poltergeist (Phantom.js) :(

@Dagnan

This comment has been minimized.

Copy link

@Dagnan Dagnan commented Apr 1, 2013

@deepj it works for me (poltergeist-1.1.0)

@Dagnan

This comment has been minimized.

Copy link

@Dagnan Dagnan commented Apr 1, 2013

Actually I had to update the select list through the command given by @rheaton.

page.execute_script("$('##{field[:id]}').trigger('liszt:updated').trigger('change')")
@curreta

This comment has been minimized.

Copy link

@curreta curreta commented Apr 11, 2013

Thanks so much for this. I used @rheaton 's snippet, and it ran beautifully.

@jgarber

This comment has been minimized.

Copy link

@jgarber jgarber commented Apr 16, 2013

If using @rheaton's snippet with Capybara 2.1, you will need to pass visible: false to find_field since Chosen hides the field.

@simonmorley

This comment has been minimized.

Copy link

@simonmorley simonmorley commented Apr 29, 2013

+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.

@bbuchalter

This comment has been minimized.

Copy link

@bbuchalter bbuchalter commented Jul 11, 2013

@tjmcewan

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.

@pduey

This comment has been minimized.

Copy link

@pduey pduey commented Aug 16, 2013

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)
@tinderfields

This comment has been minimized.

Copy link

@tinderfields tinderfields commented May 30, 2014

+1 for pduey's comment I also had to add visible: false

@michaelbrawn

This comment has been minimized.

Copy link

@michaelbrawn michaelbrawn commented May 30, 2014

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
@arojoal

This comment has been minimized.

Copy link

@arojoal arojoal commented Aug 24, 2014

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)
@nTraum

This comment has been minimized.

Copy link

@nTraum nTraum commented Sep 1, 2014

Can confirm this works like a charm. Thanks a lot! 👯

(RSpec 3, Capybara 2.4.1)

@ralphos

This comment has been minimized.

Copy link

@ralphos ralphos commented Oct 14, 2014

Thanking you!!!

@flexybiz

This comment has been minimized.

Copy link

@flexybiz flexybiz commented Dec 11, 2014

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)
@corporealfunk

This comment has been minimized.

Copy link

@corporealfunk corporealfunk commented Mar 17, 2015

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")
@jonahwh

This comment has been minimized.

Copy link

@jonahwh jonahwh commented Mar 31, 2015

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.

@ChunAllen

This comment has been minimized.

Copy link

@ChunAllen ChunAllen commented Jun 19, 2015

It only works for a single selection of chosen dropdown but not on grouped options chosen select

@osiro

This comment has been minimized.

Copy link

@osiro osiro commented Aug 24, 2015

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
@thijsc

This comment has been minimized.

Copy link
Owner Author

@thijsc thijsc commented Sep 7, 2015

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
@idabmat

This comment has been minimized.

Copy link

@idabmat idabmat commented Feb 12, 2016

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
@brunzino

This comment has been minimized.

Copy link

@brunzino brunzino commented Jun 24, 2016

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
@westonganger

This comment has been minimized.

Copy link

@westonganger westonganger commented Aug 29, 2016

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]')

@JeremiahChurch

This comment has been minimized.

Copy link

@JeremiahChurch JeremiahChurch commented Nov 20, 2016

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment