Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Automation workflows executed by GitHub Actions.
- [`postsubmit.yml`](postsubmit.yml) - Postsubmit workflow that generates LCOV coverage and uploads it to Codecov.
- [`publish-crates-io.yml`](publish-crates-io.yml) - Publishes the `ag-forge` and `agentty` crates to crates.io on release-tag pushes.
- [`release.yml`](release.yml) - Release pipeline for tagging and publishing.
- [`tui-e2e.yml`](tui-e2e.yml) - TUI end-to-end test suite using `ag-tui-test` with VHS and PTY-driven assertions.
56 changes: 56 additions & 0 deletions .github/workflows/tui-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: TUI E2E Tests

on:
pull_request:
branches:
- main
push:
branches:
- main

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
tui-e2e:
runs-on: ubuntu-22.04

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Restore Rust cache
uses: Swatinem/rust-cache@v2

- name: Install VHS
run: |
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
sudo apt-get update && sudo apt-get install -y vhs

- name: Build agentty binary
run: cargo build -p agentty --locked

- name: Run TUI E2E tests
run: cargo test -p agentty --test e2e -- --ignored --test-threads=1

- name: Upload failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: tui-e2e-artifacts
path: |
**/e2e_screenshots/*_actual.png
**/e2e_artifacts/
retention-days: 7
23 changes: 23 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,29 @@ after enabling or modifying `query_as!`-style queries.
This repository expects generated `crates/agentty/.sqlx/` metadata so `SQLx`
macro queries can compile in offline mode (for example, with `SQLX_OFFLINE=true`).

## TUI E2E Tests

TUI end-to-end tests use the `ag-tui-test` framework to drive the real `agentty`
binary in a PTY and assert terminal state semantically. Tests are written as Rust
scenarios and can also be compiled into VHS tapes for visual screenshots.

```sh
# Run TUI E2E tests (requires the agentty binary to be built)
cargo test -p agentty --test e2e -- --ignored

# Update snapshot baselines
TUI_TEST_UPDATE=1 cargo test -p agentty --test e2e -- --ignored
```

### Authoring Tests

Tests are written using the scenario DSL in Rust. Each scenario defines a user
journey through steps (`write_text`, `press_key`, `wait_for_text`, `capture`).
Use recipe helpers like `expect_selected_tab` and `expect_keybinding_hint`
instead of rebuilding locator logic from scratch.

VHS is generated by the framework — do not handwrite `.tape` files.

## Architecture Documentation

If your PR changes module boundaries, cross-layer control flow, trait-based external boundaries, or workspace crate ownership, update:
Expand Down
126 changes: 123 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ homepage = "https://github.com/agentty-xyz/agentty"

[workspace.dependencies]
ag-forge = { path = "crates/ag-forge", version = "0.6.7" }
ag-tui-test = { path = "crates/ag-tui-test", version = "0.6.7" }
agent-client-protocol = "0.10.2"
arboard = { version = "3", features = ["image-data"] }
askama = "0.15.4"
Expand All @@ -27,6 +28,7 @@ dirs = "6.0"
ignore = "0.4"
image = { version = "0.25", default-features = false, features = ["png"] }
mockall = "0.14"
portable-pty = "0.9"
pulldown-cmark = "0.13.1"
ratatui = "0.30.0"
schemars = { version = "1", features = ["derive"] }
Expand All @@ -43,6 +45,7 @@ tracing-subscriber = "0.3"
unicode-width = "0.2"
url = "2"
uuid = { version = "1", features = ["v4"] }
vt100 = "0.16"

[workspace.lints.rust]
unsafe_code = "deny"
Expand Down
1 change: 1 addition & 0 deletions crates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ Contains the workspace member crates.
## Directory Index

- [`ag-forge/`](ag-forge/) - Forge review-request library crate shared across the workspace.
- [`ag-tui-test/`](ag-tui-test/) - Rust-native TUI E2E testing framework with PTY-driven semantic assertions and VHS screenshot capture.
- [`ag-xtask/`](ag-xtask/) - Workspace maintenance tasks and automation.
- [`agentty/`](agentty/) - The main CLI binary crate for Agentty.
12 changes: 12 additions & 0 deletions crates/ag-tui-test/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# TUI E2E Testing Framework

Rust-native TUI end-to-end testing framework using PTY-driven semantic
assertions and VHS screenshot capture.

## Directory Index

- [`src/`](src/) - Source code directory.
- [`AGENTS.md`](AGENTS.md) - Crate documentation.
- [`Cargo.toml`](Cargo.toml) - Crate dependencies and metadata.
- [`CLAUDE.md`](CLAUDE.md) - Symlink to AGENTS.md.
- [`GEMINI.md`](GEMINI.md) - Symlink to AGENTS.md.
19 changes: 19 additions & 0 deletions crates/ag-tui-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "ag-tui-test"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
description = "Rust-native TUI end-to-end testing framework using PTY-driven semantic assertions and VHS screenshot capture."
repository.workspace = true
homepage.workspace = true

[lints]
workspace = true

[dependencies]
image.workspace = true
portable-pty.workspace = true
tempfile.workspace = true
thiserror.workspace = true
vt100.workspace = true
Loading