expect-webdriverio extends Jest's expect API with WebDriverIO-specific enhancements. It can be used standalone or within other testing environments.
It is highly recommended to use this package with the WDIO Testrunner and a compatible framework adapter, which together provide a plug-and-play experience.
Pair it with your preferred framework using the appropriate adapter:
- Mocha: Use
@wdio/mocha-framework - Cucumber: Use
@wdio/cucumber-framework - Jasmine: Use
@wdio/jasmine-framework - Jest: Works (no framework exists; requires manual configuration—see note below)
Note: When using Jest, or when running outside of the WDIO Testrunner without a compatible framework adapter, additional manual configuration may be required, such as adding types to your
tsconfig.jsonand configuring WDIO matchers, soft assertions, and the snapshot service.
Example playgrounds are available, though their tsconfig.json files may use modified configurations for development purposes.
- See Mocha, Jasmine, and Jest examples here.
When pairing with Mocha, you can use expect-webdriverio directly or combine it with chai (or any other assertion library).
- It is strongly recommended to leverage
@wdio/mocha-frameworkfor automatic configuration and a plug-and-play experience. - See Mocha playground example here
No import is required; everything is set globally.
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await expect(browser).toHaveUrl('https://example.com')
})
}) Minimum types expected in tsconfig.json:
When depending on @wdio/mocha-framework
{
"compilerOptions": {
"types": [
"@wdio/mocha-framework",
"@wdio/globals/types"
]
}
}When not depending on @wdio/mocha-framework
{
"compilerOptions": {
"types": [
"@types/mocha",
"expect-webdriverio/expect-global"
]
}
}expect-webdriverio can coexist with the Chai assertion library by importing both libraries explicitly.
See also this documentation.
You can use expect-webdriverio with Jest by leveraging either @types/jest (which provides global ambient support) or @jest/globals alone.
- Note: Jest maintainers do not officially support
@types/jest. Should this package become outdated or experience issues, support may be dropped. - Note: With Jest, the matchers
toMatchSnapshotandtoMatchInlineSnapshotare overloaded. To resolve the types correctly,expect-webdriverio/jestmust be listed last. - Note: WebdriverIO does not provide a compatible framework adapter for Jest; manual configuration is required.
When paired with @types/jest, no imports are required in your test files. Global ambient types are already defined correctly, allowing you to use Jest's expect directly after some manual configuration.
- Note:
jestandts-jestare also required. - See the Jest with
@types/jestplayground example.
Since no WDIO Testrunner and framework adapter are used, additional prerequisite configuration is required.
import { expect } from "expect-webdriverio";
(globalThis as any).expect = expect;If not already set, define a file path for setupFilesAfterEnv in your Jest configuration:
setupFilesAfterEnv: ['./jest.setup.after-env.ts'],Then, add the following configuration to your jest.setup.after-env.ts file:
import { expect } from "@jest/globals";
import { wdioCustomMatchers } from "expect-webdriverio";
beforeAll(async () => {
// Extend the imported Jest expect instance with WDIO matchers
expect.extend(wdioCustomMatchers);
});Optional: For soft assertions, createSoftExpect is currently not correctly exposed, but the configuration below works:
import { SoftAssertService } from "expect-webdriverio";
// @ts-ignore
import * as createSoftExpect from "expect-webdriverio/lib/softExpect";
beforeAll(async () => {
Object.defineProperty(expect, "soft", {
value: <T = unknown>(actual: T) => createSoftExpect.default(actual),
});
// Add soft assertions utility methods
Object.defineProperty(expect, "getSoftFailures", {
value: (testId?: string) => SoftAssertService.getInstance().getFailures(testId),
});
Object.defineProperty(expect, "assertSoftFailures", {
value: (testId?: string) => SoftAssertService.getInstance().assertNoFailures(testId),
});
Object.defineProperty(expect, "clearSoftFailures", {
value: (testId?: string) => SoftAssertService.getInstance().clearFailures(testId),
});
});Then, as shown below, no imports are required and we can use WDIO matchers directly on Jest's expect:
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await expect(browser).toHaveUrl('https://example.com')
})
}) Expected in tsconfig.json:
{
"compilerOptions": {
"types": [
"@types/jest",
"expect-webdriverio/jest" // Must be last for overloaded matchers `toMatchSnapshot` and `toMatchInlineSnapshot`
]
}
}When using @jest/globals directly instead of global ambient types, you explicitly import Jest's utilities. To use expect-webdriverio you have two approaches:
- Note: No example playground available
import { expect } from 'expect-webdriverio'
import { describe, it, expect as jestExpect } from '@jest/globals'
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await expect(browser).toHaveUrl('https://example.com')
// Jest's native expect is still usable
jestExpect(myFunction).toHaveBeenCalled()
})
}) No types are expected in tsconfig.json.
To avoid explicitly importing expect from expect-webdriverio in every test file, add the global entry point to your tsconfig.json:
{
"compilerOptions": {
"types": ["expect-webdriverio/expect-global"]
}
}Unlike @types/jest, @jest/globals does not export a global namespace that can be easily extended. While module augmentation is possible, it does not support inheriting matchers via the extends keyword. Supporting it would require manually duplicating all expect-webdriverio matcher interfaces inside the module declaration.
This limitation is a known upstream issue tracked in Jest.
When paired with Jasmine, @wdio/jasmine-framework is required to ensure proper runtime configuration. The adapter forces the global expect to map to Jasmine's native expectAsync and registers the necessary WDIO matchers via addAsyncMatcher.
Jasmine differs from other standard assertion libraries in two key ways:
- Built-in Soft Assertions: Jasmine executes soft assertions out-of-the-box by tracking and collecting validation failures until a spec block finishes execution. Because this mechanism is native to Jasmine, the
expect-webdriverioSoftAssertion service is neither required nor supported. - Implicit Promise Handling: Forcing
expectAsyncto act as the globalexpectbinding makes even basic matchers asynchronous. Because Jasmine automatically hooks into outstanding spec promises and flushes them at the end of the test, assertions may appear to execute correctly even if you omit theawaitkeyword—unlike in other frameworks whereawaitis strictly mandatory.
⚠️ Warning: Omittingawaitdirectly conflicts with Jasmine's official async matcher recommendations and can introduce silent timing issues or unhandled rejections into your test suite. Always explicitlyawaityour assertions.
-
expect-webdriverio/jasmineAugments Jasmine's nativeexpectAsyncinterface directly with WebDriverIO custom matchers. -
expect-webdriverio/jasmine-wdio-expect-asyncSpecifically dedicated to aligning with the@wdio/jasmine-frameworkarchitecture. This entry point is subject to breaking changes and may be moved directly into the framework adapter in a future release. It performs the following modifications:- Augments
expectwith WebDriverIO custom matchers. - Transforms synchronous, native Jasmine matchers on the
expectinterface to return promises (making them asynchronous). - Establishes a global
expecttype definition with the above modifications.
- Augments
When using @wdio/jasmine-framework, the global ambient expect is forced to behave as Jasmine's native expectAsync under the hood. It is strongly recommended to explicitly await all assertions—including basic, non-WDIO matchers. While Jasmine automatically processes un-awaited spec promises at the end of test execution, omitting the keyword can introduce unpredictable timing issues or silent validation bypasses.
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await expect(browser).toHaveUrl('https://example.com')
// Always await basic assertions as well since they resolve to promises under the hood
await expect(true).toBe(true)
})
}) Expected in tsconfig.json:
{
"compilerOptions": {
"types": [
// Enforces Promise-based assertion return types (Beta: Subject to future integration into @wdio/jasmine-framework)
"expect-webdriverio/jasmine-wdio-expect-async",
"@wdio/globals/types",
"@types/jasmine"
]
}
}Warning: Because
@wdio/jasmine-frameworkoverrides synchronous matchers and introduces complicated type augmentations, a proposal was made for WebdriverIO v10 to preserve Jasmine's cleanexpectAsyncAPI, attach custom WDIO matchers directly to it, and keep basic matchers synchronous.
Note: When using Jasmine, Jest's expect matchers are not leveraged, meaning standard Jest-specific assertion matchers are unavailable.
When you do not use @wdio/globals/types (or when @types/jasmine takes type-resolution priority), the global ambient expect resolves to Jasmine's native behavior. By defining expect-webdriverio/jasmine in your types, you can use WDIO custom matchers directly on expectAsync. Note that if you are running outside of @wdio/jasmine-framework, these matchers must be registered manually.
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await expectAsync(browser).toHaveUrl('https://example.com')
// Standard Jasmine async matchers work as expected
await expectAsync(Promise.resolve(true)).toBeResolvedTo(true)
})
})Expected in tsconfig.json:
{
"compilerOptions": {
"types": [
"@types/jasmine",
"expect-webdriverio/jasmine"
]
}
}The expect export from expect-webdriverio remains available under Jasmine if you prefer an explicit import strategy. See the playground example.
import { expect as wdioExpect } from 'expect-webdriverio'
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
await wdioExpect(browser).toHaveUrl('https://example.com')
// Does not require await
wdioExpect(true).toBe(true)
})
}) Jasmine's asymmetric matchers have improved, but certain limitations may still exist.
jasmine.stringContaining,jasmine.stringMatching,jasmine.any(Type), andjasmine.anything()work seamlessly across the board.- Network matchers support
jasmine.objectContaining, whereas support in other areas (such as element matchers) might be limited. - WDIO asymmetric matchers also work properly.
import { expect as wdioExpect } from 'expect-webdriverio'
describe('My tests', async () => {
it('should verify my browser to have the expected url', async () => {
// Working Jasmine asymmetric matchers
await expectAsync(browser).toHaveUrl(jasmine.stringContaining('WebdriverIO'))
await expectAsync(browser).toHaveUrl(jasmine.stringMatching('/WebdriverIO/'))
await expectAsync(browser).toHaveUrl(jasmine.any(String))
await expectAsync(browser).toHaveUrl(jasmine.anything())
// Working WDIO asymmetric matcher
await expectAsync(browser).toHaveUrl(wdioExpect.stringContaining('WebdriverIO'))
})
})More details to come. In short, when paired with @wdio/cucumber-framework, you can use WebDriverIO's expect library seamlessly within your Cucumber step definitions and Gherkin-based tests.
Browser Runner (@wdio/browser-runner) allows you to leverage component frameworks like React, Preact, Vue.js, Svelte, and SolidJS. It relies on the Jest's standard expect library and only registers core expect-webdriverio matchers. Because it runs within a browser environment rather than fully embedding expect-webdriverio, global features like DefaultOption and SoftAssertion are currently unsupported.
The asymmetric matcher expect.oneOf and the modifier some have been adapted to work in Browser Runner:
expect.oneOf: Works standard out of the box.expect.some: Reimplemented to integrate with Jest's native expect runner and exposed directly viaexpect.some. Note: Importingsomefromexpect-webdriverio/apiwill not work in Browser Runner mode. Always access it throughexpect.some
it('some elements are displayed', async () => {
// `expect.some` uses global export. Ignore TypeScript errors on `expect.some` until proper typings are provided.
await expect(expect.some($$('p=Times clicked: 1'))).toBeDisplayed()
})