Testland
Browse all skills & agents

webdriverio-testing

Authors WebdriverIO E2E tests - `npm init wdio@latest` scaffolding, services architecture (sauce, browserstack, appium, devtools), reporters (spec, allure, junit), built-in Mocha/Jasmine/Cucumber framework integrations. WebdriverIO sits between Selenium (W3C protocol) and Playwright (modern API) - Selenium-protocol-compatible with rich plugin ecosystem. Use when the team needs WebDriver protocol + service-based device-farm integration.

Install with skills.sh (any agent)

npx skills add testland/qa --skill webdriverio-testing
View source

webdriverio-testing

Overview

WebdriverIO (wdio) is a JavaScript / TypeScript E2E framework built on the W3C WebDriver protocol. It differentiates from Selenium by:

  • Modern JS/TS API.
  • Service architecture (browserstack, sauce, appium, devtools).
  • Built-in framework integrations (Mocha, Jasmine, Cucumber).
  • Multi-protocol support (WebDriver classic, WebDriver Bidi, Chrome DevTools Protocol).

When to use

  • A JS/TS project wants WebDriver-protocol compatibility (legacy systems / device farms).
  • Service-based architecture matters (BrowserStack / Sauce Labs via wdio services is clean).
  • Team already on Mocha or Jasmine; wants Cucumber-style BDD via wdio-cucumber-framework.
  • Cross-platform via Appium service (mobile + web in one runner).

For pure-modern E2E, Playwright is simpler. For non-WebDriver architecture, Cypress.

Step 1 - Scaffold

npm init wdio@latest

Interactive prompts pick:

  • Test runner type (E2E for browsers, mobile via appium).
  • Framework (Mocha / Jasmine / Cucumber).
  • Reporters (spec, allure, junit, etc.).
  • Services (browserstack, sauce, appium, devtools, ...).
  • Browser(s) to test.

What lands: wdio.conf.ts + tests/specs/*.e2e.ts + package.json updates.

Step 2 - Configure

// wdio.conf.ts
export const config: WebdriverIO.Config = {
  runner: 'local',
  specs: ['./tests/specs/**/*.e2e.ts'],
  exclude: [],
  maxInstances: 4,
  capabilities: [
    {
      browserName: 'chrome',
      'goog:chromeOptions': { args: ['--headless=new'] },
    },
    {
      browserName: 'firefox',
      'moz:firefoxOptions': { args: ['-headless'] },
    },
  ],
  baseUrl: 'http://localhost:3000',
  services: ['chromedriver', 'geckodriver'],
  framework: 'mocha',
  reporters: ['spec', ['junit', { outputDir: 'reports/junit' }]],
  mochaOpts: { ui: 'bdd', timeout: 30000 },
};

maxInstances: 4 runs 4 specs in parallel.

Step 3 - Author a test (Mocha)

// tests/specs/checkout.e2e.ts
import { browser, $ } from '@wdio/globals';
import { expect } from 'chai';

describe('Checkout flow', () => {
  beforeEach(async () => {
    await browser.url('/login');
    await $('[data-testid=email]').setValue('user@example.com');
    await $('[data-testid=password]').setValue('pwd');
    await $('button[type=submit]').click();
    await expect($('h1=Welcome')).toBeDisplayed();
  });

  it('completes checkout', async () => {
    await browser.url('/products/BOOK-001');
    await $('[data-testid=add-to-cart]').click();
    await expect($('[data-testid=cart-count]')).toHaveText('1');

    await browser.url('/checkout');
    await $('[name=card]').setValue('4242 4242 4242 4242');
    await $('button=Place order').click();
    await expect($('h1=Order confirmed')).toBeDisplayed();
  });
});

The $ selector returns a wdio element (Promise-wrapped); methods auto-wait. WDIO selectors have shortcuts:

  • $('h1=Welcome') - text equals
  • $('h1*=Welcome') - text contains
  • $('[data-testid=foo]') - CSS selector
  • $('//button[@type="submit"]') - XPath

