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

best-tempmail-playwright

v1.0.0

Published

Playwright fixture for disposable email inboxes. Sign up, wait for the verification email, read the code, in three lines.

Readme

best-tempmail-playwright

Disposable email inboxes as a Playwright fixture. Sign up, wait for the verification email, read the code.

npm install -D best-tempmail-playwright
import { test, expect } from "best-tempmail-playwright";

test("a new user can verify their email", async ({ page, tempMail }) => {
  const inbox = await tempMail.createInbox();

  await page.goto("/signup");
  await page.fill("#email", inbox.address);
  await page.click("#submit");

  await page.fill("#code", await inbox.waitForCode());
  await page.click("#verify");

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

No mail server to run, no shared test inbox, no polling loop. The inbox is created for that test and deleted when it finishes.

Setup

Set your API key once:

export BEST_TEMPMAIL_API_KEY=btm_sk_live_...

Get one at best-tempmail.com/api. Code extraction needs a paid plan; everything else works on the free tier, which allows three inboxes per IP per day and needs no key at all.

Import test from this package instead of @playwright/test and every test gains tempMail. If you already extend test with your own fixtures, compose instead:

import { test as base } from "@playwright/test";
import { tempMailFixture, type TempMailFixture } from "best-tempmail-playwright";

export const test = base.extend<{ tempMail: TempMailFixture } & MyFixtures>({
  ...tempMailFixture(),
  ...myFixtures,
});

Getting the code

waitForCode() waits for mail and pulls the verification code out of it, so you don't write a regex per service:

const code = await inbox.waitForCode();

It throws rather than returning null, with a message that says which inbox and how long it waited, because a null surfaces three lines later as something much harder to read:

No email arrived at [email protected] within 60s.
Check that the flow under test actually sent to this address, and that the
sending service is not itself delayed.

If mail arrives but no code can be trusted, you get the subject, the sender and the codes that were considered:

A message arrived at [email protected] but no verification code scored highly
enough to be trusted.
  subject:    Welcome to Acme
  from:       "Acme" <[email protected]>
  considered: 2026 (score 2)

That is deliberate. A wrong code fails a test in a way that is very hard to trace, so the API returns nothing rather than guessing.

Waiting for a particular email

A signup flow often sends more than one message. Filter by sender or subject, with a string (case-insensitive substring) or a regex:

const code = await inbox.waitForCode({ from: "[email protected]" });
const msg  = await inbox.waitForMessage({ subject: /reset your password/i });

Each message is offered once. Two waits in a row give you two different emails, in the order they arrived:

const welcome = await inbox.waitForMessage({ subject: "Welcome" });
const receipt = await inbox.waitForMessage({ subject: "Your receipt" });

Timeouts are per call, in milliseconds, defaulting to 60 seconds:

await inbox.waitForCode({ timeout: 120_000 });

Reading the whole message

const msg  = await inbox.waitForMessage();
const full = await inbox.read(msg.id);

full.text;
full.html;
full.attachments;      // metadata on every plan

inbox.messages() lists what has arrived, newest first, without waiting.

Two things it handles that a plain HTTP client does not

Mail that arrives before you ask for it. The API's long-poll treats anything already in the inbox when the call starts as seen. If your app's mailer is fast, the email can land between clicking submit and calling waitForCode(), and a naive wait then sits there until it times out. This reads the inbox before every poll, so that window is closed.

The concurrent-wait limit. The API allows a small number of long-polls open at once per key, shared across every Playwright worker and every machine running the suite. Each worker holds at most one by default, and a worker that does hit the limit waits and retries within your timeout rather than failing the test. If you run many workers and see waits queuing, lower workers in your Playwright config or raise your plan rather than raising maxConcurrentWaits.

Options

tempMailFixture({
  apiKey: process.env.MY_KEY,   // default: BEST_TEMPMAIL_API_KEY, then BTM_API_KEY
  defaultTimeout: 60_000,       // ms
  maxConcurrentWaits: 1,        // long-polls per worker
  autoCleanup: true,            // delete this test's inboxes when it ends
  clientOptions: { baseUrl: "..." },
});

Turn off autoCleanup while debugging a failure if you want to inspect what actually arrived. Inboxes expire on their own after two hours regardless.

Errors

import {
  MailTimeoutError,          // nothing arrived in time
  NoVerificationCodeError,   // mail arrived, no trustworthy code
  AuthenticationError,       // key missing, wrong or revoked
  PaymentRequiredError,      // plan does not include this (e.g. OTP on free)
  RateLimitError,
  NotFoundError,
} from "best-tempmail-playwright";

tempMail.client exposes the underlying best-tempmail client for anything this fixture does not wrap: webhooks, attachment downloads, inbox lookups.

Requirements

Node 18 or newer, @playwright/test 1.30 or newer.

Licence

MIT