TestKase Docs
AutomationTest Frameworks

WebdriverIO

Set up WebdriverIO for browser and mobile testing and integrate test results with TestKase.

Overview

WebdriverIO is a progressive automation framework for web and mobile testing built on the WebDriver and Chrome DevTools protocols. It provides a concise, expressive API for browser interactions and supports both Mocha and Jasmine test runners out of the box.

To integrate WebdriverIO test results with TestKase, use the @wdio/junit-reporter package to generate JUnit XML output, then push results with:

--format junit

Prerequisites

  • Node.js 18 or later
  • A modern browser (Chrome, Firefox, or Edge)
  • A TestKase account with a project, test cycle, and Automation IDs configured on your test cases

Installation

WebdriverIO provides an interactive CLI that scaffolds a complete project with your preferred configuration:

npm init wdio@latest

The CLI will prompt you to select:

  • Where to run tests (local, cloud service)
  • Framework (Mocha, Jasmine, or Cucumber)
  • Compiler (TypeScript or Babel, optional)
  • Reporter (select JUnit for TestKase integration)
  • Services (ChromeDriver, Selenium Standalone, etc.)

If you did not select the JUnit reporter during setup, install it separately:

npm install --save-dev @wdio/junit-reporter

Project Setup

After running the init command, your project structure looks like this:

my-wdio-project/
├── package.json
├── wdio.conf.js
└── test/
    └── specs/
        ├── login.test.js
        └── search.test.js

Configure the JUnit reporter in wdio.conf.js:

// wdio.conf.js
export const config = {
    runner: 'local',

    specs: [
        './test/specs/**/*.test.js'
    ],

    capabilities: [{
        browserName: 'chrome'
    }],

    framework: 'mocha',
    mochaOpts: {
        ui: 'bdd',
        timeout: 60000
    },

    reporters: [
        'spec',
        ['junit', {
            outputDir: 'test-results',
            outputFileFormat: function (options) {
                return 'junit.xml';
            }
        }]
    ],

    baseUrl: 'https://example.com',
};

Key configuration sections:

SectionPurpose
specsGlob pattern for test files
capabilitiesBrowsers to run tests against
frameworkTest runner (mocha, jasmine, or cucumber)
reportersOutput formats — add junit for TestKase
baseUrlDefault base URL for browser.url() calls

Writing Tests

Create a test file at test/specs/login.test.js:

describe('Login', () => {
    beforeEach(async () => {
        await browser.url('/login');
    });

    it('[48271] should login with valid credentials', async () => {
        await $('#username').setValue('testuser');
        await $('#password').setValue('password123');
        await $('#login-btn').click();

        const dashboard = await $('#dashboard');
        await dashboard.waitForDisplayed({ timeout: 5000 });

        await expect(dashboard).toBeDisplayed();
    });

    it('[48272] should show error for invalid credentials', async () => {
        await $('#username').setValue('invalid');
        await $('#password').setValue('wrong');
        await $('#login-btn').click();

        const error = await $('.error-message');
        await error.waitForDisplayed({ timeout: 5000 });

        await expect(error).toHaveText('Invalid credentials');
    });

    it('[48273] should navigate to forgot password', async () => {
        await $('a=Forgot Password').click();

        const resetForm = await $('#reset-form');
        await resetForm.waitForDisplayed({ timeout: 5000 });

        await expect(browser).toHaveUrl(expect.stringContaining('/reset-password'));
    });
});

Each test name includes a 5-digit Automation ID in square brackets. The @testkase/reporter CLI extracts these IDs using the regex \[(\d{5})\]. For the example above, the extracted Automation IDs are:

  • 48271 → linked to the "valid login" test case in TestKase
  • 48272 → linked to the "invalid credentials" test case in TestKase
  • 48273 → linked to the "forgot password" test case in TestKase

The [XXXXX] pattern can appear anywhere in the it() name:

describe('Auth', () => {
    describe('Login', () => {
        it('[48271] should work', async () => { /* ... */ });
        // Reporter extracts ID: 48271
    });
});

Running Tests

