I have a function below
def fill_in_sources_details
click_button 'New Source'
assert_text 'Sources'
fill_in 'text', :with => #source.text
fill_in 'url', :with => #source.url
end
After New Source clicked, new fields added to the page. Sometimes the test fails because the test doesn't wait for new elements to be added so new fields cannot be found be Capybara. I tried to add assert_text 'Sources' which is obvious but it still fails sometimes. Any idea how to fix it?
A number of things could be going on here, some of which depend on which driver and driver version you're using.
You don't actually have a field that matches 'text' - That's not likely here since it works sometimes.
The initial click_button 'New Source' is missing the actual button. This could happen depending on which driver you're using if the button animates on to the page (slide in, zoom in, etc). You can tell if that's what's happening by putting a sleep 5 before the click_button. If that fixes the issue then you should look into disabling animation in test mode. It will speed up your tests and make them more reliable.
The click_button is happening but it takes too long for the fields to show on the page (slow ajax request, etc). Putting the sleep 5 after the click_button should have diagnosed that, however maybe it's taking longer than 5 seconds. The assert_text 'Sources' is a good attempt to diagnose that, as long as the text 'Sources' would only be on the page when the input is visible but that text seems too generic for that (it's probably on the current page already). You can always pass a wait option to fill_in, which will make it wait longer for the element to appear
fill_in 'text', :with => #source.text, wait: 20
You have an intermittent error in your app that's preventing the fields from showing. Diagnose that by catching the ElementNotFound, pausing the app and looking at the errors in your browser
begin
fill_in 'text', :with => #source.text, wait: 20
rescue Capybara::ElementNotFound
binding.pry # byebug, etc - whatever debugging you use
end
Related
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
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.
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.
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.
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) }
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
I have a small trouble with my tests suite: when I run spec which the checking ajax action on the page, sometimes I get random error
Failure/Error: expect(page).to have_content 'DHH'
This error shows very rarely (about 1/100), but this very confused me. I decided this a 'race condition' cause, and I add this config in my spec/rails_helper.rb
Capybara.default_max_wait_time = 10
but this is don't help for me, and I decided add timestamps
it 'adds new DHH', js: true do
find('#modal_new_dhh').click
fill_in('name', with: 'DHH')
p 'click button'
p Time.now.strftime('%H:%M:%S.%L')
click_button('Submit')
p 'checking content'
p Time.now.strftime('%H:%M:%S.%L')
expect(page).to have_content 'DHH'
p 'after checking content'
p Time.now.strftime('%H:%M:%S.%L')
end
and see that
"click button"
"17:34:43.083"
"before checking content"
"17:34:43.127"
"after checking content"
"17:34:43.213"
why Capybara don't wait after click button?
sorry for my bad English
The wait in your example occurs in the have_content matcher. Where you're outputting times from will never show a delay because click_button has nothing to wait for, it just clicks a button and moves on (because it has no idea what it would wait for, clicking a button could do anything), however the have_content matcher will wait up to Capybara.default_max_wait_time for the content to appear.
Note your find, 'fill_in' and click_button calls also wait for the relevant elements to appear BEFORE performing their actions
As you said this is a race condition. As to why it happens, I can't really say, the only reason I could think of would be that there is a difference between a user experience and an automated testing, because computers are very fast.
I had faces the same challenge sometime ago and I found this tutorial which showed me a way to go about resolving these kind of issues. I hope it would be useful to you too.
I am proceeding to integration testing and I have to populate some hidden field. The problem is this seem not to be taken into account
it "submits project form" do
page.all("#project_description", :visible => false).set(#project.description)
click_button "submit"
expect(page).to have_content #project.description
end
the problem is that I get this error message when launching the task Description can't be blank. I am sure the the hidden field is found, but the filling is not done. How can I fix it ? (I set Capybara.ignore_hidden_elements to false)
It may not be the best solution, but you could do it with javascript. For exemple, if you're using jQuery:
page.execute_script "$('#project_description').val('#{#project.description}')"
If there are some ' characters in the #project.description, you need to escape them (see JavaScriptHelper, not sure how to use it in integration test).
I am new to Rspec and here I am trying to test one of the integration test. When I (Using Capybara) clicks button then page content gets replaced with post response. Now i am checking for page content, it present on the page but still my test is failing.
Below is the spec
it "get force check takeover1" do
visit('/las_xml')
select('Showroom', :from => 'urlVal')
fill_in('fpHeaderForce', :with => 'PSD.L.Syn')
fill_in('currentDate', :with => '2013-09-11')
click_button('submit')
page.should have_content('Buy 2013 Labor Law Posters')
end
But Result is,
1) las box get force check takeover1
Failure/Error: page.should have_content('Test page')
expected #has_content?("Buy 2013 Labor Law Posters") to return true, got false
# ./integration/las_box_spec.rb:19:in `block (2 levels) in <top (required)>'
and resulted response html contains,
<div class="las-link" id="">
<div class="laborLink_actual labor_01">
<span class="laborLink"></span>
Buy 2013 Labor Law Posters
</div>
</div>
Possible causes
Provided you're indeed testing a html page as your result excerpt seems to show, there may be several possibilities, here :
the text you're trying to test is not the actual one. Double check there's no typo and you use the same locale if multiple language website
the element containing the text may be hidden. Capybara won't consider the content to exist if it is not visible
the previous click on button may simply not work or may lead to an other page
Ways to isolate problem
As often with capybara since fails are just symptoms of real problem, we can deal with all problems using debugging features :
page.save_screenshot( filename ) : saves a screenshot to given file name (for example, 'shot.png')
save_and_open_page : open an inspectable html page in your browser
binding.pry : from pry gem, always useful to inspect context
For an example of screenshot creation through poltergeist, see this.
Edit : dynamic page problems
As mentioned in comments, problem seems to be tied to javascript content editing.
You should still use debugging mentioned before to be sure to isolate your problem. Once done, you can use capybara's #synchronize method to wait for your content :
page.document.synchronize( 10 ) do
result = page.evaluate_script %($(':contains("Post")').length)
raise Capybara::ElementNotFound unless result > 0
end
The synchronize method will be retried for 10 seconds, as long that the containing block raises a Capybara::ElementNotFound exception (any other exception is not catched). So basically, here, we check (using jQuery) if the "Post" content is present in page, and retry until it is or until ten seconds have passed.
If element was not found, test will fail by raising an uncaught exception.
If element is found, you can now proceed with your regular test. This allow to add timing condition in specs.
Artists cannot rate their own artworks
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
page.should have_content("Can't vote on your own artwork")
end
This was passing just fine.
However, I can't click on like_post_1 anymore because I added a feature to prevent voting links from appearing next to your own artworks.
Does this mean I no longer need test coverage for this scenario because it's extremely rare that someone can click on a voting link for their own artwork? Or should still have coverage to test the ajax response, because it's not tested anywhere else and it's possible for some stale page of links to somehow exist in a tabbed browser window. If so... how do I test it if I cannot call click_link?
I could try to create a POST request to create the vote, but capybara doesn't support posts, and I can't test the ajax response that way...
Or is there a way to simulate tabbed browsing in capybara?
Suggestions?
You can use CSS display:none or visibility:hidden for the own artwork instead of eliminating the link from the DOM. You might have to set
Capybara.ignore_hidden_elements = false
Another way is giving up Capybara and putting them into the controller/model spec. Controller/model spec might be a better place for the extremely rare case or safeguarding your app case.
Please try this:
Use sleep function.
sleep 10 use to wait process upto 10 seconds..
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
sleep 10
page.should have_content("Can't vote on your own artwork")
end