plugwrightTest<Env> or by the matrix:
JSON
status is pass, fail or skip. plugin names the plugin a test came from when it was inherited rather than found in your test directory. botUsername is the bot that ran it, when one connected.
Every skip carries its reason: excluded by name, wrong environment, a capability the environment doesn’t have, or an earlier test in the same describe.serial block that stopped the chain. A skipped test that doesn’t say why is worse than a failing one, because it reads as coverage.
Tests from a serial block appear as ordinary entries, in the order they ran, under their full describe path.
Concurrent tests
A test (or block) run withconcurrency still gets one entry, not N. durationMs is the slowest instance, and instances carries every instance’s own outcome:
instances is null for an ordinary, non-concurrent test — botUsername on the row itself is where its bot lives instead. The JUnit report doesn’t carry this breakdown; it only ever sees the one aggregated pass/fail/duration, so read the JSON report when a concurrent test fails.
Read skipped before passed. Instances of a describe.serial block diverge: one whose earlier test failed stops there, and every position after it is recorded with passed: true and durationMs: 0 without having run. Counting those as passes is how a block that broke on three of ten bots comes out looking like seven-out-of-ten coverage.
The row’s own status is skip only when every instance skipped, so summary.skipped misses instance-level skips entirely. summary.skippedInstances counts them across the whole run; the console summary prints the same number next to Skipped.
JUnit XML
plugwright.<env>, so a matrix run produces one suite per environment and CI keeps them apart. classname is the spec file, name is the full test name including its describe chain. Failures carry the message as the attribute and the stack as the body.
Most CI systems pick these up with a glob:
Matrix summary
ERROR: line instead of counts: