start-vibing 2.0.11 → 2.0.13
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/README.md +177 -177
- package/dist/cli.js +19 -2
- package/package.json +42 -42
- package/template/.claude/CLAUDE.md +174 -174
- package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
- package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
- package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
- package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
- package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
- package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
- package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
- package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
- package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
- package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
- package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
- package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
- package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
- package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
- package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
- package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
- package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
- package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
- package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
- package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
- package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
- package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
- package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
- package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
- package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
- package/template/.claude/agents/03-testing/tester-integration.md +278 -278
- package/template/.claude/agents/03-testing/tester-unit.md +207 -207
- package/template/.claude/agents/03-testing/vitest-config.md +287 -287
- package/template/.claude/agents/04-docker/container-health.md +255 -255
- package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
- package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
- package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
- package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
- package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
- package/template/.claude/agents/05-database/database-seeder.md +273 -273
- package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
- package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
- package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
- package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
- package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
- package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
- package/template/.claude/agents/06-security/owasp-checker.md +97 -97
- package/template/.claude/agents/06-security/permission-auditor.md +100 -100
- package/template/.claude/agents/06-security/security-auditor.md +84 -84
- package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
- package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
- package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
- package/template/.claude/agents/07-documentation/documenter.md +76 -76
- package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
- package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
- package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
- package/template/.claude/agents/08-git/branch-manager.md +58 -58
- package/template/.claude/agents/08-git/commit-manager.md +63 -63
- package/template/.claude/agents/08-git/pr-creator.md +76 -76
- package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
- package/template/.claude/agents/09-quality/quality-checker.md +67 -67
- package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
- package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
- package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
- package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
- package/template/.claude/agents/10-research/research-web.md +98 -98
- package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
- package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
- package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
- package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
- package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
- package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
- package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
- package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
- package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
- package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
- package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
- package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
- package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
- package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
- package/template/.claude/agents/13-debugging/debugger.md +149 -149
- package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
- package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
- package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
- package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
- package/template/.claude/agents/14-validation/final-validator.md +93 -93
- package/template/.claude/agents/_backup/analyzer.md +134 -134
- package/template/.claude/agents/_backup/code-reviewer.md +279 -279
- package/template/.claude/agents/_backup/commit-manager.md +219 -219
- package/template/.claude/agents/_backup/debugger.md +280 -280
- package/template/.claude/agents/_backup/documenter.md +237 -237
- package/template/.claude/agents/_backup/domain-updater.md +197 -197
- package/template/.claude/agents/_backup/final-validator.md +169 -169
- package/template/.claude/agents/_backup/orchestrator.md +149 -149
- package/template/.claude/agents/_backup/performance.md +232 -232
- package/template/.claude/agents/_backup/quality-checker.md +240 -240
- package/template/.claude/agents/_backup/research.md +315 -315
- package/template/.claude/agents/_backup/security-auditor.md +192 -192
- package/template/.claude/agents/_backup/tester.md +566 -566
- package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
- package/template/.claude/config/README.md +30 -30
- package/template/.claude/config/mcp-config.json +344 -344
- package/template/.claude/config/project-config.json +53 -53
- package/template/.claude/config/quality-gates.json +46 -46
- package/template/.claude/config/security-rules.json +45 -45
- package/template/.claude/config/testing-config.json +164 -164
- package/template/.claude/hooks/SETUP.md +126 -126
- package/template/.claude/hooks/run-hook.ts +176 -176
- package/template/.claude/hooks/stop-validator.ts +914 -824
- package/template/.claude/hooks/user-prompt-submit.ts +886 -886
- package/template/.claude/scripts/mcp-quick-install.ts +151 -151
- package/template/.claude/scripts/setup-mcps.ts +651 -651
- package/template/.claude/settings.json +275 -275
- package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
- package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
- package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
- package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
- package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
- package/template/.claude/skills/git-workflow/SKILL.md +454 -454
- package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
- package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
- package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
- package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
- package/template/.claude/skills/react-patterns/SKILL.md +389 -389
- package/template/.claude/skills/research-cache/SKILL.md +222 -222
- package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
- package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
- package/template/.claude/skills/test-coverage/SKILL.md +467 -467
- package/template/.claude/skills/trpc-api/SKILL.md +434 -434
- package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
- package/template/.claude/skills/zod-validation/SKILL.md +403 -403
- package/template/CLAUDE.md +117 -117
|
@@ -1,438 +1,438 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: playwright-automation
|
|
3
|
-
description: Playwright testing automation patterns. E2E tests, browser automation, visual testing, API testing. Use when writing automated tests with Playwright.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Playwright Automation - E2E Testing Patterns
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
Expert guidance for Playwright:
|
|
12
|
-
|
|
13
|
-
- **E2E Testing** - Full user journey tests
|
|
14
|
-
- **Browser Automation** - Cross-browser testing
|
|
15
|
-
- **Visual Testing** - Screenshots and comparisons
|
|
16
|
-
- **API Testing** - Request mocking and validation
|
|
17
|
-
- **Page Object Model** - Maintainable test architecture
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Project Structure
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
tests/
|
|
25
|
-
├── e2e/
|
|
26
|
-
│ ├── fixtures/
|
|
27
|
-
│ │ ├── index.ts # Custom fixtures
|
|
28
|
-
│ │ ├── auth.fixture.ts # Auth helpers
|
|
29
|
-
│ │ └── db.fixture.ts # Database helpers
|
|
30
|
-
│ ├── pages/
|
|
31
|
-
│ │ ├── base.page.ts # Base page object
|
|
32
|
-
│ │ ├── login.page.ts
|
|
33
|
-
│ │ └── dashboard.page.ts
|
|
34
|
-
│ ├── flows/
|
|
35
|
-
│ │ ├── auth.spec.ts # Auth flow tests
|
|
36
|
-
│ │ └── crud.spec.ts # CRUD flow tests
|
|
37
|
-
│ └── api/
|
|
38
|
-
│ └── endpoints.spec.ts # API-only tests
|
|
39
|
-
├── playwright.config.ts
|
|
40
|
-
└── global-setup.ts
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Configuration
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
// playwright.config.ts
|
|
49
|
-
import { defineConfig, devices } from '@playwright/test';
|
|
50
|
-
|
|
51
|
-
export default defineConfig({
|
|
52
|
-
testDir: './tests/e2e',
|
|
53
|
-
fullyParallel: true,
|
|
54
|
-
forbidOnly: !!process.env['CI'],
|
|
55
|
-
retries: process.env['CI'] ? 2 : 0,
|
|
56
|
-
workers: process.env['CI'] ? 1 : undefined,
|
|
57
|
-
reporter: 'html',
|
|
58
|
-
|
|
59
|
-
use: {
|
|
60
|
-
baseURL: 'http://localhost:3000',
|
|
61
|
-
trace: 'on-first-retry',
|
|
62
|
-
screenshot: 'only-on-failure',
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
projects: [
|
|
66
|
-
// Desktop browsers
|
|
67
|
-
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
68
|
-
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
69
|
-
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
70
|
-
|
|
71
|
-
// Mobile viewports
|
|
72
|
-
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
|
|
73
|
-
{ name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
|
|
74
|
-
|
|
75
|
-
// Tablet
|
|
76
|
-
{ name: 'iPad', use: { ...devices['iPad Pro 11'] } },
|
|
77
|
-
],
|
|
78
|
-
|
|
79
|
-
webServer: {
|
|
80
|
-
command: 'bun run dev',
|
|
81
|
-
url: 'http://localhost:3000',
|
|
82
|
-
reuseExistingServer: !process.env['CI'],
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## Page Object Model
|
|
90
|
-
|
|
91
|
-
### Base Page
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// tests/e2e/pages/base.page.ts
|
|
95
|
-
import { type Page, type Locator, expect } from '@playwright/test';
|
|
96
|
-
|
|
97
|
-
export abstract class BasePage {
|
|
98
|
-
protected readonly page: Page;
|
|
99
|
-
|
|
100
|
-
constructor(page: Page) {
|
|
101
|
-
this.page = page;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Common elements
|
|
105
|
-
get loadingSpinner(): Locator {
|
|
106
|
-
return this.page.getByTestId('loading-spinner');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
get errorMessage(): Locator {
|
|
110
|
-
return this.page.getByTestId('error-message');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
get successMessage(): Locator {
|
|
114
|
-
return this.page.getByTestId('success-message');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Common actions
|
|
118
|
-
async waitForLoad(): Promise<void> {
|
|
119
|
-
await this.loadingSpinner.waitFor({ state: 'hidden' });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async expectError(message: string): Promise<void> {
|
|
123
|
-
await expect(this.errorMessage).toContainText(message);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async expectSuccess(message: string): Promise<void> {
|
|
127
|
-
await expect(this.successMessage).toContainText(message);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Navigation
|
|
131
|
-
async goto(path: string): Promise<void> {
|
|
132
|
-
await this.page.goto(path);
|
|
133
|
-
await this.waitForLoad();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Login Page
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
// tests/e2e/pages/login.page.ts
|
|
142
|
-
import { type Page, type Locator } from '@playwright/test';
|
|
143
|
-
import { BasePage } from './base.page';
|
|
144
|
-
|
|
145
|
-
export class LoginPage extends BasePage {
|
|
146
|
-
readonly emailInput: Locator;
|
|
147
|
-
readonly passwordInput: Locator;
|
|
148
|
-
readonly submitButton: Locator;
|
|
149
|
-
readonly forgotPasswordLink: Locator;
|
|
150
|
-
|
|
151
|
-
constructor(page: Page) {
|
|
152
|
-
super(page);
|
|
153
|
-
this.emailInput = page.getByTestId('email-input');
|
|
154
|
-
this.passwordInput = page.getByTestId('password-input');
|
|
155
|
-
this.submitButton = page.getByTestId('submit-button');
|
|
156
|
-
this.forgotPasswordLink = page.getByRole('link', { name: /forgot/i });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async goto(): Promise<void> {
|
|
160
|
-
await super.goto('/auth/login');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async login(email: string, password: string): Promise<void> {
|
|
164
|
-
await this.emailInput.fill(email);
|
|
165
|
-
await this.passwordInput.fill(password);
|
|
166
|
-
await this.submitButton.click();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## Custom Fixtures
|
|
174
|
-
|
|
175
|
-
### Auth Fixture
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// tests/e2e/fixtures/auth.fixture.ts
|
|
179
|
-
import { test as base, type Page } from '@playwright/test';
|
|
180
|
-
|
|
181
|
-
export interface TestUser {
|
|
182
|
-
name: string;
|
|
183
|
-
email: string;
|
|
184
|
-
password: string;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function generateTestUser(): TestUser {
|
|
188
|
-
const timestamp = Date.now();
|
|
189
|
-
const random = Math.random().toString(36).substring(7);
|
|
190
|
-
return {
|
|
191
|
-
name: `Test User ${timestamp}`,
|
|
192
|
-
email: `testuser_${timestamp}_${random}@test.com`,
|
|
193
|
-
password: 'TestPassword123!',
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export async function registerUser(page: Page, user: TestUser): Promise<void> {
|
|
198
|
-
await page.goto('/auth/register');
|
|
199
|
-
await page.getByTestId('name-input').fill(user.name);
|
|
200
|
-
await page.getByTestId('email-input').fill(user.email);
|
|
201
|
-
await page.getByTestId('password-input').fill(user.password);
|
|
202
|
-
await page.getByTestId('confirm-password-input').fill(user.password);
|
|
203
|
-
await page.getByTestId('submit-button').click();
|
|
204
|
-
await page.waitForURL(/\/app/, { timeout: 10000 });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export async function loginUser(page: Page, user: TestUser): Promise<void> {
|
|
208
|
-
await page.goto('/auth/login');
|
|
209
|
-
await page.getByTestId('email-input').fill(user.email);
|
|
210
|
-
await page.getByTestId('password-input').fill(user.password);
|
|
211
|
-
await page.getByTestId('submit-button').click();
|
|
212
|
-
await page.waitForURL(/\/app/, { timeout: 10000 });
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Database Cleanup Fixture
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
// tests/e2e/fixtures/db.fixture.ts
|
|
220
|
-
import { test as base } from '@playwright/test';
|
|
221
|
-
import { MongoClient, type Db, type ObjectId } from 'mongodb';
|
|
222
|
-
|
|
223
|
-
type CleanupFixtures = {
|
|
224
|
-
db: Db;
|
|
225
|
-
createdIds: Map<string, ObjectId[]>;
|
|
226
|
-
trackCreated: (collection: string, id: ObjectId) => void;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
export const test = base.extend<CleanupFixtures>({
|
|
230
|
-
db: async ({}, use) => {
|
|
231
|
-
const client = await MongoClient.connect(process.env['MONGODB_URI']!);
|
|
232
|
-
const db = client.db();
|
|
233
|
-
await use(db);
|
|
234
|
-
await client.close();
|
|
235
|
-
},
|
|
236
|
-
|
|
237
|
-
createdIds: async ({}, use) => {
|
|
238
|
-
await use(new Map());
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
trackCreated: async ({ createdIds }, use) => {
|
|
242
|
-
await use((collection: string, id: ObjectId) => {
|
|
243
|
-
const existing = createdIds.get(collection) || [];
|
|
244
|
-
existing.push(id);
|
|
245
|
-
createdIds.set(collection, existing);
|
|
246
|
-
});
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Auto-cleanup after each test
|
|
251
|
-
test.afterEach(async ({ db, createdIds }) => {
|
|
252
|
-
for (const [collection, ids] of createdIds.entries()) {
|
|
253
|
-
if (ids.length > 0) {
|
|
254
|
-
await db.collection(collection).deleteMany({ _id: { $in: ids } });
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
export { expect } from '@playwright/test';
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Test Patterns
|
|
265
|
-
|
|
266
|
-
### Auth Flow Test
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
// tests/e2e/flows/auth.spec.ts
|
|
270
|
-
import { test, expect } from '../fixtures';
|
|
271
|
-
import { generateTestUser, registerUser, loginUser } from '../fixtures/auth.fixture';
|
|
272
|
-
import { LoginPage } from '../pages/login.page';
|
|
273
|
-
|
|
274
|
-
test.describe('Authentication Flow', () => {
|
|
275
|
-
test('complete registration and login journey', async ({ page, db, trackCreated }) => {
|
|
276
|
-
const user = generateTestUser();
|
|
277
|
-
|
|
278
|
-
// 1. Register
|
|
279
|
-
await registerUser(page, user);
|
|
280
|
-
|
|
281
|
-
// 2. Verify in database
|
|
282
|
-
const dbUser = await db.collection('users').findOne({ email: user.email });
|
|
283
|
-
expect(dbUser).toBeTruthy();
|
|
284
|
-
trackCreated('users', dbUser!._id);
|
|
285
|
-
|
|
286
|
-
// 3. Logout
|
|
287
|
-
await page.getByTestId('logout-button').click();
|
|
288
|
-
await expect(page).toHaveURL('/auth/login');
|
|
289
|
-
|
|
290
|
-
// 4. Login again
|
|
291
|
-
await loginUser(page, user);
|
|
292
|
-
await expect(page).toHaveURL(/\/app/);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
test('shows error for invalid credentials', async ({ page }) => {
|
|
296
|
-
const loginPage = new LoginPage(page);
|
|
297
|
-
await loginPage.goto();
|
|
298
|
-
|
|
299
|
-
await loginPage.login('invalid@email.com', 'wrongpassword');
|
|
300
|
-
|
|
301
|
-
await loginPage.expectError('Invalid credentials');
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Multi-Viewport Test
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
// tests/e2e/flows/responsive.spec.ts
|
|
310
|
-
import { test, expect } from '@playwright/test';
|
|
311
|
-
|
|
312
|
-
const viewports = [
|
|
313
|
-
{ name: 'mobile', width: 375, height: 667 },
|
|
314
|
-
{ name: 'tablet', width: 768, height: 1024 },
|
|
315
|
-
{ name: 'desktop', width: 1280, height: 800 },
|
|
316
|
-
] as const;
|
|
317
|
-
|
|
318
|
-
for (const viewport of viewports) {
|
|
319
|
-
test.describe(`Responsive - ${viewport.name}`, () => {
|
|
320
|
-
test.use({ viewport: { width: viewport.width, height: viewport.height } });
|
|
321
|
-
|
|
322
|
-
test('navigation adapts to viewport', async ({ page }) => {
|
|
323
|
-
await page.goto('/');
|
|
324
|
-
|
|
325
|
-
if (viewport.width < 768) {
|
|
326
|
-
// Mobile: hamburger menu
|
|
327
|
-
await expect(page.getByTestId('hamburger-menu')).toBeVisible();
|
|
328
|
-
await expect(page.getByTestId('sidebar')).toBeHidden();
|
|
329
|
-
|
|
330
|
-
// Open mobile menu
|
|
331
|
-
await page.getByTestId('hamburger-menu').click();
|
|
332
|
-
await expect(page.getByTestId('mobile-nav')).toBeVisible();
|
|
333
|
-
} else {
|
|
334
|
-
// Desktop: sidebar visible
|
|
335
|
-
await expect(page.getByTestId('sidebar')).toBeVisible();
|
|
336
|
-
await expect(page.getByTestId('hamburger-menu')).toBeHidden();
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### API Testing
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
// tests/e2e/api/endpoints.spec.ts
|
|
347
|
-
import { test, expect } from '@playwright/test';
|
|
348
|
-
|
|
349
|
-
test.describe('API Endpoints', () => {
|
|
350
|
-
test('requires authentication', async ({ request }) => {
|
|
351
|
-
const response = await request.get('/api/users');
|
|
352
|
-
expect(response.status()).toBe(401);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
test('validates request body', async ({ request }) => {
|
|
356
|
-
const response = await request.post('/api/users', {
|
|
357
|
-
data: { email: 'invalid-email' },
|
|
358
|
-
});
|
|
359
|
-
expect(response.status()).toBe(400);
|
|
360
|
-
|
|
361
|
-
const body = await response.json();
|
|
362
|
-
expect(body.errors).toBeDefined();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
test('creates user with valid data', async ({ request }) => {
|
|
366
|
-
const response = await request.post('/api/users', {
|
|
367
|
-
data: {
|
|
368
|
-
name: 'Test User',
|
|
369
|
-
email: 'test@example.com',
|
|
370
|
-
password: 'Password123!',
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
expect(response.status()).toBe(201);
|
|
374
|
-
|
|
375
|
-
const user = await response.json();
|
|
376
|
-
expect(user.id).toBeDefined();
|
|
377
|
-
expect(user.email).toBe('test@example.com');
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
---
|
|
383
|
-
|
|
384
|
-
## Commands
|
|
385
|
-
|
|
386
|
-
```bash
|
|
387
|
-
# Run all tests
|
|
388
|
-
bunx playwright test
|
|
389
|
-
|
|
390
|
-
# Run with UI
|
|
391
|
-
bunx playwright test --ui
|
|
392
|
-
|
|
393
|
-
# Run specific file
|
|
394
|
-
bunx playwright test auth.spec.ts
|
|
395
|
-
|
|
396
|
-
# Run specific project (viewport)
|
|
397
|
-
bunx playwright test --project="Mobile Chrome"
|
|
398
|
-
|
|
399
|
-
# Debug mode
|
|
400
|
-
bunx playwright test --debug
|
|
401
|
-
|
|
402
|
-
# Generate report
|
|
403
|
-
bunx playwright show-report
|
|
404
|
-
|
|
405
|
-
# Update snapshots
|
|
406
|
-
bunx playwright test --update-snapshots
|
|
407
|
-
|
|
408
|
-
# Trace viewer
|
|
409
|
-
bunx playwright show-trace trace.zip
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
---
|
|
413
|
-
|
|
414
|
-
## Agent Integration
|
|
415
|
-
|
|
416
|
-
This skill is used by:
|
|
417
|
-
|
|
418
|
-
- **playwright-e2e** agent
|
|
419
|
-
- **playwright-fixtures** agent
|
|
420
|
-
- **playwright-page-objects** agent
|
|
421
|
-
- **playwright-multi-viewport** agent
|
|
422
|
-
- **test-coverage** skill
|
|
423
|
-
|
|
424
|
-
---
|
|
425
|
-
|
|
426
|
-
## FORBIDDEN
|
|
427
|
-
|
|
428
|
-
1. **Hardcoded test data** - Generate unique data
|
|
429
|
-
2. **Skipped tests** - Never use `.skip()` or `.only()`
|
|
430
|
-
3. **No cleanup** - Always clean up test data
|
|
431
|
-
4. **Mocked auth** - Use real authentication
|
|
432
|
-
5. **Single viewport** - Test all viewports
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
## Version
|
|
437
|
-
|
|
438
|
-
- **v1.0.0** - Initial implementation based on Playwright best practices
|
|
1
|
+
---
|
|
2
|
+
name: playwright-automation
|
|
3
|
+
description: Playwright testing automation patterns. E2E tests, browser automation, visual testing, API testing. Use when writing automated tests with Playwright.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Playwright Automation - E2E Testing Patterns
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Expert guidance for Playwright:
|
|
12
|
+
|
|
13
|
+
- **E2E Testing** - Full user journey tests
|
|
14
|
+
- **Browser Automation** - Cross-browser testing
|
|
15
|
+
- **Visual Testing** - Screenshots and comparisons
|
|
16
|
+
- **API Testing** - Request mocking and validation
|
|
17
|
+
- **Page Object Model** - Maintainable test architecture
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
tests/
|
|
25
|
+
├── e2e/
|
|
26
|
+
│ ├── fixtures/
|
|
27
|
+
│ │ ├── index.ts # Custom fixtures
|
|
28
|
+
│ │ ├── auth.fixture.ts # Auth helpers
|
|
29
|
+
│ │ └── db.fixture.ts # Database helpers
|
|
30
|
+
│ ├── pages/
|
|
31
|
+
│ │ ├── base.page.ts # Base page object
|
|
32
|
+
│ │ ├── login.page.ts
|
|
33
|
+
│ │ └── dashboard.page.ts
|
|
34
|
+
│ ├── flows/
|
|
35
|
+
│ │ ├── auth.spec.ts # Auth flow tests
|
|
36
|
+
│ │ └── crud.spec.ts # CRUD flow tests
|
|
37
|
+
│ └── api/
|
|
38
|
+
│ └── endpoints.spec.ts # API-only tests
|
|
39
|
+
├── playwright.config.ts
|
|
40
|
+
└── global-setup.ts
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// playwright.config.ts
|
|
49
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
50
|
+
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
testDir: './tests/e2e',
|
|
53
|
+
fullyParallel: true,
|
|
54
|
+
forbidOnly: !!process.env['CI'],
|
|
55
|
+
retries: process.env['CI'] ? 2 : 0,
|
|
56
|
+
workers: process.env['CI'] ? 1 : undefined,
|
|
57
|
+
reporter: 'html',
|
|
58
|
+
|
|
59
|
+
use: {
|
|
60
|
+
baseURL: 'http://localhost:3000',
|
|
61
|
+
trace: 'on-first-retry',
|
|
62
|
+
screenshot: 'only-on-failure',
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
projects: [
|
|
66
|
+
// Desktop browsers
|
|
67
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
68
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
69
|
+
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
70
|
+
|
|
71
|
+
// Mobile viewports
|
|
72
|
+
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
|
|
73
|
+
{ name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
|
|
74
|
+
|
|
75
|
+
// Tablet
|
|
76
|
+
{ name: 'iPad', use: { ...devices['iPad Pro 11'] } },
|
|
77
|
+
],
|
|
78
|
+
|
|
79
|
+
webServer: {
|
|
80
|
+
command: 'bun run dev',
|
|
81
|
+
url: 'http://localhost:3000',
|
|
82
|
+
reuseExistingServer: !process.env['CI'],
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Page Object Model
|
|
90
|
+
|
|
91
|
+
### Base Page
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// tests/e2e/pages/base.page.ts
|
|
95
|
+
import { type Page, type Locator, expect } from '@playwright/test';
|
|
96
|
+
|
|
97
|
+
export abstract class BasePage {
|
|
98
|
+
protected readonly page: Page;
|
|
99
|
+
|
|
100
|
+
constructor(page: Page) {
|
|
101
|
+
this.page = page;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Common elements
|
|
105
|
+
get loadingSpinner(): Locator {
|
|
106
|
+
return this.page.getByTestId('loading-spinner');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get errorMessage(): Locator {
|
|
110
|
+
return this.page.getByTestId('error-message');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get successMessage(): Locator {
|
|
114
|
+
return this.page.getByTestId('success-message');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Common actions
|
|
118
|
+
async waitForLoad(): Promise<void> {
|
|
119
|
+
await this.loadingSpinner.waitFor({ state: 'hidden' });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async expectError(message: string): Promise<void> {
|
|
123
|
+
await expect(this.errorMessage).toContainText(message);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async expectSuccess(message: string): Promise<void> {
|
|
127
|
+
await expect(this.successMessage).toContainText(message);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Navigation
|
|
131
|
+
async goto(path: string): Promise<void> {
|
|
132
|
+
await this.page.goto(path);
|
|
133
|
+
await this.waitForLoad();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Login Page
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// tests/e2e/pages/login.page.ts
|
|
142
|
+
import { type Page, type Locator } from '@playwright/test';
|
|
143
|
+
import { BasePage } from './base.page';
|
|
144
|
+
|
|
145
|
+
export class LoginPage extends BasePage {
|
|
146
|
+
readonly emailInput: Locator;
|
|
147
|
+
readonly passwordInput: Locator;
|
|
148
|
+
readonly submitButton: Locator;
|
|
149
|
+
readonly forgotPasswordLink: Locator;
|
|
150
|
+
|
|
151
|
+
constructor(page: Page) {
|
|
152
|
+
super(page);
|
|
153
|
+
this.emailInput = page.getByTestId('email-input');
|
|
154
|
+
this.passwordInput = page.getByTestId('password-input');
|
|
155
|
+
this.submitButton = page.getByTestId('submit-button');
|
|
156
|
+
this.forgotPasswordLink = page.getByRole('link', { name: /forgot/i });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async goto(): Promise<void> {
|
|
160
|
+
await super.goto('/auth/login');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async login(email: string, password: string): Promise<void> {
|
|
164
|
+
await this.emailInput.fill(email);
|
|
165
|
+
await this.passwordInput.fill(password);
|
|
166
|
+
await this.submitButton.click();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Custom Fixtures
|
|
174
|
+
|
|
175
|
+
### Auth Fixture
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// tests/e2e/fixtures/auth.fixture.ts
|
|
179
|
+
import { test as base, type Page } from '@playwright/test';
|
|
180
|
+
|
|
181
|
+
export interface TestUser {
|
|
182
|
+
name: string;
|
|
183
|
+
email: string;
|
|
184
|
+
password: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function generateTestUser(): TestUser {
|
|
188
|
+
const timestamp = Date.now();
|
|
189
|
+
const random = Math.random().toString(36).substring(7);
|
|
190
|
+
return {
|
|
191
|
+
name: `Test User ${timestamp}`,
|
|
192
|
+
email: `testuser_${timestamp}_${random}@test.com`,
|
|
193
|
+
password: 'TestPassword123!',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function registerUser(page: Page, user: TestUser): Promise<void> {
|
|
198
|
+
await page.goto('/auth/register');
|
|
199
|
+
await page.getByTestId('name-input').fill(user.name);
|
|
200
|
+
await page.getByTestId('email-input').fill(user.email);
|
|
201
|
+
await page.getByTestId('password-input').fill(user.password);
|
|
202
|
+
await page.getByTestId('confirm-password-input').fill(user.password);
|
|
203
|
+
await page.getByTestId('submit-button').click();
|
|
204
|
+
await page.waitForURL(/\/app/, { timeout: 10000 });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function loginUser(page: Page, user: TestUser): Promise<void> {
|
|
208
|
+
await page.goto('/auth/login');
|
|
209
|
+
await page.getByTestId('email-input').fill(user.email);
|
|
210
|
+
await page.getByTestId('password-input').fill(user.password);
|
|
211
|
+
await page.getByTestId('submit-button').click();
|
|
212
|
+
await page.waitForURL(/\/app/, { timeout: 10000 });
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Database Cleanup Fixture
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// tests/e2e/fixtures/db.fixture.ts
|
|
220
|
+
import { test as base } from '@playwright/test';
|
|
221
|
+
import { MongoClient, type Db, type ObjectId } from 'mongodb';
|
|
222
|
+
|
|
223
|
+
type CleanupFixtures = {
|
|
224
|
+
db: Db;
|
|
225
|
+
createdIds: Map<string, ObjectId[]>;
|
|
226
|
+
trackCreated: (collection: string, id: ObjectId) => void;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const test = base.extend<CleanupFixtures>({
|
|
230
|
+
db: async ({}, use) => {
|
|
231
|
+
const client = await MongoClient.connect(process.env['MONGODB_URI']!);
|
|
232
|
+
const db = client.db();
|
|
233
|
+
await use(db);
|
|
234
|
+
await client.close();
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
createdIds: async ({}, use) => {
|
|
238
|
+
await use(new Map());
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
trackCreated: async ({ createdIds }, use) => {
|
|
242
|
+
await use((collection: string, id: ObjectId) => {
|
|
243
|
+
const existing = createdIds.get(collection) || [];
|
|
244
|
+
existing.push(id);
|
|
245
|
+
createdIds.set(collection, existing);
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Auto-cleanup after each test
|
|
251
|
+
test.afterEach(async ({ db, createdIds }) => {
|
|
252
|
+
for (const [collection, ids] of createdIds.entries()) {
|
|
253
|
+
if (ids.length > 0) {
|
|
254
|
+
await db.collection(collection).deleteMany({ _id: { $in: ids } });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export { expect } from '@playwright/test';
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Test Patterns
|
|
265
|
+
|
|
266
|
+
### Auth Flow Test
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// tests/e2e/flows/auth.spec.ts
|
|
270
|
+
import { test, expect } from '../fixtures';
|
|
271
|
+
import { generateTestUser, registerUser, loginUser } from '../fixtures/auth.fixture';
|
|
272
|
+
import { LoginPage } from '../pages/login.page';
|
|
273
|
+
|
|
274
|
+
test.describe('Authentication Flow', () => {
|
|
275
|
+
test('complete registration and login journey', async ({ page, db, trackCreated }) => {
|
|
276
|
+
const user = generateTestUser();
|
|
277
|
+
|
|
278
|
+
// 1. Register
|
|
279
|
+
await registerUser(page, user);
|
|
280
|
+
|
|
281
|
+
// 2. Verify in database
|
|
282
|
+
const dbUser = await db.collection('users').findOne({ email: user.email });
|
|
283
|
+
expect(dbUser).toBeTruthy();
|
|
284
|
+
trackCreated('users', dbUser!._id);
|
|
285
|
+
|
|
286
|
+
// 3. Logout
|
|
287
|
+
await page.getByTestId('logout-button').click();
|
|
288
|
+
await expect(page).toHaveURL('/auth/login');
|
|
289
|
+
|
|
290
|
+
// 4. Login again
|
|
291
|
+
await loginUser(page, user);
|
|
292
|
+
await expect(page).toHaveURL(/\/app/);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('shows error for invalid credentials', async ({ page }) => {
|
|
296
|
+
const loginPage = new LoginPage(page);
|
|
297
|
+
await loginPage.goto();
|
|
298
|
+
|
|
299
|
+
await loginPage.login('invalid@email.com', 'wrongpassword');
|
|
300
|
+
|
|
301
|
+
await loginPage.expectError('Invalid credentials');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Multi-Viewport Test
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// tests/e2e/flows/responsive.spec.ts
|
|
310
|
+
import { test, expect } from '@playwright/test';
|
|
311
|
+
|
|
312
|
+
const viewports = [
|
|
313
|
+
{ name: 'mobile', width: 375, height: 667 },
|
|
314
|
+
{ name: 'tablet', width: 768, height: 1024 },
|
|
315
|
+
{ name: 'desktop', width: 1280, height: 800 },
|
|
316
|
+
] as const;
|
|
317
|
+
|
|
318
|
+
for (const viewport of viewports) {
|
|
319
|
+
test.describe(`Responsive - ${viewport.name}`, () => {
|
|
320
|
+
test.use({ viewport: { width: viewport.width, height: viewport.height } });
|
|
321
|
+
|
|
322
|
+
test('navigation adapts to viewport', async ({ page }) => {
|
|
323
|
+
await page.goto('/');
|
|
324
|
+
|
|
325
|
+
if (viewport.width < 768) {
|
|
326
|
+
// Mobile: hamburger menu
|
|
327
|
+
await expect(page.getByTestId('hamburger-menu')).toBeVisible();
|
|
328
|
+
await expect(page.getByTestId('sidebar')).toBeHidden();
|
|
329
|
+
|
|
330
|
+
// Open mobile menu
|
|
331
|
+
await page.getByTestId('hamburger-menu').click();
|
|
332
|
+
await expect(page.getByTestId('mobile-nav')).toBeVisible();
|
|
333
|
+
} else {
|
|
334
|
+
// Desktop: sidebar visible
|
|
335
|
+
await expect(page.getByTestId('sidebar')).toBeVisible();
|
|
336
|
+
await expect(page.getByTestId('hamburger-menu')).toBeHidden();
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### API Testing
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// tests/e2e/api/endpoints.spec.ts
|
|
347
|
+
import { test, expect } from '@playwright/test';
|
|
348
|
+
|
|
349
|
+
test.describe('API Endpoints', () => {
|
|
350
|
+
test('requires authentication', async ({ request }) => {
|
|
351
|
+
const response = await request.get('/api/users');
|
|
352
|
+
expect(response.status()).toBe(401);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('validates request body', async ({ request }) => {
|
|
356
|
+
const response = await request.post('/api/users', {
|
|
357
|
+
data: { email: 'invalid-email' },
|
|
358
|
+
});
|
|
359
|
+
expect(response.status()).toBe(400);
|
|
360
|
+
|
|
361
|
+
const body = await response.json();
|
|
362
|
+
expect(body.errors).toBeDefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test('creates user with valid data', async ({ request }) => {
|
|
366
|
+
const response = await request.post('/api/users', {
|
|
367
|
+
data: {
|
|
368
|
+
name: 'Test User',
|
|
369
|
+
email: 'test@example.com',
|
|
370
|
+
password: 'Password123!',
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
expect(response.status()).toBe(201);
|
|
374
|
+
|
|
375
|
+
const user = await response.json();
|
|
376
|
+
expect(user.id).toBeDefined();
|
|
377
|
+
expect(user.email).toBe('test@example.com');
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Commands
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# Run all tests
|
|
388
|
+
bunx playwright test
|
|
389
|
+
|
|
390
|
+
# Run with UI
|
|
391
|
+
bunx playwright test --ui
|
|
392
|
+
|
|
393
|
+
# Run specific file
|
|
394
|
+
bunx playwright test auth.spec.ts
|
|
395
|
+
|
|
396
|
+
# Run specific project (viewport)
|
|
397
|
+
bunx playwright test --project="Mobile Chrome"
|
|
398
|
+
|
|
399
|
+
# Debug mode
|
|
400
|
+
bunx playwright test --debug
|
|
401
|
+
|
|
402
|
+
# Generate report
|
|
403
|
+
bunx playwright show-report
|
|
404
|
+
|
|
405
|
+
# Update snapshots
|
|
406
|
+
bunx playwright test --update-snapshots
|
|
407
|
+
|
|
408
|
+
# Trace viewer
|
|
409
|
+
bunx playwright show-trace trace.zip
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Agent Integration
|
|
415
|
+
|
|
416
|
+
This skill is used by:
|
|
417
|
+
|
|
418
|
+
- **playwright-e2e** agent
|
|
419
|
+
- **playwright-fixtures** agent
|
|
420
|
+
- **playwright-page-objects** agent
|
|
421
|
+
- **playwright-multi-viewport** agent
|
|
422
|
+
- **test-coverage** skill
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## FORBIDDEN
|
|
427
|
+
|
|
428
|
+
1. **Hardcoded test data** - Generate unique data
|
|
429
|
+
2. **Skipped tests** - Never use `.skip()` or `.only()`
|
|
430
|
+
3. **No cleanup** - Always clean up test data
|
|
431
|
+
4. **Mocked auth** - Use real authentication
|
|
432
|
+
5. **Single viewport** - Test all viewports
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Version
|
|
437
|
+
|
|
438
|
+
- **v1.0.0** - Initial implementation based on Playwright best practices
|