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

@s4labs/pixmoat-playwright

v0.1.4

Published

Playwright integration for pixmoat visual regression testing

Readme

@s4labs/pixmoat-playwright

Playwright integration for pixmoat visual regression testing.

Installation

npm install @s4labs/pixmoat-playwright

Usage

Fixture API (recommended)

Capture pages explicitly with the pixmoat fixture. Start the build in globalSetup and finish it in globalTeardown:

// global-setup.ts
import { writeFileSync } from "node:fs";
import { startBuild } from "@s4labs/pixmoat-playwright";

export default async function globalSetup() {
  const buildId = await startBuild();
  // Hand the build ID to the CI gate step (see "CI gate" below).
  writeFileSync("pixmoat-build.env", `PIXMOAT_BUILD_ID=${buildId}\n`);
}
// global-teardown.ts
import { finishBuild } from "@s4labs/pixmoat-playwright";

export default async function globalTeardown() {
  await finishBuild();
}
// tests/visual.spec.ts
import { test } from "@s4labs/pixmoat-playwright";

test("visual check", async ({ page, pixmoat }) => {
  await page.goto("/my-page");
  await pixmoat.snapshot(page, "my-page");
});
// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  globalSetup: "./global-setup.ts",
  globalTeardown: "./global-teardown.ts",
});

finishBuild() returns Promise<void>. It marks the build complete and logs [pixmoat] Build finished: …, but it does not fail CI when the build has unreviewed changes. Add the separate gate step below.

Required environment variables:

  • PIXMOAT_PROJECT — Project slug
  • PIXMOAT_TOKEN — Project access token (pxg_...)
  • PIXMOAT_BRANCH — Git branch name
  • PIXMOAT_COMMIT — Git commit SHA

Optional environment variables:

  • PIXMOAT_URL — Base URL of the pixmoat SaaS (default: https://app.pixmoat.com)
  • PIXMOAT_BUILD_ID — Reuse an existing build instead of creating one
  • PIXMOAT_CI_URL — CI job URL
  • PIXMOAT_PR — Pull request number

Section and dialog captures

Use captureSelector to capture a section on its real page while retaining its width, theme, and responsive styles:

import { test } from "@s4labs/pixmoat-playwright";

test("payment method", async ({ page, pixmoat }) => {
  await page.goto("/settings/billing");
  await page.locator('[data-testid="payment-method"]').scrollIntoViewIfNeeded();
  await pixmoat.snapshot(page, "Payment method - Detail", {
    captureSelector: '[data-testid="payment-method"]',
    viewports: ["1440x900", "375x812"],
  });
});

The selector must match one visible element. Its visible bounds, including ancestor scroll-container clipping, define the image. For tall sections, prepare named scroll states to cover the header and footer separately. captureSelector overrides fullPage; viewport, browser, and DPR still identify the rendering environment. Element maps and overlay coordinates use the cropped image origin. Accessibility and performance audits still cover the page; violations outside the crop retain their findings without an image overlay.

Prepare layout-sensitive states independently at each viewport. Capture portalled dialogs by their own selector. Keep representative page captures for section order, spacing, and dialog placement. Changing an existing capture's scope requires a distinct name and a reviewed baseline migration.

captureSnapshotImage(page, fullPage, captureSelector) exposes the same image capture for applications that write local evidence without uploading. It performs no Pixmoat requests. Callers own page preparation and settling.

CI gate

Run a separate step after npx playwright test that reads the build verdict and fails unless it is PASS:

npx playwright test
. ./pixmoat-build.env   # sets PIXMOAT_BUILD_ID, written by global-setup.ts
verdict=$(curl --fail --silent --show-error --max-time 330 \
  -H "Authorization: Bearer $PIXMOAT_TOKEN" \
  "${PIXMOAT_URL:-https://app.pixmoat.com}/api/builds/$PIXMOAT_BUILD_ID/agent-summary?wait=true&timeout=300" \
  | jq -r '.verdict')
echo "pixmoat verdict: $verdict"
test "$verdict" = "PASS"

wait=true waits for the server to finish processing the build (up to timeout seconds, default 120, max 300). It does not wait for a human review. The step fails while changes are pending review (REVIEW, REVIEW_NEW), when a run was rejected (FAIL), and when processing times out (HTTP 408). After a reviewer approves the build in pixmoat, retry the step.

GitLab CI Integration

Fragment. The complete pipeline, with the workflow rules and variables, is in the GitLab CI guide. PIXMOAT_TOKEN is a masked, unprotected CI/CD variable.

variables:
  PIXMOAT_PROJECT: "my-project"

visual-regression:
  stage: test
  image: mcr.microsoft.com/playwright:v1.52.0-noble   # match your @playwright/test version
  variables:
    PIXMOAT_BRANCH: $CI_COMMIT_REF_NAME
    PIXMOAT_COMMIT: $CI_COMMIT_SHA
  script:
    - npm ci
    - npx playwright test
  artifacts:
    when: always
    reports:
      dotenv: pixmoat-build.env   # PIXMOAT_BUILD_ID for the gate job

visual-gate:
  stage: test
  needs:
    - job: visual-regression
      artifacts: true
  image: alpine:3.20
  script:
    - apk add --no-cache curl jq
    - |
      verdict=$(curl --fail --silent --show-error --max-time 330 \
        -H "Authorization: Bearer $PIXMOAT_TOKEN" \
        "${PIXMOAT_URL:-https://app.pixmoat.com}/api/builds/$PIXMOAT_BUILD_ID/agent-summary?wait=true&timeout=300" \
        | jq -r '.verdict')
      echo "pixmoat verdict: $verdict"
      test "$verdict" = "PASS"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Playwright Reporter (beta)

Warning: the reporter uploads only the PNG attachments it sees on test results. A passing toHaveScreenshot() assertion produces no attachment, and failed uploads are not accounted for, so snapshots it omits can be reported as removals. Approving those removals retires valid baselines. Use the fixture API for CI until the reporter can account for every expected snapshot.

// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  reporter: [
    ["@s4labs/pixmoat-playwright/reporter", {
      // Optional: override env-var configuration
      url: "http://localhost:3000",
      project: "my-project",
      token: "pxg_...",
    }]
  ],
});