Run your WebdriverIO tests with:

npx wdio run wdio.conf.js

The JUnit reporter writes the results file to the location configured in wdio.conf.js. With the configuration above, the output is written to test-results/junit.xml.

To run a specific test file:

npx wdio run wdio.conf.js --spec test/specs/login.test.js

TestKase Integration

After running your tests, use the @testkase/reporter CLI to push results to TestKase:

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file test-results/junit.xml

--cycle-id is optional. If not provided, results are reported to TCYCLE-1 — the master test cycle for the project.

Use --dry-run --verbose the first time to verify that your test names match the Automation IDs you set in TestKase. This helps catch naming mismatches before pushing real results.

Automation ID Mapping

The reporter extracts 5-digit Automation IDs from WebdriverIO test names using the [XXXXX] bracket pattern:

Test CodeExtracted ID
it('[48271] should login with valid credentials', ...)48271
it('[48272] should show error for invalid credentials', ...)48272
describe('Auth', () => { it('[48273] should work', ...) })48273

The [XXXXX] pattern can appear anywhere in the it() name. The describe block structure does not affect the Automation ID — only the 5-digit number inside brackets matters.

Complete Example

This end-to-end example shows the full flow for a WebdriverIO project.

1. Set Up the Project

mkdir wdio-testkase-demo && cd wdio-testkase-demo
npm init wdio@latest .

Select Mocha as the framework and JUnit Reporter when prompted.

2. Write a Test

Create test/specs/search.test.js:

describe('Search', () => {
    it('[48271] should return results for a valid query', async () => {
        await browser.url('/');
        await $('#search-input').setValue('webdriverio');
        await $('#search-btn').click();

        const results = await $('#search-results');
        await results.waitForDisplayed({ timeout: 5000 });

        const items = await $$('.result-item');
        await expect(items).toBeElementsArrayOfSize({ gte: 1 });
    });

    it('[48272] should show no results message for empty query', async () => {
        await browser.url('/');
        await $('#search-btn').click();

        const message = await $('.no-results');
        await message.waitForDisplayed({ timeout: 5000 });

        await expect(message).toHaveText('No results found');
    });
});

3. Set the Automation ID in TestKase

In your TestKase project, generate Automation IDs on the corresponding test cases, then embed them in your it() names using the [XXXXX] bracket pattern.

4. Run the Tests

npx wdio run wdio.conf.js

5. Verify with Dry Run

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file test-results/junit.xml \
  --dry-run --verbose

6. Push Results

Remove the --dry-run flag to push results to TestKase:

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format junit \
  --results-file test-results/junit.xml

Troubleshooting

wdio-junit-reporter not installed

ERROR: Unknown reporter "junit". Did you mean to install @wdio/junit-reporter?

Cause: The JUnit reporter package is not installed.

Fix: Install it as a dev dependency:

npm install --save-dev @wdio/junit-reporter

Then verify it is listed in the reporters array in wdio.conf.js.

Multiple browser XML files

Cause: When running tests against multiple browsers, each browser generates a separate XML file with a unique name, making it difficult to point the reporter at a single file.

Fix: Configure the outputFileFormat option in wdio.conf.js to produce a predictable file name, or use a glob pattern in the reporter command:

// wdio.conf.js
reporters: [
    ['junit', {
        outputDir: 'test-results',
        outputFileFormat: function (options) {
            return 'junit.xml';
        }
    }]
]

If you need separate files per browser, use a wildcard in the reporter command:

--results-file "test-results/*.xml"

Elements not found

Error: element ("#my-element") still not displayed after 5000ms

Cause: The element has not rendered yet or the selector is incorrect.

Fix: Use waitForDisplayed or browser.waitUntil to wait for elements before interacting:

const element = await $('#my-element');
await element.waitForDisplayed({ timeout: 10000 });
await element.click();

For dynamic content, use browser.waitUntil with a custom condition:

await browser.waitUntil(
    async () => (await $$('.result-item')).length > 0,
    { timeout: 10000, timeoutMsg: 'Results did not load' }
);