name: con-cli-e2e description: Validate Con's local socket control plane against a real running app session, and write/run con-test integration tests. Use when testing con-cli, the Unix socket API, pane control, tmux control, in-session agent calls, or when writing E2E test cases in crates/con-test/testdata/.
con-cli E2E & con-test
Use this skill when the task is to verify that Con's CLI/control plane works against a live app window, or when writing/fixing integration tests in crates/con-test/testdata/.
Primary reference:
- Read
docs/impl/con-cli-e2e.mdfor the full workflow and current live limitations.
con-cli manual E2E
Default workflow:
- Build the relevant crates.
- Launch
cargo run -p con. - Wait for
/tmp/con.sock. - Use
con-cli --json identify,tabs list, andpanes listbefore acting. - Only use
panes execon panes that exposeexec_visible_shell. - Use
tree/surfaces listonly for pane-local surface validation. - After
surfaces createorsurfaces split, usesurfaces wait-ready --surface-id <id> --timeout 10before sending input that assumes an initialized shell. - Use
agent askto verify the real in-tab built-in agent session.
Rules:
- Prefer
--jsonfor every command in automated evaluation. - Prefer
pane_idoverpane_indexfor follow-up actions. - Prefer
surface_idfor follow-up actions only when testing the explicitsurfaces.*API. - Keep existing pane and agent benchmarks on
panes.*; surfaces are additive and must not change the built-in agent's pane model. - After visible execution, confirm the pane still reports
shell_promptand keepsexec_visible_shell. - If
agent askfails, check provider config/env before blaming the socket layer.
Known current limit:
panes createnow reportssurface_ready,is_alive, andhas_shell_integration, but startup-command panes can still be in a non-shell foreground state immediately after creation. Treat them as provisional untilpanes listconfirms the capabilities you need for the next step.
con-test integration tests
con-test is the E2E test runner for integration and interactive behavior. It launches a real con session, runs .test files against it via con-cli, and checks output.
Running tests
# Build first
cargo build
# Run all tests
./target/debug/con-test crates/con-test/testdata/
# Run a single file
./target/debug/con-test crates/con-test/testdata/panes/split.test
Test file format
Test files live in crates/con-test/testdata/<group>/<name>.test. Each step is a con-cli command followed by an assertion block:
# comment
con-cli --json <command> # step description
---- <assertion>
<expected>
Assertion types:
| Assertion | Meaning |
|---|---|
---- ok |
Command exits 0 (any output accepted) |
---- contains |
stdout contains the literal string on the next line |
---- json-subset |
actual JSON is a superset of the expected JSON (subset match, deep) |
The json-subset assertion only checks the keys you specify — extra fields in the actual output are ignored. Use it to assert specific fields without coupling to the full response shape.
Writing new tests
- Unit-test functions in Rust (
#[cfg(test)]) for logic. Usecon-testonly for integration and interactive behavior that requires a live session. - No low-value tests — don't write tests just to hit coverage. Every test should catch a real bug or document a real contract.
- Group tests by domain:
panes/,tabs/,agent/,system/. - After
panes create, always add apanes waitstep before assertingis_alive— the new pane's surface may not be ready immediately. - For agent panel tests, use
agent open-panel-for-requestto drive the motion state, thenagent panel-stateto assert the result.
Example test
# panes/split.test
con-cli --json panes list --tab 1 # start with 1 pane
---- json-subset
{"panes":[{"index":1}]}
con-cli --json panes create --tab 1 --location right # split right
---- ok
con-cli --json panes wait --tab 1 --pane-index 2 --timeout 5 # wait for new pane
---- ok
con-cli --json panes list --tab 1 # both panes alive
---- json-subset
{"panes":[{"is_alive":true},{"is_alive":true}]}
Fixing failures
When a test fails with expected JSON is not a subset of actual JSON, the actual output is printed in full. Check:
- Is the command missing (unrecognized subcommand)? → Add the subcommand to
con-cliand theControlCommandhandler. - Is a field wrong/missing? → Fix the handler's response shape.
- Is the assertion racing (e.g.
is_alive: falseright after create)? → Add apanes waitorsurfaces wait-readystep before the assertion.