2
votes

I am working on a Rails 4 app. In the app, there is a page with a modal form whose behavior I am trying to test as follows:

  1. Click a link to open the modal,
  2. Click a link to add a <li> form via JS
  3. Input some field data, submit via JS
  4. See the populated form item is present
  5. Click a link to add another <li> form via JS
  6. Input some more field data, submit via JS
  7. See the populated form item is present
  8. Confirm the correct number of <li> elements

My web_step for counting the number of <li> elements uses an expect(all()) RSpec expectation like so:

Then(/^I should see the number of items equals "(.*?)"$/) do |number_of_ items |
    expect(all(:xpath, xpath_items).length).to eq(number_of_ items.to_i)
end

xpath_items is collecting the elements correctly.

The Problem

I am getting intermittent cucumber test failures. I think Capybara is looking for the <li> elements to count (and failing) before JS has changed them in the DOM. I think expect(all()) is the culprit, but not sure the best way to refactor or make it work consistently.

Failed Fixes

I have tried to fix this race condition via a few methods, including (1) increasing Capybara.default_wait_time = 120, (2) adding sleep(10) to web_steps (an anti-pattern I think), (3) wrapping web_step functionality in page.document.synchronize as shown in How to use synchronize in Capybara exactly?

I know wait_until method is removed from Capybara as shown here.

Stack: rails 4.0.3, cucumber 1.5.15, capybara 2.2.1, selenium-webdriver 2.41.0, angular.js, jquery, bootstrap. FWIW…my machine is a Retina MacBook Pro i7, so pretty fast.

Any ideas appreciated. Thanks!

1

1 Answers

1
votes

Capybara provides options (:count, :minimum, :maxium, :between) for specifying how many elements to wait for. Assuming you are using the latest version of Capybara, these can now be used in all.

By specifying the :count, you can tell the all method to wait for a specific number of items to appear. This could be added to your step as:

Then(/^I should see the number of items equals "(.*?)"$/) do |number_of_ items|
  expect(all(:xpath, xpath_items, count: number_of_ items.to_i).length).to eq(number_of_ items.to_i)
end

However, this seems a bit hard to read to me. I would suggest switching to using a have_selector, which does support these options. With this, you could simplify the step to:

Then(/^I should see the number of items equals "(.*?)"$/) do |number_of_ items|
  expect(page).to have_selector(:xpath, xpath_items, count: number_of_ items.to_i)
end