New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Capybara to interact with react-select? #832

Open
jbranchaud opened this Issue Mar 14, 2016 · 18 comments

Comments

Projects
None yet
@jbranchaud

jbranchaud commented Mar 14, 2016

While I understand there are a number of ways to approach testing applications using React, I've decided to do integration testing using Capybara. Has anyone had any luck interacting with the react-select component using Capybara? If so, how did you go about doing this?

btw, this is an awesome, flexible replacement for drop downs in React. Thanks to the author/contributors!

@dzmitry-kremez-itechart

This comment has been minimized.

Show comment
Hide comment
@dzmitry-kremez-itechart

dzmitry-kremez-itechart commented Apr 7, 2016

+1

1 similar comment
@wafendy

This comment has been minimized.

Show comment
Hide comment
@wafendy

wafendy commented Apr 13, 2016

+1

@adailey14

This comment has been minimized.

Show comment
Hide comment
@adailey14

adailey14 Apr 13, 2016

Was just struggling to click the select in a test using react testutils for a while, and finally got it. The trick was:

Update this package to the latest version (1.0.0-beta12). We couldn't get anything to work on 1.0.0-beta9 which we were on before.
Focus on the input with selector: ".Select-control input"
Click on the div with select: ".Select-control"

Can't say if this will work for Capybara but it might.

adailey14 commented Apr 13, 2016

Was just struggling to click the select in a test using react testutils for a while, and finally got it. The trick was:

Update this package to the latest version (1.0.0-beta12). We couldn't get anything to work on 1.0.0-beta9 which we were on before.
Focus on the input with selector: ".Select-control input"
Click on the div with select: ".Select-control"

Can't say if this will work for Capybara but it might.

@wafendy

This comment has been minimized.

Show comment
Hide comment
@wafendy

wafendy Apr 14, 2016

I have success with Capybara by assigning the id to react component 'user_role', then execute the following code to perform manual clicks:

find('#user_role').click
within('div.Select-menu') do
  find('div.Select-option', text: 'admin').click
end

wafendy commented Apr 14, 2016

I have success with Capybara by assigning the id to react component 'user_role', then execute the following code to perform manual clicks:

find('#user_role').click
within('div.Select-menu') do
  find('div.Select-option', text: 'admin').click
end
@kristo

This comment has been minimized.

Show comment
Hide comment
@kristo

kristo Jun 9, 2016

Does anyone have a solution in case there is a need to type at least one letter to see the available options?

kristo commented Jun 9, 2016

Does anyone have a solution in case there is a need to type at least one letter to see the available options?

@robwise

This comment has been minimized.

Show comment
Hide comment
@robwise

robwise Jul 7, 2016

This is the solution I came up with for Capybara + Poltergeist

robwise commented Jul 7, 2016

This is the solution I came up with for Capybara + Poltergeist

@chrisjones-tripletri

This comment has been minimized.

Show comment
Hide comment
@chrisjones-tripletri

chrisjones-tripletri Jul 10, 2016

I was trying to type values into the input with capybara. If I wrapped my select in a wrapper div with the id #wrapper_id, then this worked for me:

input = find('#wrapper_id').find('input')
input.set('My Value')
input.native.send_keys(:return)

chrisjones-tripletri commented Jul 10, 2016

I was trying to type values into the input with capybara. If I wrapped my select in a wrapper div with the id #wrapper_id, then this worked for me:

input = find('#wrapper_id').find('input')
input.set('My Value')
input.native.send_keys(:return)
@chrislaskey

This comment has been minimized.

Show comment
Hide comment
@chrislaskey

chrislaskey Jul 27, 2016

Building on @wafendy's answer.

I passed the Select component a className value of input-customer.

Then in Capybara I interacted with it the following ways (using release version1.0.0-beta14).

Select an option

find(".Select.input-customer").click
within(".Select.input-customer") do
  find(".Select-option", text: "Customer Name").click
