ScreenshotNeo

BlogGuides

WebdriverIO Tutorial: Cross-Browser Testing With Examples

Configure WebdriverIO to run end-to-end tests in multiple browsers, locally or remotely, with practical capability, parallelism, and troubleshooting examples.

By the ScreenshotNeo team4 October 202610 min read

To run WebdriverIO end-to-end tests in multiple browsers, define one WebDriver capability for each browser in your WDIO configuration, then run the local WDIO test runner. Each capability describes a browser session. You can run those sessions against local browsers and drivers or connect WDIO to a remote WebDriver service. Start with the browsers and operating systems your users need, and limit parallel sessions to the capacity you actually have.

This guide uses the WDIO local runner with Mocha and JavaScript. It covers setup, a working cross-browser configuration and spec, local and remote execution, framework and runner choices, parallelism, troubleshooting, and practical reliability and cost considerations. WebdriverIO’s current documentation is for version 9 and later; check the docs for the release you install if your project uses an older major version.

1. Set up a WebdriverIO project

Use a supported Node.js installation for your chosen WebdriverIO release. In an existing project, run the official setup wizard from the project root:

npm init wdio@latest .

For a new project, give the wizard a directory instead:

npm init wdio@latest ./webdriverio-cross-browser

Choose the local runner, an end-to-end test setup, Mocha, and JavaScript when prompted. The wizard installs the selected WDIO packages and generates a configuration and example test. Its options can change between releases, so follow the prompts from the version you are installing.

If you already have a WDIO configuration, you can run the wizard directly with npx wdio config. Start the suite with the config file it created:

npx wdio run ./wdio.conf.js

To run one spec file or a named suite, use:

npx wdio run ./wdio.conf.js --spec ./test/specs/example.e2e.js
npx wdio run ./wdio.conf.js --suite smoke

WDIO’s getting-started guide documents the setup wizard, run, --spec, and --suite commands. See Getting Started and the configuration reference.

2. Configure multiple browser capabilities

In wdio.conf.js, the important cross-browser setting is capabilities. Use an array with one object per browser environment you want to test. This example asks for Chrome and Firefox on the local machine:

export const config = {
    runner: 'local',
    specs: ['./test/specs/**/*.e2e.js'],
    maxInstances: 2,
    capabilities: [
        { browserName: 'chrome' },
        { browserName: 'firefox' }
    ],
    framework: 'mocha',
    reporters: ['spec'],
    mochaOpts: {
        ui: 'bdd',
        timeout: 60000
    }
};

This is an ES module config, as commonly generated by current WDIO setups. Keep the module format consistent with your project. For example, a CommonJS project may use exports.config = { ... } instead. The runner uses the selected framework adapter; the wizard normally adds the packages required by its choices.

A capability is the description of the remote browser interface WDIO should create. Standard WebDriver fields can include browserName, browserVersion, and platformName. Driver and provider extensions use namespaced keys, such as goog:chromeOptions, moz:firefoxOptions, and cloud-provider-specific options. WDIO validates user-defined capabilities against the WebDriver specification and can fail early when they do not conform. See the official capabilities documentation.

Browser and platform choices

Goal Capability approach What to check
Local Chrome or Chromium { browserName: 'chrome' } Browser and driver availability, plus any Chrome-specific options.
Local Firefox { browserName: 'firefox' } Firefox and its compatible driver setup.
Microsoft Edge { browserName: 'MicrosoftEdge' } or the browser name required by the selected driver/provider Confirm the exact name and options for your Edge driver or remote provider.
Safari { browserName: 'safari' } Safari automation must be configured on a supported macOS host. The documented Safari setup does not provide headless mode.
Specific browser version or OS Add standard fields such as browserVersion and platformName where the endpoint supports them. Local driver and hosted services differ in supported combinations and naming.

The table is a starting point, not a guarantee that every browser name or version will work on every machine. Local availability, driver behavior, and remote provider conventions determine the exact values. Read the current WDIO capability examples and the documentation for the driver or provider you use.

3. Write and run one end-to-end test

Put a spec under the path matched by specs. This example checks a visible page heading and a link on the public example domain:

