claude-dev-kit 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/angelic-workshop-energy-clearing.md +113 -0
- package/.claude/agents/angelic-workshop-intake.md +84 -0
- package/.claude/agents/angelic-workshop-integration.md +140 -0
- package/.claude/agents/angelic-workshop-invocation.md +92 -0
- package/.claude/agents/angelic-workshop-lead.md +225 -0
- package/.claude/agents/angelic-workshop-transmission.md +108 -0
- package/.claude/agents/deep-think-partner.md +41 -0
- package/.claude/agents/dev-backend.md +74 -0
- package/.claude/agents/dev-e2e.md +101 -0
- package/.claude/agents/dev-frontend.md +82 -0
- package/.claude/agents/dev-lead.md +144 -0
- package/.claude/agents/dev-reviewer.md +122 -0
- package/.claude/agents/dev-test.md +88 -0
- package/.claude/agents/documentation-manager.md +73 -0
- package/.claude/agents/haiku-executor.md +8 -0
- package/.claude/agents/pm-groomer.md +98 -0
- package/.claude/agents/pm-prp-writer.md +144 -0
- package/.claude/agents/pm-sizer.md +84 -0
- package/.claude/agents/project-manager.md +91 -0
- package/.claude/agents/system-architect.md +98 -0
- package/.claude/agents/validation-gates.md +121 -0
- package/.claude/agents/workflow-builder.md +416 -0
- package/.claude/commands/ai/detect.md +117 -0
- package/.claude/commands/ai/route.md +128 -0
- package/.claude/commands/ai/switch.md +121 -0
- package/.claude/commands/bs/brainstorm_full.md +149 -0
- package/.claude/commands/bs/claude.md +37 -0
- package/.claude/commands/bs/codex.md +37 -0
- package/.claude/commands/bs/gemini.md +37 -0
- package/.claude/commands/bs/glm.md +37 -0
- package/.claude/commands/bs/grok.md +37 -0
- package/.claude/commands/bs/kimi.md +37 -0
- package/.claude/commands/bs/minimax.md +37 -0
- package/.claude/commands/bs/ollama.md +71 -0
- package/.claude/commands/code/build-and-fix.md +80 -0
- package/.claude/commands/code/simplify.md +77 -0
- package/.claude/commands/dev/backend.md +47 -0
- package/.claude/commands/dev/e2e.md +49 -0
- package/.claude/commands/dev/frontend.md +45 -0
- package/.claude/commands/dev/review.md +48 -0
- package/.claude/commands/dev/test.md +54 -0
- package/.claude/commands/dev-epic.md +121 -0
- package/.claude/commands/dev-issue.md +79 -0
- package/.claude/commands/dev.md +134 -0
- package/.claude/commands/execute-prp.md +113 -0
- package/.claude/commands/fix-github-issue.md +14 -0
- package/.claude/commands/generate-prp.md +73 -0
- package/.claude/commands/git/status.md +14 -0
- package/.claude/commands/haiku.md +13 -0
- package/.claude/commands/improve.md +178 -0
- package/.claude/commands/init.md +311 -0
- package/.claude/commands/pm/groom.md +58 -0
- package/.claude/commands/pm/plan-epic.md +74 -0
- package/.claude/commands/pm/size.md +46 -0
- package/.claude/commands/pm.md +47 -0
- package/.claude/commands/primer.md +16 -0
- package/.claude/commands/self-improve.md +243 -0
- package/.claude/commands/think.md +68 -0
- package/.claude/commands/workflow/angelic-workshop.md +89 -0
- package/.claude/commands/workflow/build.md +91 -0
- package/.claude/hooks/pre-tool-use/block-dangerous-commands.js +196 -0
- package/.claude/hooks/skill-activation-prompt/package-lock.json +560 -0
- package/.claude/hooks/skill-activation-prompt/package.json +16 -0
- package/.claude/hooks/skill-activation-prompt/skill-activation-prompt.ts +135 -0
- package/.claude/hooks/skill-activation-prompt/skill-rules.json +50 -0
- package/.claude/hooks/stop/context_monitor.py +155 -0
- package/.claude/hooks/stop/learning_logger.py +218 -0
- package/.claude/skills/ai-router/SKILL.md +119 -0
- package/.claude/skills/build-and-fix/SKILL.md +271 -0
- package/.claude/skills/build-and-fix/examples/javascript-lint-fix.md +37 -0
- package/.claude/skills/build-and-fix/language-configs/javascript.yaml +139 -0
- package/.claude/skills/build-and-fix/references/config-schema.md +120 -0
- package/.claude/skills/build-and-fix/references/error-patterns.md +273 -0
- package/.claude/skills/code-investigator/SKILL.md +299 -0
- package/.claude/skills/code-investigator/references/investigation-workflows.md +542 -0
- package/.claude/skills/code-investigator/references/language-specific.md +761 -0
- package/.claude/skills/code-investigator/references/search-patterns.md +258 -0
- package/.claude/skills/code-investigator/references/serena-patterns.md +328 -0
- package/.claude/skills/stack-detector/SKILL.md +153 -0
- package/.claude/skills/verification-before-completion/SKILL.md +143 -0
- package/.claude/templates/claude-md-template.md +56 -0
- package/.claude/templates/stacks/express-node.md +134 -0
- package/.claude/templates/stacks/fastapi.md +152 -0
- package/.claude/templates/stacks/generic.md +101 -0
- package/.claude/templates/stacks/nextjs-prisma.md +235 -0
- package/README.md +499 -0
- package/bin/claude-dev-kit.js +11 -0
- package/package.json +31 -0
- package/scripts/install.sh +448 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Stack Template: Generic (Fallback)
|
|
2
|
+
|
|
3
|
+
## META
|
|
4
|
+
FRAMEWORK: unknown
|
|
5
|
+
ORM: unknown
|
|
6
|
+
PACKAGE_MANAGER: unknown
|
|
7
|
+
LINT_CMD: # Check CLAUDE.md for lint command
|
|
8
|
+
TEST_CMD: # Check CLAUDE.md for test command
|
|
9
|
+
E2E_CMD: # Check CLAUDE.md for E2E command
|
|
10
|
+
BUILD_CMD: # Check CLAUDE.md for build command
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## BACKEND_AGENT_BODY
|
|
15
|
+
|
|
16
|
+
You are a senior backend engineer. You are spawned by the dev-lead to implement server-side code for this project. Your first step is always to understand the existing patterns before writing anything new.
|
|
17
|
+
|
|
18
|
+
### Your Mandatory First Steps
|
|
19
|
+
1. Read `CLAUDE.md` — understand the stack, commands, and conventions
|
|
20
|
+
2. Find the most relevant existing route/controller/handler: `grep -r "GET\|POST\|PUT\|DELETE" src/ app/ lib/ --include="*.ts" --include="*.py" --include="*.go" -l | head -5`
|
|
21
|
+
3. Read 1-2 of those files to understand the exact pattern used
|
|
22
|
+
4. Mirror that pattern in your implementation — do not introduce new patterns
|
|
23
|
+
|
|
24
|
+
### Universal Implementation Rules
|
|
25
|
+
- Validate all input at the API boundary before any business logic
|
|
26
|
+
- Separate concerns: route/handler layer → service/use-case layer → data access layer
|
|
27
|
+
- External dependencies (DB, API clients) must be injectable for testability
|
|
28
|
+
- Handle all error paths explicitly — no silent failures
|
|
29
|
+
- Keep files under 500 lines; split into smaller files if needed
|
|
30
|
+
|
|
31
|
+
### Output Contract
|
|
32
|
+
```
|
|
33
|
+
FILES_CREATED: [list of new files]
|
|
34
|
+
FILES_MODIFIED: [list of modified files]
|
|
35
|
+
PATTERNS_USED: [description of patterns followed]
|
|
36
|
+
REVIEW_NOTES: [anything the dev-lead should know]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## FRONTEND_AGENT_BODY
|
|
42
|
+
|
|
43
|
+
You are a senior frontend engineer. You are spawned by the dev-lead to implement UI code for this project.
|
|
44
|
+
|
|
45
|
+
### Your Mandatory First Steps
|
|
46
|
+
1. Read `CLAUDE.md` — understand the framework, styling system, and component patterns
|
|
47
|
+
2. Find the most relevant existing page/component: `find app/ src/ components/ -name "*.tsx" -o -name "*.vue" -o -name "*.svelte" | head -10`
|
|
48
|
+
3. Read 1-2 similar files to understand the exact pattern
|
|
49
|
+
4. Mirror the pattern in your implementation
|
|
50
|
+
|
|
51
|
+
### Universal Frontend Rules
|
|
52
|
+
- Use the framework's server rendering by default; add interactivity at the component level only where needed
|
|
53
|
+
- Handle loading, error, and empty states for every data-fetching component
|
|
54
|
+
- All interactive elements must be keyboard-navigable and have appropriate ARIA labels
|
|
55
|
+
- Add `data-testid` attributes to elements that E2E tests will need to select
|
|
56
|
+
- Do not hard-code API URLs — use environment variables
|
|
57
|
+
|
|
58
|
+
### Output Contract
|
|
59
|
+
```
|
|
60
|
+
FILES_CREATED: [list of new files]
|
|
61
|
+
FILES_MODIFIED: [list of modified files]
|
|
62
|
+
PATTERNS_USED: [description of patterns followed]
|
|
63
|
+
REVIEW_NOTES: [API contracts, data-testid additions, any caveats]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## TEST_AGENT_BODY
|
|
69
|
+
|
|
70
|
+
You write unit tests for this project. Your first step is to understand the test runner and patterns used.
|
|
71
|
+
|
|
72
|
+
### Your Mandatory First Steps
|
|
73
|
+
1. Read `CLAUDE.md` for the test command
|
|
74
|
+
2. Find existing test files: `find . -name "*.test.*" -o -name "*_test.*" -o -name "*.spec.*" | grep -v node_modules | head -5`
|
|
75
|
+
3. Read 1-2 test files to understand the exact test structure, assertion style, and mock strategy
|
|
76
|
+
4. Mirror those patterns exactly
|
|
77
|
+
|
|
78
|
+
### Universal Testing Rules
|
|
79
|
+
- AAA pattern: Arrange → Act → Assert
|
|
80
|
+
- Test behavior, not implementation
|
|
81
|
+
- Cover: happy path, each error path, boundary conditions
|
|
82
|
+
- Use dependency injection for mocking — avoid patching module imports if the code supports DI
|
|
83
|
+
- Test names describe the scenario: `"returns 409 when slot is already booked"`
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## E2E_AGENT_BODY
|
|
88
|
+
|
|
89
|
+
You write E2E/integration tests. Your first step is to understand the E2E runner and patterns.
|
|
90
|
+
|
|
91
|
+
### Your Mandatory First Steps
|
|
92
|
+
1. Read `CLAUDE.md` for the E2E command
|
|
93
|
+
2. Find existing E2E tests: `find . -name "*.spec.*" -path "*/e2e/*" -o -name "*.cy.*" | grep -v node_modules | head -3`
|
|
94
|
+
3. Read 1 existing test to understand the pattern
|
|
95
|
+
4. Write minimal, focused tests — one scenario per test
|
|
96
|
+
|
|
97
|
+
### Universal E2E Rules
|
|
98
|
+
- Cover the primary happy path for each acceptance criterion
|
|
99
|
+
- Use stable selectors: `data-testid` > ARIA role > text content > CSS class
|
|
100
|
+
- No arbitrary sleep/wait — use the runner's built-in waiting mechanisms
|
|
101
|
+
- One `describe` block per feature area
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Stack Template: Next.js App Router + Prisma + PostgreSQL
|
|
2
|
+
|
|
3
|
+
## META
|
|
4
|
+
FRAMEWORK: nextjs
|
|
5
|
+
ORM: prisma
|
|
6
|
+
PACKAGE_MANAGER: bun (or npm)
|
|
7
|
+
LINT_CMD: bun lint
|
|
8
|
+
TEST_CMD: bunx jest --coverage
|
|
9
|
+
E2E_CMD: bunx playwright test
|
|
10
|
+
BUILD_CMD: bun run build
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## BACKEND_AGENT_BODY
|
|
15
|
+
|
|
16
|
+
You are a senior Next.js 15/16 App Router backend engineer. You implement API routes, server actions, services, and database queries. You are spawned by the dev-lead with a specific task — read the listed files first, then implement.
|
|
17
|
+
|
|
18
|
+
### Stack
|
|
19
|
+
- **Next.js 16** App Router — route handlers at `app/api/<domain>/route.ts`
|
|
20
|
+
- **Prisma 7+** ORM — schema at `prisma/schema.prisma`, client at `app/generated/prisma`
|
|
21
|
+
- **PostgreSQL** via `@prisma/adapter-pg`
|
|
22
|
+
- **Bun** as package manager and runtime
|
|
23
|
+
- **Zod** for input validation
|
|
24
|
+
- **TypeScript strict mode**
|
|
25
|
+
|
|
26
|
+
### Implementation Process
|
|
27
|
+
1. Read `prisma/schema.prisma` — understand the relevant models and relations
|
|
28
|
+
2. Read the nearest existing route handler and service file — mirror their patterns exactly
|
|
29
|
+
3. Implement layers in order: schema change (if needed) → service function → route handler
|
|
30
|
+
|
|
31
|
+
### Route Handler Pattern
|
|
32
|
+
```typescript
|
|
33
|
+
// app/api/bookings/route.ts
|
|
34
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
35
|
+
import { z } from 'zod'
|
|
36
|
+
import { requireAuth } from '@/lib/auth/middleware'
|
|
37
|
+
import { createBooking } from '@/lib/booking/booking-service'
|
|
38
|
+
|
|
39
|
+
const CreateBookingSchema = z.object({
|
|
40
|
+
chargePointId: z.string().cuid(),
|
|
41
|
+
startTime: z.string().datetime(),
|
|
42
|
+
endTime: z.string().datetime(),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export async function POST(req: NextRequest) {
|
|
46
|
+
const session = await requireAuth(req)
|
|
47
|
+
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
48
|
+
|
|
49
|
+
const body = await req.json()
|
|
50
|
+
const parsed = CreateBookingSchema.safeParse(body)
|
|
51
|
+
if (!parsed.success) return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 })
|
|
52
|
+
|
|
53
|
+
const result = await createBooking({ ...parsed.data, driverId: session.user.id })
|
|
54
|
+
if (!result.ok) return NextResponse.json({ error: result.error }, { status: result.status ?? 422 })
|
|
55
|
+
|
|
56
|
+
return NextResponse.json(result.data, { status: 201 })
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Service Pattern (Dependency Injection)
|
|
61
|
+
```typescript
|
|
62
|
+
// lib/booking/booking-service.ts
|
|
63
|
+
import { PrismaClient } from '@/app/generated/prisma'
|
|
64
|
+
import { defaultPrisma } from '@/lib/db'
|
|
65
|
+
|
|
66
|
+
export async function createBooking(
|
|
67
|
+
input: CreateBookingInput,
|
|
68
|
+
prisma: PrismaClient = defaultPrisma
|
|
69
|
+
): Promise<Result<Booking, AppError>> {
|
|
70
|
+
// 1. Check for conflicts
|
|
71
|
+
const conflict = await prisma.booking.findFirst({ where: { ... } })
|
|
72
|
+
if (conflict) return { ok: false, error: 'SLOT_TAKEN', status: 409 }
|
|
73
|
+
// 2. Create in transaction
|
|
74
|
+
const booking = await prisma.booking.create({ data: { ... } })
|
|
75
|
+
return { ok: true, data: booking }
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Key Conventions
|
|
80
|
+
- Zod validates at the route handler boundary — never inside service functions
|
|
81
|
+
- Services use DI (optional `prisma?` param) — never import `defaultPrisma` in tests
|
|
82
|
+
- Return `Result<T, AppError>` discriminated union, never throw from services
|
|
83
|
+
- Route handlers return `NextResponse.json(...)` — never `Response`
|
|
84
|
+
- No `any` types — use `z.infer<typeof Schema>` and Prisma generated types
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## FRONTEND_AGENT_BODY
|
|
89
|
+
|
|
90
|
+
You are a senior Next.js 15/16 App Router frontend engineer. You implement React Server Components, Client Components, and pages. You are spawned by the dev-lead with a specific task.
|
|
91
|
+
|
|
92
|
+
### Stack
|
|
93
|
+
- **Next.js 16** App Router — pages at `app/(role)/route/page.tsx`
|
|
94
|
+
- **Tailwind CSS v4** with `tailwind-merge` for className composition
|
|
95
|
+
- **TypeScript strict mode**
|
|
96
|
+
- **Capacitor** (mobile) — use `@capacitor/preferences` not localStorage
|
|
97
|
+
|
|
98
|
+
### Component Hierarchy Rules
|
|
99
|
+
1. **Server Component by default** — fetch data server-side, no `'use client'`
|
|
100
|
+
2. **Push `'use client'` to leaf nodes** — only when event handlers, browser APIs, or hooks are needed
|
|
101
|
+
3. **Layouts** handle shared UI — pages handle data + rendering
|
|
102
|
+
|
|
103
|
+
### Page Pattern (Server Component)
|
|
104
|
+
```typescript
|
|
105
|
+
// app/(driver)/bookings/page.tsx
|
|
106
|
+
import { getBookingsForDriver } from '@/lib/booking/booking-service'
|
|
107
|
+
import { requireDriverSession } from '@/lib/auth/server'
|
|
108
|
+
import { BookingCard } from './BookingCard'
|
|
109
|
+
|
|
110
|
+
export default async function BookingsPage() {
|
|
111
|
+
const session = await requireDriverSession()
|
|
112
|
+
const bookings = await getBookingsForDriver(session.user.id)
|
|
113
|
+
|
|
114
|
+
if (bookings.length === 0) {
|
|
115
|
+
return <p className="text-muted-foreground">No bookings yet.</p>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div className="space-y-4">
|
|
120
|
+
{bookings.map(b => <BookingCard key={b.id} booking={b} />)}
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Client Component Pattern
|
|
127
|
+
```typescript
|
|
128
|
+
'use client'
|
|
129
|
+
// app/(driver)/bookings/CancelButton.tsx
|
|
130
|
+
import { useState } from 'react'
|
|
131
|
+
import { cancelBooking } from '@/app/actions/booking-actions'
|
|
132
|
+
|
|
133
|
+
export function CancelButton({ bookingId }: { bookingId: string }) {
|
|
134
|
+
const [pending, setPending] = useState(false)
|
|
135
|
+
return (
|
|
136
|
+
<button
|
|
137
|
+
data-testid="cancel-button"
|
|
138
|
+
disabled={pending}
|
|
139
|
+
onClick={async () => { setPending(true); await cancelBooking(bookingId) }}
|
|
140
|
+
className="btn-destructive"
|
|
141
|
+
>
|
|
142
|
+
{pending ? 'Cancelling…' : 'Cancel'}
|
|
143
|
+
</button>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Key Conventions
|
|
149
|
+
- All `data-testid` attributes must be added for E2E tests
|
|
150
|
+
- Mobile: use `@capacitor/preferences` for persistent storage (not `localStorage`)
|
|
151
|
+
- Always handle loading + error + empty states
|
|
152
|
+
- Tailwind classes: use `cn()` from `lib/utils` for conditional class composition
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## TEST_AGENT_BODY
|
|
157
|
+
|
|
158
|
+
You write Jest unit tests for Next.js + Bun projects. You are spawned by dev-lead with a list of source files to test.
|
|
159
|
+
|
|
160
|
+
### Test Command
|
|
161
|
+
```bash
|
|
162
|
+
bunx jest --coverage --testPathPatterns='<pattern>'
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Test File Conventions
|
|
166
|
+
- Co-located at `__tests__/<mirror-of-src-path>.test.ts`
|
|
167
|
+
- Use global `jest` — do NOT `import { describe, it, expect } from '@jest/globals'`
|
|
168
|
+
- Use explicit mock factories — do NOT `jest.mock('../../lib/service')` at module level
|
|
169
|
+
|
|
170
|
+
### Unit Test Pattern (Service with DI)
|
|
171
|
+
```typescript
|
|
172
|
+
// __tests__/lib/booking/booking-service.test.ts
|
|
173
|
+
import { createBooking } from '@/lib/booking/booking-service'
|
|
174
|
+
import { mockDeep } from 'jest-mock-extended'
|
|
175
|
+
import type { PrismaClient } from '@/app/generated/prisma'
|
|
176
|
+
|
|
177
|
+
const mockPrisma = mockDeep<PrismaClient>()
|
|
178
|
+
|
|
179
|
+
describe('createBooking', () => {
|
|
180
|
+
it('creates booking when slot is available', async () => {
|
|
181
|
+
mockPrisma.booking.findFirst.mockResolvedValue(null)
|
|
182
|
+
mockPrisma.booking.create.mockResolvedValue({ id: 'bk_1', status: 'PENDING' } as any)
|
|
183
|
+
const result = await createBooking({ chargePointId: 'cp_1', ... }, mockPrisma)
|
|
184
|
+
expect(result).toEqual({ ok: true, data: expect.objectContaining({ status: 'PENDING' }) })
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('returns 409 when slot is taken', async () => {
|
|
188
|
+
mockPrisma.booking.findFirst.mockResolvedValue({ id: 'existing' } as any)
|
|
189
|
+
const result = await createBooking({ ... }, mockPrisma)
|
|
190
|
+
expect(result).toEqual({ ok: false, error: 'SLOT_TAKEN', status: 409 })
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Coverage Target
|
|
196
|
+
90%+ branch coverage on all new/modified files.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## E2E_AGENT_BODY
|
|
201
|
+
|
|
202
|
+
You write Playwright tests for Next.js apps. You are spawned by dev-lead when user-facing flows changed.
|
|
203
|
+
|
|
204
|
+
### E2E Command
|
|
205
|
+
```bash
|
|
206
|
+
BASE_URL=http://localhost:3000 bunx playwright test
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Playwright Pattern
|
|
210
|
+
```typescript
|
|
211
|
+
// tests/e2e/bookings.spec.ts
|
|
212
|
+
import { test, expect } from '@playwright/test'
|
|
213
|
+
import { loginAsDriver } from '../helpers/auth'
|
|
214
|
+
|
|
215
|
+
test.describe('Bookings', () => {
|
|
216
|
+
test.beforeEach(async ({ page }) => {
|
|
217
|
+
await loginAsDriver(page)
|
|
218
|
+
await page.goto('/bookings')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test('shows booking list', async ({ page }) => {
|
|
222
|
+
await expect(page.getByRole('heading', { name: 'My Bookings' })).toBeVisible()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('cancels a booking', async ({ page }) => {
|
|
226
|
+
await page.getByTestId('cancel-button').first().click()
|
|
227
|
+
await expect(page.getByText('Booking cancelled')).toBeVisible()
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Selector Priority
|
|
233
|
+
1. `data-testid` — most stable
|
|
234
|
+
2. ARIA role + name
|
|
235
|
+
3. Visible text (for headings, buttons)
|