generate-tests
Generate Tests
Generate reusable Playwright .spec.ts test files from your app-navigator map and playbooks.
Core principle: Mapped knowledge should produce executable tests. This skill reads what app-navigator discovered and writes a test suite that lives in your repo and runs with npx playwright test.
When to Use
- After running
/app-navigator setupand you want persistent, rerunnable tests - When you need a baseline test suite for CI
- When trust-but-verify reveals gaps that should be codified as assertions
- When recommended by app-navigator or trust-but-verify
Not for: Backend/API tests, replacing hand-written integration tests, or one-shot verification (use trust-but-verify for that).
Invocation
/generate-tests— generates smoke-depth tests (default)/generate-tests --depth functional— smoke + interaction tests/generate-tests --depth full— functional + edge cases + responsive
Process
digraph generate_tests {
"Phase 1:\nPrerequisites" [shape=box];
"Phase 2:\nGenerate Auth" [shape=box];
"Phase 3:\nGenerate Page Tests" [shape=box];
"Phase 4:\nValidate (optional)" [shape=box];
"Done" [shape=doublecircle];
"Phase 1:\nPrerequisites" -> "Phase 2:\nGenerate Auth";
"Phase 2:\nGenerate Auth" -> "Phase 3:\nGenerate Page Tests";
"Phase 3:\nGenerate Page Tests" -> "Phase 4:\nValidate (optional)";
"Phase 4:\nValidate (optional)" -> "Done";
}
Phase 1: Prerequisites
-
App map: Check
~/.claude/skills/app-navigator/app-map.mdexists- If no: "Run
/app-navigator setupfirst — I need the app map to generate tests." Offer to invoke it.
- If no: "Run
-
Playwright installed: Check if
@playwright/testis in rootpackage.jsondevDependencies- If no: run
pnpm add -D -w @playwright/testandnpx playwright install chromium
- If no: run
-
Config: Check if
playwright.config.tsexists in the project root- If no: scaffold one with this template:
import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';
dotenv.config({ path: '.env.test.local' });
export default defineConfig({
testDir: './tests/e2e',
timeout: 30_000,
expect: { timeout: 10_000 },
fullyParallel: true,
retries: 1,
use: {
baseURL: process.env.BB_TEST_BASE_URL || 'http://localhost:5173',
trace: 'on-first-retry',
},
projects: [
{ name: 'setup', testMatch: /auth\.setup\.ts/, teardown: undefined },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'tests/e2e/.auth/user.json',
},
dependencies: ['setup'],
},
],
});
-
Directories: Create
tests/e2e/andtests/e2e/.auth/if they don't exist -
Gitignore: Add
tests/e2e/.auth/and.env.test.localto.gitignoreif not present
Phase 2: Generate Auth Setup
Read:
~/.claude/skills/app-navigator/playbooks/auth.mdfor the login flow~/.claude/projects/<project>/memory/reference_local_auth.mdfor credentials
Create .env.test.local (gitignored) from memory credentials:
BB_TEST_EMAIL=<email from memory>
BB_TEST_PASSWORD=<password from memory>
BB_TEST_BASE_URL=<app URL from memory, default http://localhost:5173>
Generate tests/e2e/auth.setup.ts:
- Read
BB_TEST_EMAILandBB_TEST_PASSWORDfromprocess.env - Navigate to the login URL (e.g.,
http://localhost:5173/login) - Follow the SSO/OAuth redirect to the auth domain
- Fill the email and password fields on the login form
- Click the submit/login button
- Wait for redirect through the OAuth callback path (e.g.,
/auth/callback) back to the app domain - If an org selector appears: select the first org or one matching
BB_TEST_ORGenv var - Save
storageStatetotests/e2e/.auth/user.json
The auth setup captures cookies from all domains visited (app + auth), which preserves the auth session across tests.
Important: The exact login flow (field names, redirect chain, org selector) comes from playbooks/auth.md. Read it carefully — don't assume a generic login form.
Phase 3: Generate Page Tests
Read:
~/.claude/skills/app-navigator/app-map.md— routes, key elements, interactions~/.claude/skills/app-navigator/playbooks/navigation.md— how to navigate~/.claude/skills/app-navigator/playbooks/interactions.md— common UI patterns
For each route in the app map, generate tests/e2e/<page-slug>.spec.ts.
Every generated file starts with:
// AUTO-GENERATED by generate-tests skill from app-navigator map
// Regenerate with: /generate-tests --depth <level>
// Last generated: YYYY-MM-DD
Depth: smoke (default)
test.describe('Page Name', () => {
test('page loads', async ({ page }) => {
await page.goto('/route');
await expect(page.locator('text=Expected Heading')).toBeVisible();
});
test('key elements present', async ({ page }) => {
await page.goto('/route');
// Assert 2-3 key elements from app-map "Key Elements" field
await expect(page.getByRole('button', { name: 'Create' })).toBeVisible();
});
});
Depth: functional — adds interaction tests:
test('create button opens dropdown', async ({ page }) => {
await page.goto('/route');
await page.getByRole('button', { name: 'Create' }).click();
await expect(page.getByText('Create Document')).toBeVisible();
});
Depth: full — adds edge cases + responsive:
test('responsive: mobile layout', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 });
await page.goto('/route');
await expect(page.locator('text=Expected Heading')).toBeVisible();
});
Test conventions:
- Tests grouped by depth with comments:
// --- SMOKE ---,// --- FUNCTIONAL ---,// --- FULL --- - All tests reuse
storageStatefrom the setup project — no re-login - Selectors use
getByRole,getByText,getByLabel— resilient to DOM changes - Each test is independent — no ordering dependencies
- Routes sharing a component (e.g., tracked items) are grouped in one file
- Settings sub-pages are included in
settings.spec.ts
Phase 4: Validate (optional)
After generating, offer:
"Tests generated. Want me to run them to make sure they pass?"
If yes:
- Check dev server is reachable
- Run
npx playwright test - If tests fail: read the error, fix the test (adjust selector/wait/assertion), re-run. Max 3 fix iterations per test, then skip with a note.
- Report: X/Y tests passing, Z skipped with reasons
If no: files are ready. Do NOT auto-commit — let the user decide.
Regeneration
Running /generate-tests again overwrites all files with the AUTO-GENERATED header. Hand-written test files without the header are NOT touched.
Red Flags
- Never hardcode credentials in test files. Always use env vars.
- Never commit
.env.test.local— it contains real credentials. - Never auto-commit generated tests. Let the user review and commit.
- Don't generate tests without an app-map. The map is the source of truth.
- Don't invent selectors. Every element name and assertion comes from the app-map.
After Generation
"Tests generated at
tests/e2e/. Run them withnpx playwright test.Want to verify your current feature branch against its plan? Run
/trust-but-verify."
If the user says yes, invoke the trust-but-verify skill.
Integration
- Depends on: app-navigator (reads app-map.md and playbooks)
- Credential source:
reference_local_auth.mdin project memory →.env.test.local - Recommended by: app-navigator (after setup), trust-but-verify (if no tests exist)
- Recommends: trust-but-verify (after generation)
More from buildbetter-app/bb-skills
trust-but-verify
Use when a feature branch has been implemented and you need to verify the UI/UX and functionality match the original plan before merging
11bb-analyze
Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation.
11bb-review
Run a BuildBetter-first UX/usability and/or code review for the current feature.
10bb-plan
Execute the implementation planning workflow using the plan template to generate design artifacts.
9bb-tasks
Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
9bb-checklist
Generate a custom checklist for the current feature based on user requirements.
9