Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

tui-test for Python

Control, inspect, and test terminal apps from Python.

Install

pip install --pre tui-test

Python 3.8+ is supported.

Quick start

from tui_test import TuiTest

async with TuiTest.ephemeral() as terminal:
    await terminal.run("my-app")
    await terminal.get_by_text("Ready").expect()
    await terminal.get_by_text("Continue").click()
    await terminal.get_by_text("Done").expect()

API

TuiTest

TuiTest(session=None, *, backend=None, timeouts=None, profile=None, artifacts=None, recording=None)
Option Type Default
session str TUI_TEST_SESSION or "default"
backend "alacritty" | "ghostty" | "rio" | "xtermjs" "alacritty"
timeouts Timeouts | dict built-in defaults
profile Profile | dict built-in profile
artifacts dict off
recording AutomaticRecording | dict {"mode": "always"}

artifacts["on_failure"] is "svg", "text", or "none". Recording mode is "disabled", "on-failure", or "always".

Properties

Property Type
session str
keyboard keyboard helper
mouse mouse helper

Lifecycle

Method Description
TuiTest.ephemeral(prefix=None, **options) Create a unique session.
await open(**options) Open a shell.
await run(program, *args, **options) Run a program.
await restart(graceful_timeout=5000) Restart the session.
await close() Close the session.
await close_quiet() Close without raising.
async with TuiTest() Close on exit.

open() options are shell, backend, cols, rows, cwd, env, wait_ready, restart, retries, profile, and timeouts. run() accepts the same options except shell.

The default size is 80 by 30. Timeout defaults are 5 seconds for text and idle, and 30 seconds for command, exit, and ready.

Input

Method Description
await submit(text=None) Type text and press Enter.
await type(text) Type text.
await write(data) Write raw bytes.
await press(*keys) Alias for keyboard.press().
await resize(cols, rows) Resize the terminal.
await signal(name) Send INT, TERM, KILL, or QUIT.
await kill() Kill the child process.

State

Method Returns
await state() State
await text(full=False) str
await cells(x, y, w=1, h=1) list[Cell]
await get_command() str | None
await get_output() str | None
await get_exit_code() int | None
await get_cwd() str | None
await get_cursor() dict
await get_size() dict
await get_title() str | None
await get_clipboard() str
await get_bell_count() int
await get_bell_events() list[BellEvent]

Waits and assertions

Method Description
await wait_title(text, regex=False, not_=False, timeout=None) Wait for a title.
await wait_clipboard(text=None, timeout=None) Wait for a clipboard change or match.
await wait_idle(timeout=None) Wait for the screen to stop changing.
await wait_command(timeout=None) Wait for a submitted command.
await wait_exit(timeout=None) Wait for the program to exit.
await wait_ready(timeout=None) Wait for a shell prompt.
await wait_bell(timeout=None) Wait for a bell.
await expect_title(text, regex=False, not_=False, timeout=None) Assert the title.
await expect_exit_code(code, timeout=None) Assert the last exit code.
await expect_output(text, regex=False) Assert command output.
await expect_bell_count(count, timeout=None) Wait until the cumulative bell count reaches count.
await expect_snapshot(name, **options) Assert or update a snapshot.

wait_clipboard() waits for the next change. A string matches text. A compiled re.Pattern matches a regular expression.

Snapshot options are update, include_style, and include_title.

Capture

Method Description
await screenshot(path=None, full=False, zoom=None, background=None, transparent=False) Return text or save SVG or PNG.
await start_recording(path, **options) Start APNG, GIF, MP4, or asciinema recording.
await stop_recording() Finish the recording and return its path.

Recording options: format, fps, speed, idle_time_limit, zoom, background, and transparent. MP4 requires ffmpeg and does not support transparency.

The extension selects the format: .png or .apng, .gif, .mp4, or .cast. format overrides it.

Locator

Locators resolve against the latest terminal screen before every read or action.

from tui_test import TextStyle

save = (
    terminal
    .get_by_text("Settings")
    .get_by_text("Save", direction="after")
    .get_by_style(TextStyle(foreground="green"))
    .unique()
)

await save.click()

Create a locator

Method Options
terminal.get_by_text(text, **options) regex, full, whitespace
terminal.get_by_style(style, **options) full
terminal.get_by_link(uri, **options) full
locator.get_by_text(text, **options) regex, full, whitespace, direction
locator.get_by_style(style, **options) full, direction
locator.get_by_link(uri, **options) full, direction

whitespace is "exact" or "normalize". direction is "within", "after", or "before".

TextStyle fields are foreground, background, bold, dim, italic, underline_style, underline_color, inverse, hidden, strikethrough, and blink.

