Skip to content

Report Playwright results as they run

@plune-ai/playwright is a Playwright reporter: it sends results to the Plune platform while the suite runs, instead of parsing a report afterwards.

v0.2.2no runtime depsNode ≥ 20

  1. Install it beside the Playwright you already have:

    Terminal window
    npm install --save-dev @plune-ai/playwright
    npx plune login # from @plune-ai/cli — saves your API token
  2. Add it as a reporter:

    playwright.config.ts
    import { defineConfig } from "@playwright/test";
    export default defineConfig({
    reporter: [["list"], ["@plune-ai/playwright"]],
    });

That is the whole setup. Everything below is for cases the default does not cover.

A committed config cannot know the run key of a job that does not exist yet, so the environment fills in what the file left out — and anything set in the file wins.

env:
PLUNE_TOKEN: ${{ secrets.PLUNE_TOKEN }}
PLUNE_RUN: ${{ github.run_id }}-${{ github.run_attempt }}
PLUNE_ENV: staging
PLUNE_LABELS: smoke,nightly
VariableWhat it does
PLUNE_TOKENthe API token, so a CI job need not run plune login
PLUNE_RUNthe shared run key — every process with the same value lands in one run
PLUNE_API_URLwhich deployment to report to
PLUNE_SHARED_RUNthis process is one of several — do not close the run
PLUNE_PROCEEDthe job closes the run itself, later
PLUNE_BATCH_SIZEresults per request (default 100, max 500)
PLUNE_RUN_TITLE · PLUNE_ENV · PLUNE_LABELSwhat the run is called, where it ran, how it is marked
PLUNE_CREATEoffer tests Plune has no case for to the review queue

Flags are read by value, not by presence: PLUNE_PROCEED=0 in a matrix cell means off, not on.

Give every shard the same key and they land in one run rather than one run each:

env:
PLUNE_RUN: ${{ github.run_id }}-${{ github.run_attempt }}
PLUNE_SHARED_RUN: "1"

The Playwright test id is stable across shards and merges, which is what makes the same test in shard 3 of 8 resolve to the same case it resolved to yesterday.

First match wins:

  1. A PluneId annotation — names the case outright.

    test("rejects a negative quantity",
    { annotation: { type: "PluneId", description: "tc-1a2b" } },
    async ({ page }) => { /* … */ });
  2. A @P<id> token in the title — the same statement, where you can read it in the report. No space after @P: @Ptc-1a2b.

  3. An application/plune.metadata+json attachment — for a fixture that knows the id, or a key from another tool: {"keys": [{"kind": "qase", "value": "Q-9"}]}.

  4. The Playwright test id — stable across shards.

  5. File and title pathcart.spec.ts#cart#rejects a negative quantity.

A result that matches no case is not sent and not invented. A made-up id would write into some other test’s history, which is worse than a gap; the run summary counts them instead. To turn those into proposals, set PLUNE_CREATE=1 — they reach the review queue and wait for a person.

Nothing is lost and nothing fails. An unreachable platform, a refused token or an already-closed run all append the batch to .plune/pending-results.jsonl, print one line saying so, and let your suite exit with its own code. Your tests do not depend on our uptime.

The status. Playwright’s own word — passed, failed, timedOut, interrupted, skipped — goes to Plune as-is, and the project maps it. So your team can decide a timeout counts as broken (the default) or as something else, and change its mind, without waiting for a release of this package.