Kris MP
Kris MP

Reputation: 2415

Capybara should have_content is not waiting long enough

So I am writing an acceptance test using capybara. The scenario was to connect our newsletter system to external mail service.

We will get redirected to our external service page to request access to the external mail service. And we will be redirected back to our system page when succeed.

When "I grant authorization" do
  fill_in "username", :with => "myapp"
  fill_in "password", :with => "secret"
  click_button "Allow Access"
end

Then "I should see 'Connection Established'" do
  page.should have_content "Connection Established"
end

And "I should see that I'm connected to Sample External Service" do
  page.should have_content "Connection Established: Sample External Service"
  page.should have_content "Deactivate Sample External Service syncing"
end

But if I am not using sleep before the page.should have_content "Connection Established". The spec will fail. From what I know, using sleep is not the best practice, because it will make our test run slow.

How to make it waiting until it got redirected back to our system

Upvotes: 13

Views: 11574

Answers (5)

Viktor Ivliiev
Viktor Ivliiev

Reputation: 1324

My solution to similar problems:

module WaitUntil
  def wait_until(max_wait_time = Capybara.default_max_wait_time)
    Timeout.timeout(max_wait_time) do
      sleep(0.2) until yield
    end
  end
end

Using:

    wait_until { page.has_css?('li', text: 'test', visible: true) }

Upvotes: 1

Ricardo Villagrana
Ricardo Villagrana

Reputation: 466

You can use capybara_watcher gem, it is an elegant way of waiting for the pege to have a change in its content. Check it out on RubyGems.org

Example:

wait_until_content_has "Connection Established" do |text|
  page.should have_content text
end

The perks of using this is that the sleep time is the actual time the page takes to have a change and you can configure it to exit after the second you choose if the page didn't change.

Upvotes: 3

Nathaniel C
Nathaniel C

Reputation: 564

For page transitions, I like to wait for the URL to change first, and then wait for content on the page. It gives you a more specific error if the redirect fails, and it naturally splits the long wait into two chunks. With two opportunities to hit default_max_wait_time, the timeout is potentially doubled without actually changing it. If it's still not enough, you can always pass a custom timeout into have_current_path with the wait: parameter.

expect(page).to have_current_path("externalservice.com")
expect(page).to have_content("Connection Established")

There might be a page.should equivalent, but I think the official guidance is to move to the expect syntax, since the should syntax was deprecated.

Upvotes: 3

Thomas Walpole
Thomas Walpole

Reputation: 49870

There are 3 ways to adjust the maximum amount of time Capybaras methods will wait for their expectations to be true/elements to exist

Capybara.default_max_wait_time = <seconds> - This is the global setting which should be set high enough for the vast majority of your method calls

Capybara.using_wait_time(<seconds>) do ... end - This temporarily changes default_max_wait_time inside the block and then returns it to its original setting when done. This makes sense when you have a number of methods you want to call with a new wait time, or you need to call a helper method with the wait time set to a different value.

:wait option - All Capybara finder and expectation methods accept a :wait option which will change the maximum wait time for that method call. This makes sense to use when you have a specific case that requires a bit more waiting than normal

# This will wait up to 10 seconds for the content to exist in the page
page.should have_content "Connection Established: Sample External Service", wait: 10 

Note: In the future when posting questions it is generally helpful if you provide the full exact error message you get as part of your question.

Upvotes: 17

Unixmonkey
Unixmonkey

Reputation: 18784

You can use Capybara.using_wait_time(seconds) to temporarily change the value of Capybara.default_max_wait_time for special cases:

Capybara.using_wait_time(10) do 
  page.should have_content "Connection Established"
end

Upvotes: 1

Related Questions