Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to wait for a second page to load with Capybara when the first has the same field as the second

I'm having a problem with a spec that visits two forms with the same field ("Email") on both forms. If I don't manually sleep, Capybara seems to be finding the "Email" field from the first visit in the second portion of the test.

# visit the first form and fill out a subscription
visit new_front_form_subscription_path(@web_form_1.id)
fill_in "Email", with: "[email protected]"
fill_in "Field 1", with: "my first data"
click_button "Subscribe"

# visit the second form and fill out a subscription
visit new_front_form_subscription_path(@web_form_2.id)
sleep 1
fill_in "Email", with: "[email protected]"
fill_in "Field 2", with: "my second data"
click_button "Subscribe"

With the sleep in there, the spec passes with flying colors. Without the sleep, the second form submission gets a validation error -- blaming a blank "Email" value.

Is there a proper way to handle this? I dislike introducing manual sleeps into the specs.

like image 263
Alex Avatar asked Jan 23 '14 16:01

Alex


2 Answers

Here are a couple of ways using which you can solve it:

  1. Use another locator that is present only at the second page to select "Email" field:

    find('#second_page #email').set('[email protected]')
    
  2. Write a statement that will wait for a second page to be loaded instead of sleep:

    visit new_front_form_subscription_path(@web_form_2.id)
    expect(page).to have_css('locator_present_only_at_second_page')
    fill_in "Email", with: "[email protected]"
    
like image 70
Andrei Botalov Avatar answered Sep 22 '22 06:09

Andrei Botalov


The above answers are correct, but difficult to implement if you have a large test suite, because you have to find a unique element on each page. If you are a graduate from the Michael Hartl school of rails there is an easy way to solve this. Hartl teaches setting a different @title instance variable for every controller action. This is then used in application.html.erb to set an html title element for every page.

I certainly do, and it allowed a very easy way to fix up all my flaky tests that failed because the page had not loaded properly. You cannot find the title tag because it is not visible. So what I did was put at the bottom of the application.html.erb, the following code;

<%= content_tag :div, "[#{title}]", class: 'rspec_eyes_only' if Rails.env.test? %>

This inserts, if you are in test mode, a div with title text, surrounded by square brackets. Then I wrote a helper that goes in rails_helper.rb

def wait_for_page_load(title)
  find('div.rspec_eyes_only', text: title)
end

I already has another helper to test for the correct title, so I modified it to add in wait_for_page_load i.e;

def title_is_correct(title)
  full_title = "#{BaseTitle} | #{title}"
  wait_for_page_load "[#{full_title}]"
  expect(page.title).to eq full_title
end

These simple changes cleared up almost all of my flaky tests.

like image 21
Obromios Avatar answered Sep 22 '22 06:09

Obromios