WebdriverIO
This guide covers WebdriverIO 9.0 or newer. You need Node.js 22.12 or newer to run test-inventory and an existing JavaScript or TypeScript test suite. The scanner reads your source without executing your tests or starting a browser. These are minimum supported versions; CI comparisons list tested versions.
Choose another framework · Use an AI client
Install
npm install --save-dev test-inventoryScan your suite
Run from the folder containing your project's package.json:
npx test-inventory --framework webdriverio --pretty --output inventory.jsonTo select files explicitly, use quoted globs. This example matches the broker-portal fixture used below; adjust the globs to your suite's layout:
npx test-inventory "test/specs/*.e2e.js" "test/specs/{orders,accounts}/*.e2e.js" --framework webdriverio --output inventory.jsonThe command replaces inventory.json once the new output is complete. It does not change your test files.
Check the result
Print the counts and diagnostics from the saved file:
node -e "const i = require('./inventory.json'); console.log(i.summary); console.log(i.diagnostics)"For matching tests, summary.fileCount and summary.testCount should be greater than zero. Review diagnostics even when the command exits with 0: errors can mean tests are missing. If the result is empty or unexpected, use troubleshooting.
The inventory describes source declarations, not execution results. Conditional skips are not proof that a run skipped the test. Loops and tables are one record by default; supported cases can be expanded with --expand-cases. Expected-to-fail tests execute and check for failure; they are not disabled tests.
Tests created by unreadable helpers can be missing without a diagnostic. A dependency hash does not prove complete coverage of runtime dependencies. Read the shared limits before using scans to make CI decisions.
Find skipped tests
npx test-inventory --framework webdriverio --state skip --fields fullName,state,condition,reason --prettyThis uses the same file selection as the first scan. If you supplied globs, --root or --framework-version, repeat those options here.
This selects source declarations marked skip; it is not a report of a test run. See query recipes for other inventory queries.
Example
broker-portal checks a broker's client portal with WebdriverIO on Mocha. A number after a test's function is WebdriverIO's retry count, and this.timeout() sets the describe's timeout over the config's mochaOpts.timeout.
// test/specs/orders/ticket.e2e.js
describe('client order ticket', function () {
this.timeout(90_000);
// ...
it('blocks an order above the buying power', async () => {
// ...
}, 2);
// ...
context('after the close', () => {
it('queues the order for the open', async function () {
if (!process.env.AFTER_HOURS) this.skip();npx test-inventory "test/specs/*.e2e.js" "test/specs/{orders,accounts}/*.e2e.js" --framework webdriverio --output inventory.jsonWithout the globs and --framework, the scan finds the files from wdio.conf.js, whose exclude leaves out the drafts, and WebdriverIO from package.json:
{
"fullName": "client order ticket > blocks an order above the buying power",
"state": "active",
"timeout": 90000,
"retries": 2
}A test that skips itself under a condition keeps the condition:
{
"fullName": "client order ticket > after the close > queues the order for the open",
"state": "skip",
"isConditional": true,
"condition": "!process.env.AFTER_HOURS"
}WebdriverIO with Jasmine
broker-portal-jasmine models a WebdriverIO project using the Jasmine adapter. Its config selects Jasmine and sets the default spec timeout:
export const config = {
runner: 'local',
specs: ['./test/specs/**/*.e2e.js'],
baseUrl: 'http://localhost:4173',
capabilities: [{ browserName: 'chrome' }],
framework: 'jasmine',
jasmineOpts: { defaultTimeoutInterval: 45000 },
};A spec can override that timeout with its third argument. This spec also marks itself pending under a condition:
// test/specs/statements.e2e.js
it('offers archived statements', async () => {
if (!process.env.ARCHIVE_ENABLED) pending('Archived statements require archive access');
await browser.url('/statements/archive');
await expect($('[data-test="archive-download"]')).toBeEnabled();
}, 15000);Keep --framework webdriverio; the framework setting in wdio.conf.js selects Jasmine's syntax:
npx test-inventory "test/specs/**/*.e2e.js" --framework webdriverio --prettyOmitting the glob finds the same files from specs. These record excerpts show the config timeout and the spec's override:
{
"fullName": "statement downloads > offers the latest monthly statement",
"timeout": 45000,
"timeoutSource": "config"
}{
"fullName": "statement downloads > offers archived statements",
"state": "skip",
"isConditional": true,
"condition": "!process.env.ARCHIVE_ENABLED",
"reason": "Archived statements require archive access",
"timeout": 15000,
"timeoutSource": "test"
}The conditional pending() keeps its reason; the scanner does not evaluate the environment variable or run the spec. Use WebdriverIO's supported syntax for adapter-specific behavior.
Configuration and limits
The scan reads the root wdio.conf.js, .ts, .mjs, .mts, .cjs or .cts, using its config export. The config's framework selects Mocha (the default) or Jasmine. Their syntax is part of WebdriverIO support; use the matching syntax link below. Cucumber feature files are not read and that selection is reported.
Explicit scan globs replace specs, less excludes. suites, which need the runner's --suite, and mochaOpts.grep or jasmineOpts.grep are not read. A spec used by several capabilities or workers is listed once. The inventory counts declarations, not browser executions.
The config's mochaOpts.timeout, retries and ui, or jasmineOpts.defaultTimeoutInterval, apply. The default timeout is WebdriverIO's 10 seconds; defaultTimeoutInterval: 0 means 60 seconds.
Exact file selection
For WebdriverIO: the files under rootDir, the config's folder unless the config sets it, that a specs glob matches, also in the nested lists that group files into one worker, with an extension WebdriverIO takes, such as .js or .ts, without dot files and folders, less those exclude leaves out. A capability's own wdio:specs, or before WebdriverIO 10 its specs, replace the config's for that capability, and its wdio:exclude, or before 10 its exclude, adds to the config's, read inside its alwaysMatch when it has one; a file runs when a capability runs it. As in WebdriverIO, a glob without a / matches a file's name at any depth, though ./ counts as one, a glob that starts with ! matches nothing, and a \ is read as /; when every exclude entry holds a /, \ or *, they are globs too, and otherwise each leaves out every file whose path contains it. suites run only with --suite, so they are not read. Scanned with globs, every file they match counts as run, less the excludes, as files given with --spec replace specs. A file that two capabilities run, or that a group of files and another entry both name, runs in two workers, but is listed once. The config's framework, Mocha by default, or Jasmine, decides how the files are read, and its mochaOpts.timeout, retries and ui, or jasmineOpts.defaultTimeoutInterval, apply to every test. Without them the timeout is WebdriverIO's, 10 seconds, and a defaultTimeoutInterval of 0 is 60 seconds. Cucumber runs feature files, which the scan doesn't read, and is reported. mochaOpts.grep and jasmineOpts.grep are not read.
Supported syntax
WebdriverIO 9.0 and newer, with Mocha or Jasmine. The config's framework decides which, Mocha by default; Cucumber's feature files are not read.
Where test comes from
| Form | Example |
|---|---|
| Globals | With Mocha, its BDD globals, or with mochaOpts.ui: 'tdd' suite and test; with Jasmine, describe, it and their x and f forms. WebdriverIO sets them in every spec file. |
| Kept in a variable | const later = it.skip: only what both branches of a choice share applies, also of one written in place, as (process.env.CI ? it.skip : it)(...) |
| Local helpers file | import { it } from './helpers' or require('./helpers'), followed through local imports, and helper functions that declare tests, read at each call |
Calls
Tests and describes are read as in Mocha or Jasmine, with these differences:
| Call | Read as |
|---|---|
it(title, fn, n) with Mocha | retries: WebdriverIO runs the function up to n more times when it fails. It does so inside each of Mocha's attempts, so with this.retries() as well, the record shows WebdriverIO's count; 0 leaves Mocha's. it.only and it.skip drop the count, as they pass Mocha only the title and function. |
it(title, fn, ms, n) with Jasmine | timeout, as in Jasmine, and retries, WebdriverIO's. |
| A Mocha or Jasmine version gate | Read with the version WebdriverIO depends on: Mocha 12 and Jasmine 6 from WebdriverIO 10, Mocha 10.8 and Jasmine 5 before. So describe(...).timeout(ms) reaches its tests from WebdriverIO 10, and Jasmine 7's notApplicable() is not read. |
| A call the runner refuses | The same errors as in Mocha or Jasmine, with what WebdriverIO does then. With Mocha, the file stops loading at the call: the tests declared above it run, those below are notLoaded, and the spec files grouped after it in the same worker don't run. With Jasmine, a call refused at the top level fails the whole worker: the file's tests are notLoaded, and the files grouped with it don't run either. Other workers run. |
The shared syntax rules also apply.
Next
- Shared limits: imports, generated tests, conditions and dependency coverage.
- Key concepts: counts, states, IDs and hashes.
- Output reference and diagnostics: interpret every field and warning.
- CLI, library and MCP: use the same inventory through each interface.
- Compare with WebdriverIO: differences from runner collection and execution reports.