When ending a Selenium test Capybara resets the browser state by closing the tab, clearing cookies, localStorage
, etc.
It may be a good idea to wait for all in-flight AJAX requests to finish before ending a scenario:
- You may have client-side JavaScript that freaks out when the tab closure kills their pending requests. If that JavaScript opens an error alert or spams errors to the console, your test may fail after the last step.
- With unlucky timing the server may receive an AJAX request as the browser tab closes, causing a connection abort when trying to send the response. Since Capybara fails a test if there is a server error this may produce a flaky test.
Waiting for pending AJAX requests
The following module exposes a method conclude_browser_requests
. It prevents the browser from sending further AJAX requests and waits until all in-flight requests have finished:
module ConcludeBrowserRequests
def conclude_browser_requests
return unless page.driver.is_a?(Capybara::Selenium::Driver)
page.execute_script(<<-JS)
window.XMLHttpRequest.prototype.send = function() {
// Don't send a request. The readyState will remain at XMLHttpRequest.UNSENT.
}
window.fetch = function() {
// To prevent callbacks, return a promise that never settles.
return new Promise(function() {})
}
JS
Capybara::Lockstep.synchronize
end
end
Note that this method uses capybara-lockstep Show archive.org snapshot in the last line.
Include this module in your test suite and call conclude_browser_requests
at the end of each test. For example in a Cucumber suite:
World(ConcludeBrowserRequests)
After do
conclude_browser_requests
end
Legacy solution for jQuery frontends
The module above is compatible with all JavaScript frameworks (or VanillaJS). If you cannot use capybara-lockstep Show archive.org snapshot and you're working on an application with a pure jQuery frontend, you may also use the module below stead.
Note that this will only stop and wait for requests made by jQuery's $.ajax()
or $.get()
functions.
module ConcludeBrowserRequestsWithJquery
def conclude_browser_requests_with_jquery
return unless page.driver.is_a?(Capybara::Selenium::Driver)
# disable future ajax requests
page.execute_script <<-JAVASCRIPT
if ( (typeof jQuery) !== 'undefined' ) {
jQuery.ajaxPrefilter(function(options, originalOptions, jqXHR) {
jqXHR.abort();
});
}
JAVASCRIPT
# wait for active ajax requests to complete
patiently do
page.execute_script("return (typeof jQuery) === 'undefined' || jQuery.active === 0").should == true
end
end
end
The code above depends on
Spreewald
Show archive.org snapshot
for patiently
.