npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@diffdeckai/playwright-reporter

v1.0.1

Published

Playwright reporter that uploads each test's video recording, with a per-step timeline, to DiffDeck.

Downloads

257

Readme

@diffdeckai/playwright-reporter

A Playwright reporter that uploads each test's video recording — together with a per-step timeline — to DiffDeck. DiffDeck hosts the videos and renders the step timeline over them so you can scrub a failing run step by step.

The reporter does not run or change your tests. It hooks into Playwright's reporting lifecycle: on each test end it finds the recorded video attachment, packages the step timing it captured via onStepBegin/onStepEnd, and POSTs both to your DiffDeck repository.

Install

npm install --save-dev @diffdeckai/playwright-reporter

@playwright/test is a peer dependency (Playwright >=1.30).

Configure

Add the reporter to playwright.config.ts and make sure Playwright is recording video (the reporter has nothing to upload otherwise):

import { defineConfig } from "@playwright/test";

export default defineConfig({
  use: {
    video: "on", // or "retain-on-failure"
  },
  reporter: [
    ["list"],
    ["@diffdeckai/playwright-reporter"],
  ],
});

That's the whole integration. The only thing you must supply is the project token via DIFFDECK_TOKEN (see below) — host is optional and defaults to https://diffdeck.ai (production), and branch/commit auto-resolve from CI env vars. Point at a self-hosted / non-default instance only if you need to:

reporter: [["list"], ["@diffdeckai/playwright-reporter", { host: "https://diffdeck.example" }]],

Options

All options are optional and fall back to environment variables.

| Option | Type | Env fallback | Default | Description | | ---------------- | ---------- | ---------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- | | host | string | DIFFDECK_HOST | https://diffdeck.ai | DiffDeck base URL. Recordings are POSTed to <host>/api/products/ui-review/recordings. | | token | string | DIFFDECK_TOKEN | — | Per-repo project token, sent as the X-UI-Review-Token header. Required to upload. | | branch | string | DIFFDECK_BRANCH, GITHUB_HEAD_REF, GITHUB_REF_NAME, CI_COMMIT_REF_NAME, GIT_BRANCH | — | Git branch tagged on each recording. | | commitSha | string | DIFFDECK_COMMIT, GITHUB_SHA, CI_COMMIT_SHA, GIT_COMMIT | — | Git commit SHA tagged on each recording. | | stepCategories | string[] | — | all categories | If set, only steps with these Playwright categories are captured (e.g. ["test.step","expect"]). | | quiet | boolean | — | false | Silence the reporter's console output. | | mode | "upload" \| "write" | DIFFDECK_MODE | "upload" | "upload" (default) posts each recording during the run. "write" instead writes a metadata sidecar (see below). |

Environment variables

The only thing CI must provide is the token (and usually the host):

export DIFFDECK_TOKEN="your-repo-project-token"
export DIFFDECK_HOST="https://diffdeck.ai"   # optional; this is the default

If no token is found, the reporter logs a one-time warning and uploads nothing — your test run is never failed by a missing token.

What gets uploaded

For each completed test that has a video, the reporter sends a multipart/form-data request:

| Field | Source | | ------------ | --------------------------------------------------- | | video | the recorded video blob (video/webm or video/mp4) | | videoType | the video MIME type | | testTitle | test.title | | testFile | spec file, relative to the Playwright rootDir | | testId | test.id | | status | passed | failed | timedOut | skipped | interrupted | | durationMs | result.duration | | retries | result.retry | | branch | resolved branch (see options) | | commitSha | resolved commit (see options) | | metadata | JSON string — the step-timeline document (below) |

Authentication is the X-UI-Review-Token header.

If the repository does not have the DiffDeck recordings add-on enabled, the route responds 402. The reporter treats this as a soft signal: it logs once and stops uploading for the rest of the run, without failing the test run.

Deferred upload (mode: "write")

By default the reporter uploads during the run — that's the recommended integration and needs no extra CI step. If you'd rather upload after the run (for example to keep the test job offline, or to batch the upload in a separate step), set mode: "write" (or DIFFDECK_MODE=write). Instead of uploading, the reporter writes a <video>.json metadata sidecar next to each recorded video, carrying the same fields it would have uploaded (test title, file, id, status, duration, retries, branch, commit, and the step timeline). No token is needed in write mode.

A later step then uploads them — e.g. the diffdeck CLI / GitHub Action, which reads those sidecars so nothing has to be derived from file paths:

- uses: diffdeck/diffdeck-action@v1
  with:
    command: recording
    token: ${{ secrets.DIFFDECK_TOKEN }}
    videos: test-results   # dir of videos + their .json sidecars

Most projects should stay on the default "upload" mode.

Step-timeline metadata shape

The metadata field is a JSON document describing the per-step timeline. This is the data the DiffDeck player renders over the video. All step times are in milliseconds relative to the start of the recording, where the recording start equals the test's startTime (recordingStartTime).

{
  "schemaVersion": 1,
  "reporter": "@diffdeckai/playwright-reporter",
  "reporterVersion": "0.1.0",
  "recordingStartTime": "2026-06-30T12:00:00.000Z", // ISO; timeline zero point
  "testDurationMs": 1500,
  "steps": [
    {
      "title": "sign in",            // step label
      "category": "test.step",       // test.step | expect | pw:api | hook | fixture | ...
      "startMs": 100,                // offset from recordingStartTime (>= 0)
      "endMs": 500,                  // offset from recordingStartTime (>= startMs)
      "durationMs": 400,             // endMs - startMs
      "depth": 0,                    // nesting depth (0 = top level)
      "location": "e2e/login.spec.ts:12:5", // optional source location
      "error": "locator.click: timeout"     // optional; present only on failed steps
    },
    {
      "title": "expect(page).toHaveURL(\"/dashboard\")",
      "category": "expect",
      "startMs": 300,
      "endMs": 350,
      "durationMs": 50,
      "depth": 1
    }
  ]
}

TypeScript types for this document are exported from the package:

import type { RecordingMetadata, RecordingStep } from "@diffdeckai/playwright-reporter";
import { RECORDING_METADATA_SCHEMA_VERSION } from "@diffdeckai/playwright-reporter";

Notes on alignment

  • startMs/endMs are computed from each step's startTime minus the test's startTime, then clamped to >= 0. The test start time is the best alignment to the video that Playwright exposes; sub-frame drift between the first painted frame and startTime is expected and small.
  • Steps are ordered by startMs (ties keep capture order), and depth reflects nesting so a consumer can render the timeline hierarchically.

Development

npm install
npm run build       # tsc -> dist/
npm test            # node:test unit tests (mocked fetch)
npm run typecheck

License

MIT