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

testlens-playwright

v0.3.1

Published

Lightweight Playwright reporter that summarizes why tests failed.

Downloads

660

Readme

The problem

Your Playwright test fails in CI. Now what?

You open the logs. You see:

Error: expect(received).toBe(expected)
  Expected: "Dashboard"
  Received: "Login"

Was it a backend API returning 500? A race condition that passes locally? Bad test data hitting a 401? You don't know — and finding out means digging through traces, network logs, and screenshots for 10–30 minutes.

What TestLens does

TestLens adds a single focused summary at the end of every Playwright run that tells you the likely cause of each failure — in plain English, without opening anything.

----------------------------------
TestLens Failure Summary
----------------------------------

FAIL  Checkout › Place Order
Likely Cause : Backend Issue
Reason       : POST /api/orders returned 500 (3 times)
Confidence   : High

FLAKY  Search › Filter by category
Likely Cause : Flaky Test
Reason       : Passed on retry
Confidence   : High

----------------------------------
Summary:
- Total failed tests: 1
- Flaky tests: 1
----------------------------------

It automatically classifies each failure as one of:

| Classification | Signal | |---|---| | Backend Issue | An API returned >= 500 during the test | | Client / Test Data Issue | An API returned 4xx during the test | | Assertion Failure | expect() didn't match — shows expected vs received | | Flaky Test | Failed on first attempt, passed on retry | | Test Issue | Timeout, console error, or uncaught page exception |

No configuration. No dashboard. No cloud. Just a clear answer at the bottom of your terminal.

Install & Requirements

  • Node.js (modern LTS recommended)
  • @playwright/test >= 1.40.0 (peer dependency)

Install

npm i -D testlens-playwright

Quickstart

There are two integration paths. Choose the one that matches your project setup.


Path A — Simple suites (specs import directly from @playwright/test)

1) Adopt once

Playwright reporters can't subscribe to page network/console events directly. TestLens bridges that by installing small hooks in a shared test wrapper, then having specs import that wrapper.

Run:

npx testlens-playwright adopt

This creates tests/test.ts and rewrites imports inside tests/ to point to it (with correct relative paths).

If your specs live elsewhere:

npx testlens-playwright adopt --dir e2e

2) Enable the reporter

In playwright.config.ts:

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

export default defineConfig({
  reporter: [['testlens-playwright/reporter']],
});

3) Write tests normally

After adoption, your specs import from the wrapper:

import { test, expect } from './test';

test('example', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/example/i);
});

Path B — Custom wrapper (your own test/expect utility)

If your project already has a shared utility like utils/test-base.ts that extends Playwright's test—this is the right path. No spec changes needed.

1) Add TestLens to your wrapper

In your utils/test-base.ts, add attachTestLens and flushTestLens to your existing page fixture:

import { test as base, expect, type Page, type TestInfo } from '@playwright/test';
import { attachTestLens, flushTestLens } from 'testlens-playwright';

const test = base.extend<{}>({
  page: async ({ page }, use, testInfo: TestInfo) => {
    attachTestLens(page, testInfo);   // attach network + console listeners
    // ...your existing setup (e.g. ensureGlobalListenersAttached(page))
    await use(page);
    await flushTestLens(testInfo);    // write per-test attachment
  },
});

// ...rest of your wrapper (beforeAll, beforeEach, afterAll etc.)

export { test, expect };

2) Enable the reporter

In playwright.config.ts:

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

export default defineConfig({
  reporter: [['testlens-playwright/reporter']],
});

3) Specs stay unchanged

Your specs keep importing exactly as before — no changes required:

import { test } from '../utils/test-base';
import { expect } from '@playwright/test';

test.describe.serial('App checker', () => {
  test('to verify user is able to login', async ({ page }) => {
    await page.goto('/login');
    await expect(page).toHaveTitle('Dashboard');
  });

  test('to verify user is able to logout', async ({ page }) => {
    // ...
  });
});

expect: import from @playwright/test directly, or re-export it from your wrapper for a single import line. TestLens only needs page + testInfo.

For suites with a shared page in beforeAll

If your suite creates its own context/page in beforeAll (not via the page fixture), attach per-test using beforeEach/afterEach inside your wrapper:

import { test as base, type Page, type BrowserContext, type TestInfo } from '@playwright/test';
import { attachTestLens, flushTestLens } from 'testlens-playwright';

let page: Page;
let context: BrowserContext;

const test = base;

test.beforeAll(async ({ browser }) => {
  context = await browser.newContext();
  page = await context.newPage();
});

test.beforeEach(async ({}, testInfo: TestInfo) => {
  attachTestLens(page, testInfo);
});

test.afterEach(async ({}, testInfo: TestInfo) => {
  await flushTestLens(testInfo);
});

test.afterAll(async () => {
  await context.close();
});

export { test };

How it works

Playwright reporter plugins can't directly subscribe to page events. TestLens uses:

  • a small in-test hook that captures signals and attaches a compact JSON summary to each test result
  • a reporter that reads those attachments and prints a focused end-of-run summary
  • test title + status + retry count + error message
  • network aggregates: { method, status, url, count } for responses >= 400
  • console aggregates: console.error and pageerror messages with counts
  • URL redaction: querystring/hash stripped before aggregation

CLI

adopt command reference

npx testlens-playwright adopt [--dir <testsDir>] [--wrapper <path>] [--dry-run]
  • --dir: directory containing specs (default tests)
  • --wrapper: wrapper path (default <dir>/test.ts)
  • --dry-run: prints what would change (no files written)

Colors

  • Default: auto-detected (TTY only)
  • Force on: TESTLENS_COLOR=always
  • Force off: TESTLENS_COLOR=never
  • Respects NO_COLOR / FORCE_COLOR

Capture scope

  • Network: records responses with status >= 400 (by design, to avoid noise)
  • URLs: querystring/hash are stripped before aggregation

I don't see network signals

  • For Path A: ensure specs import from the wrapper created by adopt.
  • For Path B: ensure attachTestLens(page, testInfo) is called in your wrapper's page fixture or beforeEach.
  • If your tests are not under tests/, re-run adoption with --dir.

I only see console/pageerror reasons

Often the test failed before any API calls were made, or the failure isn't represented by an HTTP response (DNS issues, request never left, etc.). MVP focuses on HTTP status + console/page errors.

Colors look weird in CI

  • Set TESTLENS_COLOR=never or NO_COLOR=1
  • Or force colors with TESTLENS_COLOR=always if your CI supports it

Development

npm test
npm run test:integration
  • No UI dashboard
  • No database / cloud sync
  • No auth