describe('example page', () => {
    it('shows the expected heading and link', async () => {
        await browser.url('https://example.com/');

        await expect($('h1')).toHaveText('Example Domain');
        await expect($('a')).toBeDisplayed();
    });
});

With the WDIO test runner, the active session is available as the global browser object when global injection is enabled. WDIO’s browser and element commands are asynchronous, so await navigation and assertions. If your config disables global injection, import the globals your setup uses, such as browser, $, and expect from @wdio/globals. The Browser Object documentation explains the runner and standalone styles.

Run the spec across the configured capability list:

npx wdio run ./wdio.conf.js --spec ./test/specs/example.e2e.js

WDIO schedules workers according to available specs, capabilities, and instance limits. A test that passes in Chrome but fails in Firefox may reveal a real browser difference, a timing assumption, unsupported behavior, or a test selector that depends on browser-specific markup. Inspect the failing session and assertion before treating every difference as an application defect.

4. Choose test framework and runner for the job

The WDIO local runner supports integrations for Mocha, Jasmine, and Cucumber.js. Select the framework that fits your team’s test style and install its corresponding WDIO adapter as part of setup. Framework-specific settings live in fields such as mochaOpts, jasmineOpts, or cucumberOpts. Check the current framework guide for package and configuration details.

The local runner runs framework tests in Node.js worker processes and creates browser sessions for the configured capabilities. It is the normal choice for end-to-end flows. Each spec runs in an isolated worker/session context; don’t assume data or in-memory state is shared between spec files. Group related specs or use a documented shared-store approach if workers must coordinate. See Runner.

The browser runner executes tests inside a real browser and is intended for browser-based unit and component testing. It uses Vite to load a test harness and has its own configuration and constraints. It is not a drop-in switch for multiplying a Node-based end-to-end suite across arbitrary capabilities. See the component testing guide.

5. Control parallelism and suite selection

Parallelism is a capacity setting. WDIO can run specs concurrently, but each active worker uses a browser session and consumes machine, grid, or provider capacity. Start with a conservative global limit, then tune it based on available resources and service limits.

  • maxInstances sets the total number of parallel workers.
  • maxInstancesPerCapability limits concurrent workers for each capability.
  • wdio:maxInstances on an individual capability can set a browser-specific cap and takes precedence according to the capability documentation.
  • specs and suites select which test files to run; a capability can also define browser-specific specs.

For example, if your grid has more Chrome slots than Firefox slots, set a lower per-capability limit for Firefox and keep the global limit within the grid’s total session capacity. Exact scheduling depends on spec count and runner configuration. See Organizing Test Suite and Configuration.

6. Run against a remote WebDriver service

Remote services are useful when you need browser and operating-system combinations that are not installed locally or need more session capacity. Keep standard WebDriver capabilities separate from provider-specific extensions. The provider controls its supported browser matrix, authentication, endpoint, and namespaced options; do not copy one vendor’s capability keys to another.

WDIO configuration supports connection details such as hostname, port, path, user, and key, but endpoint and authentication requirements depend on the service. Store credentials in environment variables rather than committing them:

// Illustrative shape only: use the endpoint, credentials, and capability
// extension documented by your chosen WebDriver provider.
export const config = {
    hostname: process.env.WEBDRIVER_HOST,
    user: process.env.WEBDRIVER_USER,
    key: process.env.WEBDRIVER_KEY,
    capabilities: [
        {
            browserName: 'chrome',
            browserVersion: 'provider-supported-version',
            'provider:options': {
                // Replace with the provider's documented namespaced options.
            }
        }
    ]
};

The placeholder provider:options is deliberately not a real provider configuration. Replace it with the exact namespace and fields in the selected provider’s current documentation. WDIO lists common extension namespaces such as sauce:options, bstack:options, and LT:Options as examples, but their fields and availability are provider-specific. See Capabilities and Configuration.

7. Headless runs and CI

Headless support depends on browser and runner. The WDIO capability guide describes headless examples for Chrome, Firefox, and Edge; Safari does not support headless mode in that setup. Browser-specific arguments belong in that browser’s namespaced options, for example goog:chromeOptions. Consult the current guide before applying flags because driver and browser behavior can change.

The Browser Runner has its own headless setting and enables it by default in CI when the CI environment variable is 1 or true. That is a Browser Runner detail, not a universal promise about every local-runner capability. For CI end-to-end runs, configure each target browser and driver appropriately and verify the environment can launch them.

