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-ussd

v0.1.0

Published

Playwright-based framework for testing real USSD flows -- single-shot codes and interactive multi-step menus alike -- against a physical Android device.

Downloads

184

Readme

playwright-ussd

Test USSD codes from Playwright, against a real Android device.

USSD (the *123#-style codes used for mobile money, airtime, and carrier menus) has no browser and no standard testing API. This package drives the same on-screen prompt a person sees when dialing a code, so you can write ordinary Playwright tests against it — single-shot codes and multi-step menus alike.

Install

npm install --save-dev playwright-ussd @playwright/test

Requirements

  • A physical Android device with an active SIM, connected over USB
  • adb available on your PATH
  • Node.js 18+

The device screen needs to be unlockable without a PIN, pattern, or password. Dialing a code always wakes the screen and clears the keyguard first, which only works without a secure lock set.

If more than one device is connected, set ANDROID_SERIAL to target a specific one.

Usage

import { test, expect, dialStep, replyStep } from "playwright-ussd/fixture";

test("balance check", async ({ ussd }) => {
  const screen = await dialStep(ussd, "*123#");
  expect(screen.isFinal).toBe(true);
  expect(screen.text).toContain("Balance");
});

test("menu navigation", async ({ ussd }) => {
  const menu = await dialStep(ussd, "*100#");
  expect(menu.text).toContain("Send Money");

  const submenu = await replyStep(ussd, "1", "Send Money");
  expect(submenu.text).toContain("Enter recipient number");
});

The ussd fixture gives each test a fresh session and closes it automatically when the test ends. dialStep/replyStep are optional wrappers around ussd.dial()/ussd.reply() that record each call as a named Playwright step with the response logged — useful for reports, but you can call the session methods directly if you'd rather not have the extra step nesting:

import { test, expect } from "playwright-ussd/fixture";

test("balance check", async ({ ussd }) => {
  const screen = await ussd.dial("*123#");
  expect(screen.text).toContain("Balance");
});

Your project also needs a playwright.config.ts. Since there's no browser involved, keep workers: 1 (only one USSD session can be active on a device at a time) and skip the projects field entirely:

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

export default defineConfig({
  testDir: "./tests",
  workers: 1,
});

API

UssdSession (import { UssdSession } from "playwright-ussd")

| Method | Returns | Description | |---|---|---| | dial(code) | Promise<{ text, isFinal }> | Dials the code, returns the first screen | | reply(text) | Promise<{ text, isFinal }> | Sends a reply to an open menu, returns the next screen | | cancel() | void | Ends an open session; does nothing if it already ended | | .history | UssdScreen[] | Every screen seen so far, in order |

text is the raw response shown on screen. isFinal is true once the session has ended — there's nothing left to reply to at that point, and calling reply() will throw.

How it works

Dialing a code sends it as a normal phone call (android.intent.action.CALL), which Android intercepts as USSD instead of placing a real call. The response shows up in a system dialog, which this package reads and drives with adb shell uiautomator dump and adb shell input.

There's a public Android API for this too (TelephonyManager.sendUssdRequest), but it only supports a single response and can't be used for menus that need a reply — the telephony stack receives that response internally but has no way to hand it back through that API. Reading the on-screen dialog directly works for both cases, since it's the same path the built-in dialer itself uses.

Limitations

  • No emulator support — USSD requires a real SIM and carrier signaling.
  • The dialog is matched by resource-id, which can vary slightly across Android versions and OEM skins. If a device isn't recognized, run adb shell uiautomator dump while a USSD dialog is open and compare the ids against what UssdSession looks for in src/ussdSession.ts.
  • Only one session can run at a time per device.

License

MIT