> ## Documentation Index
> Fetch the complete documentation index at: https://plugwright.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Reports

> JSON and JUnit XML output, per environment.

Every run writes two report files, whether it was started by `plugwrightTest<Env>` or by the matrix:

```
build/reports/plugwright/<env>.json        machine-readable, what the matrix aggregates
build/reports/plugwright/junit/<env>.xml   JUnit XML for CI
build/reports/plugwright/<env>.log         per-environment output, matrix runs only
```

## JSON

```json theme={null}
{
  "environment": "staging",
  "summary": { "total": 47, "passed": 33, "failed": 0, "skipped": 14, "durationMs": 152340 },
  "tests": [
    {
      "file": "…/dist/commands.spec.js",
      "name": "help command shows available commands",
      "status": "pass",
      "durationMs": 63,
      "error": null,
      "skipReason": null,
      "plugin": null
    },
    {
      "file": "…/dist/simple-ts.spec.js",
      "name": "server logs command execution",
      "status": "skip",
      "durationMs": 0,
      "error": null,
      "skipReason": "requires capability [consoleOutput:full], unavailable on \"staging\"",
      "plugin": null
    }
  ]
}
```

`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`](/writing-tests) 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 with [`concurrency`](/writing-tests) still gets one entry, not N. `durationMs` is the slowest instance, and `instances` carries every instance's own outcome:

```json theme={null}
{
  "file": "…/dist/claim.spec.js",
  "name": "only one player can claim the chest",
  "status": "fail",
  "durationMs": 812,
  "error": "Expected message matching \"Claimed\" not received",
  "skipReason": null,
  "plugin": null,
  "botUsername": null,
  "instances": [
    { "index": 1, "botUsername": "pw_a1", "passed": true, "durationMs": 640, "error": null },
    { "index": 2, "botUsername": "pw_b2", "passed": false, "durationMs": 812, "error": "Expected message matching \"Claimed\" not received" },
    { "index": 3, "botUsername": "pw_c3", "passed": true, "durationMs": 701, "error": null }
  ]
}
```

`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.

## JUnit XML

```xml theme={null}
<testsuite name="plugwright.staging" tests="47" failures="0" skipped="14" time="152.340">
  <testcase classname="…/dist/commands.spec.js" name="help command shows available commands" time="0.063"/>
  <testcase classname="…/dist/simple-ts.spec.js" name="server logs command execution" time="0.000">
    <skipped message="requires capability [consoleOutput:full], unavailable on &quot;staging&quot;"/>
  </testcase>
</testsuite>
```

The suite name is `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:

```yaml theme={null}
- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: plugwright-reports
    path: build/reports/plugwright/
```

## Matrix summary

```
Environment summaries:
  local     47 passed,  0 failed,  0 skipped   (4m 09s)
  staging   33 passed,  2 failed, 14 skipped   (2m 35s)   [allowFailure]
```

An environment that produced no report at all gets an `ERROR:` line instead of counts:

```
  staging   ERROR: Command '…cli.js --config …' failed with exit code: 1   [allowFailure]
```

Failed tests and an unreachable server are different problems, and the summary keeps them apart so you know whether to read the diff or fix the stand.
