Frameworks
WebdriverIO Runner has built-in support for Mocha, Jasmine, and Cucumber.js. You can also integrate it with 3rd-party open-source frameworks, such as Serenity/JS.
To integrate WebdriverIO with a test framework, you need an adapter package available on NPM. Note that the adapter package must be installed in the same location where WebdriverIO is installed. So, if you installed WebdriverIO globally, be sure to install the adapter package globally, too.
Integrating WebdriverIO with a test framework lets you access the WebDriver instance using the global browser variable
in your spec files or step definitions.
Note that WebdriverIO will also take care of instantiating and ending the Selenium session, so you don't have to do it
yourself.
Using Mochaโ
First, install the adapter package from NPM:
- npm
- Yarn
- pnpm
- Bun
npm install @wdio/mocha-framework --save-dev
yarn add @wdio/mocha-framework --dev
pnpm add @wdio/mocha-framework --save-dev
bun add @wdio/mocha-framework --dev
By default WebdriverIO provides an assertion library that is built-in which you can start right away:
describe('my awesome website', () => {
it('should do some assertions', async () => {
await browser.url('https://webdriver.io')
await expect(browser).toHaveTitle('WebdriverIO ยท Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
})
})
WebdriverIO v10 ships Mocha 12 and supports Mocha's BDD (default), TDD, and QUnit interfaces.
If you like to write your specs in TDD style, set the ui property in your mochaOpts config to tdd. Now your test files should be written like this:
suite('my awesome website', () => {
test('should do some assertions', async () => {
await browser.url('https://webdriver.io')
await expect(browser).toHaveTitle('WebdriverIO ยท Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
})
})
If you want to define other Mocha-specific settings, you can do it with the mochaOpts key in your configuration file. A list of all options can be found on the Mocha project website.
Note: WebdriverIO does not support the deprecated usage of done callbacks in Mocha:
it('should test something', (done) => {
done() // throws "done is not a function"
})
Mocha Optionsโ
The following options can be applied in your wdio.conf.js to configure your Mocha environment. Note: not all Mocha options are supported. parallel still belongs to Mocha's own worker pool and will error here โ the WDIO testrunner already parallelizes specs across capabilities and workers. Mocha 12's CLI also moved off yargs onto Node's util.parseArgs; that only affects a direct mocha invocation, not mochaOpts passed through wdio. You can pass these framework options as arguments, e.g.:
wdio run wdio.conf.ts --mochaOpts.grep "my test" --mochaOpts.bail --no-mochaOpts.checkLeaks
This will pass along the following Mocha options:
{
grep: ['my-test'],
bail: true
checkLeacks: false
}
The following Mocha options are supported:
requireโ
- Type
string|string[]- Default
[]
The require option is useful when you want to add or extend some basic functionality (WebdriverIO framework option).
allowUncaughtโ
- Type
boolean- Default
false
Propagate uncaught errors.
bailโ
- Type
boolean- Default
false
Bail after first test failure.
checkLeaksโ
- Type
boolean- Default
false
Check for global variable leaks.
delayโ
- Type
boolean- Default
false
Delay root suite execution.
failHookAffectedTestsโ
- Type
boolean- Default
true
Report each test skipped by a failing before or beforeEach hook as a failure. WebdriverIO enables this so a broken setup hook is visible on every spec it skipped. Set it to false to report only the hook.
fgrepโ
- Type
string- Default
null
Test filter given string.
forbidOnlyโ
- Type
boolean- Default
false
Tests marked only fail the suite.
forbidPendingโ
- Type
boolean- Default
false
Pending tests fail the suite.
fullTraceโ
- Type
boolean- Default
false
Full stacktrace upon failure.
globalโ
- Type
string[]- Default
[]
Variables expected in global scope.
grepโ
- Type
RegExp|string- Default
null
Test filter given regular expression. Mocha 12 accepts modern RegExp flags in this filter (for example s or d).
invertโ
- Type
boolean- Default
false
Invert test filter matches.
retriesโ
- Type
number- Default
0
Number of times to retry failed tests.
timeoutโ
- Type
number- Default
30000
Timeout threshold value (in ms).
Using Jasmineโ
First, install the adapter package from NPM:
- npm
- Yarn
- pnpm
- Bun
npm install @wdio/jasmine-framework --save-dev
yarn add @wdio/jasmine-framework --dev
pnpm add @wdio/jasmine-framework --save-dev
bun add @wdio/jasmine-framework --dev
You can then configure your Jasmine environment by setting a jasmineOpts property in your config. A list of all options can be found on the Jasmine project website.
Jasmine Optionsโ
The following options can be applied in your wdio.conf.js to configure your Jasmine environment using the jasmineOpts property. For more information on these configuration options, check out the Jasmine docs. You can pass these framework options as arguments, e.g.:
wdio run wdio.conf.ts --jasmineOpts.grep "my test" --jasmineOpts.failSpecWithNoExpectations --no-jasmineOpts.random
This will pass along the following Jasmine options:
{
grep: 'my test',
failSpecWithNoExpectations: true,
random: false
}
The following Jasmine options are supported:
defaultTimeoutIntervalโ
- Type
number- Default
60000
Default Timeout Interval for Jasmine operations.
helpersโ
- Type
string[]- Default
[]
Array of filepaths (and globs) relative to spec_dir to include before jasmine specs.
requiresโ
- Type
string[]- Default
[]
The requires option is useful when you want to add or extend some basic functionality.
randomโ
- Type
boolean- Default
false
Whether to randomize spec execution order. Jasmine's own default is true, but WebdriverIO runs the specs in order unless you set this option.
seedโ
- Type
Function- Default
null
Seed to use as the basis of randomization. Null causes the seed to be determined randomly at the start of execution.
failSpecWithNoExpectationsโ
- Type
boolean- Default
false
Whether to fail the spec if it ran no expectations. By default a spec that ran no expectations is reported as passed. Setting this to true will report such spec as a failure.
oneFailurePerSpecโ
- Type
boolean- Default
false
Stop a spec at its first failed expectation. A failed sync matcher stops the spec at once, and an awaited async matcher stops it when its promise settles. The other specs continue to run.
specFilterโ
- Type
Function- Default
(spec) => true
Function to use to filter specs.
grepโ
- Type
string|Regexp- Default
null
Only run tests matching this string or regexp. (Only applicable if no custom specFilter function is set)
invertGrepโ
- Type
boolean- Default
false
If true it inverts the matching tests and only runs tests that don't match with the expression used in grep. (Only applicable if no custom specFilter function is set)
stopOnSpecFailureโ
- Type
boolean- Default
false
Stop the spec file at its first failed spec (it): the other specs of the file do not run, also in other describe blocks. Other spec files run in their own workers and continue.
cleanStackโ
- Type
boolean- Default
true
Remove the lines of node_modules packages from the stack traces of failures.
expectationResultHandlerโ
- Type
Function- Default
null
Called with (passed, assertion) for each expectation, for example to take a screenshot when an expectation fails. If the function throws for a passed expectation, the expectation fails with that error.
Assertionsโ
With Jasmine, the global expect combines Jasmine's matchers and the WebdriverIO matchers:
- Jasmine's matchers (
toBe,toEqual,toHaveBeenCalled, โฆ) and the matchers that you add withjasmine.addMatchersare synchronous. They returnundefined, so you do not needawait. - WebdriverIO matchers, Jasmine's async matchers (
toBeResolved,toBeRejectedWith, โฆ) and the matchers that you add withjasmine.addAsyncMatchersreturn a promise. Alwaysawaitthem.
Use expect() for both kinds: it sends each matcher to Jasmine's expect or expectAsync for you. await expectAsync($('#logo')).toBeDisplayed() also works. For TypeScript, expectAsync() with WebdriverIO matchers needs expect-webdriverio/jasmine in types.
it('checks the page', async () => {
expect([1, 2]).toHaveSize(2) // Jasmine, sync
await expect($('#logo')).toHaveSize({ width: 32, height: 32 }) // WebdriverIO, async
await expect(loadData()).toBeResolved() // Jasmine async matcher
})
toHaveSize exists in both libraries. The WebdriverIO matcher runs on WebdriverIO values: an element, an element array or Element[] (for example the result of $$().filter()), a multi-remote element, a browser, a browsing context, a mock, the some() wrapper, or a promise such as a chainable $(). Jasmine's matcher runs on every other value.
The asymmetric matchers of both libraries work, in Jasmine and in WebdriverIO matchers: jasmine.any(), jasmine.objectContaining(), jasmine.stringMatching(), โฆ and expect.any(), expect.stringContaining(), expect.oneOf(), expect.not.stringContaining(), โฆ. To use some(), import it:
import { some } from 'expect-webdriverio/api'
await expect(some($$('li'))).toHaveAttribute('data-state', 'on')
The Jest parts of expect are not available with Jasmine: Jest-only matchers such as toStrictEqual or toHaveLength, expect.soft() and expect.extend(). To add a custom matcher, use jasmine.addMatchers for a sync matcher or jasmine.addAsyncMatchers for an async matcher.
For TypeScript, add jasmine to types, see TypeScript Setup.
Using Cucumberโ
First, install the adapter package from NPM:
- npm
- Yarn
- pnpm
- Bun
npm install @wdio/cucumber-framework --save-dev
yarn add @wdio/cucumber-framework --dev
pnpm add @wdio/cucumber-framework --save-dev
bun add @wdio/cucumber-framework --dev
If you want to use Cucumber, set the framework property to cucumber by adding framework: 'cucumber' to the config file .
Options for Cucumber can be given in the config file with cucumberOpts. Check out the whole list of options here. The adapter uses Cucumber 13. tagExpression has been removed; filter with tags. See the v10 migration guide.
To get up and running quickly with Cucumber, have a look on our cucumber-boilerplate project that comes with all the step definitions you need to get stared, and you'll be writing feature files right away.
Cucumber Optionsโ
The following options can be applied in your wdio.conf.js to configure your Cucumber environment using the cucumberOpts property:
The cucumberOpts, such as custom tags for filtering tests, can be specified through the command line. This is accomplished by using the cucumberOpts.{optionName}="value" format.
For example, if you want to run only the tests that are tagged with @smoke, you can use the following command:
# When you only want to run tests that hold the tag "@smoke"
npx wdio run ./wdio.conf.js --cucumberOpts.tags="@smoke"
npx wdio run ./wdio.conf.js --cucumberOpts.name="some scenario name" --cucumberOpts.failFast
This command sets the tags option in cucumberOpts to @smoke, ensuring that only tests with this tag are executed.
backtraceโ
- Type
Boolean- Default
true
Show full backtrace for errors.
requireModuleโ
- Type
string[]- Default
[]
Require modules prior to requiring any support files.
Example:
cucumberOpts: {
requireModule: ['@babel/register']
// or
requireModule: [
[
'@babel/register',
{
rootMode: 'upward',
ignore: ['node_modules']
}
]
]
}
failFastโ
- Type
boolean- Default
false
Abort the run on first failure.
nameโ
- Type
RegExp[]- Default
[]
Only execute the scenarios with name matching the expression (repeatable).
requireโ
- Type
string[]- Default
[]
Require files containing your step definitions before executing features. You can also specify a glob to your step definitions.
Example:
cucumberOpts: {
require: [path.join(__dirname, 'step-definitions', 'my-steps.js')]
}
importโ
- Type
String[]- Default
[]
Paths to where your support code is, for ESM.
Example:
cucumberOpts: {
import: [path.join(__dirname, 'step-definitions', 'my-steps.js')]
}
strictโ
- Type
boolean- Default
false
Fail if there are any undefined or pending steps.
tagsโ
- Type
String- Default
""
Only execute the features or scenarios with tags matching the expression. Please see the Cucumber documentation for more details.
timeoutโ
- Type
Number- Default
30000
Timeout in milliseconds for step definitions.
retryโ
- Type
Number- Default
0
Specify the number of times to retry failing test cases.
retryTagFilterโ
- Type
RegExp
Only retries the features or scenarios with tags matching the expression (repeatable). This option requires '--retry' to be specified.
languageโ
- Type
String- Default
en
Default language for your feature files
orderโ
- Type
String- Default
defined
Run tests in defined / random order
formatโ
- Type
string[]
Name and output file path of formatter to use. WebdriverIO primarily supports only the Formatters that writes output to a file.
formatOptionsโ
- Type
object
Options to be provided to formatters
tagsInTitleโ
- Type
Boolean- Default
false
Add cucumber tags to feature or scenario name
Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself
ignoreUndefinedDefinitionsโ
- Type
Boolean- Default
false
Treat undefined definitions as warnings.
Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself
failAmbiguousDefinitionsโ
- Type
Boolean- Default
false
Treat ambiguous definitions as errors.
Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself
profileโ
- Type
string[]- Default
[]
Specify the profile to use.
Kindly take note that only specific values (worldParameters, name, retryTagFilter) are supported within profiles, as cucumberOpts takes precedence. Additionally, when using a profile, make sure that the mentioned values are not declared within cucumberOpts.
Skipping tests in cucumberโ
Note that if you want to skip a test using regular cucumber test filtering capabilities available in cucumberOpts, you will do it for all the browsers and devices configured in the capabilities. In order to be able to skip scenarios only for specific capabilities combinations without having a session started if not necessary, webdriverio provides the following specific tag syntax for cucumber:
@skip([condition])
were condition is an optional combination of capabilities properties with their values that when all matched with cause the tagged scenario or feature to be skipped. Of course you can add several tags to scenarios and features to skip a tests under several different conditions.
You can also use the '@skip' annotation to skip tests without changing tags. In this case the skipped tests will be displayed in the test report.
Here you have some examples of this syntax:
@skipor@skip(): will always skip the tagged item@skip(browserName="chrome"): the test will not be executed against chrome browsers.@skip(browserName="firefox";platformName="linux"): will skip the test in firefox over linux executions.@skip(browserName=["chrome","firefox"]): tagged items will be skipped for both chrome and firefox browsers.@skip(browserName=/i.*explorer/): capabilities with browsers matching the regexp will be skipped (likeiexplorer,internet explorer,internet-explorer, ...).
Import Step Definition Helperโ
In order to use step definition helper like Given, When or Then or hooks, you are suppose to import then from @cucumber/cucumber, e.g. like this:
import { Given, When, Then } from '@cucumber/cucumber'
Now, if you use Cucumber already for other types of tests unrelated to WebdriverIO for which you use a specific version you need to import these helpers in your e2e tests from the WebdriverIO Cucumber package, e.g.:
import { Given, When, Then, world, context } from '@wdio/cucumber-framework'
This ensures that you use the right helpers within the WebdriverIO framework and allows you to use an independent Cucumber version for other types of testing.
Publishing Reportโ
Cucumber provides a feature to publish your test run reports to https://reports.cucumber.io/, which can be controlled either by setting the publish flag in cucumberOpts or by configuring the CUCUMBER_PUBLISH_TOKEN environment variable. However, when you use WebdriverIO for test execution, there's a limitation with this approach. It updates the reports separately for each feature file, making it difficult to view a consolidated report.
To overcome this limitation, we've introduced a promise-based method called publishCucumberReport within @wdio/cucumber-framework. This method should be called in the onComplete hook, which is the optimal place to invoke it. publishCucumberReport requires the input of the report directory where cucumber message reports are stored.
You can generate cucumber message reports by configuring the format option in your cucumberOpts. It's highly recommended to provide a dynamic file name within the cucumber message format option to prevent overwriting reports and ensure that each test run is accurately recorded.
Before using this function, make sure to set the following environment variables:
- CUCUMBER_PUBLISH_REPORT_URL: The URL where you want to publish the Cucumber report. If not provided, the default URL 'https://messages.cucumber.io/api/reports' will be used.
- CUCUMBER_PUBLISH_REPORT_TOKEN: The authorization token required to publish the report. If this token is not set, the function will exit without publishing the report.
Here's an example of the necessary configurations and code samples for implementation:
import { v4 as uuidv4 } from 'uuid'
import { publishCucumberReport } from '@wdio/cucumber-framework';
export const config = {
// ... Other Configuration Options
cucumberOpts: {
// ... Cucumber Options Configuration
format: [
['message', `./reports/${uuidv4()}.ndjson`],
['json', './reports/test-report.json']
]
},
async onComplete() {
await publishCucumberReport('./reports');
}
}
Please note that ./reports/ is the directory where cucumber message reports will be stored.
Using Serenity/JSโ
Serenity/JS is an open-source framework designed to make acceptance and regression testing of complex software systems faster, more collaborative, and easier to scale.
For WebdriverIO test suites, Serenity/JS offers:
- Enhanced Reporting - You can use Serenity/JS as a drop-in replacement of any built-in WebdriverIO framework to produce in-depth test execution reports and living documentation of your project.
- Screenplay Pattern APIs - To make your test code portable and reusable across projects and teams, Serenity/JS gives you an optional abstraction layer on top of native WebdriverIO APIs.
- Integration Libraries - For test suites that follow the Screenplay Pattern, Serenity/JS also provides optional integration libraries to help you write API tests, manage local servers, perform assertions, and more!