Step 4 - Services

services: [
  // Local browser drivers
  'chromedriver',
  'geckodriver',

  // Cloud device farms
  ['browserstack', { user: 'USER', key: 'KEY' }],
  ['sauce', { user: 'USER', key: 'KEY' }],

  // Mobile
  ['appium', { command: 'appium', args: { port: 4723 } }],

  // DevTools (Puppeteer-style fast browser)
  'devtools',
];

Services handle setup / teardown; tests connect via the configured capability.

Step 5 - Run

# Run all specs
npx wdio run wdio.conf.ts

# Specific spec
npx wdio run wdio.conf.ts --spec ./tests/specs/checkout.e2e.ts

# Watch mode
npx wdio run wdio.conf.ts --watch

Step 6 - Cucumber framework (BDD)

// wdio.conf.ts
framework: 'cucumber',
specs: ['./features/**/*.feature'],
cucumberOpts: {
  require: ['./features/step-definitions/**/*.ts'],
  backtrace: false,
  requireModule: ['ts-node/register'],
  timeout: 60000,
},
# features/checkout.feature
Feature: Checkout

  Scenario: Complete a successful checkout
    Given I am logged in as "user@example.com"
    When I add "BOOK-001" to my cart
    And I complete checkout
    Then I see the order confirmation
// features/step-definitions/checkout.steps.ts
import { Given, When, Then } from '@wdio/cucumber-framework';
import { $ } from '@wdio/globals';

Given(/^I am logged in as "(.+)"$/, async (email) => {
  await browser.url('/login');
  await $('[data-testid=email]').setValue(email);
  await $('[data-testid=password]').setValue('pwd');
  await $('button[type=submit]').click();
});

// ... etc.

Pairs with cucumber-testing (in the qa-bdd plugin) conventions for the Gherkin layer.

Step 7 - CI integration

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm ci
      - run: npx wdio run wdio.conf.ts
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: wdio-reports
          path: reports/

JUnit XML in reports/junit/ feeds junit-xml-analysis (in the qa-test-reporting plugin).

Anti-patterns

Anti-patternWhy it failsFix
Forgetting await on commandsReturns a Promise; subsequent steps race.Always await (TS strict mode helps catch).
browser.pause(2000)Flaky; defeats wdio's auto-waits.Trust assertion auto-waits; explicit waits when needed via waitFor*.
Massive wdio.conf.tsHard to navigate; merge conflicts.Split into env-specific configs; share via spread.
Mixing services that conflict (chromedriver + selenium-standalone)Driver conflict.Pick one approach.
Running mobile + web in same wdio.conf.tsTightly coupled config; hard to maintain.Separate configs per stack.

Limitations

  • Async-await everywhere. Forgetting an await is the #1 bug source.
  • Service config can be opaque. Per-service docs vary.
  • Smaller community than Playwright / Cypress. Stack overflow hit rate lower.

References

  • WebdriverIO at webdriver.io.
  • playwright-testing, cypress-testing, selenium-testing - alternatives.
  • appium-testing - wdio's appium service uses Appium underneath.
  • cucumber-testing - wdio's cucumber framework integration.

Related skills

browser-matrix-strategy-reference

Pure-reference for designing and reviewing a browser / OS / device test matrix from traffic data - the T1/T2/T3 tier-membership heuristics (T1 >=5% traffic, T2 1-5% or statutory, T3 <1% with customer demand), the traffic-share sources (own analytics, StatCounter, MDN browser-compat-data), a worked matrix template with tier-change log, the matrix review checklist (staleness, T1 oversize, below-threshold T1 entries, missing real-device coverage), how to justify dropping a legacy browser (IE11, old iOS Safari), and the compatibility budget (tier caps, CI cost formula, published support statement) in references/compatibility-budget.md. Use when designing an initial matrix, capping or publishing a support policy, running a quarterly re-tier review, or making the case to drop a browser. This is the WHAT-to-test strategy reference - to execute the matrix use playwright-testing browser projects (bundled engines), selenium-grid-4-runner (self-hosted), or cloud-grid-e2e (managed grids).

