mod scripted

module scripted

Deterministic scripted driving surface for the TUI applications.

Both tui::run() (the generator wizard) and vault_tui::run() (the vault manager) normally read real keyboard events from a CrosstermBackend terminal, which is why the only existing end-to-end coverage (tests/test_tui_e2e.py) has to fork a real PTY. That PTY layer stays as the sole terminal-emulation-accurate check; this module adds a second, deterministic path that feeds a script of key tokens straight into the same App::handle_key step function used by the real event loop, backed by ratatui’s in-memory TestBackend instead of a real terminal.

A scripted run is activated by setting PARANOID_TUI_SCRIPT=<path> before calling tui::run() or vault_tui::run(config). The path must contain newline-delimited key tokens (see parse_script). Blank lines and lines starting with # are ignored. The run drives the real App — including, for the generator wizard, its background worker thread — and returns a final-frame text dump of the terminal buffer so callers can assert on rendered content without a PTY.

Token grammar

One token per line, whitespace-trimmed:

  • A single printable character is sent as its own literal KeyCode::Char key event. Multi-character text is scripted as one character per line (there is no inline string literal).

  • <enter>, <esc>, <tab>, <backspace>, <up>, <down> map to the matching KeyCode variant.

  • <ctrl-u> sends KeyCode::Char('u') with KeyModifiers::CONTROL (matches the custom-charset / form “clear field” shortcut).

  • <wait-idle> does not send a key event. It repeatedly polls the app (worker + hardening polling, when the app exposes it) until any background worker thread has drained, up to a bounded timeout. Use it after triggering an action that spawns a worker thread (e.g. launching the generator audit) before scripting further keys or ending the run.

  • Blank lines and lines starting with # are ignored (comments).

Variables

const DEFAULT_COLS: u16

Default terminal size used for scripted runs, matching the PTY e2e layer.

const DEFAULT_ROWS: u16

Functions

fn drive<S>(terminal: &mut Terminal<TestBackend>, tokens: &[ScriptToken], mut step: S) -> anyhow::Result<()>
where
    S: FnMut(&mut Terminal<TestBackend>, Option<KeyEvent>) -> anyhow::Result<StepOutcome>

Generic scripted-run driver shared by tui::run_scripted and vault_tui::run_scripted.

step performs one iteration of the app’s own event loop body: when key is Some, it should forward the event to the app’s handle_key before polling/rendering; when None, it should just poll (worker / hardening) and render, as the real event loop does on each tick even without a key event.

fn dump_buffer(terminal: &Terminal<TestBackend>) -> String

Renders the given TestBackend terminal buffer to a plain-text dump, one line per terminal row, trailing whitespace trimmed. This is the “final-frame” text callers assert against after a scripted run.

fn is_script_active() -> bool

true when PARANOID_TUI_SCRIPT is set. A scripted run is, by definition, a deliberate non-interactive drive of the TUI (there is no real terminal to detect), so callers should treat this the same as an explicit --tui request when deciding whether to launch the TUI instead of the scriptable CLI/ops path.

fn load_script(path: &Path) -> anyhow::Result<Vec<ScriptToken>>

Loads and parses a script file.

fn parse_script(contents: &str) -> anyhow::Result<Vec<ScriptToken>>

Parses newline-delimited key tokens into ScriptTokens. See the module docs for the token grammar.

fn prepare_scripted_terminal(cols: u16, rows: u16) -> anyhow::Result<Option<PreparedScript>>

Convenience for run() implementations: reads the script path from PARANOID_TUI_SCRIPT, builds a TestBackend terminal of the given size, and returns both plus the parsed tokens, ready to hand to drive.

fn script_from_env() -> Option<anyhow::Result<Vec<ScriptToken>>>

Reads PARANOID_TUI_SCRIPT from the environment and, if set, parses the script file at that path. Returns None when the variable is unset so callers can fall back to the normal interactive event loop.

Enums

enum ScriptToken

A single step to apply to the application under script control.

Key(KeyEvent)

Send this key event to App::handle_key.

WaitIdle

Poll until any background worker has drained, or time out.

Structs and Unions

struct PreparedScript

A ready-to-drive scripted terminal, its parsed token script, and the script file path it was loaded from (for error messages).

terminal: Terminal<TestBackend>
tokens: Vec<ScriptToken>
path: PathBuf
struct StepOutcome

Result of a single drive step.

idle: bool

true once any background work the app owns has fully drained (so <wait-idle> knows when to stop polling). Apps with no background work should always report true.

quit: bool

true when the app requested to quit in response to a key event. Ignored for poll-only steps (key: None).