@bugzy-ai/bugzy 1.9.2 → 1.9.4
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/LICENSE +21 -21
- package/README.md +273 -273
- package/dist/cli/index.cjs +23 -50
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +22 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +20 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -46
- package/dist/index.js.map +1 -1
- package/dist/subagents/index.cjs.map +1 -1
- package/dist/subagents/index.js.map +1 -1
- package/dist/subagents/metadata.cjs.map +1 -1
- package/dist/subagents/metadata.js.map +1 -1
- package/dist/tasks/index.cjs +20 -9
- package/dist/tasks/index.cjs.map +1 -1
- package/dist/tasks/index.js +20 -9
- package/dist/tasks/index.js.map +1 -1
- package/dist/templates/init/.bugzy/runtime/knowledge-base.md +61 -0
- package/dist/templates/init/.bugzy/runtime/knowledge-maintenance-guide.md +97 -0
- package/dist/templates/init/.bugzy/runtime/project-context.md +35 -0
- package/dist/templates/init/.bugzy/runtime/subagent-memory-guide.md +87 -0
- package/dist/templates/init/.bugzy/runtime/templates/test-plan-template.md +50 -0
- package/dist/templates/init/.bugzy/runtime/templates/test-result-schema.md +498 -0
- package/dist/templates/init/.bugzy/runtime/test-execution-strategy.md +535 -0
- package/dist/templates/init/.bugzy/runtime/testing-best-practices.md +632 -0
- package/dist/templates/init/.gitignore-template +25 -0
- package/package.json +95 -95
- package/templates/init/.bugzy/runtime/knowledge-base.md +61 -61
- package/templates/init/.bugzy/runtime/knowledge-maintenance-guide.md +97 -97
- package/templates/init/.bugzy/runtime/project-context.md +35 -35
- package/templates/init/.bugzy/runtime/subagent-memory-guide.md +87 -87
- package/templates/init/.bugzy/runtime/templates/test-plan-template.md +50 -50
- package/templates/init/.bugzy/runtime/templates/test-result-schema.md +498 -498
- package/templates/init/.bugzy/runtime/test-execution-strategy.md +535 -535
- package/templates/init/.bugzy/runtime/testing-best-practices.md +724 -724
- package/templates/init/.env.testdata +18 -18
- package/templates/init/.gitignore-template +24 -24
- package/templates/init/AGENTS.md +155 -155
- package/templates/init/CLAUDE.md +157 -157
- package/templates/init/test-runs/README.md +45 -45
- package/templates/playwright/BasePage.template.ts +190 -190
- package/templates/playwright/auth.setup.template.ts +89 -89
- package/templates/playwright/dataGenerators.helper.template.ts +148 -148
- package/templates/playwright/dateUtils.helper.template.ts +96 -96
- package/templates/playwright/pages.fixture.template.ts +50 -50
- package/templates/playwright/playwright.config.template.ts +97 -97
- package/templates/playwright/reporters/bugzy-reporter.ts +454 -454
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
# Testing Best Practices Reference
|
|
2
|
+
|
|
3
|
+
## Two-Phase Test Automation Workflow
|
|
4
|
+
|
|
5
|
+
**Critical Distinction**: Separate test scenario discovery from automation implementation.
|
|
6
|
+
|
|
7
|
+
### Phase 1: Test Scenario Discovery (WHAT to test)
|
|
8
|
+
|
|
9
|
+
**Goal**: Understand application behavior and identify what needs testing coverage.
|
|
10
|
+
|
|
11
|
+
**Activities**:
|
|
12
|
+
- Explore features and user workflows through manual interaction
|
|
13
|
+
- Identify critical user paths and edge cases
|
|
14
|
+
- Document test scenarios in human-readable format
|
|
15
|
+
- Evaluate automation ROI for each scenario
|
|
16
|
+
- Create manual test case documentation
|
|
17
|
+
|
|
18
|
+
**Output**: Test plan with prioritized scenarios and automation decisions
|
|
19
|
+
|
|
20
|
+
### Phase 2: Automation Implementation (HOW to automate)
|
|
21
|
+
|
|
22
|
+
**Goal**: Build robust test automation framework validated with working tests.
|
|
23
|
+
|
|
24
|
+
**Activities**:
|
|
25
|
+
- Technical exploration to identify correct selectors
|
|
26
|
+
- Create Page Object infrastructure
|
|
27
|
+
- Generate ONE smoke test to validate framework
|
|
28
|
+
- Run and debug until test passes consistently
|
|
29
|
+
- Scale to additional tests only after validation
|
|
30
|
+
|
|
31
|
+
**Output**: Working test automation with validated Page Objects
|
|
32
|
+
|
|
33
|
+
### The "Test One First" Validation Loop
|
|
34
|
+
|
|
35
|
+
**CRITICAL**: Always validate your framework with ONE working test before scaling.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
1. Explore app for selectors (use Playwright MCP or codegen)
|
|
39
|
+
2. Create Page Objects with verified selectors
|
|
40
|
+
3. Write ONE critical path test (e.g., login)
|
|
41
|
+
4. Run the test: npx playwright test <test-file>
|
|
42
|
+
5. If fails → Debug and fix → Go to step 4
|
|
43
|
+
6. If passes → Run 3-5 more times to ensure stability
|
|
44
|
+
7. Once stable → Scale to additional tests
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Why this matters**:
|
|
48
|
+
- Catches framework issues early (config, setup, auth)
|
|
49
|
+
- Validates selectors work in real application
|
|
50
|
+
- Prevents generating 50 broken tests
|
|
51
|
+
- Builds confidence in Page Object reliability
|
|
52
|
+
|
|
53
|
+
**Example validation workflow**:
|
|
54
|
+
```bash
|
|
55
|
+
# Generate ONE test first
|
|
56
|
+
npx playwright test tests/specs/auth/login.spec.ts
|
|
57
|
+
|
|
58
|
+
# Run multiple times to verify stability
|
|
59
|
+
npx playwright test tests/specs/auth/login.spec.ts --repeat-each=5
|
|
60
|
+
|
|
61
|
+
# Check for flakiness
|
|
62
|
+
npx playwright test tests/specs/auth/login.spec.ts --workers=1
|
|
63
|
+
|
|
64
|
+
# Once stable, generate more tests
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Page Object Model (POM) Architecture
|
|
68
|
+
|
|
69
|
+
**Core Principle**: Separate locators, actions, and assertions into distinct layers to isolate UI changes from test logic.
|
|
70
|
+
|
|
71
|
+
### Page Object Structure
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { type Page, type Locator } from '@playwright/test';
|
|
75
|
+
|
|
76
|
+
export class LoginPage {
|
|
77
|
+
readonly page: Page;
|
|
78
|
+
|
|
79
|
+
// Centralized selectors as readonly properties
|
|
80
|
+
readonly emailInput: Locator;
|
|
81
|
+
readonly passwordInput: Locator;
|
|
82
|
+
readonly loginButton: Locator;
|
|
83
|
+
|
|
84
|
+
constructor(page: Page) {
|
|
85
|
+
this.page = page;
|
|
86
|
+
this.emailInput = page.getByLabel('Email');
|
|
87
|
+
this.passwordInput = page.getByLabel('Password');
|
|
88
|
+
this.loginButton = page.getByRole('button', { name: 'Sign In' });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async navigate(): Promise<void> {
|
|
92
|
+
await this.page.goto('/login');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async login(email: string, password: string): Promise<void> {
|
|
96
|
+
await this.emailInput.fill(email);
|
|
97
|
+
await this.passwordInput.fill(password);
|
|
98
|
+
await this.loginButton.click();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Key Rules for Page Objects
|
|
104
|
+
|
|
105
|
+
- ✅ Define all locators as `readonly` properties
|
|
106
|
+
- ✅ Initialize locators in constructor
|
|
107
|
+
- ✅ Use method names that describe actions (login, fillEmail, clickSubmit)
|
|
108
|
+
- ✅ Return data, never assert in page objects
|
|
109
|
+
- ❌ Never put `expect()` assertions in page objects
|
|
110
|
+
- ❌ Never use hardcoded waits (`waitForTimeout`)
|
|
111
|
+
|
|
112
|
+
## Selector Priority (Most to Least Resilient)
|
|
113
|
+
|
|
114
|
+
1. **Role-based**: `page.getByRole('button', { name: 'Submit' })` - Best for semantic HTML
|
|
115
|
+
2. **Label**: `page.getByLabel('Email')` - Perfect for form inputs
|
|
116
|
+
3. **Text**: `page.getByText('Welcome back')` - Good for headings/static content
|
|
117
|
+
4. **Placeholder**: `page.getByPlaceholder('Enter email')` - Inputs without labels
|
|
118
|
+
5. **Test ID**: `page.getByTestId('submit-btn')` - Stable but requires data-testid attributes
|
|
119
|
+
6. **CSS selectors**: `page.locator('.btn-primary')` - Avoid; breaks with styling changes
|
|
120
|
+
|
|
121
|
+
### When to Use Test IDs
|
|
122
|
+
|
|
123
|
+
Add `data-testid` attributes for:
|
|
124
|
+
- Critical user flows (checkout, login, signup)
|
|
125
|
+
- Complex components (data tables, multi-step forms)
|
|
126
|
+
- Elements where role-based selectors are ambiguous
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<button data-testid="checkout-submit">Complete Purchase</button>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
await page.getByTestId('checkout-submit').click();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Playwright Codegen for Selector Discovery
|
|
137
|
+
|
|
138
|
+
**Playwright's built-in codegen is faster and more reliable than manual selector creation.**
|
|
139
|
+
|
|
140
|
+
### Using Codegen
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Start codegen from specific URL
|
|
144
|
+
npx playwright codegen https://your-app.com
|
|
145
|
+
|
|
146
|
+
# With authentication (loads saved state)
|
|
147
|
+
npx playwright codegen --load-storage=tests/.auth/user.json https://your-app.com
|
|
148
|
+
|
|
149
|
+
# Target specific browser
|
|
150
|
+
npx playwright codegen --browser=chromium https://your-app.com
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Workflow**:
|
|
154
|
+
1. Run codegen and interact with your application
|
|
155
|
+
2. Playwright generates test code with verified selectors
|
|
156
|
+
3. Copy generated selectors to your Page Objects
|
|
157
|
+
4. Refactor code to follow Page Object Model pattern
|
|
158
|
+
5. Extract reusable logic to fixtures and helpers
|
|
159
|
+
|
|
160
|
+
### Hybrid Approach: Codegen + AI Refactoring
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
1. Use Playwright codegen → Generates working test with selectors
|
|
164
|
+
2. Use AI (Claude) → Refactor to Page Objects, extract fixtures, add types
|
|
165
|
+
3. Best of both worlds: Reliability (codegen) + Intelligence (AI)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Example**:
|
|
169
|
+
```typescript
|
|
170
|
+
// Raw codegen output
|
|
171
|
+
await page.goto('https://example.com/');
|
|
172
|
+
await page.getByLabel('Email').click();
|
|
173
|
+
await page.getByLabel('Email').fill('test@example.com');
|
|
174
|
+
|
|
175
|
+
// After AI refactoring into Page Object
|
|
176
|
+
class LoginPage {
|
|
177
|
+
readonly emailInput = this.page.getByLabel('Email');
|
|
178
|
+
|
|
179
|
+
async fillEmail(email: string) {
|
|
180
|
+
await this.emailInput.fill(email);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Smoke Test Strategy
|
|
186
|
+
|
|
187
|
+
**Smoke tests are a minimal suite of critical path tests that validate core functionality.**
|
|
188
|
+
|
|
189
|
+
### Characteristics
|
|
190
|
+
|
|
191
|
+
- **Fast**: Target < 5 minutes total execution time
|
|
192
|
+
- **Critical**: Cover must-work features (login, core user flows)
|
|
193
|
+
- **Stable**: High reliability, minimal flakiness
|
|
194
|
+
- **CI/CD**: Run on every commit/pull request
|
|
195
|
+
|
|
196
|
+
### Tagging Smoke Tests
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// tests/specs/auth/login.spec.ts
|
|
200
|
+
test('should login with valid credentials @smoke', async ({ page }) => {
|
|
201
|
+
// Critical path test
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('should show error with invalid password', async ({ page }) => {
|
|
205
|
+
// Not tagged - functional test only
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Running Smoke Tests
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Run only smoke tests
|
|
213
|
+
npx playwright test --grep @smoke
|
|
214
|
+
|
|
215
|
+
# In CI/CD pipeline
|
|
216
|
+
npx playwright test --grep @smoke --workers=2
|
|
217
|
+
|
|
218
|
+
# Smoke tests as gate for full suite
|
|
219
|
+
npx playwright test --grep @smoke && npx playwright test
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Smoke Test Suite Example
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
@smoke test coverage:
|
|
226
|
+
✓ Login with valid credentials
|
|
227
|
+
✓ Navigate to dashboard
|
|
228
|
+
✓ Create new item (core feature)
|
|
229
|
+
✓ View item details
|
|
230
|
+
✓ Logout
|
|
231
|
+
|
|
232
|
+
Target: < 5 minutes, 100% pass rate
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Test Organization
|
|
236
|
+
|
|
237
|
+
### File Structure by Feature
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
tests/
|
|
241
|
+
├── specs/ # Tests organized by feature
|
|
242
|
+
│ ├── auth/
|
|
243
|
+
│ │ └── login.spec.ts
|
|
244
|
+
│ └── checkout/
|
|
245
|
+
│ └── purchase-flow.spec.ts
|
|
246
|
+
├── pages/ # Page Object Models
|
|
247
|
+
│ ├── LoginPage.ts
|
|
248
|
+
│ └── CheckoutPage.ts
|
|
249
|
+
├── components/ # Reusable UI components
|
|
250
|
+
├── fixtures/ # Custom test fixtures
|
|
251
|
+
├── helpers/ # Utility functions
|
|
252
|
+
└── setup/ # Global setup/teardown
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Test Structure with test.step()
|
|
256
|
+
|
|
257
|
+
**REQUIRED**: All tests must use `test.step()` to organize actions into high-level logical phases. This enables:
|
|
258
|
+
- Video navigation by step (users can jump to specific phases in test execution videos)
|
|
259
|
+
- Clear test structure and intent
|
|
260
|
+
- Granular error tracking (know exactly which phase failed)
|
|
261
|
+
- Better debugging with step-level timing
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
test.describe('Purchase flow', () => {
|
|
265
|
+
test.beforeEach(async ({ page }) => {
|
|
266
|
+
// Common setup
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('should complete purchase with credit card', async ({ page }) => {
|
|
270
|
+
const checkoutPage = new CheckoutPage(page);
|
|
271
|
+
|
|
272
|
+
await test.step('Add item to cart', async () => {
|
|
273
|
+
await checkoutPage.addItemToCart('Product A');
|
|
274
|
+
await expect(checkoutPage.cartCount).toHaveText('1');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
await test.step('Navigate to checkout', async () => {
|
|
278
|
+
await checkoutPage.goToCheckout();
|
|
279
|
+
await expect(page).toHaveURL('/checkout');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await test.step('Fill payment information', async () => {
|
|
283
|
+
await checkoutPage.fillPaymentInfo({
|
|
284
|
+
cardNumber: '4111111111111111',
|
|
285
|
+
expiry: '12/25',
|
|
286
|
+
cvv: '123'
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await test.step('Submit order', async () => {
|
|
291
|
+
await checkoutPage.submitOrder();
|
|
292
|
+
await expect(page).toHaveURL('/confirmation');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await test.step('Verify order confirmation', async () => {
|
|
296
|
+
await expect(checkoutPage.confirmationMessage).toBeVisible();
|
|
297
|
+
await expect(checkoutPage.orderNumber).toContain('ORD-');
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Step Granularity Guidelines**:
|
|
304
|
+
- Target **3-7 steps per test** for optimal video navigation
|
|
305
|
+
- Each step should represent a logical phase (e.g., "Login", "Navigate to settings", "Update profile")
|
|
306
|
+
- Avoid micro-steps (e.g., "Click button", "Fill field") - group related actions
|
|
307
|
+
- Step titles should be user-friendly and descriptive
|
|
308
|
+
|
|
309
|
+
## Video-Synchronized Test Steps
|
|
310
|
+
|
|
311
|
+
**REQUIRED for all tests**: Use `test.step()` API to create video-navigable test execution.
|
|
312
|
+
|
|
313
|
+
### Why test.step() is Required
|
|
314
|
+
|
|
315
|
+
Every test generates a video recording with `steps.json` file containing:
|
|
316
|
+
- Step-by-step breakdown of test actions
|
|
317
|
+
- Video timestamps for each step (in seconds from test start)
|
|
318
|
+
- Step status (success/failed)
|
|
319
|
+
- Step duration
|
|
320
|
+
|
|
321
|
+
This enables users to:
|
|
322
|
+
- Click on a step to jump to that point in the video
|
|
323
|
+
- See exactly when and where a test failed
|
|
324
|
+
- Navigate through test execution like a timeline
|
|
325
|
+
- Debug issues by reviewing specific test phases
|
|
326
|
+
|
|
327
|
+
### test.step() Best Practices
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
import { test, expect } from '@playwright/test';
|
|
331
|
+
|
|
332
|
+
test('user can update profile settings', async ({ page }) => {
|
|
333
|
+
const settingsPage = new SettingsPage(page);
|
|
334
|
+
const profilePage = new ProfilePage(page);
|
|
335
|
+
|
|
336
|
+
await test.step('Navigate to settings page', async () => {
|
|
337
|
+
await settingsPage.navigate();
|
|
338
|
+
await expect(settingsPage.pageHeading).toBeVisible();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
await test.step('Open profile section', async () => {
|
|
342
|
+
await settingsPage.clickProfileTab();
|
|
343
|
+
await expect(profilePage.nameInput).toBeVisible();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await test.step('Update profile information', async () => {
|
|
347
|
+
await profilePage.updateName('John Doe');
|
|
348
|
+
await profilePage.updateEmail('john@example.com');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
await test.step('Save changes', async () => {
|
|
352
|
+
await profilePage.clickSaveButton();
|
|
353
|
+
await expect(profilePage.successMessage).toBeVisible();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
await test.step('Verify changes persisted', async () => {
|
|
357
|
+
await page.reload();
|
|
358
|
+
await expect(profilePage.nameInput).toHaveValue('John Doe');
|
|
359
|
+
await expect(profilePage.emailInput).toHaveValue('john@example.com');
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### What Gets Recorded in steps.json
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"steps": [
|
|
369
|
+
{
|
|
370
|
+
"index": 1,
|
|
371
|
+
"timestamp": "2025-11-17T09:26:22.335Z",
|
|
372
|
+
"videoTimeSeconds": 0,
|
|
373
|
+
"action": "Navigate to settings page",
|
|
374
|
+
"status": "success",
|
|
375
|
+
"description": "Navigate to settings page - completed successfully",
|
|
376
|
+
"technicalDetails": "test.step",
|
|
377
|
+
"duration": 1234
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"index": 2,
|
|
381
|
+
"timestamp": "2025-11-17T09:26:23.569Z",
|
|
382
|
+
"videoTimeSeconds": 1,
|
|
383
|
+
"action": "Open profile section",
|
|
384
|
+
"status": "success",
|
|
385
|
+
"description": "Open profile section - completed successfully",
|
|
386
|
+
"technicalDetails": "test.step",
|
|
387
|
+
"duration": 856
|
|
388
|
+
}
|
|
389
|
+
],
|
|
390
|
+
"summary": {
|
|
391
|
+
"totalSteps": 5,
|
|
392
|
+
"successfulSteps": 5,
|
|
393
|
+
"failedSteps": 0,
|
|
394
|
+
"skippedSteps": 0
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Step Naming Conventions
|
|
400
|
+
|
|
401
|
+
✅ **Good step names** (user-friendly, high-level):
|
|
402
|
+
- "Navigate to login page"
|
|
403
|
+
- "Login with valid credentials"
|
|
404
|
+
- "Add item to cart"
|
|
405
|
+
- "Complete checkout process"
|
|
406
|
+
- "Verify order confirmation"
|
|
407
|
+
|
|
408
|
+
❌ **Bad step names** (too technical, too granular):
|
|
409
|
+
- "Click the login button"
|
|
410
|
+
- "Fill email field"
|
|
411
|
+
- "Wait for page load"
|
|
412
|
+
- "Assert element visible"
|
|
413
|
+
- "page.goto('/login')"
|
|
414
|
+
|
|
415
|
+
### Smoke Test Example with test.step()
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// tests/specs/auth/login.spec.ts
|
|
419
|
+
test('should login and navigate through all main pages @smoke', async ({ page }) => {
|
|
420
|
+
const loginPage = new LoginPage(page);
|
|
421
|
+
const dashboardPage = new DashboardPage(page);
|
|
422
|
+
|
|
423
|
+
await test.step('Navigate to login page', async () => {
|
|
424
|
+
await loginPage.navigate();
|
|
425
|
+
await expect(loginPage.pageHeading).toBeVisible();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
await test.step('Login with valid credentials', async () => {
|
|
429
|
+
await loginPage.login(
|
|
430
|
+
process.env.TEST_OWNER_EMAIL!,
|
|
431
|
+
process.env.TEST_OWNER_PASSWORD!
|
|
432
|
+
);
|
|
433
|
+
await page.waitForURL(/.*\/dashboard/);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
await test.step('Navigate to Overview page', async () => {
|
|
437
|
+
await dashboardPage.navigateToOverview();
|
|
438
|
+
await expect(dashboardPage.overviewNavLink).toBeVisible();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
await test.step('Navigate to Settings page', async () => {
|
|
442
|
+
await dashboardPage.navigateToSettings();
|
|
443
|
+
await expect(dashboardPage.settingsNavLink).toBeVisible();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
await test.step('Logout and verify redirect', async () => {
|
|
447
|
+
await dashboardPage.logout();
|
|
448
|
+
await page.waitForURL(/.*\/login/);
|
|
449
|
+
await expect(loginPage.pageHeading).toBeVisible();
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Authentication & Session Management
|
|
455
|
+
|
|
456
|
+
**Always authenticate once and reuse session state** across tests.
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// tests/setup/auth.setup.ts
|
|
460
|
+
import { test as setup } from '@playwright/test';
|
|
461
|
+
|
|
462
|
+
const authFile = 'tests/.auth/user.json';
|
|
463
|
+
|
|
464
|
+
setup('authenticate', async ({ page }) => {
|
|
465
|
+
await page.goto('/login');
|
|
466
|
+
await page.getByLabel('Email').fill(process.env.USER_EMAIL!);
|
|
467
|
+
await page.getByLabel('Password').fill(process.env.USER_PASSWORD!);
|
|
468
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
469
|
+
|
|
470
|
+
await page.waitForURL('/dashboard');
|
|
471
|
+
await page.context().storageState({ path: authFile });
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Configure in `playwright.config.ts`:
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
projects: [
|
|
479
|
+
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
|
|
480
|
+
{
|
|
481
|
+
name: 'chromium',
|
|
482
|
+
use: { storageState: 'tests/.auth/user.json' },
|
|
483
|
+
dependencies: ['setup'],
|
|
484
|
+
},
|
|
485
|
+
]
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## Async Operations & Waiting
|
|
489
|
+
|
|
490
|
+
### Use Built-in Auto-waiting
|
|
491
|
+
|
|
492
|
+
Playwright automatically waits for elements to be:
|
|
493
|
+
- Visible
|
|
494
|
+
- Enabled
|
|
495
|
+
- Stable (not animating)
|
|
496
|
+
- Ready to receive events
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// ✅ GOOD: Auto-waiting
|
|
500
|
+
await page.click('#submit');
|
|
501
|
+
await expect(page.locator('.result')).toBeVisible();
|
|
502
|
+
|
|
503
|
+
// ❌ BAD: Manual arbitrary wait
|
|
504
|
+
await page.click('#submit');
|
|
505
|
+
await page.waitForTimeout(3000);
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Explicit Waiting (when needed)
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
// Wait for element state
|
|
512
|
+
await page.locator('.loading').waitFor({ state: 'hidden' });
|
|
513
|
+
|
|
514
|
+
// Wait for URL change
|
|
515
|
+
await page.waitForURL('**/dashboard');
|
|
516
|
+
|
|
517
|
+
// Wait for network request
|
|
518
|
+
const response = await page.waitForResponse(
|
|
519
|
+
resp => resp.url().includes('/api/data') && resp.status() === 200
|
|
520
|
+
);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
## Common Anti-Patterns to Avoid
|
|
524
|
+
|
|
525
|
+
| ❌ Anti-Pattern | ✅ Correct Approach |
|
|
526
|
+
|----------------|-------------------|
|
|
527
|
+
| `await page.waitForTimeout(3000)` | `await expect(element).toBeVisible()` |
|
|
528
|
+
| `const el = await page.$('.btn')` | `await page.locator('.btn').click()` |
|
|
529
|
+
| Tests depend on execution order | Each test is fully independent |
|
|
530
|
+
| Assertions in Page Objects | Assertions only in test files |
|
|
531
|
+
| `#app > div:nth-child(2) > button` | `page.getByRole('button', { name: 'Submit' })` |
|
|
532
|
+
| `retries: 5` to mask flakiness | `retries: 2` + fix root cause |
|
|
533
|
+
|
|
534
|
+
## Debugging Workflow
|
|
535
|
+
|
|
536
|
+
When a test fails:
|
|
537
|
+
|
|
538
|
+
1. **Reproduce locally**: `npx playwright test failing-test.spec.ts --headed`
|
|
539
|
+
2. **Enable trace**: `npx playwright test --trace on`
|
|
540
|
+
3. **View trace**: `npx playwright show-trace test-results/.../trace.zip`
|
|
541
|
+
4. **Identify failure**: Scrub timeline, check DOM snapshots
|
|
542
|
+
5. **Review network**: Look for failed API calls
|
|
543
|
+
6. **Check console**: JavaScript errors/warnings
|
|
544
|
+
7. **Fix selector**: Use inspector's locator picker
|
|
545
|
+
8. **Verify fix**: Run test 10 times to ensure stability
|
|
546
|
+
|
|
547
|
+
## API Testing for Speed
|
|
548
|
+
|
|
549
|
+
**Use API calls for test setup** (10-20x faster than UI):
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
test('should display user dashboard', async ({ request, page }) => {
|
|
553
|
+
// FAST: Create test data via API
|
|
554
|
+
await request.post('/api/users', {
|
|
555
|
+
data: { name: 'Test User', email: 'test@example.com' }
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// UI: Test the actual user experience
|
|
559
|
+
await page.goto('/dashboard');
|
|
560
|
+
await expect(page.getByText('Test User')).toBeVisible();
|
|
561
|
+
});
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Configuration Essentials
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
// playwright.config.ts
|
|
568
|
+
export default defineConfig({
|
|
569
|
+
testDir: './tests/specs',
|
|
570
|
+
fullyParallel: true,
|
|
571
|
+
retries: process.env.CI ? 2 : 0,
|
|
572
|
+
workers: process.env.CI ? 1 : undefined,
|
|
573
|
+
|
|
574
|
+
timeout: 30000,
|
|
575
|
+
expect: { timeout: 5000 },
|
|
576
|
+
|
|
577
|
+
use: {
|
|
578
|
+
baseURL: process.env.BASE_URL,
|
|
579
|
+
trace: 'on-first-retry',
|
|
580
|
+
screenshot: 'only-on-failure',
|
|
581
|
+
video: 'retain-on-failure',
|
|
582
|
+
actionTimeout: 10000,
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
## Production-Ready Checklist
|
|
588
|
+
|
|
589
|
+
**Configuration:**
|
|
590
|
+
- [ ] Parallel execution enabled (`fullyParallel: true`)
|
|
591
|
+
- [ ] Retry strategy configured (2 in CI, 0 locally)
|
|
592
|
+
- [ ] Base URL from environment variables
|
|
593
|
+
- [ ] Artifact capture optimized (`on-first-retry`)
|
|
594
|
+
|
|
595
|
+
**Architecture:**
|
|
596
|
+
- [ ] Page Object Model for all major pages
|
|
597
|
+
- [ ] Component Objects for reusable UI elements
|
|
598
|
+
- [ ] Custom fixtures for common setup
|
|
599
|
+
- [ ] Tests organized by feature/user journey
|
|
600
|
+
|
|
601
|
+
**Best Practices:**
|
|
602
|
+
- [ ] No `waitForTimeout()` usage
|
|
603
|
+
- [ ] Tests are independent (run in any order)
|
|
604
|
+
- [ ] Assertions in test files, not Page Objects
|
|
605
|
+
- [ ] Role-based selectors prioritized
|
|
606
|
+
- [ ] No hardcoded credentials
|
|
607
|
+
- [ ] Framework validated with ONE working test before scaling
|
|
608
|
+
- [ ] Smoke tests tagged with @smoke for CI/CD
|
|
609
|
+
- [ ] All tests use `test.step()` for video-navigable execution (3-7 steps per test)
|
|
610
|
+
|
|
611
|
+
**Test Independence Validation:**
|
|
612
|
+
- [ ] Each test can run in isolation: `npx playwright test <single-test>`
|
|
613
|
+
- [ ] Tests pass in parallel: `npx playwright test --workers=4`
|
|
614
|
+
- [ ] Tests pass in random order: `npx playwright test --shard=1/3` (run multiple shards)
|
|
615
|
+
- [ ] No shared state between tests (each uses fixtures)
|
|
616
|
+
- [ ] Tests cleanup after themselves (via fixtures or API)
|
|
617
|
+
|
|
618
|
+
**CI/CD:**
|
|
619
|
+
- [ ] Smoke tests run on every commit (`npx playwright test --grep @smoke`)
|
|
620
|
+
- [ ] Full suite runs on pull requests
|
|
621
|
+
- [ ] Artifacts uploaded (reports, traces)
|
|
622
|
+
- [ ] Failure notifications configured
|
|
623
|
+
- [ ] Test results published to PR comments
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
**Remember**: The five critical pillars are:
|
|
628
|
+
1. **Two-Phase Approach** - Separate WHAT to test from HOW to automate
|
|
629
|
+
2. **Test One First** - Validate framework with ONE working test before scaling
|
|
630
|
+
3. **Page Object Model** - Isolate UI changes from test logic
|
|
631
|
+
4. **Role-based selectors** - Resist breakage with semantic HTML
|
|
632
|
+
5. **Authentication state reuse** - Maximize speed and reliability
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Environment files (keep .env.testdata tracked, .env.example managed externally)
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
|
|
5
|
+
# Logs and temporary files
|
|
6
|
+
logs/
|
|
7
|
+
tmp/
|
|
8
|
+
|
|
9
|
+
# Playwright MCP cache
|
|
10
|
+
.playwright-mcp/
|
|
11
|
+
|
|
12
|
+
# Playwright test results
|
|
13
|
+
test-results/
|
|
14
|
+
playwright-report/
|
|
15
|
+
playwright/.cache/
|
|
16
|
+
tests/.auth/
|
|
17
|
+
|
|
18
|
+
# Node modules if using any Node.js tooling
|
|
19
|
+
node_modules/
|
|
20
|
+
.DS_Store
|
|
21
|
+
|
|
22
|
+
# Test result media files
|
|
23
|
+
**/*.webm
|
|
24
|
+
**/*.zip
|
|
25
|
+
**/*.png
|