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-labs/fixture-timers

v1.0.1

Published

Timers nodejs api support for Playwright

Downloads

129

Readme

@playwright-labs/fixture-timers

Promise-based Node.js timer APIs as Playwright test fixtures, with custom matchers for timing assertions.

Installation

npm install @playwright/test @playwright-labs/fixture-timers # npm
pnpm add @playwright/test @playwright-labs/fixture-timers # pnpm
yarn add @playwright/test @playwright-labs/fixture-timers # yarn

Features

  • Promise-based setTimeout, setInterval, setImmediate, and scheduler as Playwright fixtures
  • Async iterator support for setInterval (use for await...of)
  • AbortSignal support on all timer fixtures
  • 6 custom expect matchers for timing assertions
  • Generic type support (setTimeout<T> resolves to Promise<T>)
  • Composable with other fixtures via mergeTests / mergeExpects

Fixtures

Merging with your test setup

// fixture.ts
import { mergeTests, mergeExpects } from "@playwright/test";
import {
  test as timersTest,
  expect as timersExpect,
} from "@playwright-labs/fixture-timers";

export const test = mergeTests(timersTest);
export const expect = mergeExpects(timersExpect);

Or use directly:

import { test, expect } from "@playwright-labs/fixture-timers";

setTimeout

Promise-based timeout that resolves after a delay with an optional value.

import { test, expect } from "@playwright-labs/fixture-timers";

test("resolves with value after delay", async ({ setTimeout }) => {
  const result = await setTimeout(100, "hello");
  expect(result).toBe("hello");
});

test("basic delay", async ({ setTimeout }) => {
  await setTimeout(500); // wait 500ms
});

Signature:

setTimeout<T = void>(delay?: number, value?: T, options?: TimerOptions) => Promise<T>

setInterval

Returns an async iterator that yields a value at regular intervals. Use for await...of to consume it, and call .return?.() to clean up.

import { test, expect } from "@playwright-labs/fixture-timers";

test("poll at intervals", async ({ setInterval }) => {
  const poller = setInterval(200, "tick");

  let count = 0;
  for await (const value of poller) {
    expect(value).toBe("tick");
    count++;
    if (count >= 3) break;
  }

  expect(count).toBe(3);
});

test("manual iteration", async ({ setInterval }) => {
  const iterator = setInterval(100, "value");

  const first = await iterator.next();
  expect(first.value).toBe("value");
  expect(first.done).toBe(false);

  await iterator.return?.(); // cleanup
});

Signature:

setInterval<T = void>(delay?: number, value?: T, options?: TimerOptions) => NodeJS.AsyncIterator<T>

setImmediate

Promise that resolves on the next iteration of the event loop, before setTimeout(0).

import { test, expect } from "@playwright-labs/fixture-timers";

test("immediate resolves before timeout", async ({ setImmediate, setTimeout }) => {
  const order: string[] = [];

  const t = setTimeout(0).then(() => { order.push("timeout"); });
  const i = setImmediate().then(() => { order.push("immediate"); });

  await Promise.all([t, i]);
  expect(order[0]).toBe("immediate");
});

test("resolves with value", async ({ setImmediate }) => {
  const result = await setImmediate("fast");
  expect(result).toBe("fast");
});

Signature:

setImmediate<T = void>(value?: T, options?: TimerOptions) => Promise<T>

scheduler

The Node.js scheduler API for precise timing control.

import { test } from "@playwright-labs/fixture-timers";

test("scheduler wait and yield", async ({ scheduler }) => {
  await scheduler.wait(100); // wait 100ms
  await scheduler.yield();   // yield control to the event loop
});

Methods:

  • scheduler.wait(delay, options?) -- wait for a specified time (ms)
  • scheduler.yield() -- yield control to the event loop

AbortSignal Support

All timer fixtures accept an options parameter with an optional signal for cancellation:

import { test, expect } from "@playwright-labs/fixture-timers";

test("abort cancels timer", async ({ setTimeout }) => {
  const ac = new AbortController();
  const promise = setTimeout(10000, "late", { signal: ac.signal });

  ac.abort();

  await expect(promise).rejects.toThrow();
});

test("abort cancels interval", async ({ setInterval }) => {
  const ac = new AbortController();
  const iterator = setInterval(100, "tick", { signal: ac.signal });

  const first = await iterator.next();
  expect(first.value).toBe("tick");

  ac.abort();

  await expect(iterator.next()).rejects.toThrow();
});

Custom Matchers

toResolveWithin(maxMs)

Asserts that a promise resolves within a maximum time window.

test("operation completes within SLA", async ({ setTimeout }) => {
  const promise = setTimeout(50, "done");
  await expect(promise).toResolveWithin(100);
});

toTakeAtLeast(minMs)

Asserts that a promise takes at least a minimum amount of time to resolve.

test("delay is enforced", async ({ setTimeout }) => {
  const promise = setTimeout(100);
  await expect(promise).toTakeAtLeast(95);
});

