Testing
Render offscreen, press keys and assert on the frame as text.
In this chapter
- Your first test
- Pressing keys
- Run it
- Snapshots
@textjs/react/testing renders your app offscreen, presses keys and returns the frame as plain text. Tests run without a browser or DevTools.
Your first test
Added: import { afterEach, beforeEach, expect, mock, spyOn, test } from "bun:test";Added: import { cleanup, render } from "@textjs/react/testing";Added: import { DEPLOYMENTS } from "../../lib/deployments";Added: import DeploymentsPage from "./page";Added: Added: beforeEach(() => {Added: spyOn(globalThis, "fetch").mockResolvedValue(Response.json(DEPLOYMENTS));Added: });Added: Added: afterEach(() => {Added: mock.restore();Added: return cleanup();Added: });Added: Added: test("lists every deployment", async () => {Added: const app = await render(<DeploymentsPage />, { cols: 80, rows: 12 });Added: await app.waitForText("feat: stream cells downward");Added: expect(app.lines()).toHaveLength(12);Added: });The page fetches /api/deployments, which has no server under bun test, so beforeEach stubs fetch with the same data. render defaults to an 80×24 grid. lastFrame() returns the whole frame as text, lines() returns it row by row, and cell(x, y) gives you a single cell's character and colors.
Pressing keys
press takes the same key strings as useKeymap. Each call runs inside act() and flushes the resulting frames, so the next assertion sees the update.
Added: test("↓ moves the highlight", async () => {Added: const app = await render(<DeploymentsPage />);Added: await app.waitForText("feat: stream cells downward");Added: await app.press("down");Added: const row = app.lines().find((line) => line.includes("›"));Added: expect(row).toContain("perf: merge spans across rows");Added: });Added: Added: test("4 sorts by build time", async () => {Added: const app = await render(<DeploymentsPage />);Added: await app.waitForText("Build");Added: await app.press("4"); // sort by the 4th columnAdded: expect(app.locate("12s")?.y).toBeLessThan(app.locate("52s")?.y ?? 0);Added: });The table draws › in front of the highlighted row, so the ↓ test finds that row and checks its commit. Digits sort by a column, and the fourth column is Build.
Run it
bun testIt works with any runner that supports async tests: Bun, Vitest or Jest. In the console, press Enter to run the suite and f to break a test and see what a failure looks like.
Snapshots
toAnsi() returns the frame as truecolor ANSI, which makes snapshots that catch color regressions too:
expect(app.toAnsi()).toMatchSnapshot();Try it
This is Acme Deploys at the end of the chapter, running in DevTools. Click the pad, then use the keys below.
Open the console
The app renders in DevTools. Then click here and press Enter.
Open on a desktop
This demo renders in the Chrome DevTools console, which needs a desktop browser.
Quiz
2 questionsAnswer every question to complete the chapter.