create-shhs 1.0.0 → 1.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/INSTALLATION-GUIDE.md +424 -0
- package/README.md +16 -8
- package/UPDATE-GUIDE.md +500 -0
- package/bin/install.js +20 -0
- package/bin/update.js +396 -0
- package/package.json +8 -6
- package/template/.ai/.shhs-version +1 -0
- package/template/.ai/agents/architecture-reviewer.md +92 -0
- package/template/.ai/agents/fitness-enforcer.md +408 -0
- package/template/.ai/agents/knowledge-curator.md +242 -0
- package/template/.ai/architecture/governance/.gitkeep +7 -0
- package/template/.ai/architecture/governance/fitness/README.md +410 -0
- package/template/.ai/architecture/governance/fitness/config.json +48 -0
- package/template/.ai/architecture/governance/fitness/exemptions.json +20 -0
- package/template/.ai/architecture/governance/fitness/rules.json +102 -0
- package/template/.ai/governance/constitution.md +260 -0
- package/template/.ai/governance/definition-of-done.md +481 -0
- package/template/.ai/knowledge/knowledge-base.md +210 -0
- package/template/.ai/reports/fitness-results.json +201 -0
- package/template/.ai/skills/context7/skill.md +458 -0
- package/template/.ai/skills/playwright/skill.md +565 -0
- package/template/.ai/skills/tdd/skill.md +416 -0
- package/template/CLAUDE.md +51 -13
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
# Skill: Playwright End-to-End Testing
|
|
2
|
+
|
|
3
|
+
**ID:** `playwright`
|
|
4
|
+
**Version:** 1.0.0
|
|
5
|
+
**Status:** MANDATORY for critical paths
|
|
6
|
+
**Authority:** Constitution Article III
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## When to Apply
|
|
11
|
+
|
|
12
|
+
This skill is MANDATORY for:
|
|
13
|
+
|
|
14
|
+
- Authentication flows
|
|
15
|
+
- Payment/transaction flows
|
|
16
|
+
- Data mutation operations
|
|
17
|
+
- Primary user journeys (signup → activation → core action)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What Is E2E Testing?
|
|
22
|
+
|
|
23
|
+
End-to-End testing validates the **entire user flow** from UI to database and back.
|
|
24
|
+
|
|
25
|
+
Unlike unit tests (which test functions in isolation), E2E tests verify:
|
|
26
|
+
- UI renders correctly
|
|
27
|
+
- User interactions work (clicks, typing, navigation)
|
|
28
|
+
- Backend processes data correctly
|
|
29
|
+
- Database persists changes
|
|
30
|
+
- Edge cases and error states
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Setup
|
|
35
|
+
|
|
36
|
+
### Install Playwright
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -D @playwright/test
|
|
40
|
+
npx playwright install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Directory Structure
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
tests/
|
|
47
|
+
├── e2e/
|
|
48
|
+
│ ├── auth/
|
|
49
|
+
│ │ ├── login.spec.ts
|
|
50
|
+
│ │ └── signup.spec.ts
|
|
51
|
+
│ ├── billing/
|
|
52
|
+
│ │ ├── checkout.spec.ts
|
|
53
|
+
│ │ └── subscription.spec.ts
|
|
54
|
+
│ └── fixtures/
|
|
55
|
+
│ ├── test-users.json
|
|
56
|
+
│ └── test-data.ts
|
|
57
|
+
└── playwright.config.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Configuration
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// playwright.config.ts
|
|
64
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
65
|
+
|
|
66
|
+
export default defineConfig({
|
|
67
|
+
testDir: './tests/e2e',
|
|
68
|
+
fullyParallel: true,
|
|
69
|
+
forbidOnly: !!process.env.CI,
|
|
70
|
+
retries: process.env.CI ? 2 : 0,
|
|
71
|
+
workers: process.env.CI ? 1 : undefined,
|
|
72
|
+
reporter: 'html',
|
|
73
|
+
use: {
|
|
74
|
+
baseURL: 'http://localhost:3000',
|
|
75
|
+
trace: 'on-first-retry',
|
|
76
|
+
},
|
|
77
|
+
projects: [
|
|
78
|
+
{
|
|
79
|
+
name: 'chromium',
|
|
80
|
+
use: { ...devices['Desktop Chrome'] },
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
webServer: {
|
|
84
|
+
command: 'npm run dev',
|
|
85
|
+
url: 'http://localhost:3000',
|
|
86
|
+
reuseExistingServer: !process.env.CI,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Writing E2E Tests
|
|
94
|
+
|
|
95
|
+
### Anatomy of a Playwright Test
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { test, expect } from '@playwright/test';
|
|
99
|
+
|
|
100
|
+
test.describe('User Authentication', () => {
|
|
101
|
+
|
|
102
|
+
test('should log in with valid credentials', async ({ page }) => {
|
|
103
|
+
// ARRANGE: Navigate to login page
|
|
104
|
+
await page.goto('/login');
|
|
105
|
+
|
|
106
|
+
// ACT: Fill in form and submit
|
|
107
|
+
await page.fill('input[name="email"]', 'user@example.com');
|
|
108
|
+
await page.fill('input[name="password"]', 'SecurePass123');
|
|
109
|
+
await page.click('button[type="submit"]');
|
|
110
|
+
|
|
111
|
+
// ASSERT: Verify successful login
|
|
112
|
+
await expect(page).toHaveURL('/dashboard');
|
|
113
|
+
await expect(page.locator('h1')).toContainText('Welcome');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Arrange-Act-Assert Pattern
|
|
120
|
+
|
|
121
|
+
| Phase | Purpose | Example |
|
|
122
|
+
|-------|---------|---------|
|
|
123
|
+
| **Arrange** | Set up initial state | Navigate to page, seed database |
|
|
124
|
+
| **Act** | Perform user action | Click button, fill form, submit |
|
|
125
|
+
| **Assert** | Verify outcome | Check URL, verify text, validate data |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Best Practices
|
|
130
|
+
|
|
131
|
+
### 1. Use Data-Testid for Stable Selectors
|
|
132
|
+
|
|
133
|
+
❌ **Fragile:**
|
|
134
|
+
```typescript
|
|
135
|
+
await page.click('button:nth-child(3)'); // Breaks if button order changes
|
|
136
|
+
await page.click('.btn-primary'); // Breaks if CSS class changes
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
✅ **Robust:**
|
|
140
|
+
```typescript
|
|
141
|
+
await page.click('[data-testid="submit-button"]');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Implementation:**
|
|
145
|
+
```html
|
|
146
|
+
<button data-testid="submit-button" class="btn-primary">
|
|
147
|
+
Submit
|
|
148
|
+
</button>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 2. Test User Flows, Not Implementation
|
|
152
|
+
|
|
153
|
+
❌ **Wrong:**
|
|
154
|
+
```typescript
|
|
155
|
+
test('should call POST /api/users with correct payload', async ({ page }) => {
|
|
156
|
+
// Testing API implementation, not user experience
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
✅ **Correct:**
|
|
161
|
+
```typescript
|
|
162
|
+
test('should display success message after user signup', async ({ page }) => {
|
|
163
|
+
await page.goto('/signup');
|
|
164
|
+
await page.fill('[data-testid="email"]', 'new@example.com');
|
|
165
|
+
await page.fill('[data-testid="password"]', 'Password123');
|
|
166
|
+
await page.click('[data-testid="signup-button"]');
|
|
167
|
+
|
|
168
|
+
await expect(page.locator('[data-testid="success-message"]'))
|
|
169
|
+
.toContainText('Account created successfully');
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 3. Use Fixtures for Reusable Setup
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// tests/e2e/fixtures/auth.fixture.ts
|
|
177
|
+
import { test as base } from '@playwright/test';
|
|
178
|
+
|
|
179
|
+
type AuthFixtures = {
|
|
180
|
+
authenticatedPage: Page;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const test = base.extend<AuthFixtures>({
|
|
184
|
+
authenticatedPage: async ({ page }, use) => {
|
|
185
|
+
// Log in before each test
|
|
186
|
+
await page.goto('/login');
|
|
187
|
+
await page.fill('[data-testid="email"]', 'test@example.com');
|
|
188
|
+
await page.fill('[data-testid="password"]', 'password');
|
|
189
|
+
await page.click('[data-testid="login-button"]');
|
|
190
|
+
await page.waitForURL('/dashboard');
|
|
191
|
+
|
|
192
|
+
await use(page);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Usage:**
|
|
198
|
+
```typescript
|
|
199
|
+
import { test } from './fixtures/auth.fixture';
|
|
200
|
+
|
|
201
|
+
test('should access protected dashboard', async ({ authenticatedPage }) => {
|
|
202
|
+
await expect(authenticatedPage).toHaveURL('/dashboard');
|
|
203
|
+
// No need to log in manually - fixture handles it
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 4. Test Edge Cases and Error States
|
|
208
|
+
|
|
209
|
+
Don't just test happy paths.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
test.describe('Login Error Handling', () => {
|
|
213
|
+
|
|
214
|
+
test('should show error for invalid credentials', async ({ page }) => {
|
|
215
|
+
await page.goto('/login');
|
|
216
|
+
await page.fill('[data-testid="email"]', 'wrong@example.com');
|
|
217
|
+
await page.fill('[data-testid="password"]', 'wrongpassword');
|
|
218
|
+
await page.click('[data-testid="login-button"]');
|
|
219
|
+
|
|
220
|
+
await expect(page.locator('[data-testid="error-message"]'))
|
|
221
|
+
.toContainText('Invalid email or password');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('should disable submit while request is pending', async ({ page }) => {
|
|
225
|
+
await page.goto('/login');
|
|
226
|
+
await page.fill('[data-testid="email"]', 'user@example.com');
|
|
227
|
+
await page.fill('[data-testid="password"]', 'password');
|
|
228
|
+
|
|
229
|
+
const submitButton = page.locator('[data-testid="login-button"]');
|
|
230
|
+
await submitButton.click();
|
|
231
|
+
|
|
232
|
+
// Button should be disabled during request
|
|
233
|
+
await expect(submitButton).toBeDisabled();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 5. Ensure Idempotency
|
|
240
|
+
|
|
241
|
+
Tests must be repeatable without manual cleanup.
|
|
242
|
+
|
|
243
|
+
❌ **Non-idempotent:**
|
|
244
|
+
```typescript
|
|
245
|
+
test('should create new user', async ({ page }) => {
|
|
246
|
+
await page.goto('/signup');
|
|
247
|
+
await page.fill('[data-testid="email"]', 'test@example.com'); // Fails on second run!
|
|
248
|
+
// ...
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
✅ **Idempotent:**
|
|
253
|
+
```typescript
|
|
254
|
+
import { randomUUID } from 'crypto';
|
|
255
|
+
|
|
256
|
+
test('should create new user', async ({ page }) => {
|
|
257
|
+
const uniqueEmail = `test-${randomUUID()}@example.com`;
|
|
258
|
+
await page.goto('/signup');
|
|
259
|
+
await page.fill('[data-testid="email"]', uniqueEmail);
|
|
260
|
+
// ...
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Or use database cleanup:**
|
|
265
|
+
```typescript
|
|
266
|
+
test.beforeEach(async () => {
|
|
267
|
+
// Clean up test data before each test
|
|
268
|
+
await database.query('DELETE FROM users WHERE email LIKE "test-%"');
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Critical Flow Examples
|
|
275
|
+
|
|
276
|
+
### Example 1: Authentication Flow
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { test, expect } from '@playwright/test';
|
|
280
|
+
|
|
281
|
+
test.describe('Complete Authentication Flow', () => {
|
|
282
|
+
|
|
283
|
+
test('should sign up, verify email, and log in', async ({ page, context }) => {
|
|
284
|
+
const email = `user-${Date.now()}@example.com`;
|
|
285
|
+
|
|
286
|
+
// Step 1: Sign up
|
|
287
|
+
await page.goto('/signup');
|
|
288
|
+
await page.fill('[data-testid="email"]', email);
|
|
289
|
+
await page.fill('[data-testid="password"]', 'SecurePass123');
|
|
290
|
+
await page.click('[data-testid="signup-button"]');
|
|
291
|
+
|
|
292
|
+
// Step 2: Verify redirect to email verification page
|
|
293
|
+
await expect(page).toHaveURL('/verify-email');
|
|
294
|
+
await expect(page.locator('[data-testid="verification-message"]'))
|
|
295
|
+
.toContainText('Check your email');
|
|
296
|
+
|
|
297
|
+
// Step 3: Simulate email verification (use test API or direct DB update)
|
|
298
|
+
await page.goto(`/api/test/verify-email?email=${email}`);
|
|
299
|
+
|
|
300
|
+
// Step 4: Log in
|
|
301
|
+
await page.goto('/login');
|
|
302
|
+
await page.fill('[data-testid="email"]', email);
|
|
303
|
+
await page.fill('[data-testid="password"]', 'SecurePass123');
|
|
304
|
+
await page.click('[data-testid="login-button"]');
|
|
305
|
+
|
|
306
|
+
// Step 5: Verify successful login
|
|
307
|
+
await expect(page).toHaveURL('/dashboard');
|
|
308
|
+
await expect(page.locator('[data-testid="user-email"]'))
|
|
309
|
+
.toContainText(email);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Example 2: Payment Flow
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
test.describe('Checkout and Payment', () => {
|
|
319
|
+
|
|
320
|
+
test('should complete purchase with credit card', async ({ page }) => {
|
|
321
|
+
// Arrange: Log in and add item to cart
|
|
322
|
+
await page.goto('/login');
|
|
323
|
+
await page.fill('[data-testid="email"]', 'buyer@example.com');
|
|
324
|
+
await page.fill('[data-testid="password"]', 'password');
|
|
325
|
+
await page.click('[data-testid="login-button"]');
|
|
326
|
+
|
|
327
|
+
await page.goto('/products/premium-plan');
|
|
328
|
+
await page.click('[data-testid="add-to-cart"]');
|
|
329
|
+
|
|
330
|
+
// Act: Proceed to checkout
|
|
331
|
+
await page.goto('/checkout');
|
|
332
|
+
await page.fill('[data-testid="card-number"]', '4242424242424242'); // Test card
|
|
333
|
+
await page.fill('[data-testid="card-expiry"]', '12/25');
|
|
334
|
+
await page.fill('[data-testid="card-cvc"]', '123');
|
|
335
|
+
await page.click('[data-testid="complete-purchase"]');
|
|
336
|
+
|
|
337
|
+
// Assert: Verify successful payment
|
|
338
|
+
await expect(page).toHaveURL(/\/success/);
|
|
339
|
+
await expect(page.locator('[data-testid="order-confirmation"]'))
|
|
340
|
+
.toBeVisible();
|
|
341
|
+
|
|
342
|
+
// Assert: Verify order in database
|
|
343
|
+
const response = await page.request.get('/api/orders/latest');
|
|
344
|
+
const order = await response.json();
|
|
345
|
+
expect(order.status).toBe('completed');
|
|
346
|
+
expect(order.amount).toBe(9900); // $99.00
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Assertions
|
|
355
|
+
|
|
356
|
+
### Common Assertions
|
|
357
|
+
|
|
358
|
+
| Assertion | Purpose |
|
|
359
|
+
|-----------|---------|
|
|
360
|
+
| `await expect(page).toHaveURL('/dashboard')` | Check URL |
|
|
361
|
+
| `await expect(locator).toContainText('Welcome')` | Check text content |
|
|
362
|
+
| `await expect(locator).toBeVisible()` | Check visibility |
|
|
363
|
+
| `await expect(locator).toBeDisabled()` | Check disabled state |
|
|
364
|
+
| `await expect(locator).toHaveCount(3)` | Check number of elements |
|
|
365
|
+
| `await expect(locator).toHaveAttribute('href', '/profile')` | Check attribute |
|
|
366
|
+
|
|
367
|
+
### API Assertions
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
test('should create order in database', async ({ page }) => {
|
|
371
|
+
// Perform UI action
|
|
372
|
+
await page.click('[data-testid="submit-order"]');
|
|
373
|
+
|
|
374
|
+
// Assert via API
|
|
375
|
+
const response = await page.request.get('/api/orders/latest');
|
|
376
|
+
expect(response.status()).toBe(200);
|
|
377
|
+
|
|
378
|
+
const order = await response.json();
|
|
379
|
+
expect(order).toMatchObject({
|
|
380
|
+
status: 'pending',
|
|
381
|
+
userId: 'user-123',
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Debugging
|
|
389
|
+
|
|
390
|
+
### Visual Debugging
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Run with headed browser (see what's happening)
|
|
394
|
+
npx playwright test --headed
|
|
395
|
+
|
|
396
|
+
# Run in debug mode (pause and step through)
|
|
397
|
+
npx playwright test --debug
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Screenshots on Failure
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
test('should display dashboard', async ({ page }) => {
|
|
404
|
+
await page.goto('/dashboard');
|
|
405
|
+
|
|
406
|
+
// Automatically capture screenshot on failure
|
|
407
|
+
await expect(page.locator('h1')).toContainText('Dashboard');
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Configure in `playwright.config.ts`:
|
|
412
|
+
```typescript
|
|
413
|
+
use: {
|
|
414
|
+
screenshot: 'only-on-failure',
|
|
415
|
+
video: 'retain-on-failure',
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Trace Viewer
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
# Generate trace
|
|
423
|
+
npx playwright test --trace on
|
|
424
|
+
|
|
425
|
+
# View trace
|
|
426
|
+
npx playwright show-trace trace.zip
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## CI/CD Integration
|
|
432
|
+
|
|
433
|
+
### GitHub Actions
|
|
434
|
+
|
|
435
|
+
```yaml
|
|
436
|
+
# .github/workflows/e2e.yml
|
|
437
|
+
name: E2E Tests
|
|
438
|
+
|
|
439
|
+
on: [push, pull_request]
|
|
440
|
+
|
|
441
|
+
jobs:
|
|
442
|
+
test:
|
|
443
|
+
runs-on: ubuntu-latest
|
|
444
|
+
steps:
|
|
445
|
+
- uses: actions/checkout@v3
|
|
446
|
+
- uses: actions/setup-node@v3
|
|
447
|
+
with:
|
|
448
|
+
node-version: 18
|
|
449
|
+
|
|
450
|
+
- name: Install dependencies
|
|
451
|
+
run: npm ci
|
|
452
|
+
|
|
453
|
+
- name: Install Playwright browsers
|
|
454
|
+
run: npx playwright install --with-deps
|
|
455
|
+
|
|
456
|
+
- name: Run E2E tests
|
|
457
|
+
run: npx playwright test
|
|
458
|
+
|
|
459
|
+
- name: Upload test results
|
|
460
|
+
if: always()
|
|
461
|
+
uses: actions/upload-artifact@v3
|
|
462
|
+
with:
|
|
463
|
+
name: playwright-report
|
|
464
|
+
path: playwright-report/
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Validation Criteria
|
|
470
|
+
|
|
471
|
+
Before considering Playwright testing complete:
|
|
472
|
+
|
|
473
|
+
- [ ] All critical flows have E2E tests
|
|
474
|
+
- [ ] Tests pass in CI/CD pipeline
|
|
475
|
+
- [ ] Tests are idempotent (can run repeatedly)
|
|
476
|
+
- [ ] Error states are tested
|
|
477
|
+
- [ ] Tests use stable selectors (data-testid)
|
|
478
|
+
- [ ] No flaky tests (tests pass consistently)
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Common Mistakes
|
|
483
|
+
|
|
484
|
+
### Mistake 1: Testing Too Much in One Test
|
|
485
|
+
|
|
486
|
+
❌ **Wrong:**
|
|
487
|
+
```typescript
|
|
488
|
+
test('should do everything', async ({ page }) => {
|
|
489
|
+
// Signup + login + profile update + logout = too much
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
✅ **Correct:**
|
|
494
|
+
```typescript
|
|
495
|
+
test('should sign up', async ({ page }) => { /* ... */ });
|
|
496
|
+
test('should log in', async ({ page }) => { /* ... */ });
|
|
497
|
+
test('should update profile', async ({ page }) => { /* ... */ });
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Mistake 2: Hardcoding Waits
|
|
501
|
+
|
|
502
|
+
❌ **Wrong:**
|
|
503
|
+
```typescript
|
|
504
|
+
await page.click('[data-testid="submit"]');
|
|
505
|
+
await page.waitForTimeout(3000); // Fragile!
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
✅ **Correct:**
|
|
509
|
+
```typescript
|
|
510
|
+
await page.click('[data-testid="submit"]');
|
|
511
|
+
await page.waitForURL('/success'); // Wait for specific condition
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Mistake 3: Not Cleaning Up Test Data
|
|
515
|
+
|
|
516
|
+
❌ **Wrong:**
|
|
517
|
+
```typescript
|
|
518
|
+
test('should create user', async ({ page }) => {
|
|
519
|
+
// Creates user but never deletes it → subsequent runs fail
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
✅ **Correct:**
|
|
524
|
+
```typescript
|
|
525
|
+
test.afterEach(async () => {
|
|
526
|
+
await database.query('DELETE FROM users WHERE email LIKE "test-%"');
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Integration with SHHS
|
|
533
|
+
|
|
534
|
+
### Pipeline Position
|
|
535
|
+
|
|
536
|
+
E2E tests run during **QA Validator** phase.
|
|
537
|
+
|
|
538
|
+
```
|
|
539
|
+
Developer implements feature
|
|
540
|
+
↓
|
|
541
|
+
Static Reviewer validates structure
|
|
542
|
+
↓
|
|
543
|
+
QA Validator runs E2E tests ← Playwright runs here
|
|
544
|
+
↓
|
|
545
|
+
Domain Architect approves
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Enforcement by QA Validator
|
|
549
|
+
|
|
550
|
+
QA Validator MUST verify:
|
|
551
|
+
- All critical paths have Playwright tests
|
|
552
|
+
- All tests pass
|
|
553
|
+
- No flaky tests (tests pass 3 consecutive runs)
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## References
|
|
558
|
+
|
|
559
|
+
- Constitution Article III (E2E testing mandate)
|
|
560
|
+
- [Playwright Documentation](https://playwright.dev)
|
|
561
|
+
- [Playwright Best Practices](https://playwright.dev/docs/best-practices)
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
**END OF PLAYWRIGHT SKILL**
|