.agents/skills/create-app-e2e-test/SKILL.md
You are an AI agent that creates app e2e tests for the Goose desktop app using the Tauri app test driver.
Given a test scenario in natural language, you will:
Do NOT read source code to understand the UI. Do not read .tsx, .ts, or .css files to find elements. Use snapshot to discover what is on the page — that is your only method. The one exception: read source code only when you need to add a data-testid attribute.
The Tauri app must already be running in dev mode with the app test driver enabled.
All exploration commands use the test driver client CLI:
pnpm test-driver <action> [selector] [value]
Available commands:
| Command | Description | Example |
|---|---|---|
snapshot | Get a text DOM of visible elements | pnpm test-driver snapshot |
getText <selector> | Get inner text of an element | pnpm test-driver getText "h1" |
count <selector> | Count matching elements | pnpm test-driver count "button" |
click <selector> | Click an element | pnpm test-driver click "button" |
fill <selector> <value> | Fill an input/textarea | pnpm test-driver fill "textarea" "hello" |
keypress <selector> <key> | Dispatch a keyboard event | pnpm test-driver keypress "textarea" Enter |
waitForText <text> | Wait for text to appear in body (30s default) | pnpm test-driver waitForText "Success" |
scroll <direction> | Scroll the page (up/down/top/bottom) | pnpm test-driver scroll down |
screenshot [path] | Take a screenshot | pnpm test-driver screenshot test.png |
The snapshot command returns a simplified text DOM:
[e1] input type="text" placeholder="Ask anything..."
[t1] label "Name:"
[e2] button "Send"
[t2] span "Click me"
[t3] h1 "Good afternoon"
[eN] — interactive elements (input, button, select, textarea, a) with auto-assigned data-tid[tN] — visible text nodesdata-tid attributes (e.g., [data-tid='e3']) are assigned dynamically during each snapshot and must not be used in test files — they change between runs.
Navigate to home first, then snapshot to see the current page state.
pnpm test-driver click '[data-testid="nav-home"]'
pnpm test-driver snapshot
Tests always start from the home screen (useTestDriver() navigates home in beforeEach), so exploration should too.
For each element you need to interact with or verify:
[e3] button "Send")data-tidcount with that stable selector to verify it matches exactly one elementgetText to verify text contentclick/fill to perform actions during explorationAfter each action, run snapshot again — the DOM may have changed.
Example exploration session:
# 1. See what's on the page
pnpm test-driver snapshot
# 2. Pick a selector for the element you need, verify it matches exactly 1
pnpm test-driver count 'textarea[placeholder="Ask anything..."]'
# 3. Interact
pnpm test-driver fill 'textarea[placeholder="Ask anything..."]' "hello world"
# 4. Snapshot again to see the result
pnpm test-driver snapshot
Create a Vitest test file at tests/app-e2e/<name>.test.ts.
Use useTestDriver() from tests/app-e2e/lib/setup.ts to get a shared test driver connection with automatic teardown and screenshot-on-failure. See tests/app-e2e/chat.test.ts as a reference:
import { describe, it, expect } from "vitest";
import { useTestDriver } from "./lib/setup";
describe("<Feature>", () => {
const testDriver = useTestDriver();
it("does something", async () => {
const text = await testDriver.getText('[data-testid="greeting"]');
expect(text).toContain("Good");
});
});
Test driver API methods available in tests:
testDriver.snapshot() → text DOM stringtestDriver.getText(selector, { timeout? }) → inner text stringtestDriver.count(selector) → number of matching elementstestDriver.click(selector, { timeout? }) → clicks elementtestDriver.fill(selector, value, { timeout? }) → fills input/textareatestDriver.keypress(selector, key, { timeout? }) → dispatches keyboard eventtestDriver.waitForText(text, { selector?, timeout? }) → waits for text to appear (default: body, 30s)testDriver.scroll(direction) → scrolls page ("up", "down", "top", "bottom")testDriver.screenshot(path) → saves screenshotAll timeout options default to 5 seconds. waitForText defaults to 30 seconds.
Run the test to make sure it passes:
pnpm test:app-e2e
If it fails, use the test driver CLI to re-explore. The issue could be a wrong selector, an incorrect assertion, or a bug in the app implementation.
Always verify uniqueness with count before using any selector in a test. If count > 1, the test will be flaky.
For each element, find a stable selector using this priority:
data-testid (preferred): if the element already has a data-testid, use '[data-testid="my-element"]'.
count that it's uniqueSemantic selector: combine attributes, hierarchy, or roles to target a specific element.
'input[placeholder="Ask anything..."]' — narrow by attribute value'.sidebar [role="navigation"] a' — narrow by parent contextcount that the selector matches exactly 1 elementAdd a data-testid (last resort): if no stable selector exists, add a data-testid to the source code.
home-greeting not greeting, sidebar-new-chat-button not button)data-testid attribute — do not change any other source codeNever use data-tid attributes ([data-tid='e3']) in test files — they are assigned dynamically by snapshot and change between runs.
home.test.ts, sidebar.test.ts, settings.test.ts)count === 1 for selectors before using them in assertionssnapshot in test assertions — it's for exploration only{ timeout }) for elements to appear before click, fill, getText, and keypresswaitForText to wait for specific text content to appear (e.g., after submitting a form or waiting for an LLM response)snapshot again to see the new state before writing assertions