description: 'Generate UAT checklist and runnable E2E test stubs from a spec' when_to_use: "After /generate-tests, when UAT checklist and E2E stubs are needed from a spec." name: generate-uat version: 1.0.0
Dispatch
Phase A — Generate + preview (spawn → collect → present):
Spawn Agent tool: model: "sonnet", subagent_type: "general-purpose".
Prompt: Steps 1–7 from the skill below + spec path from $ARGUMENTS. Working dir: <cp-root>.
Collect the subagent's output (test count, categories, file paths, preview content). Present to user.
Ask: "Does this look correct? Should I save all 3 test files? (y/n)"
Phase B — Save files (only after user approves):
Spawn a second Agent tool: model: "sonnet", subagent_type: "general-purpose".
Prompt: Step 8 (save) from the skill below + the exact file content from Phase A output. Write all 3 files.
Report subagent output verbatim.
/generate-tests
Generate User Acceptance Test checklist and runnable E2E test files from a feature specification.
Announce at start: "I'm using the /generate-tests skill to extract testable criteria from the spec and create test files."
Usage
/generate-tests <path-to-spec> [--format full|minimal] [--output <path>]
Examples:
/generate-tests features/p61_events_complete_tech_spec.md/generate-tests features/p142_export_csv.md --format minimal
What This Skill Does
- Read the spec — Parse the entire specification file
- Extract success criteria — Find testable statements from these sections (in priority order):
- "Acceptance Criteria"
- "Success Criteria"
- "User Stories"
- "Requirements"
- Bullet points under "Goals" or "Objectives"
- Categorize tests — Group by feature area and test type
- Generate 3 types of test files:
- UAT checklist (
features/uat/p{N}.md) — Manual validation checklist - E2E test stubs (
e2e/p{N}-{slug}.spec.ts) — Runnable Playwright test file with TODO stubs - Smoke tests (
e2e/p{N}-smoke.spec.ts) — Fast regression tests (page loads, no errors)
- UAT checklist (
What It Generates
1. UAT Checklist (Manual Validation)
File: features/uat/p{N}.md
Purpose: Manual checklist for user to validate:
- Business outcomes achieved
- User experience feels right
- Edge cases handled
- Acceptance criteria met
Format: Given/When/Then scenarios with verification methods
Use case: User validates quality (not just functionality)
2. E2E Test File Stubs (Automated Testing)
File: e2e/p{N}-{feature-slug}.spec.ts
Purpose: Runnable Playwright test file with TODO stubs for implementation
Contains:
- One test per acceptance criterion
- Setup/teardown scaffolding
- Page navigation stubs
- Assertion stubs (to be filled by /dev agent)
- Test helpers for auth, data creation, cleanup
Example stub:
import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user';
test.describe('P142: Export CSV', () => {
let testUser: TestUser;
test.beforeEach(async () => {
testUser = await createTestUser({ name: 'CSV Test User' });
});
test.afterEach(async () => {
if (testUser?.user?.id) {
await deleteTestUser(testUser.user.id);
}
});
test('exports sifter responses as CSV', async ({ page }) => {
// TODO: Setup - create sifter with responses
// TODO: Navigate to results page
// TODO: Click export button
// TODO: Verify CSV file downloaded
// TODO: Verify CSV contains expected data
});
test('disables export when no responses', async ({ page }) => {
// TODO: Setup - create sifter with 0 responses
// TODO: Navigate to results page
// TODO: Verify export button disabled
// TODO: Verify tooltip shows "No responses to export"
});
});
3. Smoke Test File (Fast Regression Detection)
File: e2e/p{N}-smoke.spec.ts
Purpose: Fast smoke tests to catch basic breakage
Contains:
- Page loads test (no 404/500)
- No console errors test
- Critical elements present test
Example:
import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user';
test.describe('P142 Smoke Tests', () => {
let testUser: TestUser;
test.beforeEach(async () => {
testUser = await createTestUser({ name: 'Smoke Test User' });
});
test.afterEach(async () => {
if (testUser?.user?.id) {
await deleteTestUser(testUser.user.id);
}
});
test('results page loads without errors', async ({ page }) => {
// TODO: Setup - create sifter
// TODO: Navigate to results page
// TODO: Verify page loads (no 404/500)
// TODO: Verify no console errors
// TODO: Verify main heading present
});
});
Process
Step 1: Read the Spec
Read the entire spec file provided as argument.
If no file path provided, ask:
Which spec file should I generate a UAT for? (e.g.,
features/p70_new_feature.md)
Step 2: Find Success Criteria
Search for sections containing testable criteria. Look for these headers (case-insensitive):
Explicit sections (highest priority):
## Success Criteria## Acceptance Criteria## Requirements### Tests
User story format:
As a [user], I want [action] so that [outcome]
→ Convert to: "User can [action]"
Requirements tables:
| Requirement | Priority |
|-------------|----------|
| Events have title | Must |
→ Convert to: "Events have title"
Implicit in description (lowest priority):
The events page shows upcoming events with title, date, and location.
→ Extract: "Events show title", "Events show date", "Events show location"
Step 3: Categorize Tests
Assign each criterion to a category based on keywords:
| Keywords in criterion | Category | |----------------------|----------| | table, column, migration, SQL, RLS, database | Database | | page, render, display, show, UI, component, screen | UI | | login, auth, session, permission, role, authenticated | Auth | | API, endpoint, request, response, fetch | API | | form, input, validation, submit, field | Forms | | navigation, route, link, redirect, URL | Navigation | | error, fail, invalid, edge case | Error Handling | | test, unit, e2e, playwright | Testing |
If no keywords match, use "General".
Group tests by category, then number sequentially: UAT-{category}.{sequence}
Step 4: Convert to Given/When/Then
For each criterion, create a structured test:
### UAT-{N}.{M}: {Short description}
**Given:** {precondition/context}
**When:** {action (if applicable)}
**Then:** {expected outcome}
**Verify:** {how to verify - Playwright MCP, DB query, visual inspection, etc.}
Rules:
- If criterion is a state (e.g., "Events table exists"), omit "When"
- For UI tests, verification is typically "Playwright MCP screenshot"
- For database tests, verification is "Run query in Supabase dashboard" or "npm run build"
- For API tests, verification is "Playwright MCP network tab" or "curl command"
Step 5: Generate UAT Checklist Output
Use this template for features/uat/p{N}.md:
# {Feature Name} — Acceptance Tests
**Purpose:** Testable acceptance criteria for {feature} implementation.
**Usage:** Ralph Loop iterates until ALL tests pass (score 100%).
**Source:** {spec_path}
**Generated:** {date}
**Generated by:** /generate-uat
---
## Test Scoring
\`\`\`
Score = passed_tests / {total} (shown as X/{total} or N%)
Total tests: {total}
Pass threshold: {total}/{total} (100% — all tests must pass)
\`\`\`
---
## Pre-Checks (must pass before UAT)
\`\`\`bash
npm run lint # No errors
npm run build # Compiles successfully
npm test # All unit tests pass
\`\`\`
---
{For each category}
## Category {N}: {Category Name} ({count} tests)
{For each test in category}
### UAT-{N}.{M}: {Short description}
**Given:** {precondition}
**When:** {action}
**Then:** {expected outcome}
**Verify:** {verification method}
{End for each test}
---
{End for each category}
## Test Execution Log
| Test | Status | Notes |
|------|--------|-------|
| UAT-1.1 | ⬜ | |
| UAT-1.2 | ⬜ | |
| ... | ... | ... |
**Legend:** ⬜ Not tested | ✅ Pass | ❌ Fail | ⏭️ Skipped (blocked — add note)
---
## Success Criteria
Ralph Loop completes when:
1. All {total} UAT tests show ✅
2. `./scripts/pre-commit-checks.sh` passes
3. No console errors during Playwright verification
Output `<promise>{Feature ID} UAT COMPLETE</promise>` when done.
---
## Notes for Agent
- **Use Playwright MCP** for visual verification
- **Use Chrome DevTools MCP** if network issues
- **Commit after each category passes** — Progress is preserved
- **Update scorecard** after each test — This file is your state
Step 6: Generate E2E Test Stubs
File: e2e/p{N}-{feature-slug}.spec.ts
Process:
-
Extract feature slug from spec filename (e.g.,
p142_export_csv.md→export-csv) -
Extract acceptance criteria from spec
-
Create test structure:
import { test, expect } from '@playwright/test'; import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user'; test.describe('P{N}: {Feature Name}', () => { let testUser: TestUser; test.beforeEach(async () => { testUser = await createTestUser({ name: '{Feature} Test User' }); }); test.afterEach(async () => { if (testUser?.user?.id) { await deleteTestUser(testUser.user.id); } }); {For each acceptance criterion} test('{test name from acceptance criterion}', async ({ page }) => { // TODO: Setup - {describe setup needed} // TODO: Navigate to {page} // TODO: {action to perform} // TODO: Verify {expected outcome} }); }); -
Generate one test per acceptance criterion
-
Use descriptive TODO comments for /dev agent to implement
-
Include setup/teardown scaffolding (auth, data creation, cleanup)
Rules:
- Test names should be concise, lowercase, descriptive
- Use test helpers from
./helpers/test-userfor auth - Leave implementation as TODOs (filled by /dev)
- Include common imports (test, expect, helpers)
Step 7: Generate Smoke Tests
File: e2e/p{N}-smoke.spec.ts
Process:
-
Create basic smoke test structure:
import { test, expect } from '@playwright/test'; import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user'; test.describe('P{N} Smoke Tests', () => { let testUser: TestUser; test.beforeEach(async () => { testUser = await createTestUser({ name: 'Smoke Test User' }); }); test.afterEach(async () => { if (testUser?.user?.id) { await deleteTestUser(testUser.user.id); } }); test('{main page} loads without errors', async ({ page }) => { // TODO: Setup - {minimal setup needed} // TODO: Navigate to {main page of feature} // TODO: Verify page loads (no 404/500) // TODO: Verify no console errors // TODO: Verify main heading/content present }); }); -
Identify main page/route from spec (UX section, technical section, or acceptance criteria)
-
Create single smoke test for page load
-
Use TODOs for implementation details
Purpose: Fast test to catch basic breakage (page doesn't load, console errors, critical elements missing)
Step 8: Confirm and Save
Before saving, show the user:
- Number of UAT tests extracted: {N}
- Number of E2E test stubs: {M}
- Categories: {list}
- Output paths:
- UAT:
features/uat/p{N}.md - E2E:
e2e/p{N}-{slug}.spec.ts - Smoke:
e2e/p{N}-smoke.spec.ts
- UAT:
Ask: "Does this look correct? Should I save all 3 test files?"
If approved, save all 3 files (or custom paths if --output specified).
Options
| Flag | Default | Description |
|------|---------|-------------|
| --output <path> | features/uat/p{N}.md | Custom output path |
| --format full | full | Include Given/When/Then for each test |
| --format minimal | - | Scorecard table only (no detailed tests) |
Edge Cases
| Scenario | Behavior | |----------|----------| | Spec has no acceptance/success criteria section | Warning: "No explicit acceptance criteria found. Extracting from description. Review generated tests carefully." | | Spec is too vague to extract tests | Error: "Couldn't extract testable criteria. Add an 'Acceptance Criteria' section to the spec." | | UAT file already exists | Ask: "UAT file exists at {path}. Overwrite / Merge / Cancel?" | | E2E test file already exists | Ask: "E2E test file exists at {path}. Overwrite / Merge / Cancel?" | | Spec has 50+ criteria | Warning: "Large spec ({N} criteria). Consider breaking into smaller features." | | Category has no tests | Skip the category in UAT, don't create empty sections | | Can't determine main page for smoke test | Use generic page (e.g., homepage or main feature page), add TODO for /dev to specify | | Feature is backend-only (no UI) | Skip smoke test generation, only create E2E test stubs for API/database tests |
Example Input → Output
Input spec excerpt:
# P142: Export CSV
## Acceptance Criteria
- [ ] User can export sifter responses as CSV file
- [ ] Export button is disabled when sifter has 0 responses
- [ ] CSV includes all response data (timestamp, answers, respondent)
- [ ] File downloads with name format: `{sifter-title}-responses-{date}.csv`
Output 1: UAT Checklist (features/uat/p142.md):
# Export CSV — Acceptance Tests
## Category 1: UI (2 tests)
### UAT-1.1: Export button works
**Given:** Sifter has 5 responses
**When:** User navigates to results page and clicks "Export CSV"
**Then:** CSV file downloads with correct data
**Verify:** Playwright MCP click + download verification
### UAT-1.2: Export disabled when empty
**Given:** Sifter has 0 responses
**When:** User navigates to results page
**Then:** Export button is disabled
**Verify:** Playwright MCP screenshot
## Category 2: Data (2 tests)
### UAT-2.1: CSV includes all data
**Given:** Sifter has responses with answers
**When:** CSV is exported
**Then:** File contains timestamp, answers, respondent columns
**Verify:** Open CSV file, check headers
### UAT-2.2: Filename format correct
**Given:** Sifter titled "Remote Trust"
**When:** CSV exported on 2026-02-13
**Then:** Filename is `remote-trust-responses-2026-02-13.csv`
**Verify:** Check downloaded file name
Output 2: E2E Test Stubs (e2e/p142-export-csv.spec.ts):
import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user';
test.describe('P142: Export CSV', () => {
let testUser: TestUser;
test.beforeEach(async () => {
testUser = await createTestUser({ name: 'CSV Test User' });
});
test.afterEach(async () => {
if (testUser?.user?.id) {
await deleteTestUser(testUser.user.id);
}
});
test('exports sifter responses as CSV', async ({ page }) => {
// TODO: Setup - create sifter with responses
// TODO: Navigate to results page
// TODO: Click export button
// TODO: Verify CSV file downloaded
// TODO: Verify CSV contains expected data
});
test('disables export when no responses', async ({ page }) => {
// TODO: Setup - create sifter with 0 responses
// TODO: Navigate to results page
// TODO: Verify export button disabled
// TODO: Verify tooltip shows "No responses to export"
});
test('CSV includes all response data', async ({ page }) => {
// TODO: Setup - create sifter with multiple responses
// TODO: Export CSV
// TODO: Verify CSV has timestamp, answers, respondent columns
});
test('filename format is correct', async ({ page }) => {
// TODO: Setup - create sifter titled "Remote Trust"
// TODO: Export CSV
// TODO: Verify filename matches pattern: {slug}-responses-{date}.csv
});
});
Output 3: Smoke Tests (e2e/p142-smoke.spec.ts):
import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser, setTestSession, TestUser } from './helpers/test-user';
test.describe('P142 Smoke Tests', () => {
let testUser: TestUser;
test.beforeEach(async () => {
testUser = await createTestUser({ name: 'Smoke Test User' });
});
test.afterEach(async () => {
if (testUser?.user?.id) {
await deleteTestUser(testUser.user.id);
}
});
test('results page loads without errors', async ({ page }) => {
// TODO: Setup - create sifter
// TODO: Navigate to results page
// TODO: Verify page loads (no 404/500)
// TODO: Verify no console errors
// TODO: Verify main heading present
});
});
Related Skills
/create-spec→/ux→/architect→/generate-tests→/dev— New sequential flow (P143)/prep-spec— Legacy skill (deprecated, but still calls/generate-tests)/dev— Reads UAT + E2E stubs, implements tests, runs them, iterates until pass
Backward Compatibility
Skill name change: /generate-uat → /generate-tests
/generate-uatstill works (alias to/generate-tests)- Old behavior preserved: still generates
features/uat/p{N}.md - New behavior added: also generates E2E stubs + smoke tests
What's new in this version:
- Generates 3 files instead of 1
- E2E test stubs are runnable Playwright files (not manual checklists)
- Smoke tests for fast regression detection
- Integrates with
/devskill (reads stubs, implements, runs, iterates)
Migration path:
- Existing UAT files (features/uat/*.md) still valid
- New features after P143 get all 3 files
- Old features can be retro-fitted with E2E stubs if needed
Validation
This skill was validated against:
- P61 acceptance tests (UAT generation)
- P135, P131, P140 E2E tests (test stub format)
- P143 sequential flow requirements (test automation)
TDD Red-Green-Refactor
Testing
Skill qui guide Claude a travers le cycle TDD complet.
Audit d'Accessibilité Web
Testing
Réalise un audit d'accessibilité web complet selon les normes WCAG.
Générateur de Tests UAT
Testing
Génère des cas de test d'acceptation utilisateur structurés et complets.