---
url: https://huldoser.github.io/test-inventory/frameworks/webdriverio.md
description: >-
  Scan WebdriverIO tests without running them, interpret the inventory, and
  check WebdriverIO syntax and limitations.
---

# 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](/guide/runners#checked-in-ci) list tested versions.

[Choose another framework](/frameworks/) · [Use an AI client](/guide/agents)

## Install

```sh
npm install --save-dev test-inventory
```

## Scan your suite

Run from the folder containing your project's `package.json`:

```sh
npx test-inventory --framework webdriverio --pretty --output inventory.json
```

To select files explicitly, use quoted globs. This example matches the `broker-portal` fixture used below;
adjust the globs to your suite's layout:

```sh
npx test-inventory "test/specs/*.e2e.js" "test/specs/{orders,accounts}/*.e2e.js" --framework webdriverio --output inventory.json
```

The 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:

```sh
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](/guide/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](/guide/limits) before using scans to make CI decisions.

## Find skipped tests

```sh
npx test-inventory --framework webdriverio --state skip --fields fullName,state,condition,reason --pretty
```

This 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](/guide/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`.

```js excerpt
// 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();
```

```sh
npx test-inventory "test/specs/*.e2e.js" "test/specs/{orders,accounts}/*.e2e.js" --framework webdriverio --output inventory.json
```

Without the globs and `--framework`, the scan finds the files from `wdio.conf.js`, whose `exclude` leaves out the
drafts, and WebdriverIO from `package.json`:

```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:

```json
{
  "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:

```js
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:

```js excerpt
// 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:

```sh
npx test-inventory "test/specs/**/*.e2e.js" --framework webdriverio --pretty
```

Omitting the glob finds the same files from `specs`. These record excerpts show the config timeout and the
spec's override:

```json
{
  "fullName": "statement downloads > offers the latest monthly statement",
  "timeout": 45000,
  "timeoutSource": "config"
}
```

```json
{
  "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](/frameworks/webdriverio#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.

::: details 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](/frameworks/mocha#supported-syntax) or [Jasmine](/frameworks/jasmine#supported-syntax), 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](/reference/syntax#in-every-runner) also apply.

## Next

* [Shared limits](/guide/limits): imports, generated tests, conditions and dependency coverage.
* [Key concepts](/guide/concepts): counts, states, IDs and hashes.
* [Output reference](/reference/output) and [diagnostics](/reference/diagnostics): interpret every field and warning.
* [CLI](/guide/cli), [library](/guide/library) and [MCP](/guide/agents): use the same inventory through each interface.
* [Compare with WebdriverIO](/guide/runners#webdriverio-s-list): differences from runner collection and execution reports.
