Since upgrading to Capybara 2.4, I've been running into this issue. Previously, this block worked fine:
page.document.synchronize do
page.should have_no_css('#ajax_indicator', :visible => true)
end
It's meant to force waiting until the ajax indicator disappears before proceeding with the next step.
Since the above returns a RSpec::Expectations::ExpectationNotMetError, the synchronize doesn't rerun the block and instead just throws the error. Not sure why this was working in the version I was using before (I believe 2.1).
The synchronize block only reruns blocks that return something like:
Capybara::ElementNotFound
Capybara::ExpectationNotMet
And whatever a certain driver adds to that list.
See Justin's response for a more comprehensive explanation and examples not using synchronize, or look at my response for the direct solution.
Follow-up: I've since revisited this problem so here's a few tips:
1) document.synchronize more often than not does nothing useful, as mentioned by others most of the finders have built-in wait. You can manually force it to not wait using wait: 0 when that makes sense.
Note that due to this, the following are not equivalent:
!assert_css('#ajax_indicator')
assert_no_css('#ajax_indicator')
The former will wait until the element exists, while the latter will wait until the element doesn't exist, even if they are otherwise logically equivalent.
2) Problems that lead to us initially inserting synchronize were due to a chicken and egg problem of sorts.
#ajax_indicator in this case will appear when there is active loading, then disappear after. We cannot distinguish from the indicator having not appeared yet, and having appeared then disappeared.
Although I have yet to 100% resolve this issue, what has improved our test reliability is looking for indicators to ensure page loading has reached a certain point, e.g.
do_thing_that_triggers_ajax
find('#thing-that-should-exist')
assert_no_selector('#ajax_indicator', visible: true)
This differents from asserting #ajax_indicator exists and then assert it doesn't exist, because in that case if the ajax happens too fast capybara may not catch it in action.
Depending on your scripts, you could possibly find more reliable indicators.
The have_no_css matcher already waits for the element to disappear. The problem seems to be using it within a synchronize block. The synchronize method only re-runs for certain exceptions, which does not include RSpec::Expectations::ExpectationNotMetError.
Removing the synchronize seems to do what you want - ie forces a wait until the element disappears. In other words, just do:
page.should have_no_css('#ajax_indicator', :visible => true)
Working Example
Here is a page, say "wait.htm", that I think reproduces your problem. It has a link that when clicked, waits 6 seconds and then hides the indicator element.
<html>
<head>
<title>wait test</title>
<script type="text/javascript" charset="utf-8">
function setTimeoutDisplay(id, display, timeout) {
setTimeout(function() {
document.getElementById(id).style.display = display;
}, timeout);
}
</script>
</head>
<body>
<div id="ajax_indicator" style="display:block;">indicator</div>
<a id="hide_foo" href="#" onclick="setTimeoutDisplay('ajax_indicator', 'none', 6000);">hide indicator</a>
</body>
</html>
The following spec shows that by using the page.should have_no_css without manually calling synchronize, Capybara is already forcing a wait. When waiting only 2 seconds, the spec fails since the element does not disappear. When waiting 10 seconds, the spec passes since the element has time to disappear.
require 'capybara/rspec'
Capybara.run_server = false
Capybara.current_driver = :selenium
Capybara.app_host = 'file:///C:/test/wait.htm'
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = [:should, :expect]
end
end
RSpec.describe "#have_no_css", :js => true, :type => :feature do
it 'raise exception when element does not disappear in time' do
Capybara.default_wait_time = 2
visit('')
click_link('hide indicator')
page.should have_no_css('#ajax_indicator', :visible => true)
end
it 'passes when element disappears in time' do
Capybara.default_wait_time = 10
visit('')
click_link('hide indicator')
page.should have_no_css('#ajax_indicator', :visible => true)
end
end
Since version of Capybara 2.0 you can customize inline wait time parameter to pass it into the #have_no_css method:
page.should have_no_css('#ajax_indicator', visible: true, wait: 3)
The solution I've settled for is the following:
page.document.synchronize do
page.assert_no_selector('#ajax_indicator', :visible => true)
end
The assert_no_selector method properly throws a Capybara::ExpectationNotMet error and appears to work in the same way as has_no_css, so I'm satisfied with this solution.
I still have no idea why the RSpec error is being thrown for some methods but not others.
Edit: While this works it isn't actually a good idea to do so, see other responses.
Related
Newbie engineer question here, but I've searched and can't find a solution.
Environment: Mac OS, Chrome, Capybara with Selenium Chrome-driver, Ruby with Rspec running the test.
Situation:
Testing a React app where the user logs in using a username and password, followed by clicking on a sidebar nav link that loads up....followed by other actions. Capybara continues to fail to find the sidebar nav link to click on. I believe the sidebar nav is its own React component and loads asynchronously
App & Test Behavior:
Sometimes Capybara finds the link element, clicks the link and the test passes. Other times it completely fails to find the element and fails the test.
Solutions I've tried:
Upping the default time for finder methods to continue to search to 15+ seconds(I've never noticed the app take more than 5 seconds on anything)
I only have used finder methods that SHOULD be repeat-searching for the default time for the element to appear (page.find, page.has_css?, etc)before attempting the .click action. What I have found is that after the login happens, and while the nav is loading, the test just seems to fail with element not found. The page.find does NOT seem to continue to search for 15 seconds before failing - instead, the login happens, then a second later I get a fail with element not found.
I have tried passing a wait into the find (example: page.find(some element, wait:15).click . This runs into the same problem as above where it doesn't seem to continue searching for 15 seconds for the element to appear and then click it.
What does seem to work is adding in sleeps before searching for an element (example: login, sleep(5), page.find(something).click).
Unfortunately I'm having this same problem with other elements all over in the app - for example a div may have a bunch of cards in it. When a new card is added it takes 2-3 seconds to show up in the list (probably due to sending out the card info to the database, and refreshing the cards on the page). If I try and page.find immediately after adding a card, the test will almost immediately fail with an element not found message. If I add the card, sleep(3), THEN try page.find, it will find it.
I can't just add sleep all over the place in the app, because its huge and it would slow down the tests immensely. I think I've tried all the typically suggested fixes for asynchronous loading. Any idea what is going on here and what I can do to fix it
editing to add some requested code.
I'm using capybara 3.2.
We are using a bit of a page object style framework so I"ll try and post the actual test with its methods in bold and helper methods nested in it.
Its getting caught in the before action of this particular feature on the final method to click on the sidebar. I'm a little limited on what I can show, but I think this will make sense....
The actual before action:
before do
**app.launch_app(app.url)**
# this instantiates an instance of the framework and helper methods and # goes to the app url
**app.login.full_login(app.username('Some User'), app.password)**
# def full_login(user, pass)
# enter_email(user)
# def enter_email(user)
# return if already_logged_in?
# def already_logged_in?
# page.has_css?("a[href*='/me']", wait: false)
# end
# fill_field('email', user)
# def fill_field(field, text)
# sleep 0.1
# page.find("input[type=#{field}]").send_keys text
# end
# click_button 'Log In'
# def click_button(text)
# page.click_on text
# end
# end
# login_using_second_auth(user, pass)
# def login_using_second_auth(user, pass)
# page.fill_in 'username', with: user
# page.fill_in 'password', with: pass
# click_button 'Sign In'
# end
# end
app.nav.click_sidebar_link('Admin Account', 'Admin')
# def click_sidebar_link(link, section)
# sleep(2)
# page.find('div[class^=app__wrapper] > div > div > div', text:
# section)
# .find('span[class^=nav-item]', text: link).click
# end
end
Sorry that looks so messy, but I think you guys can make sense of it.
The test is flaky so after running it a few times I can't get the exact error, but its usually element not found on the span with the text Admin
Recently we upgraded our selenium web driver from 2.47.1 to 2.48.0.
With this upgrade I need to add sleep for a few seconds in rspec to pass. Spec was working properly without sleep with the older version.
sleep(inspection_time=5) // why do I need this?
my_form_page.save_button.click
// some assertion here
Edit
I tried using implicit wait instead of sleep.But it's not working. Is there any specific reason behind it?
Capybara.current_session.driver.browser.manage.timeouts.implicit_wait = 50
Generally speaking, rspec selenium tests are known to be "flakey". Sometimes rspec tries to search for an element before it appears on page due to many reasons (ie: element appears upon ajax response).
Here's a tip that may help you solve this, if you will wrap your capybara finders inside of a within block, your tests will wait until it finds that within selector FIRST before trying to run the code inside of it.
This more-often-than-not will help solve a test running too fast on a page that takes a while to load and your button or selector or whatever isn't actually on the page yet (which is why it fails).
So take a look at these 2 examples and try the within method...
# spec/features/home_page_spec.rb
require "spec_helper"
describe "the home page", type: :feature do
context "form" do
# THIS MIGHT FAIL!!!!
it "submits the form", js: true, driver: :selenium do
visit "/"
find("#submit_button").click
end
# THIS PROBABLY WILL PASS!!!
it "submits the form", js: true, driver: :selenium do
visit "/"
within "form" do
find("#submit_button").click
end
end
end
end
I'm using the Selenium Webdriver gem to try to click on the facebook chat bar, sometimes it work and sometimes it doesn't. When it does not work it returns the Selenium Element not visible error, but it clearly is visible. I'm not sure what's wrong with my code.
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome # instantiates a google chrome session
driver.navigate.to 'https://www.facebook.com/' # takes you to facebook.com
emailBar = driver.find_element(:id,"email") #finds email input bar
passwordBar = driver.find_element(:id,"pass") #find password input bar
loginButton = driver.find_element(:id,"u_0_n") #finds login button
emailBar.send_keys "austinspreadsheet#gmail.com" # puts in the email
passwordBar.send_keys "YOURPASSWORD" # puts in the password
loginButton.click # clicks the login button
#THIS IS THE CODE BLOCK THAT DOES NOT WORK
links = driver.find_elements(:class,"fbNubButton") # finds the chat bar
#driver.manage.timeouts.page_load = 10
links[0].click # opens the chat bar
links[1].click # NOTE that sometime it clicks and sometimes it doesn't but if you click both chat box classes it usually works, so the error is ok
I have tried not clicking both chat links and it works less when I do that.
I am using Selenium with Python. In case like yours the issue is related to waiting until all the elements in the page are full loaded.
The basic behavior of Selenium offers you Explicit and Implicit Waits. So basicly you can force the system to wait a default number of second or wait until an element is loaded.
From Selenium documentation (http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp)
Explicit wait
An explicit waits is code you define to wait for a certain condition to occur before proceeding further in the code. The worst case of this is Thread.sleep(), which sets the condition to an exact time period to wait. There are some convenience methods provided that help you write code that will wait only as long as required. WebDriverWait in combination with ExpectedCondition is one way this can be accomplished.
require 'rubygems' # not required for ruby 1.9 or if you installed without gem
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://somedomain/url_that_delays_loading"
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
begin
element = wait.until { driver.find_element(:id => "some-dynamic-element") }
ensure
driver.quit
end
Implicit wait
An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object instance.
require 'rubygems' # not required for ruby 1.9 or if you installed without gem
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.manage.timeouts.implicit_wait = 10 # seconds
driver.get "http://somedomain/url_that_delays_loading"
element = driver.find_element(:id => "some-dynamic-element")
The answer that aberna gives you on this thread has a lot of great information but it isn't going to solve your issue. If you use the Explicit wait method that aberna suggests, you also probably need to make sure the element is visible. Using .findElements on its own doesn't guarantee clickability/visibility . You could try to use expectedconditions .visibilityOfElementLocated which will also check for visibility as well as presence.
Or, alternatively, you can check for presence of the element on the DOM using .findElement but then use the expectedconditions.visibilityOf to check for the visibility part of it.
I am using sleep(5) before run main logic
I was facing the same issue. Solution that worked for me was to maximise my browser window. This solved many of failing specs.
Capybara.current_session.driver.browser.manage.window.maximize
I have to say I am new both to Ruby and to RSpec. Anyway I completed one RSpec script but after refactoring it failed. Here is the original working version:
describe Site do
browser = Watir::Browser.new :ie
site = Site.new(browser, "http://localhost:8080/site")
it "can navigate to any page at the site" do
site.pages_names.each do |page_name|
site.goto(page_name)
site.actual_page.name.should eq page_name
end
end
browser.close
end
and here is the modified version - I wanted to have reported all the pages which were visited during the test:
describe Site do
browser = Watir::Browser.new :ie
site = Site.new(browser, "http://localhost:8080/site")
site.pages_names.each do |page_name|
it "can navigate to #{page_name}" do
site.goto(page_name)
site.actual_page.name.should eq page_name
end
end
browser.close
end
The problem in the latter case is that site gets evaluated to nil within the code block associated with 'it' method.
But when I did this:
...
s = site
it "can navigate to #{page_name}" do
s.goto(page_name)
s.actual_page.name.should eq page_name
end
...
the nil problem was gone but tests failed with the reason "browser was closed"
Apparently I am missing something very basic Ruby knowledge - because the browser reference is not working correctly in modified script. Where did I go wrong? What refactoring shall be applied to make this work?
Thanks for your help!
It's important to understand that RSpec, like many ruby programs, has two runtime stages:
During the first stage, RSpec loads each of your spec files, and executes each of the describe and context blocks. During this stage, the execution of your code defines your examples, the hooks, etc. But your examples and hooks are NOT executed during this stage.
Once RSpec has finished loading the spec files (and all examples have been defined), it executes them.
So...trimming down your example to a simpler form, here's what you've got:
describe Site do
browser = Watir::Browser.new :ie
it 'does something with the browser' do
# do something with the browser
end
browser.close
end
While visually it looks like the browser instance is instantiated, then used in the example, then closed, here's what's really happening:
The browser instance is instantiated
The example is defined (but not run)
The browser is closed
(Later, after all examples have been defined...) The example is run
As O.Powell's answer shows, you can close the browser in an after(:all) hook to delay the closing until after all examples in this example group have run. That said, I'd question if you really need the browser instance at example definition time. Generally you're best off lazily creating resources (such as the browser instance) when examples need them as they are running, rather than during the example definition phase.
I replicated your code above using fake classes for Site and Watir. It worked perfectly. My only conclusion then is that the issue must lie with either one of the above classes. I noticed the Site instance only had to visit one page in your first working version, but has to visit multiple pages in the non working version. There may be an issue there involving the mutation happening inside the instance.
See if this makes a difference:
describe Site do
uri = "http://localhost:8080/site"
browser = Watir::Browser.new :ie
page_names = Site.new(browser, uri).page_names
before(:each) { #site = Site.new(browser, uri) }
after(:all) { browser.close }
pages_names.each do |page_name|
it "can navigate to #{page_name}" do
#site.goto(page_name)
#site.actual_page.name.should eq page_name
end
end
end
I am trying to test selecting an option from select tag (these options are fetched from a remote database server). During normal interation with the website, it does not take more than a fraction of a second to populate this dropdown. However, when I run the following test,
When /^(?:|I )select "([^"]*)" from "([^"]*)" in search form$/ do |value, field|
within "#select_container" do
save_and_open_page
page.should have_css("#criteria_div_code > option:nth-child(10)")
select(value, :from => field)
end
end
I get the following error,
expected css "#criteria_div_code > option:nth-child(10)" to return something (RSpec::Expectations::ExpectationNotMetError)
The dropdown is populated with at least 20 options and so I just test for the presence of the 10th option (for now).
save_and_open_page shows that only one option (default option) exists instead of at least 10 and hence the "ExpectionNotMetError" comes up.
Capybara.default_wait_time = 30 - Ample time for the lists to get populated.
Isn't capybara waiting for the ajax call to finish?
Am I missing something here?
You might want to check my response to setting timeouts for ajax resynchronization Using Capybara for AJAX integration tests. Resynchronization timeout defaults to 10secs and if your response does not return before that time, you will not get any responses especially if you have set :resynchronize to false in your configurations. below is a snippet to set that timeout
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :firefox, :resynchronization_timeout => 1000)
end
NOTE: if you previously set :resynchronize to false, you need to set this to true.
I guess you need to user js driver for ajax testing,
describe 'some stuff which requires js', :js => true do
it 'will use the default js driver'
it 'will switch to one specific driver', :driver => :celerity
end
Also note the following line - Capybara can block and wait for Ajax requests to finish after you’ve interacted with the page. To enable this behaviour, set the :resynchronize driver option to true.