jquerycucumbercapybarapoltergeist

How to trigger a JQuery 'change' function from a Capybara/Poltergeist test


On a form we have the following select field:

<select class="hundred js-select2 js-country-check select2-hidden-accessible" name="pledge[pledgor_home_country]" id="pledge_pledgor_home_country" tabindex="-1" aria-hidden="true">
  <option value="GB">United Kingdom</option>
  <option value="US">United States</option>
  <option value="DE">Germany</option>
  <option value="AU">Australia</option>
  <option value="BR">Brazil</option>
  <option value="BG">Bulgaria</option>
  <option value="CA">Canada</option>
  <option value="CN">China</option>
  <option value="DK">Denmark</option>
  <option value="EE">Estonia</option>
  <option value="FI">Finland</option>
  <option value="FR">France</option>
  <option value="HK">Hong Kong</option>
  <option value="IS">Iceland</option>
  <option value="IN">India</option>
  <option value="IE">Ireland</option>
  <option value="IL">Israel</option>
  <option value="KE">Kenya</option>
  <option value="LB">Lebanon</option>
  <option value="LU">Luxembourg</option>
  <option value="NL">Netherlands</option>
  <option value="NO">Norway</option>
  <option value="PL">Poland</option>
  <option value="PT">Portugal</option>
  <option value="SG">Singapore</option>
  <option value="ES">Spain</option>
  <option value="SE">Sweden</option>
  <option value="CH">Switzerland</option>
  <option value="TR">Turkey</option>
</select>

And we have a JQuery function associated with it:

 $('#pledge_pledgor_home_country').change(function() { //change stuff on the page})

I want to have some kind of check on this in our feature tests, so I've tried variations of this:

When("I select a non-EU country such as Switzerland") do
  find('#pledge_pledgor_home_country').find('option[value="CH"]').select_option    
  script = "$('#pledge_pledgor_home_country').change();"
  page.execute_script(script)
end

But I can't get it to work - the Capybara#execute_script method always returns nil, so I don't get any feedback, and the page contents don't change as expected. If I save_and_open_page, and just directly run the line $('#pledge_pledgor_home_country') from the console, it returns the html <select> element rather than the JQuery object I get from running the same script on the same page in Chrome. But I'm not sure if that's related to the problem of the test or an issue with running scripts in the headless browser.

I also tried using this script in this Capybara test instead: script = "$('#pledge_pledgor_home_country').trigger('change');". That also didn't work.

In the Gemfile we have this:

group :development, :test do
  gem "poltergeist", "~> 1.18.1"
  gem "phantomjs", "~> 2.1.1.0", require: 'phantomjs/poltergeist'
end

In the features/support folder of the Rails project I've set up the JS driver thusly:

Capybara.javascript_driver = :poltergeist

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, timeout: 1.day, js_errors: false)
end

And the Cucumber feature file looks like this:

@javascript
@vcr
Scenario: Updating GDPR display by country
  Given I am on the new pledge page # (just loads the page) in question
  When I select a non-EU country such as Switzerland
  ...

I'm not sure what else to try at this point. Is there a (good) way to do this?


Solution

  • You're using Poltergeist for your driver, which is basically equivalent to a 7 year old version of Safari in terms of JS/CSS support (because of PhantomJS abandonment). This leads to two possible reasons for your issue - First is that you could have modern JS in your page that is not compatible with PhantomJS which is preventing your change handler from ever being installed. Second possible issue is that the change event may not be triggered until the select loses focus.

    The second issue is easiest to test for - click on another element on the page after selecting the option.

    The first issue is tougher since PhantomJS doesn't report some incompatibilities (like using let/const) and just silently doesn't parse the JS. Initially start by turning on errors in your driver registration (js_errors: true) so you're not actively hiding the errors and fix the JS errors reported. However the real solution is to move to using headless chrome or firefox via the capybara selenium driver, rather than poltergeist, so you have support for current JS/CSS.

    On your other issues, execute_script doesn't return any results but evaluate_script will and is what you should be using if you expect results from your JS. However using execute/evaluate_script in tests to work around "issues" is a TERRIBLE idea since you are now doing things a user never could and effectively hiding real problems in your app.

    Note that from the js-select2 class on your select element, I assume you actually have a JS driven select widget that is replacing the select. That lends more evidence towards the first potential issue since if that library was loading you probably wouldn't be able to use select_option - This is because JS widgets usually hide the original element, making it non-interactable, and you need to find and click the individual elements that make up the widget (often ul/li elements).