Testing

Render offscreen, press keys and assert on the frame as text.

This chapter's app is running in your console. Open it with ⌘⌥JCtrl+Shift+J, then try it.
Watch the chapter3:18 · narrated · captions

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

app/deployments/page.test.tsx+19tsx
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.

app/deployments/page.test.tsx+14tsx
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 test

It 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.

▼ 08 · Testing

Open the console

⌘⌥J
CtrlShiftJ

The app renders in DevTools. Then click here and press Enter.

Enterrun testsFbreak a test

Open on a desktop

This demo renders in the Chrome DevTools console, which needs a desktop browser.

Quiz

2 questions

Answer every question to complete the chapter.

What does @textjs/react/testing need to run?

 

What does app.lastFrame() return?