Selenium: Disable CSS transitions and fail on JavaScript errors
I've done both steps for a second time (in a different project) now, so I thought it is worth a blog post.
1. CSS transitions
If you use CSS animations, you have three choices for your tests:
- Sleep for a specific amount of time and hope the animation has finished after
- Introduce wait statements to make sure the animation ended
- Disable animations
Option 1. was out of the question, sleep statements are just not reliable and bad practice.
I stepped away from 2., because it might be a) a lot of statements which clutter your test and b) there's no out-of-the-box solution provided by Selenium. You'd have to write your own hacky one.
So I tried 3.
Some helpful links:
- https://www.lambdatest.com/blog/java-event-listeners/
- https://stackoverflow.com/questions/57481903/run-javascript-after-every-page-load
- https://stackoverflow.com/questions/51007542/how-to-turn-off-css-for-faster-automation-tests-execution
I ended up with this in Kotlin (kotest) using the Testcontainers Selenium solution. Still hacky, but my tests run reliably without the sleep statements now. :)
(chrome
is a BrowserWebDriverContainer)
Selenium 3 (with kotest 4):
override fun beforeTest(testCase: TestCase) {
super.beforeTest(testCase)
driver = EventFiringWebDriver(chrome.webDriver)
driver.register(object : AbstractWebDriverEventListener() {
override fun afterNavigateTo(url: String, driver: WebDriver) {
val disableTransitions =
"const styleElement = document.createElement('style');styleElement.setAttribute('id','disable-transitions');const styleTagCSSes = document.createTextNode('*,:after,:before{-webkit-transition:none!important;-moz-transition:none!important;-ms-transition:none!important;-o-transition:none!important;transition:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important;-o-transform:none!important;transform:none!important}');styleElement.appendChild(styleTagCSSes);document.head.appendChild(styleElement);"
(driver as JavascriptExecutor).executeScript(disableTransitions)
super.afterNavigateTo(url, driver)
}
})
}
Selenium 4 (with kotest 5):
override suspend fun beforeTest(testCase: TestCase) {
super.beforeTest(testCase)
val disableAnimationsListener = object : WebDriverListener {
override fun afterGet(driver: WebDriver, url: String) {
applyStyle("*,:after,:before{-webkit-transition:none!important;-moz-transition:none!important;-ms-transition:none!important;-o-transition:none!important;transition:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important;-o-transform:none!important;transform:none!important}")
super.afterGet(driver, url)
}
}
driver = EventFiringDecorator(disableAnimationsListener).decorate(chrome.webDriver)
}
2. JavaScript console logging errors
Second thing I wanted to check is that no JavaScript errors occured during the test.
Step one: I enabled logging of all levels, just in case I want to inspect non-errors also:
val loggingPreferences = LoggingPreferences()
loggingPreferences.enable(BROWSER, ALL)
val options = ChromeOptions()
options.setCapability(LOGGING_PREFS, loggingPreferences)
options.setCapability("goog:loggingPrefs", loggingPreferences)
The goog:loggingPrefs
is for Google Chrome only, see here.
Update: With Selenium 4 there is now a constant: org.openqa.selenium.chrome.ChromeOptions#LOGGING_PREFS. The previous LOGGING_PREFS from org.openqa.selenium.remote.CapabilityType.LOGGING_PREFS is now deprecated.
Step two: Assert the logs (via kotest again):
fun assertBrowserLogs() {
val entries = driver.manage().logs().get(BROWSER)
val warningsAndHigher = entries.filter { it.level.intValue() >= WARNING.intValue() }
withClue("Browser logs") { warningsAndHigher.shouldBeEmpty() }
}