Sessions¶
A TuiSession is one running TUI under one PTY. Most tests use one
session via the tui fixture; for multi-pane scenarios, use
tui_factory.
Lifecycle¶
async def test_thing(tui):
# 1. start — spawn the binary, hook listeners
await tui.start("myapp", env={"FOO": "bar"}, cols=120, rows=40)
# 2. drive — input + waits
await tui.wait_for_text("Ready")
await tui.type("hello")
# 3. assert
assert "hello" in tui.screen.text
# 4. stop — automatic when the fixture tears down
The tui fixture handles steps 1-end-of-test for you: it constructs
the session, yields it, then calls await session.stop() in cleanup.
Starting¶
await tui.start(
cmd, # str (shlex-split) or list[str]
*,
env=None, # extra env vars, merged with os.environ
cwd=None, # working dir
cols=80, # terminal width
rows=24, # terminal height
cast_path=None, # explicit cast file location
)
The string form runs through shlex.split, so "myapp --flag arg"
becomes ["myapp", "--flag", "arg"]. Use the list form when arguments
contain spaces.
Common environment knobs¶
| Var | Purpose |
|---|---|
TERM |
Defaults to xterm-256color. Override if your app expects something specific. |
COLORTERM |
Defaults to truecolor. Some apps gate true-colour output on this. |
PAGER / LESS |
Defaulted to safe values to prevent paging from breaking the layout. |
You can override any of these via env={} and they'll take precedence.
Stopping¶
Sequence:
- Send
SIGTERM. - Wait up to
timeoutseconds for the child to exit. - If still alive, send
SIGKILL. - Close the PTY and recorder.
The fixture calls this for you with the default timeout. Override explicitly only when you know the app needs more shutdown grace.
Context manager¶
For one-off scripts (not pytest), use the async context manager:
import asyncio
from tuiwright import TuiSession, TuiConfig
async def main():
async with TuiSession(TuiConfig(cols=120, rows=40)) as tui:
await tui.start("myapp")
await tui.wait_for_text("Ready")
await tui.type("hello")
asyncio.run(main())
Multiple sessions¶
async def test_two_panes(tui_factory):
left = tui_factory()
right = tui_factory()
await left.start("myapp", cols=80, rows=24)
await right.start("myapp", cols=80, rows=24)
await left.wait_for_text("Ready")
await right.wait_for_text("Ready")
# both are independent; the fixture stops both on teardown
What lives on TuiSession¶
| Property | Use |
|---|---|
tui.screen |
Snapshot of the current grid (cheap; rebuilt on each access) |
tui.alive |
True until the child exits |
tui.cast_path |
Path to the live asciinema cast file |
tui.config |
The active TuiConfig |
| Method | Use |
|---|---|
tui.start(...) |
Spawn |
tui.stop(...) |
Graceful shutdown |
tui.press(key) |
One key event (reference) |
tui.type(text) |
Plain text input |
tui.paste(text) |
Bracketed paste |
tui.click / scroll / drag / hover(...) |
Mouse |
tui.resize(cols, rows) |
TIOCSWINSZ + SIGWINCH |
tui.focus(in_=) |
Focus in/out |
tui.wait_for_text(...) |
See Waiting |
tui.wait_for_predicate(...) |
Custom condition |
tui.wait_for_stable(...) |
Settle on no-change |
tui.region(title=, rows=, cols=) |
Screen subview |
tui.assert_region(...) |
Convenience assertion |
tui.png() |
Render current cast to PNG via agg |
Configuration¶
TuiConfig is a frozen-ish dataclass; tweak it per-test via the
tui_config fixture or via the @pytest.mark.tui marker.
Defaults:
TuiConfig(
cols=80,
rows=24,
default_timeout=5.0,
poll_interval=0.02,
stable_quiet_ms=50,
agg_path=None, # auto-discovered from PATH
cast_dir=None, # tempdir per session
strict_mouse=False, # warn instead of raise
)
Next: Screens & regions →