| title | termui/testing |
|---|
Headless testing utilities for TermUI components. Captures Ink render output as plain text for use with Vitest, Jest, or any assertion library.
pnpm add -D termuiRenders a React element headlessly and returns the plain-text output (ANSI stripped).
renderToString(element: ReactElement, options?: {
cols?: number; // terminal width (default: 80)
rows?: number; // terminal height (default: 24)
waitMs?: number; // settle time in ms (default: 50)
}): Promise<string>Example:
import { renderToString } from 'termui/testing';
import { Spinner } from 'termui/components';
const output = await renderToString(<Spinner style="dots" />);
expect(output).toContain('⠋');Creates a reusable renderer. Better for multi-step interaction tests.
createTestRenderer(options?: { cols?: number; rows?: number }): {
render(element: ReactElement): RenderResult;
cleanup(): void;
}RenderResult:
| Property | Type | Description |
|---|---|---|
output |
string |
ANSI-stripped output |
rawOutput |
string |
Raw output with ANSI codes |
rerender() |
() => RenderResult |
Re-read current output |
unmount() |
() => void |
Unmount the component |
instance |
Instance |
Underlying Ink instance |
Query helpers for plain-text output.
| Method | Signature | Description |
|---|---|---|
getByText |
(text, output) => string |
Returns first matching line; throws if not found |
queryByText |
(text, output) => string | null |
Returns first matching line or null |
getAllByText |
(text, output) => string[] |
Returns all matching lines |
hasText |
(text, output) => boolean |
True if any line matches |
countByText |
(text, output) => number |
Count of matching lines |
getLines |
(output) => string[] |
Trimmed non-empty lines |
Accepts string or RegExp for text.
Simulate keyboard input.
fireEvent.key('up' | 'down' | 'enter' | 'escape' | 'tab' | 'space' | ...)
fireEvent.type(text: string)
fireEvent.press(char: string)Named keys: up, down, left, right, enter, return, tab, backspace, delete, escape, space, ctrlC, ctrlN, ctrlP, ctrlS, ctrlZ, home, end, pageUp, pageDown
Polls an assertion until it passes or times out.
waitFor(fn: () => void | Promise<void>, options?: {
timeout?: number; // ms (default: 1000)
interval?: number; // ms (default: 50)
}): Promise<void>Example:
await waitFor(() => {
expect(result.rerender().output).toContain('Loading complete');
});import { describe, it, expect, afterEach } from 'vitest';
import React, { useState } from 'react';
import { Text } from 'ink';
import { createTestRenderer, screen, fireEvent, waitFor } from 'termui/testing';
describe('Counter', () => {
const { render, cleanup } = createTestRenderer();
afterEach(cleanup);
it('renders initial count', async () => {
const result = render(<Counter initialCount={0} />);
await new Promise((r) => setTimeout(r, 50));
expect(screen.hasText('Count: 0', result.output)).toBe(true);
});
});