Testing
The TuiTester harness drives a form's interactive panel TUI from scripted keystrokes - it pushes them onto the terminal's input pipe and runs the real panel loop - so you can assert on the collected answers and on what was rendered, without a terminal anywhere in sight. It's the form-level companion to the widget-level WidgetRunner.
Keystrokes are Key objects and/or raw byte strings (the bytes a terminal emits for a key press), so an existing keystroke helper drops straight in.
use DrevOps\Tui\Input\Key;
use DrevOps\Tui\Input\KeyName;
use DrevOps\Tui\Testing\TuiTester;
$tester = new TuiTester($form);
$answers = $tester->run(
Key::named(KeyName::Enter), // drill into the first panel
Key::named(KeyName::Enter), // open the "name" editor
'Ada', // type a value
Key::named(KeyName::Enter), // accept
Key::named(KeyName::Escape), // back to the root
Key::named(KeyName::Down), // move to Submit
Key::named(KeyName::Enter), // submit
);
// Raw bytes work too: $tester->run("\r", 'Ada', "\r", ...);
$this->assertSame('Ada', $answers->value('name'));
$this->assertStringContainsString('Ada', $tester->display());
$this->assertFalse($tester->isCancelled());
run() returns the collected Answers. display() is the ANSI-stripped output for substring assertions, output() the raw frames, and isCancelled() reports whether the run ended on the cancel button. theme(), options(), rows(), cols(), version() and directory() tune the run.
For a single widget in isolation, WidgetRunner::run($widget, ArrayKeyStream::of(...)) stays the lighter tool.
The harness outside PHPUnit is shown in playground/13-testing.php.