toResolveWith(expectedValue)

Asserts that a promise resolves with a specific value using strict equality (===).

test("resolves with expected value", async ({ setTimeout }) => {
  const promise = setTimeout(50, "test-value");
  await expect(promise).toResolveWith("test-value");
});

toResolveInTimeRange(minMs, maxMs)

Asserts that a promise resolves within a specific time window (not too fast, not too slow).

test("response within acceptable range", async ({ setTimeout }) => {
  const promise = setTimeout(100, "result");
  await expect(promise).toResolveInTimeRange(95, 150);
});

toYield(expectedValue?)

Asserts that an async iterator yields a value. Optionally checks the yielded value with strict equality.

test("interval yields expected value", async ({ setInterval }) => {
  const iterator = setInterval(100, "tick");

  await expect(iterator).toYield("tick");
  await expect(iterator).toYield(); // any value

  await iterator.return?.();
});

toYieldWithin(maxMs)

Asserts that an async iterator yields its next value within a time limit.

test("interval yields on time", async ({ setInterval }) => {
  const iterator = setInterval(100);
  await expect(iterator).toYieldWithin(150);
  await iterator.return?.();
});

Real-World Examples

Retry with Exponential Backoff

import { test, expect } from "@playwright-labs/fixture-timers";

test("retry with backoff", async ({ setTimeout }) => {
  let attempts = 0;

  const fetchWithRetry = async () => {
    for (let i = 0; i < 3; i++) {
      attempts++;
      try {
        const res = await fetch("/api/data");
        if (res.ok) return res.json();
      } catch {
        // retry
      }
      await setTimeout(100 * Math.pow(2, i)); // 100ms, 200ms, 400ms
    }
    throw new Error("All retries exhausted");
  };

  await expect(fetchWithRetry()).toResolveWithin(1000);
});

Polling with Async Iterator

test("poll until ready", async ({ setInterval }) => {
  const poller = setInterval(500, "check");

  for await (const _ of poller) {
    const res = await fetch("/api/status");
    const data = await res.json();
    if (data.ready) break;
  }

  await poller.return?.();
});

Timeout Race

test("operation vs timeout", async ({ setTimeout }) => {
  const operation = fetch("/api/slow-endpoint");
  const timeout = setTimeout(5000).then(() => {
    throw new Error("Operation timed out");
  });

  const result = await Promise.race([operation, timeout]);
  expect(result).toBeTruthy();
});

Debounce Pattern

test("debounced action", async ({ setTimeout }) => {
  let executeCount = 0;
  let pending: Promise<void> | null = null;

  const debounce = async (fn: () => void, delay: number) => {
    pending = setTimeout(delay);
    await pending;
    fn();
  };

  await debounce(() => executeCount++, 100);
  expect(executeCount).toBe(1);
});

Coordinating Multiple Timers

test("sequential delays", async ({ setTimeout, setImmediate, scheduler }) => {
  await setImmediate();          // yield control first
  await scheduler.wait(50);      // wait 50ms
  const val = await setTimeout(100, 42); // wait 100ms, get 42
  expect(val).toBe(42);
});

API Reference

Fixtures

| Fixture | Type | Description | | -------------- | ------------------------------------------------------------------------------- | --------------------------------------------- | | setTimeout | <T = void>(delay?: number, value?: T, options?: TimerOptions) => Promise<T> | Promise-based timeout with optional value | | setInterval | <T = void>(delay?: number, value?: T, options?: TimerOptions) => AsyncIterator<T> | Async iterator yielding at regular intervals | | setImmediate | <T = void>(value?: T, options?: TimerOptions) => Promise<T> | Resolves on the next event loop iteration | | scheduler | { wait(delay, options?): Promise<void>; yield(): Promise<void> } | Node.js scheduler API |

Custom Matchers

| Matcher | Input | Description | | ------------------------------------ | ------------- | ---------------------------------------------- | | toResolveWithin(maxMs) | Promise | Resolves within max time | | toTakeAtLeast(minMs) | Promise | Takes at least min time to resolve | | toResolveWith(value) | Promise | Resolves with exact value (strict ===) | | toResolveInTimeRange(minMs, maxMs) | Promise | Resolves within a min/max time window | | toYield(value?) | AsyncIterator | Yields a value (optionally matching) | | toYieldWithin(maxMs) | AsyncIterator | Next yield happens within max time |


TypeScript Support

Full TypeScript support with type definitions included:

import { test, expect, type Fixture } from "@playwright-labs/fixture-timers";

test("typed example", async ({
  setTimeout,   // <T>(delay?, value?, options?) => Promise<T>
  setInterval,  // <T>(delay?, value?, options?) => AsyncIterator<T>
  setImmediate, // <T>(value?, options?) => Promise<T>
  scheduler,    // { wait(delay, options?): Promise<void>; yield(): Promise<void> }
}) => {
  const str = await setTimeout(100, "hello");  // inferred as string
  const num = await setImmediate(42);           // inferred as number
});