cloud-grid-e2e

Author and run E2E tests on a cloud browser grid - BrowserStack Automate, Sauce Labs, or LambdaTest. All three follow one pattern: username + access-key env vars, a W3C WebDriver hub URL, a vendor options dict inside the capabilities (bstack:options / sauce:options / LT:Options), a local tunnel binary for internal apps, session pass/fail reporting, and a CI matrix throttled to the plan's parallel-session limit. Worked example uses BrowserStack; per-vendor deltas live in references/. Use for cross-browser regression on real devices + browsers beyond the engines bundled on the local machine - distinct from a local matrix runner and from self-hosted Selenium Grid.

cypress-testing

Authors and improves Cypress E2E tests - installs Cypress, configures `cypress.config.ts`, authors `cy.*` command chains, refactors existing specs (`cy.wait(ms)` sleeps into assertions, repeated flows into `cy.session` custom commands), and debugs with the time-travel GUI; Cypress Cloud for parallel runs and recording. Use for both greenfield test authoring and improving hand-written specs already in the codebase. For automated refactor of raw Cypress Studio recordings specifically, use a dedicated codegen-review pass.

playwright-testing

Authors and remediates Playwright E2E tests across Chromium, Firefox, WebKit - `npm init playwright@latest` scaffolding, `playwright.config.ts` browser projects, accessibility-first locators (`getByRole`/`getByLabelText`) to replace brittle CSS selectors, web-first assertions to eliminate `waitForTimeout` flakiness, Page Object pattern, trace viewer debugging, sharded parallel execution with merged HTML reporting, mobile-web emulation via the `devices` catalog (viewport / DPR / touch per-device projects), the cross-browser matrix with branded channels (chrome / msedge) in references/browser-matrix.md, and GitHub Actions CI integration. Use for new test authoring, flakiness remediation, mobile-breakpoint regression, cross-browser matrix setup, and CI setup; for reviewing codegen output specifically, use a dedicated codegen-review pass.

selenium-grid-4-runner

Author and operate Selenium Grid 4 - self-hosted distributed WebDriver. Covers the six-component architecture (Router / Distributor / Session Map / Event Bus / New Session Queue / Node), standalone vs hub-and-node modes, the Docker-image stack (selenium/standalone-chrome, selenium/hub, selenium/node-chrome), node registration, session-queue tuning, and observability. Use for self-hosted cross-browser testing when data residency or cost-control require an on-prem grid. This is the self-hosted execution RUNNER - for the zero-infra alternative use playwright-testing browser projects (bundled engines); for managed cloud grids use cloud-grid-e2e (BrowserStack / Sauce Labs / LambdaTest); to decide WHICH browsers and tiers to run use browser-matrix-strategy-reference.

selenium-testing

Authors Selenium WebDriver tests in any of its 6+ supported languages (Java, Python, JavaScript, C#, Ruby, Kotlin, PHP) - picks the appropriate language binding, configures WebDriver per browser, uses `By.*` locators with the team's accessibility-first preference where supported, runs locally + via Selenium Grid for distributed execution, parses results to JUnit XML. Use for legacy Selenium-locked stacks; new projects pick Playwright or Cypress.

web-e2e-overview

Teaches web end-to-end testing from first principles: what browser-driven E2E covers and how it differs from unit and integration tests, a decision table for choosing between Playwright, Cypress, Selenium WebDriver, WebdriverIO, Puppeteer, TestCafe and the BrowserStack / Sauce Labs / LambdaTest cloud grids based on files already present in the repo, install and first-run commands for each, and the flakiness traps (fixed sleeps, CSS and XPath selectors, state shared between tests) that sink new suites. Use when a web application has no E2E coverage yet, when picking or replacing an E2E framework, or when a first browser test needs to go green end to end.