WebdriverIO test reporting
@qualflare/webdriverio reports your suite from inside
each WebdriverIO worker — statuses, every attempt of a retried test, the screenshots WebdriverIO takes, and
the browser or device each test ran on — for web runs and Appium alike. The part most reporters get wrong
is the workers: each spec file runs in its own process and writes
its own report, and those reports have to be recognised as one run. This guide covers the setup, how the
workers agree on one run with nothing to configure, and the
hosted, historical analysis — AI failure clustering,
flaky-test scoring and per-launch risk — that sits on top.
One run, many workers
WebdriverIO's local runner starts a separate worker process for every spec file, and constructs a fresh instance of each reporter inside that worker. So a run of three spec files is three reporters, each knowing about one file, each writing its own report:
qualflare-results/ ├── 3f1c…e2a.json # worker 0-0: specs/login.spec.js ├── 9b07…41d.json # worker 0-1: specs/checkout.spec.js ├── a4e2…77c.json # worker 0-2: specs/search.spec.js └── 5d9f…0be.png # a screenshot, referenced from a report
qf collect merges that directory into one launch, and it
decides what belongs together by a run id stamped into every
report. That check is deliberate: it is what stops a report left over from yesterday from being merged into
today's launch. When the files disagree, it keeps the newest run and ignores the rest. Which means the
workers have to agree.
In CI they already do: the provider's run id is shared by every worker, including across the machines of a
sharded job. Outside CI, every worker of one run has something else in common — the
wdio run process that started it. Each worker derives the
run id from that launcher process, its process id and start time, so
a run's workers agree and the next run gets a new id. That holds on Linux, including inside the
xvfb-run wrapper WebdriverIO 9 puts around each headless
worker, on macOS and on Windows. If a worker ever has to fall back to an id of its own, it says so on stderr.
Send WebdriverIO results to Qualflare
npm install --save-dev @qualflare/webdriverio
Add the reporter to wdio.conf, keeping whichever reporter
prints your terminal output:
// wdio.conf.js
export const config = {
// ...
maxInstances: 4,
// The reporter alone is enough: every worker derives one run id
// from the `wdio run` launcher process.
reporters: ['spec', ['@qualflare/webdriverio', { environment: 'staging' }]],
// The idiomatic screenshot-on-failure hook. Its screenshot is
// attached to the test it was taken for, even though it runs
// after the test's verdict.
afterTest: async function (test, context, { passed }) {
if (!passed) await browser.takeScreenshot();
},
}; Run the suite, then upload the directory — never a single file, since each worker wrote its own:
npx wdio run wdio.conf.js qf myapp collect ./qualflare-results
Reports go to ./qualflare-results by default. The option
that moves them is resultsDir, not
outputDir: WebdriverIO reads a reporter option named
outputDir as the directory for the reporter's own log
file, and sharing the name would put WebdriverIO's logs among your reports. Upload with
@qualflare/cli v0.1.37 or newer, the first release that
labels these launches as WebdriverIO.
What gets captured
- Retries, attempt by attempt. With Mocha's
this.retries(n), a failed attempt keeps its own error, stack, start time and duration, and a test that passed on retry is marked flaky rather than collapsing into a green result. - Screenshots, on the right test. Every
takeScreenshot(),saveScreenshot()and element screenshot is picked up from WebdriverIO's command stream — including one taken inafterTest, after the verdict. A retried test keeps each attempt's screenshots, named by attempt. - Where it ran. The browser name and version, or for Appium the platform, device, OS version and driver, are recorded on every case, and the launch's platform is derived from the session.
- One history per capability. A suite run on Chrome and Firefox keeps two histories, not one merged one — otherwise the second browser's result would silently replace the first's.
Annotate tests from inside the suite
A small runtime API, importable on its own so a spec file does not load the reporter, lets a test label itself, group its actions into steps, and record parameters — masked ones are dropped before they are written anywhere:
import { qualflare } from '@qualflare/webdriverio/runtime';
it('checks out', async () => {
qualflare.label('team', 'payments');
qualflare.parameter('card', '4242…', { masked: true });
await qualflare.step('fill the card form', async () => {
await $('#card').setValue('4242 4242 4242 4242');
});
await $('button=Pay').click();
}); CI: upload even when the suite fails
# .github/workflows/e2e.yml
- name: WebdriverIO
run: npx wdio run wdio.conf.js
continue-on-error: true
# Runs even when the suite failed: a red run is the one
# whose screenshots you actually want.
- name: Upload results
if: always()
run: |
qf login "$QF_PROJECT" --force # token from QF_TOKEN
qf "$QF_PROJECT" collect ./qualflare-results Common WebdriverIO reporting problems
“The launch has only one spec file's tests”
The workers reported under different run ids, and the upload kept the newest. Check stderr for the
warning a worker prints when it couldn't identify its launcher, and that
@qualflare/webdriverio is 0.2.0 or newer. If you
start WebdriverIO with a programmatic Launcher more
than once in one Node process, those runs share a launcher: add
'@qualflare/webdriverio/service' to
services, which gives each run its own id.
“A failed Jasmine spec has no screenshot on WebdriverIO 8”
WebdriverIO 8 calls afterTest with
passed: true for a Jasmine spec whose expectation
failed, so an if (!passed) hook never takes one. That
is WebdriverIO's behaviour, and it is fixed in 9; on 8 with Jasmine, take the screenshot unconditionally.
“A re-run spec file shows every test twice”
specFileRetries re-runs a whole spec file in a new
worker, which writes a second report for the same run. Per-test retries are recorded as attempts of one
case, which is what flakiness tracking is built on; prefer them.
WebdriverIO is one of fifteen frameworks with a native Qualflare reporter. Running Appium through it? See Appium test reporting. Weighing tools? See how Qualflare compares to other test management platforms, or browse all framework reporting guides.
Frequently asked questions
Is there a native Qualflare reporter for WebdriverIO?
Yes — @qualflare/webdriverio, Apache-2.0 on npm. It runs inside each WebdriverIO worker and records statuses, every attempt of a retried test with its own error and timing, the screenshots WebdriverIO takes, and the session’s browser or device capabilities. It makes no network calls: each worker writes a report file, and `qf collect` merges them into one launch.
How do the reports from different workers end up in one launch?
WebdriverIO runs every spec file in its own worker process, and each worker writes its own report. `qf collect` merges the files that belong to one run, recognised by a shared run id, and keeps only the newest run when it finds several. In CI the provider’s run id is already shared by every worker. Outside CI, each worker derives the same id from the `wdio run` launcher process — its process id and start time — so a run’s workers agree and the next run gets a new id, on Linux, macOS and Windows. Nothing needs configuring. The optional @qualflare/webdriverio/service is only needed when a programmatic Launcher runs WebdriverIO more than once in one Node process, which shares one launcher; it also cleans up reports from earlier runs.
Which WebdriverIO versions and frameworks are supported?
WebdriverIO 8 and 9, with Mocha or Jasmine, on Node 18.20 or newer. Both majors are tested on every change with a real run: two workers, headless Chrome, Mocha and Jasmine. Cucumber is not supported yet, because WebdriverIO reports scenarios as suites and Gherkin steps as tests, so every step would become its own test case; the reporter warns when it sees a Cucumber run.
Does it work for Appium?
Yes — an Appium session driven through WebdriverIO is reported the same way, with the platform derived from the session: ios or android from platformName, even for mobile Safari, which also reports a browser. If you want those launches labelled as Appium in Qualflare, install @qualflare/appium instead: it is this reporter with Appium’s label.
What happens to a flaky test that passes on retry?
It arrives as one case, marked flaky, with every attempt attached: the failed attempt keeps its own error message and stack trace, and each attempt has its own start time and duration. The screenshots taken during the failed attempt are kept too, named by attempt, because a flaky test’s failure screenshot is the most useful evidence it leaves behind.
Does Qualflare run my WebdriverIO tests?
No. Qualflare is a results-management and observability layer, not a browser or device cloud. Your suite runs wherever it already runs — locally, in CI, against Selenium Grid or a device farm — and Qualflare reads the reports it writes.
Setup reflects @qualflare/webdriverio 0.1.0, the Qualflare CLI
(docs.qualflare.com)
and WebdriverIO 8 and 9 as of October 2026; the worker and event behaviour was measured on real runs.
Published 3 October 2026. Written by
İbrahim Süren, founder of Qualflare.