8. Common errors and fixes

Symptom Likely cause What to do
WDIO fails before tests with invalid capabilities A field is misspelled, has the wrong type, uses a nonstandard unnamespaced extension, or is unsupported by the endpoint. Check standard fields against the WebDriver spec and extensions against the browser or provider docs. Use the required vendor namespace.
Session cannot start or browser/driver is not found The local browser or driver is missing, incompatible, or unavailable to the process. Confirm browser installation and driver setup for the OS and WDIO release. For remote runs, check endpoint, credentials, and provider-supported combinations.
Tests pass in one browser but fail in another Timing, rendering, supported APIs, selectors, or application behavior differ. Inspect the failing browser session, wait for a meaningful condition, avoid brittle selectors, and decide whether the difference is a product bug or an intentional browser limitation.
Tests time out under parallel execution The machine or remote grid is overloaded, sessions are queued, or a test waits for an event that never occurs. Lower global or per-capability concurrency, inspect session and command logs, and use explicit waits tied to the expected page state.
Global browser or $ is undefined Global injection is disabled or code is running outside the WDIO test-runner context. In runner specs, enable injection or import globals from @wdio/globals. In standalone code, create a session with remote() instead.
Remote session rejects a provider option Options from another provider, wrong casing, or outdated fields were used. Use only the selected provider’s current capability namespace and documented field names.
Safari test cannot run headless Safari has no headless mode in the documented setup. Run Safari on the supported macOS configuration with a visible browser session or use a remote environment that offers the required setup.
Specs seem to lose state between files Local runner workers isolate test files and sessions. Make each test independently establish its state, group files where appropriate, or use WDIO’s shared-store service for deliberate cross-worker sharing.

9. Performance, reliability, and cost

Performance

More workers can shorten wall-clock duration when there are enough independent specs and browser slots, but also increase CPU and memory use locally or session demand remotely. Measure with your actual suite and cap workers to the lowest relevant limit: local resources, grid capacity, or provider concurrency. A broad browser matrix multiplies session work, so run a small smoke suite on every change and schedule longer compatibility coverage according to your release risk.

Reliability

Keep tests deterministic: wait for visible application state rather than fixed delays where possible, isolate test data, and avoid relying on state left by another spec. Record which browser and capability failed, and retain useful runner logs or provider session details. Browser versions and provider availability can change; pin or select versions only when the chosen environment supports that behavior, and revisit the matrix as the supported product changes.

Cost

Local execution costs machine time and maintenance for installed browsers and drivers. Hosted browser services may charge by plan, parallel capacity, or usage; actual terms vary and should be checked with the provider. Avoid paying for idle concurrency by setting instance limits to fit the suite and scheduling. No provider pricing or performance comparison is assumed here.

10. Website screenshots alongside browser tests

WebdriverIO is for driving browser sessions and checking behavior. If you also need a saved screenshot artifact of a public page, ScreenshotNeo is a separate website screenshot API and MCP server from Yorker Media. A screenshot can document a page, but it does not replace an interactive end-to-end test.

Or skip the browser setup

For a screenshot of a URL, call the ScreenshotNeo API directly. See the ScreenshotNeo API documentation for the request options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
  • Cookie banners, popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers identify the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

Frequently asked questions

Does a capability represent a test file?

No. A capability describes the browser session environment. Specs describe the tests WDIO runs in that environment.

Can I use one spec for every browser?

Yes. A capability array lets the runner execute the suite against multiple browser environments. Tests may still need browser-aware expectations when application behavior genuinely differs.

Can I use WebdriverIO for component tests?

Yes. The Browser Runner is designed for browser-based unit and component testing; the local runner is the typical route for Node-based end-to-end tests.

Does using CDP alone give me cross-browser coverage?

No. WebdriverIO’s overview distinguishes Chrome DevTools Protocol automation for Chromium-based browsers from WebDriver Protocol, which is the route it describes for cross-browser testing. See Why WebdriverIO?.

Where can I find the exact current provider options?

Use the chosen provider’s current WebDriver capability documentation. WDIO supports provider extensions, but their fields and browser matrices are not interchangeable.