E2E test author (Kata Agents)
Author local, macOS-first Playwright tests that launch the real Electron app.
Read e2e/README.md first.
Rules
- Real services only. No
route().fulfill(), MSW, or fake backends in specs. - Playwright owns Electron. The harness starts Vite only; never call
electron:devfrom a test or harness module. - id-based selectors for shell state. Use
#root,#onboarding-wizard,#app-ready,#workspace-picker. For in-product controls prefer stable roles/text (getByRole,getByText) or existingdata-tutorialhooks; add a new stableid/attribute to product code only as a deliberate, minimal contract. - Fail loud. Missing prerequisites (build artifacts, provider key, release
app path) must throw with the variable name and a pointer to
e2e/README.md. Never silently skip an assertion. - Keep layers separate.
tests → fixtures → harness,tests → flows,flows → harness. Never import flows from harness. The fixtures layer (e2e/src/fixtures/) is the composition root that wires flows into the launch pipeline; harness modules stay product-selector-free.
Where things go
| Need | File |
|---|---|
| New test | e2e/tests/<tier>/<name>.spec.ts, tagged @smoke / @settings / @browser / @agent |
| Shell wait | e2e/src/flows/shell.ts |
| Onboarding step | e2e/src/flows/onboarding.ts |
| Settings step | e2e/src/flows/settings.ts |
| Agent chat step | e2e/src/flows/agentChat.ts |
| Browser panel step | e2e/src/flows/browser.ts |
| Launch/process/isolation | e2e/src/harness/* |
| Playwright fixtures (composition root) | e2e/src/fixtures/testFixtures.ts |
| Launch-health assertion | e2e/src/assertions/appAssertions.ts |
Fixtures
appWindow— launched app,#rootmounted. Use for@smoke.authenticatedAppWindow— deferred-setup → ready shell. Use for@settingsand@browserpanel specs that do not need a provider.@browsercovers the integrated browser panel. Cookie import stays under@settingsbecause that UI lives in Settings.@agentdrives the configured real provider in-test. Browser annotation send uses the same provider fallback, but those specs stay under@browser.
Reaching app states
Boot is driven by getSetupNeeds():
- fresh temp config dir → onboarding (
#onboarding-wizard). - deferred setup (the wizard "Setup later" button) → ready (
#app-ready), possibly via workspace-picker (#workspace-picker).
Authoring an @agent test
- Drive the provider-specific onboarding flow. The default
openai-codexpath reuses the existingchatgpt-plusOAuth credential; explicitanthropicusescompleteApiKeyOnboarding(provider select → "I use other provider" → fill the "API Key" textbox → Continue → "Get Started"). startNewSessionto mount the composer ([data-tutorial="chat-input"]).selectModel— the onboarding default model can be stale and 404; pick a current registry model from the composer dropdown.- Send a unique deterministic prompt and assert the token appears at least twice (the prompt echo plus the real reply) to avoid a false pass on the echoed prompt.
Validate before codifying selectors
Run headed (bun run e2e:headed --grep <tag>) and read the failure
error-context.md page snapshot to confirm real labels/roles before hardcoding
them. Wizard copy and model lists change.
Verify
bun run e2e --list
bun run e2e --grep @smoke # offline
bun run e2e --grep @settings
bun run e2e --grep @browser
bun run e2e --grep @agent # uses ChatGPT OAuth by default; Anthropic needs a key