skills/ui-test-flow/SKILL.md
PRD and ad-hoc UI test execution flows. Use when running E2E tests, handling deferred tests, or managing E2E test execution. Triggers on: run e2e, e2e tests, deferred e2e, prd e2e, playwright tests.
npx skillsauth add mdmagnuson-creator/yo-go ui-test-flowInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Load this skill when: running E2E tests, handling PRD/ad-hoc mode E2E execution, or managing deferred E2E tests.
After each story completion, run the mandatory per-task quality checks (see test-flow skill — Skip Gate → Activity Resolution → Quality Check Pipeline).
After the mandatory checks pass, PRD mode handles E2E based on automatic activity resolution:
| E2E Resolution | Behavior |
|----------------|----------|
| immediate | Run E2E tests now (including scoped Playwright via postChangeWorkflow pipeline), before marking story complete |
| deferred | Queue E2E tests for PRD completion |
| skip | No E2E (docs, config, type definitions) |
ℹ️ Playwright Integration: For projects with Playwright in
postChangeWorkflow.steps[],apps.*.testing.framework, orapps.*.typeoffrontend/desktop, E2E resolves asimmediatefor ALL file types (not just auth/payment/API). This means components, hooks, pages, and styling changes trigger per-story Playwright verification instead of being deferred.testing.autoGenerateis NOT a gate — if the project has existing Playwright tests, they run regardless of the autoGenerate setting.
Story complete
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ RESOLVE ACTIVITIES (automatic) │
│ Based on files changed in this story │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ MANDATORY: Run resolved activities │
│ (baseline, unit, critics, E2E if immediate) │
└─────────────────────────────────────────────────────────────────────┘
│
├─── Any check fails ──► Fix loop ──► Still failing? STOP
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ If E2E = deferred: Queue for PRD completion │
└─────────────────────────────────────────────────────────────────────┘
│
▼
Next story (or PRD completion)
After ALL stories complete:
deferredTo flag, mark as passedAfter each ad-hoc task completes, run the mandatory per-task quality checks.
Task complete
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ MANDATORY: Per-Task Quality Checks │
│ (typecheck, lint, unit tests, critic) │
└─────────────────────────────────────────────────────────────────────┘
│
├─── Any check fails ──► Fix loop ──► Still failing? STOP
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Show completion prompt │
│ [C] Commit [N] Next task │
└─────────────────────────────────────────────────────────────────────┘
After generating E2E tests in ad-hoc mode during PRD:
📝 E2E tests generated:
• e2e/[test-name].spec.ts
Options:
[R] Run E2E tests now (then return to PRD)
[D] Defer to PRD completion (run with PRD's E2E tests)
[S] Save for later (queue without deferring)
Before running any E2E tests, determine if this project uses Electron or browser-based testing:
# Check for Electron-only architecture
DEPLOYMENT=$(jq -r '.architecture.deployment // empty' docs/project.json)
ELECTRON_APP=$(jq -r '.apps | to_entries[] | select(.value.framework == "electron") | .key' docs/project.json 2>/dev/null)
ELECTRON_TESTING=$(jq -r '.apps | to_entries[] | select(.value.testing.framework == "playwright-electron") | .value.testing' docs/project.json 2>/dev/null)
if [ "$DEPLOYMENT" = "electron-only" ] || [ -n "$ELECTRON_APP" ]; then
echo "ELECTRON_MODE=true"
# Extract Electron-specific config
TEST_DIR=$(jq -r '.apps | to_entries[] | select(.value.framework == "electron") | .value.testing.testDir // "e2e/desktop"' docs/project.json)
EXEC_PATH=$(jq -r '.apps | to_entries[] | select(.value.framework == "electron") | .value.testing.executablePath.macos // empty' docs/project.json)
# Look for Electron Playwright config
ELECTRON_CONFIG=""
for cfg in playwright.electron.config.ts e2e/playwright.electron.config.ts e2e/desktop/playwright.config.ts; do
if [ -f "$cfg" ]; then
ELECTRON_CONFIG="$cfg"
break
fi
done
else
echo "ELECTRON_MODE=false"
fi
If ELECTRON_MODE=true:
ui-test-electron skill for test writing patternsIf ELECTRON_MODE=false:
Before running any Playwright tests:
# Resolution priority:
# 1. project.json → agents.verification.testBaseUrl (explicit override)
# 2. Preview URL env vars (Vercel, Netlify, Railway, Render, Fly.io)
# 3. project.json → environments.staging.url
# 4. http://localhost:{devPort} (from projects.json)
TEST_BASE_URL=$(jq -r '.agents.verification.testBaseUrl // empty' docs/project.json)
if [ -z "$TEST_BASE_URL" ]; then
if [ -n "$VERCEL_URL" ]; then
TEST_BASE_URL="https://${VERCEL_URL}"
elif [ -n "$DEPLOY_URL" ]; then
TEST_BASE_URL="$DEPLOY_URL"
elif [ -n "$RAILWAY_PUBLIC_DOMAIN" ]; then
TEST_BASE_URL="https://${RAILWAY_PUBLIC_DOMAIN}"
elif [ -n "$RENDER_EXTERNAL_URL" ]; then
TEST_BASE_URL="$RENDER_EXTERNAL_URL"
elif [ -n "$FLY_APP_NAME" ]; then
TEST_BASE_URL="https://${FLY_APP_NAME}.fly.dev"
fi
fi
if [ -z "$TEST_BASE_URL" ]; then
TEST_BASE_URL=$(jq -r '.environments.staging.url // empty' docs/project.json)
fi
if [ -z "$TEST_BASE_URL" ]; then
DEV_PORT=$(jq -r '.projects[] | select(.path == "'$(pwd)'") | .devPort' ~/.config/opencode/projects.json)
if [ -n "$DEV_PORT" ] && [ "$DEV_PORT" != "null" ]; then
TEST_BASE_URL="http://localhost:${DEV_PORT}"
fi
fi
if [ -z "$TEST_BASE_URL" ]; then
echo "⏭️ E2E skipped: No test URL available"
exit 0
fi
export TEST_BASE_URL
if [[ "$TEST_BASE_URL" == http://localhost:* ]]; then
~/.config/opencode/scripts/check-dev-server.sh --project-path "$(pwd)"
else
if ! curl -sf --max-time 10 "$TEST_BASE_URL" > /dev/null 2>&1; then
echo "❌ Remote test URL not reachable: $TEST_BASE_URL"
exit 1
fi
fi
export TEST_BASE_URL
npx playwright test --reporter=list [list of test files]
This step replaces Steps 1-3 when ELECTRON_MODE=true.
# Use Electron config if found, otherwise default
if [ -n "$ELECTRON_CONFIG" ]; then
npx playwright test --config="$ELECTRON_CONFIG" --reporter=list [list of test files]
else
# Fallback: run from Electron test directory
npx playwright test --reporter=list "$TEST_DIR"/**/*.spec.ts
fi
Key differences from browser mode:
TEST_BASE_URL needed — Electron app launches directly via _electron.launch()--workers=1) — Electron tests cannot parallelizeui-test-electron skill)If executablePath is configured in project.json:
# Pass to tests via environment variable
export ELECTRON_EXECUTABLE_PATH="$EXEC_PATH"
npx playwright test --config="$ELECTRON_CONFIG" --workers=1 --reporter=list
⚠️ Electron projects: Do NOT use the browser config below. Electron tests use
_electron.launch()instead ofbaseURL. See theui-test-electronskill for the correct Playwright config pattern.
⚠️ Do NOT use Playwright's
webServerconfig option.Playwright's default
webServerbehavior kills the dev server when tests complete.
Correct pattern:
import { defineConfig, devices } from '@playwright/test';
const TEST_BASE_URL = process.env.TEST_BASE_URL || `http://localhost:${process.env.DEV_PORT || '3000'}`;
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
reporter: 'list',
use: {
baseURL: TEST_BASE_URL,
trace: 'on-first-retry',
},
// NO webServer config — dev server is managed externally
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
],
});
The @ui-test-full-app-auditor agent provides proactive full-app E2E auditing.
| Scenario | Use @ui-test-full-app-auditor | |----------|------------------| | Full regression testing before release | ✅ | | Periodic coverage audits | ✅ | | After large refactors | ✅ | | Testing a specific story change | ❌ Use @ui-tester-playwright |
| Aspect | Story-Driven | Audit Mode | |--------|--------------|------------| | Trigger | Code change | User request | | Scope | Changed files only | Entire application | | Retries | 3 attempts | 5 attempts | | On failure | Stop, report | Log, continue | | Commits | Batch at end | After each passing test |
Run @ui-test-full-app-auditor with:
project: {project path}
mode: full-audit | resume | prd-driven
prd: {prd path, if prd-driven mode}
When generating tests:
{
"pendingTests": {
"unit": {
"generated": ["src/__tests__/Component.test.tsx"],
"status": "pending"
},
"e2e": {
"generated": ["e2e/feature.spec.ts"],
"status": "pending",
"deferredTo": "prd-completion"
}
}
}
When running tests:
{
"pendingTests": {
"unit": {
"generated": ["src/__tests__/Component.test.tsx"],
"status": "passed",
"lastRunAt": "ISO8601",
"failureCount": 0
}
}
}
data-ai
Generate verification contracts before delegating tasks to sub-agents, defining how success will be measured. Triggers on: verification contract, delegation contract, task verification, contract-first delegation.
testing
Verify that Vercel environment variables point to the correct Supabase project for each environment to prevent staging/production cross-wiring. Triggers on: vercel supabase check, environment alignment, env var check, supabase environment.
development
Manage codebase and database vectorization for semantic search. Use when initializing, refreshing, or querying the vector index. Triggers on: vectorize init, vectorize refresh, vectorize search, semantic search, vector index, enable vectorization.
testing
Patterns for XCUITest UI tests for native Apple apps (macOS/iOS). Use when writing or reviewing XCUITest tests for Swift apps. Triggers on: XCUITest, xcuitest, native app testing, Apple UI tests, SwiftUI tests, AppKit tests, UIKit tests.