Skip to content

Test commands in an integrated shell

Use terminal.shell when a test drives several commands in one interactive shell and needs the result of each command separately.

import { fileURLToPath } from 'node:url';
import { expect, test } from 'termwright/test';
const project = fileURLToPath(new URL('../../fixture-project', import.meta.url));
test('builds the workspace', async ({ terminal }) => {
const shell = await terminal.openShell({
cwd: project,
});
const build = await shell.shell.run('npm run build');
expect(build.exitCode).toBe(0);
expect(build.output).toContain('built successfully');
expect(build.cwd).toBe(project);
expect(build.receipt.outcome).toBe('completed');
});

openShell() starts PowerShell on Windows and $SHELL -i (or /bin/sh -i) on POSIX systems. It adds exact OSC 133 command boundaries in both modes. Pass shell to choose another compatible shell command. Termwright does not identify prompts by matching their text. Managed PowerShell commands accept only the composable startup switches -NoLogo, -NoProfile, -NoExit, -ExecutionPolicy <value>, and -WorkingDirectory <value>; script and command modes are rejected because Termwright owns the startup command that publishes the initial boundary.

NeedRecommended approach
Test one CLI invocationLaunch the CLI directly and use waitForExit().
Run several commands in one shellUse terminal.openShell() and shell.run().
Drive an interactive prompt or full-screen TUIUse terminal input, locators, and assertions.
Use a shell without OSC 133 integrationDrive it with press() and type(); command boundaries are unavailable.
const status = shell.shell.status();
expect(status.ready).toBe(true);
expect(status.cwd).toBe(project);
expect(status.title).toBe('project — zsh');
expect(status.cursor).toMatchObject({ row: 4, column: 2 });
expect(status.bellCount).toBe(0);

status() reports only terminal control sequences observed in the session:

  • OSC 133 provides prompt readiness, command boundaries, and exit codes;
  • OSC 7 provides the working directory;
  • OSC 0/2 provides the terminal title;
  • terminal state provides the cursor and bell count.

Missing facts remain null or unsupported. A session created with terminal.launch() can also use this API when its child publishes OSC 133 and OSC 7 itself. Otherwise waitForPrompt() and run() throw CapabilityUnavailableError.

run() captures the exact text emitted between the command-start and command-finished marks. It excludes the prompt and the command echo emitted before the start mark. maxOutputBytes bounds the captured result:

const result = await shell.shell.run('npm test', {
timeout: 60_000,
maxOutputBytes: 16 * 1024 * 1024,
});

Only one run() may be active per terminal session. Use separate sessions for concurrent commands.

The returned receipt records the state before and after submission and the keyboard input sent through the PTY. The same information appears in the session trace.