A JavaScript error in an E2E test with Selenium will not cause your test to fail. This may cause you to miss errors in your frontend code.
Using the BrowserConsole
helper below you can check your browser's error console from your E2E tests.
The following will raise BrowserConsole::ErrorsPresent
if there is an error on the browser console:
BrowserConsole.assert_no_errors!
Ignoring errors
You can ignore errors by their exact message:
BrowserConsole.ignore('Browser is burning')
You can ignore errors with messages matching a regular expression:
BrowserConsole.ignore(/burning/i)
You may also provide a block that inspects an
error object
Show archive.org snapshot
.
To ignore the error, return a truthy value:
BrowserConsole.ignore do |error|
error.message.size < 100
end
Default ignore rules
By default BrowserConsole
ignores:
- Warnings (
error.level === "WARNING"
) - Errors containing the phrase
Failed to load resource
To delete these defaults, call BrowseConsole.ignore_nothing
before your own ignore rules:
# Remove default rules
BrowserConsole.ignore_nothing
# Add your own rules
BrowserConsole.ignore(...)
BrowserConsole.ignore(...)
Automatic checking in RSpec feature specs
In your spec_helper.rb
you may configure to automatically check for errors after every user interaction that goes through Capybara.
This requires
capybara-lockstep
Show archive.org snapshot
1.3+.
Capybara::Lockstep.after_synchronize do
BrowserConsole.assert_no_errors!
end
Note that checking the browser console after every Capybara command will slow down your E2E tests somewhat. A compromise might be to only check the console when the spec has ended:
RSpec.configure do |config|
config.after do
BrowserConsole.assert_no_errors!
end
end
Ignoring JavaScript errors for a spec
We can configure RSpec to ignore JavaScript errors in specs tagged as { mute_js_errors: true }
:
RSpec.configure do |config|
config.around(mute_js_errors: true) do |example|
BrowserConsole.mute(&example)
end
end
We can now tag individual scenarios to ignore all JavaScript errors:
scenario 'Eating errors', js: true, mute_js_errors: true do
...
end
Automatic checking in Cucumber scenarios
In your env.rb
you may configure to automatically check for errors after every user interaction that goes through Capybara.
This requires
capybara-lockstep
Show archive.org snapshot
1.3+.
Capybara::Lockstep.after_synchronize do
BrowserConsole.assert_no_errors!
end
Note that checking the browser console after every Capybara command will slow down your E2E tests somewhat. A compromise might be to only check the console when the spec has ended:
After('not @mute-js-errors') do
BrowserConsole.assert_no_errors!
end
Ignoring JavaScript errors for a scenario
We can configure Cucumber to ignore JavaScript errors in specs tagged with @mute-js-errors
:
Around('@mute-js-errors') do |_scenario, block|
BrowserConsole.mute(&block)
end
To ignore JavaScript errors for an individual scenario, tag it with @mute-js-errors
:
@javascript @mute-js-errors
Scenario: Eating errors
...
The BrowserConsole
helper
class BrowserConsole
class ErrorsPresent < StandardError; end
class << self
def all_errors
if enabled?
driver_logs_proc.call.get(:browser)
else
[]
end
rescue Capybara::NotSupportedByDriverError
[]
end
def filtered_errors
all_errors.select do |error|
ignore_rules.none? do |rule|
case rule
when Proc
rule.call(error)
when Regexp
error.message =~ rule
when String
error.message == rule
end
end
end
end
def assert_no_errors!
errors = filtered_errors
if errors.present?
message = (['There are JavaScript errors:'] + errors.map(&:message)).join("\n\n")
raise ErrorsPresent, message
end
end
def ignore(pattern=nil, &block)
ignore_rules << (pattern || block)
end
def mute(&block)
@muted = true
if block
begin
block.call
ensure
unmute
end
end
end
def unmute
@muted = false
end
def self.current
@current ||= new
end
def ignore_nothing
@ignore_rules = []
end
private
def muted?
@muted || false
end
def ignore_rules
@ignore_rules ||= default_ignore_rules
end
def default_ignore_rules
[
/Failed to load resource/,
->(error) { error.level == 'WARNING' },
]
end
def page
Capybara.current_session
end
def enabled?
!muted? && driver_has_logs? && !alert_present?
end
def alert_present?
# Chrome 54 and/or Chromedriver 2.24 introduced a breaking change on how
# accessing browser logs work.
#
# Apparently, while an alert/confirm is open, Chrome will block any requests
# to its `getLog` API. This causes Selenium to time out with a `Net::ReadTimeout` error
page.driver.browser.switch_to.alert
true
rescue Capybara::NotSupportedByDriverError, Selenium::WebDriver::Error::NoSuchAlertError
false
end
def driver_has_logs?
!!driver_logs_proc
end
def driver_logs_proc
browser = page.driver.browser
if browser.respond_to?(:logs) # selenium-webdriver >= 4
proc { browser.logs }
elsif browser.respond_to?(:manage) && browser.manage.respond_to?(:logs) # selenium-webdriver < 4
proc { browser.manage.logs }
end
end
end
end