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

playwright-interceptor

v1.2.1

Published

A helper for better Playwright testing; mocking, throttling and waiting for requests with advanced statistics and more.

Downloads

595

Readme

NPM Build Status Coverage Status

Playwright Interceptor - Quick Start Guide

For Playwright developers who want better request handling and debugging.

What is it?

Playwright Interceptor builds on Playwright's native network interception to log all network requests, provide detailed statistics, and make debugging test failures easier — with reliable waiting, mocking, throttling, delaying, WebSocket monitoring and console monitoring.

It runs entirely in Node and is exposed through Playwright fixtures — no custom commands, no browser patching.

Why use it?

| Feature | Playwright Interceptor | | --- | --- | | Log all requests | ✅ Built-in | | Request statistics | ✅ Built-in | | Timing data | ✅ Built-in | | Throttle requests (hold the response) | ✅ Built-in | | Delay requests before send | ✅ Built-in | | Wait for requests reliably | ✅ Stable | | Mock responses | ✅ Built-in | | Export logs on failure | ✅ Built-in | | WebSocket support | ✅ Built-in | | Console monitoring | ✅ Built-in | | HTML network reports | ✅ Built-in |

Installation

npm install --save-dev playwright-interceptor

@playwright/test is a peer dependency (supported range >=1.30.0 <2.0.0), so make sure it is installed in your project alongside the interceptor.

Import test and expect from playwright-interceptor instead of @playwright/test. The interceptor, wsInterceptor and watchTheConsole fixtures are then available and started automatically:

import { expect, test } from "playwright-interceptor";

For most projects that is all you need — the fixtures bind to the @playwright/test you already have installed.

Registering Playwright (monorepos & pinned versions)

The test object exported by playwright-interceptor must belong to the same @playwright/test instance that the runner uses to execute your specs. In a single-version project this happens automatically. But when more than one copy of @playwright/test is present — for example in a monorepo where a version is hoisted to the root while a package pins its own, or when you run the same specs against several Playwright versions — Playwright rejects the shared test.describe(...) calls with:

Playwright Test did not expect test.describe() to be called here

To fix this, register your pinned @playwright/test from your playwright.config.ts before any spec is loaded. Import from playwright-interceptor/register, which pulls in only the fixtures (it does not touch @playwright/test at load time), so the ordering is guaranteed:

// playwright.config.ts
import { expect, test } from "@playwright/test";
import { registerPlaywright } from "playwright-interceptor/register";

registerPlaywright({ expect, test });

// ...the rest of your config

After this, the test and expect imported from playwright-interceptor in your specs will use the exact Playwright instance you registered.

Extending an existing test

If your project already has its own extended test fixture, build the interceptor fixtures on top of it with extendTest instead:

import { test as base } from "@playwright/test";
import { extendTest } from "playwright-interceptor";

export const test = extendTest(base);

Configuring the default request timeout

The default timeout used by waitUntilRequestIsDone is 10000 ms. Set the INTERCEPTOR_REQUEST_TIMEOUT environment variable to change it globally, or override it per call with the timeout option.

Common Use Cases

1. Wait for a request reliably

// run an action and wait for the request it triggers
await interceptor.waitUntilRequestIsDone(
    () => page.locator("button").click(),
    "**/api/users"
);

2. Get request statistics

const stats = interceptor.getStats("**/api/users");

expect(stats[0].response?.statusCode).toBe(200);
expect(stats[0].duration).toBeLessThan(1000); // took less than 1 second

3. Mock a response

// mock the first matching request
interceptor.mockResponse("**/api/users", { body: { name: "John" }, statusCode: 200 });

// mock indefinitely
interceptor.mockResponse({ method: "POST" }, { statusCode: 400 }, { times: Number.POSITIVE_INFINITY });

4. Throttle a request

// hold the response for 5s AFTER the back-end is hit
interceptor.throttleRequest("**/api/users", 5000);

5. Delay a request before it is sent

// hold the request for 5s BEFORE it reaches the back-end
interceptor.delayRequest("**/api/users", 5000);

Delay vs. Throttle

  1. request starts          2. request hits               3. request done
     (your code)               the back-end              (back to your code)
         │                          │                             │
         │ <----- delayRequest ---> │ <----- throttleRequest ---> │

6. Log all requests on test failure

test.afterEach(async ({ interceptor }, testInfo) => {
    if (testInfo.status !== testInfo.expectedStatus) {
        interceptor.writeStatsToLog("./logs");
    }
});

This creates a JSON file with all requests, responses, timing and headers — perfect for debugging why tests fail.

7. Count requests

expect(interceptor.requestCalls("**/api/users")).toBe(1);

8. Get the last request

const request = interceptor.getLastRequest("**/api/users");

expect(request?.response?.statusCode).toBe(200);

9. Mock dynamic responses based on request

interceptor.mockResponse("**/api/users", {
    generateBody: (request, getJsonRequestBody) => {
        const body = getJsonRequestBody<{ id: number }>();

        return { id: body.id, name: `User ${body.id}` };
    },
    statusCode: 200
});

10. Monitor WebSocket connections

// wait for a WebSocket action
await wsInterceptor.waitUntilWebsocketAction({ url: "**/socket" });

// get WebSocket stats
expect(wsInterceptor.getStats({ url: "**/socket" }).length).toBeGreaterThan(0);

11. Monitor console errors

test("no console errors", async ({ page, watchTheConsole }) => {
    await page.goto("/");
    await watchTheConsole.flush();

    expect(watchTheConsole.error).toHaveLength(0);
    expect(watchTheConsole.jsError).toHaveLength(0);
});

// export console logs on failure
test.afterEach(async ({ watchTheConsole }) => {
    watchTheConsole.writeLogToFile("./logs");
});

Advanced: Filter requests

// only GET requests
interceptor.getStats({ method: "GET" });

// only fetch (not XHR)
interceptor.getStats({ resourceType: "fetch" });

// custom query matcher
interceptor.getStats({ queryMatcher: (query) => query?.page === 5 });

// body matcher
interceptor.getStats({ bodyMatcher: (body) => body.includes("userId") });

Real-world example

import { expect, test } from "playwright-interceptor";

test.describe("User Dashboard", () => {
    test.beforeEach(async ({ page }) => {
        await page.goto("/dashboard");
    });

    test("should load user data and display it", async ({ page, interceptor }) => {
        // click a button and wait for the specific request it triggers
        await interceptor.waitUntilRequestIsDone(
            () => page.locator("button#refresh").click(),
            "**/api/user/profile"
        );

        const stats = interceptor.getStats("**/api/user/profile");

        expect(stats[0].request.method).toBe("GET");
        expect(stats[0].duration).toBeLessThan(2000);
        expect(stats[0].response?.statusCode).toBe(200);

        await expect(page.getByText("Welcome, John")).toBeVisible();
    });

    test("should handle API errors gracefully", async ({ page, interceptor }) => {
        interceptor.mockResponse("**/api/user/profile", {
            statusCode: 500,
            body: { error: "Server error" }
        });

        await page.locator("button#refresh").click();

        await expect(page.getByText("Error loading profile")).toBeVisible();
    });
});

Key Benefits

Reliable waits - waitUntilRequestIsDone waits for completion, not just interception
Complete visibility - see every request, response and timing
Easy debugging - export logs on failure to analyze what went wrong
Better mocking - mock responses with full control
Performance insights - track request duration and identify slow endpoints
WebSocket & console monitoring - full application visibility
Big-data safe - non-mocked requests pass through untouched

Documentation

Need help?

Check the full README for advanced features like WebSocket interception, console monitoring, the test.unit store and HTML report generation.