Skip to content

Test terminal applications

Start a real CLI or TUI, interact with it, and replay what happened when a test fails.
Terminal window
npm install --save-dev termwright
import { fileURLToPath } from 'node:url';
import { expect, test } from 'termwright/test';
const program = fileURLToPath(new URL('../app.js', import.meta.url));
test('approves a command', async ({ terminal }) => {
const app = await terminal.launch({ command: [process.execPath, program] });
await app.waitForText('Permission required');
await app.press('Enter');
await expect(app).toHaveText('running: ls -la');
});

Termwright runs the command under a real pseudoterminal and reconstructs its screen instead of treating stdout as a string. The program receives terminal input, resize events, and signals as it would when run by a user.

The test above works with any terminal program. A supported framework integration can also expose controls by role or label. Visibility and pointer support vary by framework.

A failed test open in the Termwright Runner.

Failed tests retain a replayable trace by default. Run npx termwright ui while writing tests, or open a trace downloaded from CI:

Terminal window
npx termwright ui --trace path/to/test.twtrace
What you need to testUse
Text, cells, keyboard input, resize, or process exitThe built-in black-box terminal API
Roles, labels, state, or semantic snapshotsA supported framework integration
Click controls by locatorAn integration whose compatibility table lists pointer support
A pure function or component state with no terminal behaviorYour normal unit-test tools

Termwright does not infer a button from decorated text or guess a pointer target. If the integration cannot report that information, use the screen-level API or test the behavior at another layer.

  1. Install Termwright and run a test
  2. Learn the test workflow
  3. Choose stable locators
  4. Debug a failing test

See supported platforms and limitations before adding Termwright to CI.