TestPlanIt Reporter Reporter
@testplanit/wdio-reporter is a 3rd party package, for more information please see GitHub | npm
WebdriverIO reporter and service for TestPlanIt - report test results directly to your TestPlanIt instance.
This package includes:
- Reporter - Tracks test execution in worker processes and reports results to TestPlanIt
- Service - Manages the test run lifecycle in the main process, ensuring all workers report to a single test run
Installation
npm install @testplanit/wdio-reporter
# or
pnpm add @testplanit/wdio-reporter
# or
yarn add @testplanit/wdio-reporter
Quick Start
1. Generate an API Token
- Log into your TestPlanIt instance
- Go to Settings > API Tokens
- Click Generate New Token
- Copy the token (it starts with
tpi_)
2. Configure the Reporter and Service
Add both the service and reporter to your wdio.conf.js or wdio.conf.ts:
// wdio.conf.js
import { TestPlanItService } from '@testplanit/wdio-reporter';
export const config = {
services: [
[TestPlanItService, {
domain: 'https://testplanit.example.com',
apiToken: process.env.TESTPLANIT_API_TOKEN,
projectId: 1,
runName: 'E2E Tests - {date} {time}',
captureScreenshots: true,
}]
],
reporters: [
['@testplanit/wdio-reporter', {
domain: 'https://testplanit.example.com',
apiToken: process.env.TESTPLANIT_API_TOKEN,
projectId: 1,
}]
],
// ... rest of config
}
Note: The service is recommended when running with
maxInstances > 1. It creates a single test run before workers start, eliminating race conditions. Without the service, the reporter can still manage test runs on its own using file-based coordination (oneReport: true).
Service vs Reporter
| Aspect | Service | Reporter |
|---|---|---|
| Process | Main WDIO process | Each worker process |
| Timing | Runs once before/after all workers | Runs in each worker |
| Test run creation | Creates in onPrepare | Fallback: creates if no service |
| Result reporting | - | Reports each test result |
| Screenshot capture | Optional (captureScreenshots) | - |
| Screenshot upload | - | Uploads in onRunnerEnd |
| Run completion | Completes in onComplete | Skips if service-managed |
Sharing One Run Across Sharded, Parallel or Retried Executions
The service and oneReport both collapse a single WebdriverIO execution into one
test run. Neither spans separate executions: their shared state lives in a file
in the OS temp directory, so it cannot reach a second CI agent, and it is reset
once a run's workers have all finished. A suite split into shards, spread across
agents, or rerun in retry waves therefore produces one run per invocation — often
several runs with the same name.
To collect all of them in a single run, create the run in the pipeline and let every invocation attach to it:
RUN_ID=$(testplanit run create --project 9 --name "Web Regression Tests - DEV #984" --type MOCHA)
export TESTPLANIT_RUN_ID="$RUN_ID"
# Every shard, agent and retry wave attaches to $TESTPLANIT_RUN_ID
pnpm web:bs --spec ./test/specs/shard-1/**
pnpm web:bs --spec ./test/specs/shard-2/**
# ...deferred retries, other agents...
testplanit run complete --id "$RUN_ID"
testplanit is the @testplanit/cli
package (npm i -g @testplanit/cli). It reads TESTPLANIT_URL and
TESTPLANIT_API_TOKEN, or credentials stored once with
testplanit config set --url ... --token ...; run testplanit run --help for
the full list of options.
Nothing else in the config has to change. Both the service and the reporter read
TESTPLANIT_RUN_ID, so the recommended service + reporter setup works as-is —
the service reports into the pinned run instead of creating one in onPrepare,
and leaves it open in onComplete.
What Changes When a Run Is Externally Managed
A run supplied through TESTPLANIT_RUN_ID or the testRunId option is
externally managed. For such a run the reporter:
- Never creates a run. If the run cannot be read, the failure is logged and results are still attached to the given ID rather than to a replacement run.
- Never completes it, regardless of
completeRunOnFinish— the pipeline closes it withtestplanit run completeonce every invocation has finished. A shard that completed the run would push the ones behind it onto a new run. - Never discards it. The recovery paths that start a fresh run when the shared state is exhausted, completed or deleted do not apply.
- Never changes its settings.
configId,milestoneId,stateIdandtagIdsare ignored, since those belong to whoever created the run. Case creation options (parentFolderId,templateId, and the rest) still apply.
The service behaves the same way, with one addition: runLinks and runMetadata
describe the run as a whole, so it applies them only to runs it created —
otherwise every shard would duplicate the links and the last one would overwrite
the metadata. runAttachments are per-execution artifacts and still upload from
each shard.
Suites Within the Run
Each execution creates its own JUnit suite under the shared run, named
{suite} - {browser}/{platform} - {spec} by default so shards are
distinguishable. Results roll up at the run level across every suite. Override
the naming with testSuiteName, which accepts the same placeholders as
runName. The service's launcher process runs before any browser exists, so its
testSuiteName also resolves {env:VAR} — name shards from the pipeline, for
example testSuiteName: 'Shard {env:SHARD_ID}'.
Resolution Order
The first of these that yields a run wins:
testRunIdgiven as a numberTESTPLANIT_RUN_ID(ignored unless it is a positive integer, so an unresolved shell variable falls through instead of failing)testRunIdgiven as a name, looked up by exact match- the
oneReportshared-state file - a new run
Options 1–3 are externally managed. With none of them set, behaviour is
unchanged: oneReport still dedupes workers within one execution, and the run is
created and completed as before.