This skill provides patterns for implementing end-to-end tests for CLI applications using Hex1b for terminal interaction.
Install
mkdir -p .claude/skills/cli-e2e-testing && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3296" && unzip -o skill.zip -d .claude/skills/cli-e2e-testing && rm skill.zipInstalls to .claude/skills/cli-e2e-testing
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when creating, modifying, debugging, or reviewing Aspire CLI end-to-end tests that use Hex1b terminal automation under tests/Aspire.Cli.EndToEnd.Tests/.Key capabilities
- →Automate terminal sessions using Hex1b
- →Capture Aspire diagnostics during test execution
- →Manage isolated test workspaces
- →Execute terminal command sequences with deterministic prompt detection
- →Record and replay terminal sessions with asciinema
How it works
Tests use the Hex1b library to drive a headless terminal, wrapping execution in a TerminalRun to handle diagnostics and cleanup automatically.
Inputs & outputs
When to use cli-e2e-testing
- →Create new CLI E2E tests
- →Debug failing terminal automation tests
- →Refactor existing test suites
- →Implement terminal command expectations
About this skill
Aspire CLI End-to-End Testing with Hex1b
This skill provides patterns and practices for writing end-to-end tests for the Aspire CLI using the Hex1b terminal automation library.
Overview
CLI E2E tests use the Hex1b library to automate terminal sessions, simulating real user interactions with the Aspire CLI. Tests run in CI with asciinema recordings for debugging.
Location: tests/Aspire.Cli.EndToEnd.Tests/
Supported Platforms: Linux only. Hex1b requires a Linux terminal environment. Tests are configured to skip on Windows and macOS in CI.
Key Components
Core Classes
Hex1bTerminal: The main terminal class from the Hex1b library for terminal automationHex1bTerminalAutomator: Async/await API for driving aHex1bTerminal— the preferred approach for new testsHex1bAutomatorTestHelpers(shared helpers): Async extension methods onHex1bTerminalAutomator(WaitForSuccessPromptAsync,AspireNewAsync, etc.)CliE2EAutomatorHelpers(Helpers/CliE2EAutomatorHelpers.cs): CLI-specific async extension methods onHex1bTerminalAutomator(PrepareDockerEnvironmentAsync,InstallAspireCliAsync, etc.)CellPatternSearcher: Pattern matching for terminal cell contentSequenceCounter(Helpers/SequenceCounter.cs): Tracks command execution count for deterministic prompt detectionCliE2ETestHelpers(Helpers/CliE2ETestHelpers.cs): Environment variable helpers and terminal factory methodsTemporaryWorkspace: Creates isolated temporary directories for test executionHex1bTerminalInputSequenceBuilder(legacy): Fluent builder API for building sequences of terminal input/output operations. PreferHex1bTerminalAutomatorfor new tests.
Test Architecture
Each test:
- Creates a
TemporaryWorkspacefor isolation - Builds a
Hex1bTerminalwith headless mode and asciinema recording - Creates a
Hex1bTerminalAutomatorwrapping the terminal - Drives the terminal with async/await calls and awaits completion
Test Structure
public sealed class SmokeTests(ITestOutputHelper output)
{
[Fact]
public async Task MyCliTest()
{
var repoRoot = CliE2ETestHelpers.GetRepoRoot();
var strategy = CliInstallStrategy.Detect(output.WriteLine);
var workspace = TemporaryWorkspace.Create(output);
using var terminal = CliE2ETestHelpers.CreateDockerTestTerminal(repoRoot, strategy, output, workspace: workspace);
var counter = new SequenceCounter();
var auto = new Hex1bTerminalAutomator(terminal, defaultTimeout: TimeSpan.FromSeconds(500));
await using var terminalRun = CliE2ETestHelpers.StartRun(terminal, workspace, auto, counter, output, TestContext.Current.CancellationToken);
await auto.PrepareDockerEnvironmentAsync(counter, workspace);
await auto.InstallAspireCliAsync(strategy, counter);
await auto.TypeAsync("aspire --version");
await auto.EnterAsync();
await auto.WaitForSuccessPromptAsync(counter);
}
}
TerminalRun Pattern
Always use CliE2ETestHelpers.StartRun to wrap the terminal run. This returns a TerminalRun (implements IAsyncDisposable) that automatically:
- Captures Aspire diagnostics via
CaptureAspireDiagnosticsAsync(best effort) - Types
exitand presses Enter to close the terminal - Awaits the pending run task
This eliminates the need for manual exit/await pendingRun at the end of every test and ensures diagnostics are always captured, even when tests fail.
// DO: Use StartRun for consistent diagnostics capture and cleanup
using var terminal = CliE2ETestHelpers.CreateDockerTestTerminal(repoRoot, strategy, output, workspace: workspace);
var counter = new SequenceCounter();
var auto = new Hex1bTerminalAutomator(terminal, defaultTimeout: TimeSpan.FromSeconds(500));
await using var terminalRun = CliE2ETestHelpers.StartRun(terminal, workspace, auto, counter, output, TestContext.Current.CancellationToken);
// ... test body — no exit/pendingRun needed at the end
// DON'T: Manually handle exit and pendingRun
var pendingRun = terminal.RunAsync(TestContext.Current.CancellationToken);
// ... test body ...
await auto.TypeAsync("exit");
await auto.EnterAsync();
await pendingRun;
Running Tests Locally
CLI E2E tests run inside Docker containers on Linux. The workflow is: build a portable archive with localhive, then point the tests at it. This is the primary way to iterate on E2E tests during development.
Prerequisites
- Docker Desktop running (with Linux containers)
- .NET 10 SDK (installed via
./restore.shor.\restore.cmd)
Quick Start (macOS / Linux)
# 1. Build a portable archive with CLI + packages + bundle
# Use linux-arm64 on Apple Silicon, linux-x64 on Intel/Linux
./localhive.sh -o /tmp/aspire-e2e -r linux-arm64 --archive
# 2. Run a specific test
ASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
-- --filter-method "*.CreateAndRunAspireStarterProject"
# 3. Run all tests in a class
ASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
-- --filter-class "*.SmokeTests"
Quick Start (Windows / PowerShell)
# 1. Build a portable archive (Docker Desktop uses linux-x64 via WSL2)
.\localhive.ps1 -o C:\tmp\aspire-e2e -r linux-x64 -Archive
# 2. Run a specific test
$env:ASPIRE_E2E_ARCHIVE = "C:\tmp\aspire-e2e.tar.gz"
dotnet test tests\Aspire.Cli.EndToEnd.Tests\Aspire.Cli.EndToEnd.Tests.csproj `
-- --filter-method "*.CreateAndRunAspireStarterProject"
# 3. Run all tests in a class
dotnet test tests\Aspire.Cli.EndToEnd.Tests\Aspire.Cli.EndToEnd.Tests.csproj `
-- --filter-class "*.SmokeTests"
Choosing the Right RID
The archive must match the Docker container's architecture:
| Host | Docker Desktop | RID |
|---|---|---|
| Apple Silicon Mac | Linux arm64 containers | linux-arm64 |
| Intel Mac | Linux x64 containers | linux-x64 |
| Windows (any) | WSL2 Linux x64 | linux-x64 |
| Linux x64 | Native | linux-x64 |
| Linux arm64 | Native | linux-arm64 |
Development Workflow
The typical loop when writing or debugging E2E tests:
# 1. Make your code changes (CLI, hosting, templates, etc.)
# 2. Rebuild the archive (picks up all changes — ~3 min)
./localhive.sh -o /tmp/aspire-e2e -r linux-arm64 --archive
# 3. Run the specific test you're working on
ASPIRE_E2E_ARCHIVE=/tmp/aspire-e2e.tar.gz \
dotnet test tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
-- --filter-method "*.YourTestName"
# 4. If it fails, check the asciinema recording
# Recordings are saved under the test output TestResults/recordings/ directory
# Play with: asciinema play /path/to/YourTestName.cast
# 5. Fix and repeat from step 1 or 2
Install Modes
The CliInstallStrategy class auto-detects how to install the CLI in the test container. You can override via environment variables:
| Env Var | Mode | Example |
|---|---|---|
ASPIRE_E2E_ARCHIVE | LocalHive — extract archive into container | /tmp/aspire-e2e.tar.gz |
ASPIRE_E2E_QUALITY | Install script with quality | dev, staging, release |
ASPIRE_E2E_VERSION | Install script with version | 13.2.1 |
| (none, in CI) | PullRequest — install from PR artifacts | Auto-detected |
| (none, locally) | InstallScript (latest GA) | Auto-detected |
LocalHive (via ASPIRE_E2E_ARCHIVE) is the recommended mode for local development — it uses your locally-built CLI, packages, and bundle so you test exactly what you've changed.
Testing Against Released Versions
Useful for verifying tests pass against shipped versions or catching regressions:
# Test against latest GA release
ASPIRE_E2E_QUALITY=release dotnet test tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
-- --filter-method "*.CreateAndRunAspireStarterProject"
# Test against daily builds
ASPIRE_E2E_QUALITY=dev dotnet test ...
# Test against a specific version
ASPIRE_E2E_VERSION=13.2.1 dotnet test ...
Emulated channel matrix tests (identity sidecar)
A set of tests validates the CLI identity sidecar — the ability to make a locally built CLI
emulate a different channel/version via ASPIRE_CLI_* env vars. They form an AppHost-language ×
channel-emulation matrix (one test per language because C# and TypeScript scaffold through different
code paths and have diverged before):
| Class | Channel emulated | Aspire* source | NuGet.config dropped? |
|---|---|---|---|
EmulatedReleasedBuildTests | stable (latest shipped) | nuget.org | No (C# and TS) |
EmulatedStagingBuildTests | staging (latest darc build) | darc-pub-... feed | Yes — darc feed pin |
EmulatedLocalReleaseBuildTests | stable (future, local-only) | local hive via ASPIRE_CLI_PACKAGES | No (C# and TS) |
EmulatedLocalReleaseBuildTests is the all-local "future release" row: it emulates a version
(e.g. 13.5.0) that exists only in a locally built hive, so a successful resolve proves the CLI
consulted ASPIRE_CLI_PACKAGES rather than nuget.org. Run it by building a stable-shaped archive
with localhive --version:
# 1. Build a stable-shaped archive (note: --version X.Y.Z, NOT a prerelease suffix)
./localhive.sh --version 13.5.0 -o /tmp/aspire-localrelease -r linux-arm64 --archive
# 2. Run the all-local class
ASPIRE_E2E_ARCHIVE=/tmp/aspire-localrelease.tar.gz \
dotnet test --project tests/Aspire.Cli.EndToEnd.Tests/Aspire.Cli.EndToEnd.Tests.csproj \
-- --filter-class "*.EmulatedLocalReleaseBuildTests"
These tests skip unless the CLI was installed from a LocalHive archive and that archive is
stable-shaped (no prerelease suffix). In default CI the archive is a prerelease `LocalArchive
Content truncated.
When not to use it
- →Non-Linux environments
- →Windows or macOS CI pipelines
Prerequisites
Limitations
- →Requires a Linux terminal environment
- →Tests are configured to skip on Windows and macOS
How it compares
Unlike manual terminal testing, this approach uses an async-driven automator and deterministic sequence counters to ensure reliable CI execution.
Compared to similar skills
cli-e2e-testing side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| cli-e2e-testing (this skill) | 1 | 1mo | Review | Advanced |
| webapp-testing | 353 | 3mo | Review | Intermediate |
| dev-browser | 53 | 4mo | Review | Intermediate |
| playwright-browser-automation | 29 | 7mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by dotnet
View all by dotnet →You might also like
webapp-testing
anthropics
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
dev-browser
SawyerHood
Browser automation with persistent page state. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include "go to [url]", "click on", "fill out the form", "take a screenshot", "scrape", "automate", "test the website", "log into", or any browser interaction request.
playwright-browser-automation
lackeyjb
Complete browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp. Test pages, fill forms, take screenshots, check responsive design, validate UX, test login flows, check links, automate any browser task. Use when user wants to test websites, automate browser interactions, validate web functionality, or perform any browser-based testing.
windows-ui-automation
martinholovsky
Expert in Windows UI Automation (UIA) and Win32 APIs for desktop automation. Specializes in accessible, secure automation of Windows applications including element discovery, input simulation, and process interaction. HIGH-RISK skill requiring strict security controls for system access.
unity-mcp-orchestrator
CoplayDev
Orchestrate Unity Editor via MCP (Model Context Protocol) tools and resources. Use when working with Unity projects through MCP for Unity - creating/modifying GameObjects, editing scripts, managing scenes, running tests, or any Unity Editor automation. Provides best practices, tool schemas, and workflow patterns for effective Unity-MCP integration.
agent-browser
vercel-labs
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.