end

Verify existing value

within(".Select.input-customer") do
  expect(find(".Select-value-label").text).to eq("Custome Name")
end

Zero out a value

within(".Select.input-customer") do
  find(".Select-clear").click
end

chrislaskey commented Jul 27, 2016

Building on @wafendy's answer.

I passed the Select component a className value of input-customer.

Then in Capybara I interacted with it the following ways (using release version1.0.0-beta14).

Select an option

find(".Select.input-customer").click
within(".Select.input-customer") do
  find(".Select-option", text: "Customer Name").click
end

Verify existing value

within(".Select.input-customer") do
  expect(find(".Select-value-label").text).to eq("Custome Name")
end

Zero out a value

within(".Select.input-customer") do
  find(".Select-clear").click
end
@RKushnir

This comment has been minimized.

Show comment
Hide comment
@RKushnir

RKushnir Aug 24, 2016

@robwise Your link leads to localhost:3000, this seems to be the correct one https://robwise.github.io/blog/testing-react-select-with-capybara

RKushnir commented Aug 24, 2016

@robwise Your link leads to localhost:3000, this seems to be the correct one https://robwise.github.io/blog/testing-react-select-with-capybara

@robwise

This comment has been minimized.

Show comment
Hide comment
@robwise

robwise Aug 24, 2016

@RKushnir oops, fixed, thank you!!

robwise commented Aug 24, 2016

@RKushnir oops, fixed, thank you!!

@rhys-vdw

This comment has been minimized.

Show comment
Hide comment
@rhys-vdw

rhys-vdw Nov 16, 2016

Thanks all for the help... My final method for making/selecting a Creatable tag. (You need to pass something to className to differentiate it).

  def fill_in_react_select(selector, with:)
    fix_overlap = %{
      var elements = [].slice.call(document.getElementsByClassName('Select-placeholder'))
      elements.forEach(function (element) {
        element.style.zIndex = -99999
      })
    }
    page.execute_script(fix_overlap)
    container = find(selector)
    input = container.find('.Select-input')
    input.send_keys with
    input.native.send_keys :return
  end
fill_in_react_select '.MyCapybaraReference', with: 'my fun test string'

rhys-vdw commented Nov 16, 2016

Thanks all for the help... My final method for making/selecting a Creatable tag. (You need to pass something to className to differentiate it).

  def fill_in_react_select(selector, with:)
    fix_overlap = %{
      var elements = [].slice.call(document.getElementsByClassName('Select-placeholder'))
      elements.forEach(function (element) {
        element.style.zIndex = -99999
      })
    }
    page.execute_script(fix_overlap)
    container = find(selector)
    input = container.find('.Select-input')
    input.send_keys with
    input.native.send_keys :return
  end
fill_in_react_select '.MyCapybaraReference', with: 'my fun test string'
@mcfiredrill

This comment has been minimized.

Show comment
Hide comment
@mcfiredrill

mcfiredrill Jan 5, 2017