The reporter reads the same environment variables as the fixture, plus:

  • PIXMOAT_FAIL_ON_BLOCKED — Fail the run when ci_status is "blocked" (default: "true")
  • PIXMOAT_REPORT_DIR — Directory for CI report files (default: "pixmoat-report")

The reporter finishes the build itself. Don't also call finishBuild() in globalTeardown: the second finish request is rejected with HTTP 409.

CI Reports

The reporter generates three report files for CI integration:

  1. pixmoat.env — dotenv format for GitLab artifacts:reports:dotenv:

    PIXMOAT_REVIEW_URL=https://pixmoat.example.com/p/my-project/builds/abc123
    PIXMOAT_BUILD_ID=abc123
    PIXMOAT_CI_STATUS=passed
  2. junit.xml — JUnit XML test results for GitLab artifacts:reports:junit. Contains per-screenshot testcases when agent-summary data is available, or aggregate testcases by count bucket otherwise.

  3. report.html — self-contained HTML report with a build status badge, a counts table, a per-screenshot table (when available) and a link to the review URL.

GitLab example using the reporter's report files:

visual-regression:
  stage: test
  script:
    - npm ci
    - npx playwright test
  artifacts:
    when: always
    reports:
      dotenv: pixmoat-report/pixmoat.env
      junit: pixmoat-report/junit.xml
    expose_as: "Visual Report"
    paths:
      - pixmoat-report/report.html
    expire_in: 30 days
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: $PIXMOAT_REVIEW_URL

This configuration:

  • Exposes the review URL as an environment button in the merge request
  • Displays test results in the merge request test summary
  • Provides an HTML report artifact for download

Reporter Options

// Fragment: one entry of the reporter array in playwright.config.ts
["@s4labs/pixmoat-playwright/reporter", {
  // Server config
  url: "http://localhost:3000",
  project: "my-project",
  token: "pxg_...",

  // Build metadata
  branch: "main",
  commitSha: "abc123",
  ciUrl: "https://ci.example.com/jobs/123",
  prNumber: 42,

  // Behavior
  failOnBlocked: true,

  // CI reports
  reportDir: "pixmoat-report",
}]

Writing CI Reports Manually

You can also generate CI reports programmatically:

import { writeCiReports, type BuildSummary } from "@s4labs/pixmoat-playwright";

const summary: BuildSummary = {
  build_id: "abc123",
  status: "uploaded",
  ci_status: "passed",
  counts: { total: 10, new: 2, unchanged: 7, diff: 1, missing: 0, removed: 0 },
  web_url: "https://pixmoat.example.com/p/my-project/builds/abc123",
};

await writeCiReports({
  summary,
  reviewUrl: summary.web_url || "https://...",
  metadata: {
    project: "my-project",
    branch: "main",
    commitSha: "abc123",
  },
  outputDir: "pixmoat-report",
});

Upload overload retries

Snapshot uploads and unchanged-image confirmations retry explicit HTTP 429 and 503 responses, up to four attempts total. Retries use exponential backoff with jitter and honor Retry-After (seconds or HTTP date). A requested delay above ten seconds, or beyond the operation's remaining time budget, stops automatic retries and surfaces the response.

The client's requestTimeoutMs covers the entire upload/confirmation operation, including attempts and backoff. Repeated attempts reuse the same build, snapshot identifiers, metadata and image bytes. Other mutations, including build creation and finishing, are not automatically retried. Network failures and timeouts have ambiguous outcomes and are not retried; neither are authentication failures, oversized-image responses (413), other client errors, or other server errors. Reduce oversized images or address the reported error instead of retrying them unchanged.

AI Agent Integration

The pixmoat MCP server (pixmoat mcp) and the agent skill both need the pixmoat CLI, which doesn't have a public install path yet. Until it does, an agent can read a build's verdict with the same agent-summary request as the CI gate. The digest format agents read is in the agent digest reference.

License

MIT

GitLab merged-result captures

Merged-result CI jobs automatically record source, target and captured commit identities. The client checks Git HEAD before creating a new build. The server verifies the declared pair through the configured GitLab connection and compares only with frozen target visual baselines. The target needs a complete accepted capture for its exact commit.

Integration captures fail when stale, unverifiable, or awaiting review, even in report-only mode. Reporter upload and finish errors also fail the job, regardless of PIXMOAT_FAIL_ON_BLOCKED. Merge trains are rejected for now. Ordinary branch and local captures keep their existing behavior. See the GitLab guide for setup and limits.