get_by_link(uri) matches an exact OSC 8 target, not visible URL text; get_by_link("") requires no link. Root style/link selectors find runs within each row. Chained calls with the default within direction check whole matches. Styles skip blanks if visible text exists; links check every cell.

Compose locators

link = terminal.get_by_link("https://example.com")
bold = terminal.get_by_style(TextStyle(bold=True))
bold_link_cells = bold.and_(link)
either = link.or_(terminal.get_by_text("Help"))
sections = terminal.get_by_text("Docs and Help")
contains_link = sections.filter(has=link)
without_old_text = sections.filter(has_not=terminal.get_by_text("old"))
entirely_linked = sections.get_by_link("https://example.com")

and_() keeps shared cells; or_() combines cells without duplicates. Adjacent cells merge within each physical row, even across original matches. Gaps and row breaks split runs. Counts and clicks use these runs; text keeps exact whitespace.

filter accepts only locators. has requires a match inside each candidate; has_not requires none. Both conditions apply when supplied, and the inner match may cover the whole candidate. For partially linked "Docs", filter(has=link) keeps the whole word, get_by_link(uri) rejects it, and and_(link) returns its linked cells.

Use locators from one TuiTest. Composition leaves them unchanged and reads one fresh snapshot when used. Selection order matters: a.first().and_(b) differs from a.and_(b).first(). Any full branch includes scrollback for the whole query. Errors propagate.

Select matches

Method Description
any() Keep all matches.
unique() Require one match.
first() Select the first match.
last() Select the last match.
nth(index) Select a zero-based match.

Read and act

Method Description
await locations() Return all selected locations.
await location() Return one location.
await count() Return the current count.
await all() Return one locator per current match.
await wait(state="visible", timeout=None) Wait for "visible" or "hidden".
await expect(not_=False, timeout=None) Assert the locator.
await click(**options) Click the middle cell.
await highlight(timeout=None) Highlight matches.

click() accepts button, alt, ctrl, shift, clicks, and timeout. button is "left", "middle", or "right".

location() and click() require one match. all() does not wait.

Keyboard

Method Description
await keyboard.press(*keys) Press keys.
await keyboard.down(*keys) Send keydown events.
await keyboard.repeat(*keys) Send repeat events.
await keyboard.up(*keys) Send keyup events.
await terminal.keyboard.press("Ctrl+C")
await terminal.keyboard.press("Escape", ":", "w", "q", "Enter")

Named keys include Up, Down, Left, Right, Home, End, PageUp, PageDown, Insert, Delete, Backspace, Tab, Enter, Space, Escape, and F1 through F12. Join modifiers such as Ctrl, Alt, Shift, Super, Meta, or Hyper with +.

Mouse

Coordinates are zero-based terminal cells.

Method Description
await mouse.click(x=None, y=None, **options) Click a cell or on_text.
await mouse.move(x, y) Move the pointer.
await mouse.down(x, y, **options) Press a button.
await mouse.up(x, y, **options) Release a button.
await mouse.drag(x1, y1, x2, y2, **options) Drag between cells.
await mouse.scroll("up" | "down", amount=3) Scroll.

Button options are button, alt, ctrl, and shift. Click also accepts on_text and clicks.

await terminal.mouse.click(10, 5, button="right", ctrl=True)

Module functions

Function Description
await sessions() List sessions in this process.
await close_all() Close all sessions in this process.
await get_recording(session=None) Return an automatic asciinema recording.
unique_session(prefix=None) Create a unique session name.

Test helpers

Import from tui_test.testing.

Function Description
await create_terminal(**options) Create, open, and track a terminal.
async with terminal(**options) Open and close a terminal.
await close_all_tracked() Close tracked terminals.
set_terminal_defaults(**options) Set suite defaults.
reset_terminal_defaults() Reset suite defaults.
track_terminal(terminal) Track a terminal.
untrack_terminal(terminal) Stop tracking a terminal.
tracked_count() Count tracked terminals.
terminal_snapshot(text) Normalize text for snapshots.

TerminalOptions adds shell, program, session, and prefix to the client and spawn options.

DEFAULT_SHELL is the platform default.

from tui_test.testing import terminal

async with terminal(program=("my-app",)) as app:
    await app.get_by_text("Ready").expect()

Configuration

from tui_test import AutomaticRecording, Colors, Profile, Timeouts

terminal = TuiTest(
    profile=Profile(
        scrollback=500,
        colors=Colors(foreground="#ffffff", background="#000000"),
    ),
    timeouts=Timeouts(text=10_000, command=60_000),
    artifacts={"dir": "artifacts", "on_failure": "svg"},
    recording=AutomaticRecording(mode="on-failure", directory="artifacts"),
)

Types