Using the same solution as @wafendy @chrislaskey , but it seems to only work if the browser window is focused, so we fail on CI. :(

mcfiredrill commented Jan 5, 2017

Using the same solution as @wafendy @chrislaskey , but it seems to only work if the browser window is focused, so we fail on CI. :(

@asselstine

This comment has been minimized.

Show comment
Hide comment
@asselstine

asselstine Jan 31, 2017

Just came across this- thought I'd help out with our code.

The following code works with Chrome and Poltergeist:

def react_select(selector, label)
  within selector do
    find('.Select-control').click
    expect(page).to have_css('.Select-menu-outer') # menu should be open now
    find('.Select-input').set label # set the value
    # This is a little funky because when the entered text forces the select to
    # wrap, it causes React to re-render.  We need to get it to re-render
    # (if needed) by hovering.
    expect(page).to have_css('.Select-option', text: label)
    find('.Select-option', text: label).hover
    expect(page).to have_css('.Select-option', text: label)
    find('.Select-option', text: label).click
    expect(page).to have_css('.Select-value-label', text: label)
  end
end

def react_deselect_multi(selector, label)
  within selector do
    node = find('.Select-value-label', text: label)
    node.parent.find('.Select-value-icon').click
    expect(page).to_not have_css('.Select-value-label', text: label)
  end
end

def react_deselect_single(selector)
  within selector do
    find('.Select-clear-zone').click
    expect(page).not_to have_css('.Select-clear-zone')
  end
end

These are battle-tested methods and have proved very reliable to us.

asselstine commented Jan 31, 2017

Just came across this- thought I'd help out with our code.

The following code works with Chrome and Poltergeist:

def react_select(selector, label)
  within selector do
    find('.Select-control').click
    expect(page).to have_css('.Select-menu-outer') # menu should be open now
    find('.Select-input').set label # set the value
    # This is a little funky because when the entered text forces the select to
    # wrap, it causes React to re-render.  We need to get it to re-render
    # (if needed) by hovering.
    expect(page).to have_css('.Select-option', text: label)
    find('.Select-option', text: label).hover
    expect(page).to have_css('.Select-option', text: label)
    find('.Select-option', text: label).click
    expect(page).to have_css('.Select-value-label', text: label)
  end
end

def react_deselect_multi(selector, label)
  within selector do
    node = find('.Select-value-label', text: label)
    node.parent.find('.Select-value-icon').click
    expect(page).to_not have_css('.Select-value-label', text: label)
  end
end

def react_deselect_single(selector)
  within selector do
    find('.Select-clear-zone').click
    expect(page).not_to have_css('.Select-clear-zone')
  end
end

These are battle-tested methods and have proved very reliable to us.

@budiibp

This comment has been minimized.

Show comment
Hide comment
@budiibp

budiibp Feb 20, 2017

actually i using react-select-plus, but i think it's same with react-select.

try this:
find('.Select-input input').set('your_option_will_chosen').send_keys :tab

i just imagine like typing manually on field react-select then click 'tab' from my keyboard.
it's work for me (for capybara) with one option react-select.

budiibp commented Feb 20, 2017

actually i using react-select-plus, but i think it's same with react-select.

try this:
find('.Select-input input').set('your_option_will_chosen').send_keys :tab

i just imagine like typing manually on field react-select then click 'tab' from my keyboard.
it's work for me (for capybara) with one option react-select.

@davidhan527

This comment has been minimized.

Show comment
Hide comment
@davidhan527

davidhan527 Jun 26, 2017

For anyone else using Select.Async, find('.Select-input input').set(option_text).send_keys(:tab) won't work because it will send the key before the options are done loading. Also, send_keys(:tab) only works when you searchable set to true since without it, find('.Select-input input') won't exist. This solutions works for both non async and async dropdowns.

  def choose_from_react_select(selector, option_text)
    within(selector) do
      find('.Select-input input').set(option_text)

      # If the react select loads options asynchronously, this ensures
      # that the options are done loading before selecting the option.
      expect(page).not_to have_css('.Select-loading-zone')
      expect(page).to have_css(".Select-option", text: option_text)

      find('.Select-input input').send_keys(:tab)
    end
  end

davidhan527 commented Jun 26, 2017

For anyone else using Select.Async, find('.Select-input input').set(option_text).send_keys(:tab) won't work because it will send the key before the options are done loading. Also, send_keys(:tab) only works when you searchable set to true since without it, find('.Select-input input') won't exist. This solutions works for both non async and async dropdowns.

  def choose_from_react_select(selector, option_text)
    within(selector) do
      find('.Select-input input').set(option_text)

      # If the react select loads options asynchronously, this ensures
      # that the options are done loading before selecting the option.
      expect(page).not_to have_css('.Select-loading-zone')
      expect(page).to have_css(".Select-option", text: option_text)

      find('.Select-input input').send_keys(:tab)
    end
  end
@andypearson

This comment has been minimized.

Show comment
Hide comment
@andypearson

andypearson Jun 30, 2017

@mcfiredrill Did you ever find a solution for the "only works if the browser window is focused" issue? I'm just working on some integration tests at the moment and, as you describe, they only work when the window has focus. /cc @wafendy @chrislaskey

andypearson commented Jun 30, 2017

@mcfiredrill Did you ever find a solution for the "only works if the browser window is focused" issue? I'm just working on some integration tests at the moment and, as you describe, they only work when the window has focus. /cc @wafendy @chrislaskey

@cmolenda

This comment has been minimized.

Show comment
Hide comment
@cmolenda

cmolenda Dec 27, 2017

I ran into a bit of an issue where the first "tag" (React-Select Creatable component frontend to represent tags from acts-as-taggable-on backend) would not get created using the #set and send_keys methods. I imagine it has something to do with the combination of React-Select + Selenium, but didn't have time to investigate. Here was my workaround.

  def creatable_tags_field
    find('.creatable-tags') # custom name given to the react component.
  end

  def fill_in_tags(*tags)

    # BUG: Workaround for Capybara/Selenium + React-Select issue.
    # First tag does not get created, but subsequent ones do.
    creatable_tags_field.find('input').send_keys(' ')
    creatable_tags_field.find('input').send_keys(:tab)

    tags.each do |tag|
      within creatable_tags_field do
        find('input').send_keys(tag)
        find('input').send_keys(:tab)
        find('.Select-value-label', text: tag)
      end
    end
  end

cmolenda commented Dec 27, 2017

I ran into a bit of an issue where the first "tag" (React-Select Creatable component frontend to represent tags from acts-as-taggable-on backend) would not get created using the #set and send_keys methods. I imagine it has something to do with the combination of React-Select + Selenium, but didn't have time to investigate. Here was my workaround.

  def creatable_tags_field
    find('.creatable-tags') # custom name given to the react component.
  end

  def fill_in_tags(*tags)

    # BUG: Workaround for Capybara/Selenium + React-Select issue.
    # First tag does not get created, but subsequent ones do.
    creatable_tags_field.find('input').send_keys(' ')
    creatable_tags_field.find('input').send_keys(:tab)

    tags.each do |tag|
      within creatable_tags_field do
        find('input').send_keys(tag)
        find('input').send_keys(:tab)
        find('.Select-value-label', text: tag)
      end
    end
  end
@nickgnd

This comment has been minimized.

Show comment
Hide comment
@nickgnd

nickgnd Aug 16, 2018

We are using react-select (version ^2.0.0-beta.2) with a class name prefix (see prop classNamePrefix).

Below a basic helper that works fine for us (based on @asselstine one):

def react_select(selector, class_prefix, selected_option)
  # Open the options menu
  r_select = find(selector)
  r_select.click
  expect(r_select).to have_css(".#{class_prefix}__menu")

  # Select the option
  r_select.find(".#{class_prefix}__option", text: selected_option).hover
  r_select.find(".#{class_prefix}__option", text: selected_option).click
end

and the select component looks like:

<Select
  className="selector className"
  classNamePrefix="prefix className"
  ...other props
/>

nickgnd commented Aug 16, 2018

We are using react-select (version ^2.0.0-beta.2) with a class name prefix (see prop classNamePrefix).

Below a basic helper that works fine for us (based on @asselstine one):

def react_select(selector, class_prefix, selected_option)
  # Open the options menu
  r_select = find(selector)
  r_select.click
  expect(r_select).to have_css(".#{class_prefix}__menu")

  # Select the option
  r_select.find(".#{class_prefix}__option", text: selected_option).hover
  r_select.find(".#{class_prefix}__option", text: selected_option).click
end

and the select component looks like:

<Select
  className="selector className"
  classNamePrefix="prefix className"
  ...other props
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment