Skip to main content

Timeouts

Each command in WebdriverIO is an asynchronous operation. A request is fired to the Selenium server (or a cloud service like Sauce Labs), and its response contains the result once the action has completed or failed.

Therefore, time is a crucial component in the whole testing process. When a certain action depends on the state of a different action, you need to make sure that they get executed in the right order. Timeouts play an important role when dealing with these issues.

WebDriver Timeouts​

Session Script Timeout​

A session has an associated session script timeout that specifies a time to wait for asynchronous scripts to run. Unless stated otherwise, it is 30 seconds. You can set this timeout like so:

await browser.setTimeout({ 'script': 60000 })
await browser.execute(async () => {
console.log('this should not fail')
await new Promise((resolve) => setTimeout(resolve, 59000))
})

Session Page Load Timeout​

A session has an associated session page load timeout that specifies a time to wait for the page loading to complete. Unless stated otherwise, it is 300,000 milliseconds.

You can set this timeout like so:

await browser.setTimeout({ 'pageLoad': 10000 })

pageLoad is the WebDriver timeouts name. WebdriverIO v10 accepts that key only.

Session Implicit Wait Timeout​

A session has an associated session implicit wait timeout. This specifies the time to wait for the implicit element location strategy when locating elements using the findElement or findElements commands ($ or $$, respectively, when running WebdriverIO with or without the WDIO testrunner). Unless stated otherwise, it is 0 milliseconds.

You can set this timeout via:

await browser.setTimeout({ 'implicit': 5000 })

WaitFor* timeout​

WebdriverIO provides multiple commands to wait on elements to reach a certain state (e.g. enabled, visible, existing). These commands take a selector argument and a timeout number, which determines how long the instance should wait for that element to reach the state. The waitforTimeout option allows you to set the global timeout for all waitFor* commands, so you don't need to set the same timeout over and over again. (Note the lowercase f!)

// wdio.conf.js
export const config = {
// ...
waitforTimeout: 5000,
// ...
}

In your tests, you now can do this:

const myElem = await $('#myElem')
await myElem.waitForDisplayed()

// you can also overwrite the default timeout if needed
await myElem.waitForDisplayed({ timeout: 10000 })

An element that the page removed​

When the page removes or renders again an element, WebdriverIO finds it again with its selector and waits up to waitforTimeout. If it is there, the command runs on it. If not, the command fails with:

  • message: Can't call <command> on element with selector "<selector>" because element wasn't found (a $$() item: … "<selector>" at index <n> …; a removed parent: the message is about the parent)
  • cause: the stale element reference error of the driver

waitUntil and waitForEnabled fail with their own error, which has this error as cause. isDisplayed and isExisting of the removed element do not wait: they give false. For a child of a removed parent, they wait and fail with this error.

test/specs/removed.e2e.ts
import { $, browser, expect } from '@wdio/globals'

describe('a removed element', () => {
it('gives the error of an element that was not found again', async () => {
await browser.url('https://webdriver.io')
const title = await $('h1')
await browser.execute(() => document.querySelectorAll('h1').forEach((h1) => h1.remove()))

const error = await title.getText().then(() => null, (err: Error) => err)
expect(error?.message).toContain('because element wasn\'t found')
})
})

The testing framework you’re using with WebdriverIO has to deal with timeouts, especially since everything is asynchronous. It ensures that the test process doesn't get stuck if something goes wrong.

By default, the timeout is 10 seconds, which means that a single test should not take longer than that.

A single test in Mocha looks like:

it('should login into the application', async () => {
await browser.url('/login')

const form = await $('form')
const username = await $('#username')
const password = await $('#password')

await username.setValue('userXY')
await password.setValue('******')
await form.submit()

expect(await browser.getTitle()).to.be.equal('Admin Area')
})

In Cucumber, the timeout applies to a single step definition. However, if you want to increase the timeout because your test takes longer than the default value, you need to set it in the framework options.

// wdio.conf.js
export const config = {
// ...
framework: 'mocha',
mochaOpts: {
timeout: 20000
},
// ...
}

Mocha timeouts and the WebdriverIO hooks​

A Mocha test or hook can change its own timeout with this.timeout(ms). Use a function, not an arrow function, to get this. WebdriverIO follows the change, also this.timeout(0), which turns the timeout off. With mochaOpts.parallelMode, only mochaOpts.timeout applies: this.timeout() in a test or hook throws, and a timeout of a suite or of it(...).timeout() is ignored, with a warning. See Parallel Tests in a Spec File.

The timeout of a test includes the time of its beforeTest hook, as Mocha starts the timer before the hook. With the retry option of WebdriverIO, all attempts of a test share one timeout.

When a test or hook times out, WebdriverIO runs afterTest (or afterHook) before Mocha starts the next test, so that hook cannot run at the same time as the next test. Mocha waits for these hooks up to one more timeout of the test.

Welcome! How can I help?

WebdriverIO AI Copilot