Installing Serenity/JSโ
To add Serenity/JS to an existing WebdriverIO project, install the following Serenity/JS modules from NPM:
- npm
- Yarn
- pnpm
- Bun
npm install @serenity-js/{core,web,webdriverio,assertions,console-reporter,serenity-bdd} --save-dev
yarn add @serenity-js/{core,web,webdriverio,assertions,console-reporter,serenity-bdd} --dev
pnpm add @serenity-js/{core,web,webdriverio,assertions,console-reporter,serenity-bdd} --save-dev
bun add @serenity-js/{core,web,webdriverio,assertions,console-reporter,serenity-bdd} --dev
Learn more about Serenity/JS modules:
@serenity-js/core@serenity-js/web@serenity-js/webdriverio@serenity-js/assertions@serenity-js/console-reporter@serenity-js/serenity-bdd
Configuring Serenity/JSโ
To enable integration with Serenity/JS, configure WebdriverIO as follows:
- TypeScript
- JavaScript
import { WebdriverIOConfig } from '@serenity-js/webdriverio';
export const config: WebdriverIOConfig = {
// Tell WebdriverIO to use Serenity/JS framework
framework: '@serenity-js/webdriverio',
// Serenity/JS configuration
serenity: {
// Configure Serenity/JS to use the appropriate adapter for your test runner
runner: 'cucumber',
// runner: 'mocha',
// runner: 'jasmine',
// Register Serenity/JS reporting services, a.k.a. the "stage crew"
crew: [
// Optional, print test execution results to standard output
'@serenity-js/console-reporter',
// Optional, produce Serenity BDD reports and living documentation (HTML)
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
// Optional, automatically capture screenshots upon interaction failure
[ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ],
]
},
// Configure your Cucumber runner
cucumberOpts: {
// see Cucumber configuration options below
},
// ... or Jasmine runner
jasmineOpts: {
// see Jasmine configuration options below
},
// ... or Mocha runner
mochaOpts: {
// see Mocha configuration options below
},
runner: 'local',
// Any other WebdriverIO configuration
};
export const config = {
// Tell WebdriverIO to use Serenity/JS framework
framework: '@serenity-js/webdriverio',
// Serenity/JS configuration
serenity: {
// Configure Serenity/JS to use the appropriate adapter for your test runner
runner: 'cucumber',
// runner: 'mocha',
// runner: 'jasmine',
// Register Serenity/JS reporting services, a.k.a. the "stage crew"
crew: [
'@serenity-js/console-reporter',
'@serenity-js/serenity-bdd',
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
[ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ],
]
},
// Configure your Cucumber runner
cucumberOpts: {
// see Cucumber configuration options below
},
// ... or Jasmine runner
jasmineOpts: {
// see Jasmine configuration options below
},
// ... or Mocha runner
mochaOpts: {
// see Mocha configuration options below
},
runner: 'local',
// Any other WebdriverIO configuration
};
Learn more about:
- Serenity/JS Cucumber configuration options
- Serenity/JS Jasmine configuration options
- Serenity/JS Mocha configuration options
- WebdriverIO configuration file
Producing Serenity BDD reports and living documentationโ
Serenity BDD reports and living documentation are generated by Serenity BDD CLI,
a Java program downloaded and managed by the @serenity-js/serenity-bdd module.
To produce Serenity BDD reports, your test suite must:
- download the Serenity BDD CLI, by calling
serenity-bdd updatewhich caches the CLIjarlocally - produce intermediate Serenity BDD
.jsonreports, by registeringSerenityBDDReporteras per the configuration instructions - invoke the Serenity BDD CLI when you want to produce the report, by calling
serenity-bdd run
The pattern used by all the Serenity/JS Project Templates relies on using:
- a
postinstallNPM script to download the Serenity BDD CLI npm-failsafeto run the reporting process even if the test suite itself has failed (which is precisely when you need test reports the most...).rimrafas a convenience method to remove any test reports left over from the previous run
{
"scripts": {
"postinstall": "serenity-bdd update",
"clean": "rimraf target",
"test": "failsafe clean test:execute test:report",
"test:execute": "wdio wdio.conf.ts",
"test:report": "serenity-bdd run"
}
}
To learn more about the SerenityBDDReporter, please consult:
- installation instructions in
@serenity-js/serenity-bdddocumentation, - configuration examples in
SerenityBDDReporterAPI docs, - Serenity/JS examples on GitHub.
Using Serenity/JS Screenplay Pattern APIsโ
The Screenplay Pattern is an innovative, user-centred approach to writing high-quality automated acceptance tests. It steers you towards an effective use of layers of abstraction, helps your test scenarios capture the business vernacular of your domain, and encourages good testing and software engineering habits on your team.
By default, when you register @serenity-js/webdriverio as your WebdriverIO framework,
Serenity/JS configures a default cast of actors,
where every actor can:
This should be enough to help you get started with introducing test scenarios that follow the Screenplay Pattern even to an existing test suite, for example:
import { actorCalled } from '@serenity-js/core'
import { Navigate, Page } from '@serenity-js/web'
import { Ensure, equals } from '@serenity-js/assertions'
describe('My awesome website', () => {
it('can have test scenarios that follow the Screenplay Pattern', async () => {
await actorCalled('Alice').attemptsTo(
Navigate.to(`https://webdriver.io`),
Ensure.that(
Page.current().title(),
equals(`WebdriverIO ยท Next-gen browser and mobile automation test framework for Node.js | WebdriverIO`)
),
)
})
it('can have non-Screenplay scenarios too', async () => {
await browser.url('https://webdriver.io')
await expect(browser)
.toHaveTitle('WebdriverIO ยท Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
})
})
To learn more about the Screenplay Pattern, check out: