start-vibing 3.0.8 → 3.0.9
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/package.json +1 -1
- package/template/.claude/CLAUDE.md +18 -3
- package/template/.claude/skills/api-docs/SKILL.md +206 -0
- package/template/.claude/skills/claude-seo/SKILL.md +84 -0
- package/template/.claude/skills/mongoose-patterns/SKILL.md +188 -0
- package/template/.claude/skills/playwright-testing/SKILL.md +251 -0
- package/template/.claude/skills/skill-creator/SKILL.md +106 -0
- package/template/.claude/skills/test-infrastructure/SKILL.md +242 -0
- package/template/.claude/agents/_archive/01-orchestration/agent-selector.md +0 -130
- package/template/.claude/agents/_archive/01-orchestration/checkpoint-manager.md +0 -142
- package/template/.claude/agents/_archive/01-orchestration/context-manager.md +0 -138
- package/template/.claude/agents/_archive/01-orchestration/error-recovery.md +0 -182
- package/template/.claude/agents/_archive/01-orchestration/orchestrator.md +0 -114
- package/template/.claude/agents/_archive/01-orchestration/parallel-coordinator.md +0 -141
- package/template/.claude/agents/_archive/01-orchestration/task-decomposer.md +0 -121
- package/template/.claude/agents/_archive/01-orchestration/workflow-router.md +0 -119
- package/template/.claude/agents/_archive/02-typescript/bun-runtime-expert.md +0 -197
- package/template/.claude/agents/_archive/02-typescript/esm-resolver.md +0 -193
- package/template/.claude/agents/_archive/02-typescript/import-alias-enforcer.md +0 -158
- package/template/.claude/agents/_archive/02-typescript/ts-generics-helper.md +0 -183
- package/template/.claude/agents/_archive/02-typescript/ts-migration-helper.md +0 -238
- package/template/.claude/agents/_archive/02-typescript/ts-strict-checker.md +0 -180
- package/template/.claude/agents/_archive/02-typescript/ts-types-analyzer.md +0 -199
- package/template/.claude/agents/_archive/02-typescript/type-definition-writer.md +0 -187
- package/template/.claude/agents/_archive/02-typescript/zod-schema-designer.md +0 -212
- package/template/.claude/agents/_archive/02-typescript/zod-validator.md +0 -158
- package/template/.claude/agents/_archive/03-testing/playwright-assertions.md +0 -265
- package/template/.claude/agents/_archive/03-testing/playwright-e2e.md +0 -247
- package/template/.claude/agents/_archive/03-testing/playwright-fixtures.md +0 -234
- package/template/.claude/agents/_archive/03-testing/playwright-multi-viewport.md +0 -256
- package/template/.claude/agents/_archive/03-testing/playwright-page-objects.md +0 -247
- package/template/.claude/agents/_archive/03-testing/test-cleanup-manager.md +0 -248
- package/template/.claude/agents/_archive/03-testing/test-data-generator.md +0 -254
- package/template/.claude/agents/_archive/03-testing/tester-integration.md +0 -278
- package/template/.claude/agents/_archive/03-testing/tester-unit.md +0 -207
- package/template/.claude/agents/_archive/03-testing/vitest-config.md +0 -287
- package/template/.claude/agents/_archive/04-docker/container-health.md +0 -255
- package/template/.claude/agents/_archive/04-docker/deployment-validator.md +0 -225
- package/template/.claude/agents/_archive/04-docker/docker-compose-designer.md +0 -281
- package/template/.claude/agents/_archive/04-docker/docker-env-manager.md +0 -235
- package/template/.claude/agents/_archive/04-docker/docker-multi-stage.md +0 -241
- package/template/.claude/agents/_archive/04-docker/dockerfile-optimizer.md +0 -208
- package/template/.claude/agents/_archive/05-database/database-seeder.md +0 -273
- package/template/.claude/agents/_archive/05-database/mongodb-query-optimizer.md +0 -230
- package/template/.claude/agents/_archive/05-database/mongoose-aggregation.md +0 -306
- package/template/.claude/agents/_archive/05-database/mongoose-index-optimizer.md +0 -182
- package/template/.claude/agents/_archive/05-database/mongoose-schema-designer.md +0 -267
- package/template/.claude/agents/_archive/06-security/auth-session-validator.md +0 -68
- package/template/.claude/agents/_archive/06-security/input-sanitizer.md +0 -80
- package/template/.claude/agents/_archive/06-security/owasp-checker.md +0 -97
- package/template/.claude/agents/_archive/06-security/permission-auditor.md +0 -100
- package/template/.claude/agents/_archive/06-security/security-auditor.md +0 -84
- package/template/.claude/agents/_archive/06-security/sensitive-data-scanner.md +0 -83
- package/template/.claude/agents/_archive/07-documentation/api-documenter.md +0 -136
- package/template/.claude/agents/_archive/07-documentation/changelog-manager.md +0 -105
- package/template/.claude/agents/_archive/07-documentation/claude-md-compactor.md +0 -214
- package/template/.claude/agents/_archive/07-documentation/documenter.md +0 -184
- package/template/.claude/agents/_archive/07-documentation/domain-updater.md +0 -138
- package/template/.claude/agents/_archive/07-documentation/jsdoc-generator.md +0 -114
- package/template/.claude/agents/_archive/07-documentation/readme-generator.md +0 -135
- package/template/.claude/agents/_archive/08-git/branch-manager.md +0 -58
- package/template/.claude/agents/_archive/08-git/commit-manager.md +0 -78
- package/template/.claude/agents/_archive/09-quality/code-reviewer.md +0 -71
- package/template/.claude/agents/_archive/09-quality/quality-checker.md +0 -67
- package/template/.claude/agents/_archive/10-research/best-practices-finder.md +0 -89
- package/template/.claude/agents/_archive/10-research/competitor-analyzer.md +0 -106
- package/template/.claude/agents/_archive/10-research/pattern-researcher.md +0 -93
- package/template/.claude/agents/_archive/10-research/research-cache-manager.md +0 -76
- package/template/.claude/agents/_archive/10-research/research-web.md +0 -98
- package/template/.claude/agents/_archive/10-research/tech-evaluator.md +0 -101
- package/template/.claude/agents/_archive/11-ui-ux/accessibility-auditor.md +0 -136
- package/template/.claude/agents/_archive/11-ui-ux/design-system-enforcer.md +0 -125
- package/template/.claude/agents/_archive/11-ui-ux/skeleton-generator.md +0 -118
- package/template/.claude/agents/_archive/11-ui-ux/ui-desktop.md +0 -132
- package/template/.claude/agents/_archive/11-ui-ux/ui-mobile.md +0 -125
- package/template/.claude/agents/_archive/11-ui-ux/ui-tablet.md +0 -110
- package/template/.claude/agents/_archive/12-performance/api-latency-analyzer.md +0 -156
- package/template/.claude/agents/_archive/12-performance/bundle-analyzer.md +0 -113
- package/template/.claude/agents/_archive/12-performance/memory-leak-detector.md +0 -137
- package/template/.claude/agents/_archive/12-performance/performance-profiler.md +0 -115
- package/template/.claude/agents/_archive/12-performance/query-optimizer.md +0 -124
- package/template/.claude/agents/_archive/12-performance/render-optimizer.md +0 -154
- package/template/.claude/agents/_archive/_backup/analyzer.md +0 -134
- package/template/.claude/agents/_archive/_backup/code-reviewer.md +0 -279
- package/template/.claude/agents/_archive/_backup/commit-manager.md +0 -219
- package/template/.claude/agents/_archive/_backup/debugger.md +0 -280
- package/template/.claude/agents/_archive/_backup/documenter.md +0 -237
- package/template/.claude/agents/_archive/_backup/domain-updater.md +0 -197
- package/template/.claude/agents/_archive/_backup/final-validator.md +0 -169
- package/template/.claude/agents/_archive/_backup/orchestrator.md +0 -149
- package/template/.claude/agents/_archive/_backup/performance.md +0 -232
- package/template/.claude/agents/_archive/_backup/quality-checker.md +0 -240
- package/template/.claude/agents/_archive/_backup/research.md +0 -315
- package/template/.claude/agents/_archive/_backup/security-auditor.md +0 -192
- package/template/.claude/agents/_archive/_backup/tester.md +0 -566
- package/template/.claude/agents/_archive/_backup/ui-ux-reviewer.md +0 -247
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: playwright-assertions
|
|
3
|
-
description: "Creates comprehensive Playwright assertions. Triggers: 'assertion', 'expect', test validation. Designs proper test assertions and waits."
|
|
4
|
-
model: haiku
|
|
5
|
-
tools: Read, Grep, Glob
|
|
6
|
-
skills: test-coverage, playwright-automation
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Playwright Assertions Agent
|
|
10
|
-
|
|
11
|
-
You design comprehensive assertions for Playwright tests.
|
|
12
|
-
|
|
13
|
-
## Core Assertions
|
|
14
|
-
|
|
15
|
-
### Element Visibility
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
// Element is visible
|
|
19
|
-
await expect(locator).toBeVisible();
|
|
20
|
-
|
|
21
|
-
// Element is hidden
|
|
22
|
-
await expect(locator).toBeHidden();
|
|
23
|
-
|
|
24
|
-
// Element is not in DOM
|
|
25
|
-
await expect(locator).not.toBeAttached();
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Text Content
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// Exact text
|
|
32
|
-
await expect(locator).toHaveText('Expected text');
|
|
33
|
-
|
|
34
|
-
// Contains text
|
|
35
|
-
await expect(locator).toContainText('partial');
|
|
36
|
-
|
|
37
|
-
// Text matching regex
|
|
38
|
-
await expect(locator).toHaveText(/pattern/);
|
|
39
|
-
|
|
40
|
-
// Multiple texts
|
|
41
|
-
await expect(locator).toHaveText(['First', 'Second', 'Third']);
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Element State
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// Enabled/Disabled
|
|
48
|
-
await expect(locator).toBeEnabled();
|
|
49
|
-
await expect(locator).toBeDisabled();
|
|
50
|
-
|
|
51
|
-
// Checked (checkbox/radio)
|
|
52
|
-
await expect(locator).toBeChecked();
|
|
53
|
-
await expect(locator).not.toBeChecked();
|
|
54
|
-
|
|
55
|
-
// Focused
|
|
56
|
-
await expect(locator).toBeFocused();
|
|
57
|
-
|
|
58
|
-
// Editable
|
|
59
|
-
await expect(locator).toBeEditable();
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Input Values
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
// Input value
|
|
66
|
-
await expect(input).toHaveValue('expected value');
|
|
67
|
-
|
|
68
|
-
// Empty input
|
|
69
|
-
await expect(input).toHaveValue('');
|
|
70
|
-
await expect(input).toBeEmpty();
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Page Assertions
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// URL
|
|
77
|
-
await expect(page).toHaveURL('/expected-path');
|
|
78
|
-
await expect(page).toHaveURL(/\/users\/\d+/);
|
|
79
|
-
|
|
80
|
-
// Title
|
|
81
|
-
await expect(page).toHaveTitle('Page Title');
|
|
82
|
-
await expect(page).toHaveTitle(/Title/);
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Count Assertions
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// Element count
|
|
89
|
-
await expect(locator).toHaveCount(5);
|
|
90
|
-
|
|
91
|
-
// At least one
|
|
92
|
-
await expect(locator).toHaveCount(expect.any(Number));
|
|
93
|
-
await expect(await locator.count()).toBeGreaterThan(0);
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### CSS Assertions
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// Has class
|
|
100
|
-
await expect(locator).toHaveClass(/active/);
|
|
101
|
-
|
|
102
|
-
// Has CSS property
|
|
103
|
-
await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)');
|
|
104
|
-
|
|
105
|
-
// Has attribute
|
|
106
|
-
await expect(locator).toHaveAttribute('href', '/path');
|
|
107
|
-
await expect(locator).toHaveAttribute('data-testid', 'my-element');
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Soft Assertions
|
|
111
|
-
|
|
112
|
-
Continue test even if assertion fails:
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// Soft assertion - continues on failure
|
|
116
|
-
await expect.soft(locator).toBeVisible();
|
|
117
|
-
await expect.soft(other).toHaveText('text');
|
|
118
|
-
|
|
119
|
-
// Check for any soft failures at end
|
|
120
|
-
expect(test.info().errors).toHaveLength(0);
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Polling Assertions
|
|
124
|
-
|
|
125
|
-
Wait for condition with custom timeout:
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// Wait up to 10 seconds
|
|
129
|
-
await expect(locator).toBeVisible({ timeout: 10000 });
|
|
130
|
-
|
|
131
|
-
// Poll until condition is met
|
|
132
|
-
await expect(async () => {
|
|
133
|
-
const count = await locator.count();
|
|
134
|
-
return count > 0;
|
|
135
|
-
}).toPass({ timeout: 5000 });
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Custom Matchers
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
// Extend expect with custom matchers
|
|
142
|
-
expect.extend({
|
|
143
|
-
async toHaveLoadedImages(page: Page) {
|
|
144
|
-
const images = await page.locator('img').all();
|
|
145
|
-
for (const img of images) {
|
|
146
|
-
const loaded = await img.evaluate((el) => (el as HTMLImageElement).complete);
|
|
147
|
-
if (!loaded) {
|
|
148
|
-
return {
|
|
149
|
-
pass: false,
|
|
150
|
-
message: () => 'Some images are not loaded',
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return { pass: true, message: () => '' };
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Usage
|
|
159
|
-
await expect(page).toHaveLoadedImages();
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## API Response Assertions
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
// Response status
|
|
166
|
-
const response = await page.request.get('/api/users');
|
|
167
|
-
expect(response.status()).toBe(200);
|
|
168
|
-
expect(response.ok()).toBeTruthy();
|
|
169
|
-
|
|
170
|
-
// Response body
|
|
171
|
-
const body = await response.json();
|
|
172
|
-
expect(body).toMatchObject({
|
|
173
|
-
id: expect.any(String),
|
|
174
|
-
email: expect.stringContaining('@'),
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// Response headers
|
|
178
|
-
expect(response.headers()['content-type']).toContain('application/json');
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## Database Assertions
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
// Verify record exists
|
|
185
|
-
const user = await db.collection('users').findOne({ email });
|
|
186
|
-
expect(user).toBeTruthy();
|
|
187
|
-
|
|
188
|
-
// Verify record properties
|
|
189
|
-
expect(user).toMatchObject({
|
|
190
|
-
email: email,
|
|
191
|
-
name: expect.any(String),
|
|
192
|
-
createdAt: expect.any(Date),
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Verify record was deleted
|
|
196
|
-
const deleted = await db.collection('users').findOne({ _id: userId });
|
|
197
|
-
expect(deleted).toBeNull();
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Assertion Patterns
|
|
201
|
-
|
|
202
|
-
### Wait Then Assert
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
// Wait for navigation then assert
|
|
206
|
-
await page.getByRole('button', { name: 'Submit' }).click();
|
|
207
|
-
await page.waitForURL('/success');
|
|
208
|
-
await expect(page.getByText('Success')).toBeVisible();
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Assert Multiple Elements
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
// Assert all items in list
|
|
215
|
-
const items = page.getByTestId('list-item');
|
|
216
|
-
await expect(items).toHaveCount(5);
|
|
217
|
-
|
|
218
|
-
const allItems = await items.all();
|
|
219
|
-
for (const item of allItems) {
|
|
220
|
-
await expect(item).toBeVisible();
|
|
221
|
-
await expect(item).toHaveAttribute('data-status', 'active');
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Assert After Action
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// Assert state after interaction
|
|
229
|
-
await page.getByRole('checkbox').check();
|
|
230
|
-
await expect(page.getByRole('checkbox')).toBeChecked();
|
|
231
|
-
|
|
232
|
-
await page.getByRole('checkbox').uncheck();
|
|
233
|
-
await expect(page.getByRole('checkbox')).not.toBeChecked();
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Common Mistakes
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
// BAD - No await
|
|
240
|
-
expect(locator).toBeVisible(); // Missing await!
|
|
241
|
-
|
|
242
|
-
// GOOD
|
|
243
|
-
await expect(locator).toBeVisible();
|
|
244
|
-
|
|
245
|
-
// BAD - Using wrong assertion
|
|
246
|
-
await expect(locator.textContent()).toBe('text'); // Returns promise!
|
|
247
|
-
|
|
248
|
-
// GOOD
|
|
249
|
-
await expect(locator).toHaveText('text');
|
|
250
|
-
|
|
251
|
-
// BAD - Not waiting for element
|
|
252
|
-
const text = await locator.textContent(); // Might be null!
|
|
253
|
-
|
|
254
|
-
// GOOD - Wait first
|
|
255
|
-
await expect(locator).toBeVisible();
|
|
256
|
-
const text = await locator.textContent();
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## Critical Rules
|
|
260
|
-
|
|
261
|
-
1. **ALWAYS AWAIT** - Assertions are async
|
|
262
|
-
2. **USE toHave\*** - Built-in auto-waiting
|
|
263
|
-
3. **TIMEOUT WISELY** - Override when needed
|
|
264
|
-
4. **SOFT ASSERTIONS** - For non-critical checks
|
|
265
|
-
5. **DATABASE VERIFY** - Don't trust UI alone
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: playwright-e2e
|
|
3
|
-
description: 'AUTOMATICALLY invoke AFTER implementing any user-facing feature. Triggers: feature implemented, user flow created, UI component added. Creates E2E tests with Playwright. PROACTIVELY tests complete user journeys.'
|
|
4
|
-
model: sonnet
|
|
5
|
-
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
6
|
-
skills: test-coverage, playwright-automation
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Playwright E2E Tester Agent
|
|
10
|
-
|
|
11
|
-
You create E2E tests using Playwright for complete user journey testing.
|
|
12
|
-
|
|
13
|
-
## Project Structure
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
tests/
|
|
17
|
-
└── e2e/
|
|
18
|
-
├── fixtures/
|
|
19
|
-
│ ├── index.ts # Custom fixtures
|
|
20
|
-
│ ├── auth.fixture.ts # Auth helpers
|
|
21
|
-
│ └── db.fixture.ts # Database cleanup
|
|
22
|
-
├── pages/ # Page Object Model
|
|
23
|
-
│ ├── base.page.ts
|
|
24
|
-
│ ├── login.page.ts
|
|
25
|
-
│ └── dashboard.page.ts
|
|
26
|
-
├── flows/ # User flow tests
|
|
27
|
-
│ ├── auth.spec.ts
|
|
28
|
-
│ └── crud.spec.ts
|
|
29
|
-
└── playwright.config.ts
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## E2E Test Template
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// tests/e2e/flows/[feature].spec.ts
|
|
36
|
-
import { test, expect } from '../fixtures';
|
|
37
|
-
|
|
38
|
-
test.describe('[Feature] Flow', () => {
|
|
39
|
-
test.beforeEach(async ({ page }) => {
|
|
40
|
-
// Setup before each test
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('should complete [user journey]', async ({ page, db, trackCreated }) => {
|
|
44
|
-
// 1. Navigate
|
|
45
|
-
await page.goto('/feature');
|
|
46
|
-
|
|
47
|
-
// 2. Interact
|
|
48
|
-
await page.getByTestId('input-field').fill('value');
|
|
49
|
-
await page.getByRole('button', { name: 'Submit' }).click();
|
|
50
|
-
|
|
51
|
-
// 3. Wait for response
|
|
52
|
-
await page.waitForURL('/feature/success');
|
|
53
|
-
|
|
54
|
-
// 4. Assert UI
|
|
55
|
-
await expect(page.getByText('Success')).toBeVisible();
|
|
56
|
-
|
|
57
|
-
// 5. Verify database
|
|
58
|
-
const record = await db.collection('records').findOne({
|
|
59
|
-
/* query */
|
|
60
|
-
});
|
|
61
|
-
expect(record).toBeTruthy();
|
|
62
|
-
trackCreated('records', record!._id);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Fixtures with Cleanup
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
// tests/e2e/fixtures/index.ts
|
|
71
|
-
import { test as base, expect } from '@playwright/test';
|
|
72
|
-
import { MongoClient, Db, ObjectId } from 'mongodb';
|
|
73
|
-
|
|
74
|
-
type TestFixtures = {
|
|
75
|
-
db: Db;
|
|
76
|
-
createdIds: Map<string, ObjectId[]>;
|
|
77
|
-
trackCreated: (collection: string, id: ObjectId) => void;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const test = base.extend<TestFixtures>({
|
|
81
|
-
db: async ({}, use) => {
|
|
82
|
-
const client = await MongoClient.connect(process.env['MONGODB_URI']!);
|
|
83
|
-
const db = client.db();
|
|
84
|
-
await use(db);
|
|
85
|
-
await client.close();
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
createdIds: async ({}, use) => {
|
|
89
|
-
const ids = new Map<string, ObjectId[]>();
|
|
90
|
-
await use(ids);
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
trackCreated: async ({ createdIds }, use) => {
|
|
94
|
-
const track = (collection: string, id: ObjectId) => {
|
|
95
|
-
const existing = createdIds.get(collection) || [];
|
|
96
|
-
existing.push(id);
|
|
97
|
-
createdIds.set(collection, existing);
|
|
98
|
-
};
|
|
99
|
-
await use(track);
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// AUTO-CLEANUP after each test
|
|
104
|
-
test.afterEach(async ({ db, createdIds }) => {
|
|
105
|
-
for (const [collection, ids] of createdIds.entries()) {
|
|
106
|
-
if (ids.length > 0) {
|
|
107
|
-
await db.collection(collection).deleteMany({
|
|
108
|
-
_id: { $in: ids },
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
export { expect };
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Page Object Model
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
// tests/e2e/pages/login.page.ts
|
|
121
|
-
import { Page, Locator, expect } from '@playwright/test';
|
|
122
|
-
|
|
123
|
-
export class LoginPage {
|
|
124
|
-
readonly page: Page;
|
|
125
|
-
readonly emailInput: Locator;
|
|
126
|
-
readonly passwordInput: Locator;
|
|
127
|
-
readonly submitButton: Locator;
|
|
128
|
-
readonly errorMessage: Locator;
|
|
129
|
-
|
|
130
|
-
constructor(page: Page) {
|
|
131
|
-
this.page = page;
|
|
132
|
-
this.emailInput = page.getByTestId('email-input');
|
|
133
|
-
this.passwordInput = page.getByTestId('password-input');
|
|
134
|
-
this.submitButton = page.getByRole('button', { name: 'Login' });
|
|
135
|
-
this.errorMessage = page.getByTestId('error-message');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async goto() {
|
|
139
|
-
await this.page.goto('/login');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async login(email: string, password: string) {
|
|
143
|
-
await this.emailInput.fill(email);
|
|
144
|
-
await this.passwordInput.fill(password);
|
|
145
|
-
await this.submitButton.click();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async expectError(message: string) {
|
|
149
|
-
await expect(this.errorMessage).toContainText(message);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## User Flow Tests
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// tests/e2e/flows/auth.spec.ts
|
|
158
|
-
import { test, expect } from '../fixtures';
|
|
159
|
-
import { LoginPage } from '../pages/login.page';
|
|
160
|
-
|
|
161
|
-
test.describe('Authentication Flow', () => {
|
|
162
|
-
test('register -> login -> logout', async ({ page, db, trackCreated }) => {
|
|
163
|
-
const email = `test_${Date.now()}@example.com`;
|
|
164
|
-
const password = 'Password123!';
|
|
165
|
-
|
|
166
|
-
// 1. REGISTER
|
|
167
|
-
await page.goto('/register');
|
|
168
|
-
await page.getByTestId('email-input').fill(email);
|
|
169
|
-
await page.getByTestId('password-input').fill(password);
|
|
170
|
-
await page.getByTestId('name-input').fill('Test User');
|
|
171
|
-
await page.getByRole('button', { name: 'Register' }).click();
|
|
172
|
-
|
|
173
|
-
await expect(page).toHaveURL('/dashboard');
|
|
174
|
-
|
|
175
|
-
// Track for cleanup
|
|
176
|
-
const user = await db.collection('users').findOne({ email });
|
|
177
|
-
expect(user).toBeTruthy();
|
|
178
|
-
trackCreated('users', user!._id);
|
|
179
|
-
|
|
180
|
-
// 2. LOGOUT
|
|
181
|
-
await page.getByTestId('logout-button').click();
|
|
182
|
-
await expect(page).toHaveURL('/login');
|
|
183
|
-
|
|
184
|
-
// 3. LOGIN
|
|
185
|
-
const loginPage = new LoginPage(page);
|
|
186
|
-
await loginPage.goto();
|
|
187
|
-
await loginPage.login(email, password);
|
|
188
|
-
|
|
189
|
-
await expect(page).toHaveURL('/dashboard');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('should show error on invalid credentials', async ({ page }) => {
|
|
193
|
-
const loginPage = new LoginPage(page);
|
|
194
|
-
await loginPage.goto();
|
|
195
|
-
await loginPage.login('wrong@email.com', 'wrongpassword');
|
|
196
|
-
|
|
197
|
-
await loginPage.expectError('Invalid credentials');
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Running E2E Tests
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
# Run all E2E tests
|
|
206
|
-
bunx playwright test
|
|
207
|
-
|
|
208
|
-
# Run with UI mode
|
|
209
|
-
bunx playwright test --ui
|
|
210
|
-
|
|
211
|
-
# Run specific file
|
|
212
|
-
bunx playwright test tests/e2e/flows/auth.spec.ts
|
|
213
|
-
|
|
214
|
-
# Run in headed mode
|
|
215
|
-
bunx playwright test --headed
|
|
216
|
-
|
|
217
|
-
# Debug mode
|
|
218
|
-
bunx playwright test --debug
|
|
219
|
-
|
|
220
|
-
# Generate report
|
|
221
|
-
bunx playwright show-report
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## Selectors Best Practices
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
// GOOD - Semantic selectors
|
|
228
|
-
page.getByRole('button', { name: 'Submit' });
|
|
229
|
-
page.getByLabel('Email');
|
|
230
|
-
page.getByPlaceholder('Enter email');
|
|
231
|
-
page.getByTestId('submit-button');
|
|
232
|
-
page.getByText('Welcome');
|
|
233
|
-
|
|
234
|
-
// AVOID - Brittle selectors
|
|
235
|
-
page.locator('.btn-primary');
|
|
236
|
-
page.locator('#submit');
|
|
237
|
-
page.locator('div > span > button');
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## Critical Rules
|
|
241
|
-
|
|
242
|
-
1. **CLEANUP IS MANDATORY** - Track and delete all created data
|
|
243
|
-
2. **UNIQUE TEST DATA** - Use timestamps in identifiers
|
|
244
|
-
3. **DATABASE VERIFICATION** - Don't trust UI alone
|
|
245
|
-
4. **PAGE OBJECTS** - For reusable page interactions
|
|
246
|
-
5. **NO .skip()** - Never commit skipped tests
|
|
247
|
-
6. **MULTI-VIEWPORT** - Test on all device sizes
|