Type Description
State Session state and visible text.
Cell One terminal cell and its style.
TextMatch Matched text, positions, and spans.
TextStyle Locator style fields.
Profile Scrollback and colors.
Timeouts Text, idle, command, exit, and ready timeouts.
AutomaticRecording Automatic recording mode and directory.
Colors Terminal palette.
MouseButton "left", "middle", or "right".
TextPosition, TextSpan Match coordinates.

__version__ contains the package version.

Errors

Error Exit code
ExpectationError 1
UsageError 2
NoSessionError 3
InternalError 5

All errors extend TuiTestError. Expectation errors can include terminal.text and terminal.screenshot.

Sessions are local to the current process and cannot be controlled by the CLI. Cancelling a task does not stop an active terminal operation.

Release files for tui-test 0.1.0b4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tui-test 0.1.0b4
File Size Uploaded
tui_test-0.1.0b4.tar.gz 4.2 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for tui-test 0.1.0b4
File
tui_test-0.1.0b4-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_x86_64.whl CPython 3.8 abi3 Linux musl 1.2+ x86-64 Details
tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_aarch64.whl CPython 3.8 abi3 Linux musl 1.2+ ARM64 Details
tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_x86_64.whl CPython 3.8 abi3 Linux glibc 2.28+ x86-64 Details
tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_aarch64.whl CPython 3.8 abi3 Linux glibc 2.28+ ARM64 Details
tui_test-0.1.0b4-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details
tui_test-0.1.0b4-cp38-abi3-macosx_10_12_x86_64.whl CPython 3.8 abi3 macOS 10.12+ x86-64 Details

Total release size: 61.5 MB

Release files / tui_test-0.1.0b4.tar.gz

Download URL tui_test-0.1.0b4.tar.gz
Size 4.2 MB
Tags Source
SHA-256 checksum
How to use checksums
88fcb21a171b99b716a0a79140e31c6dad1db2327554f95d4a843df734fd60e8
BLAKE2b-256 checksum
How to use checksums
906249f236c2e7cd80e8e7e715d3f8ee230fda7e7dcfd2189e1c84bc9ad6541b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-win_amd64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-win_amd64.whl
Size 6.7 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
1560ba7067e7e7ce33b56069a08229e8cbaecfd12dd2463ca3550c49754976bb
BLAKE2b-256 checksum
How to use checksums
070b696709b512172224b3f1a43e1552dee75b67696cc25522f2376f808c745f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_x86_64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_x86_64.whl
Size 13.2 MB
Tags CPython 3.8 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
11f850f6385573f733bf94c94a4d512ab60529d20018d431a5ad8b46d7947ebd
BLAKE2b-256 checksum
How to use checksums
b34a3a8fce8add3509f06937576d4e0e56f259f0ab6a869f8b8ce629c5f97151
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_aarch64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-musllinux_1_2_aarch64.whl
Size 12.7 MB
Tags CPython 3.8 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
b4cbdb00c4661f446608e267f36a557b1aef4a324bfdaa0de2bd8cc195b5e93c
BLAKE2b-256 checksum
How to use checksums
f7b3762cc6c9fb440989430f91d66ef30daae257a66f4d70eff57ac4c20a452a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_x86_64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_x86_64.whl
Size 6.6 MB
Tags CPython 3.8 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
e009b4e153abe876af5ae7c7c24754ee424a49d94a4c9a44257ae8f6564c47a2
BLAKE2b-256 checksum
How to use checksums
2b386f8e9d672e1cc84720f7fac5b7362f48282c566fc70f15b425e6247771c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_aarch64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-manylinux_2_28_aarch64.whl
Size 6.2 MB
Tags CPython 3.8 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
0ecf1f415c840001a9905138356f6aba4d286a114d4d037b69df187ab076d5be
BLAKE2b-256 checksum
How to use checksums
99f3e28396e3150c7a3e2427ba9b40bc6567c1d7f3d0740ee32bf3872725fc12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-macosx_11_0_arm64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-macosx_11_0_arm64.whl
Size 5.8 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
079c10b2d91723a516daaafb2a57768d44330633c14291a1f3bde0e8faa2b428
BLAKE2b-256 checksum
How to use checksums
d281f65ac5fc200b97da53e631ac237b4e76e6f8938163a32ad9b72f2dac5435
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / tui_test-0.1.0b4-cp38-abi3-macosx_10_12_x86_64.whl

Download URL tui_test-0.1.0b4-cp38-abi3-macosx_10_12_x86_64.whl
Size 6.1 MB
Tags CPython 3.8 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
d0cc341dac936c04eb68175e8f6398ef5388f440e8af011e832a7d76ca548ee3
BLAKE2b-256 checksum
How to use checksums
a79b9340462a5dedcd9f3b4e093e6f030e18e644c1d358ce71084a125c8e2fd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page