@gridland/testing
Testing utilities API reference
@gridland/testing mounts Gridland components into an in-memory buffer for
unit tests. It exposes one entry point — renderTui — plus the Screen,
KeySender, and waitFor primitives. See the Testing guide
for end-to-end examples.
renderTui
import { renderTui } from "@gridland/testing"
const tui = renderTui(node, options?)Mounts a React tree into an in-memory buffer and returns a TuiInstance. The
call is synchronous — the initial render pass completes before renderTui
returns.
Parameters:
| Parameter | Type | Description |
|---|---|---|
node | ReactNode | The Gridland tree to render |
options | RenderTuiOptions? | Buffer dimensions |
RenderTuiOptions:
| Option | Type | Default | Description |
|---|---|---|---|
cols | number | 80 | Number of columns |
rows | number | 24 | Number of rows |
Returns: TuiInstance
TuiInstance
| Property | Type | Description |
|---|---|---|
screen | Screen | Query helpers for reading buffer content |
keys | KeySender | Keyboard input simulation |
waitFor | (condition, options?) => Promise<void> | Poll until condition holds (bound to this instance's screen) |
flush | () => void | Force a synchronous render pass and capture a new frame |
rerender | (node: ReactNode) => void | Replace the rendered tree with a new one |
unmount | () => void | Unmount the tree and release the renderer |
Screen
The Screen class provides read-only access to the cell buffer.
| Method | Description |
|---|---|
text() | Full screen text with trailing spaces trimmed per line |
rawText() | Full screen text preserving all spaces |
contains(text: string) | true if the trimmed text includes text |
matches(pattern: RegExp) | true if the trimmed text matches pattern |
line(n: number) | The nth line (0-indexed) or "" if out of range |
lines() | All non-empty lines as an array |
frames() | Captured frame snapshots — one per render pass |
captureFrame() | Manually push the current frame onto the snapshot list |
attributeAt(row, col) | Raw u32 text attributes at a cell |
fgAt(row, col) | Foreground RGBA tuple [r, g, b, a] at a cell |
width | Buffer width in cells |
height | Buffer height in cells |
KeySender
Dispatches synthetic keypress events through the focus system.
| Method | Description |
|---|---|
type(text: string) | Type a string character by character |
press(char: string) | Press a single character key |
raw(data: string) | Send a raw sequence (e.g. an escape code) |
enter() | Press Enter |
escape() | Press Escape |
tab() | Press Tab |
backspace() | Press Backspace |
delete() | Press Delete |
space() | Press Space |
up() / down() / left() / right() | Arrow keys |
home() / end() | Home / End |
pageUp() / pageDown() | Page Up / Page Down |
All key helpers are methods, not properties — call them with ().
waitFor
Standalone version of tui.waitFor. Polls a condition until it holds or times out.
import { renderTui, waitFor } from "@gridland/testing"
const tui = renderTui(<AsyncComponent />)
// Wait for a literal string on the screen
await waitFor(tui.screen, "Loaded")
// Or wait for an assertion to stop throwing
await waitFor(tui.screen, () => {
expect(tui.screen.contains("Ready")).toBe(true)
})Parameters:
| Parameter | Type | Description |
|---|---|---|
screen | Screen | The screen to poll |
condition | string | (() => void) | Literal substring, or an assertion that throws until satisfied |
options | WaitForOptions? | Timeout and poll interval |
WaitForOptions:
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 3000 | Max wait time in ms |
interval | number | 50 | Poll interval in ms |
On timeout, waitFor throws an error that includes the current screen contents.
cleanup
import { cleanup } from "@gridland/testing"
import { afterEach } from "bun:test"
afterEach(() => {
cleanup()
})Unmounts every active TuiInstance created by renderTui. Wire this into
afterEach once and you can skip calling tui.unmount() in each test.