Complete Claude Code plugin with: - 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report) - 3 specialized agents (test-architect, test-debugger, migration-planner) - 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility) - TestRail MCP server (TypeScript) — 8 tools for bidirectional sync - BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing - Smart hooks (auto-validate tests, auto-detect Playwright projects) - 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests) - Leverages Claude Code built-ins (/batch, /debug, Explore subagent) - Zero-config for core features; TestRail/BrowserStack via env vars - Both TypeScript and JavaScript support throughout Co-authored-by: Leo <leo@openclaw.ai>
138 lines
3.1 KiB
Markdown
138 lines
3.1 KiB
Markdown
# Common Pitfalls (Top 10)
|
|
|
|
## 1. waitForTimeout
|
|
|
|
**Symptom:** Slow, flaky tests.
|
|
|
|
```typescript
|
|
// BAD
|
|
await page.waitForTimeout(3000);
|
|
|
|
// GOOD
|
|
await expect(page.getByTestId('result')).toBeVisible();
|
|
```
|
|
|
|
## 2. Non-Web-First Assertions
|
|
|
|
**Symptom:** Assertions fail on dynamic content.
|
|
|
|
```typescript
|
|
// BAD — checks once, no retry
|
|
const text = await page.textContent('.msg');
|
|
expect(text).toBe('Done');
|
|
|
|
// GOOD — retries until timeout
|
|
await expect(page.getByText('Done')).toBeVisible();
|
|
```
|
|
|
|
## 3. Missing await
|
|
|
|
**Symptom:** Random passes/failures, tests seem to skip steps.
|
|
|
|
```typescript
|
|
// BAD
|
|
page.goto('/dashboard');
|
|
expect(page.getByText('Welcome')).toBeVisible();
|
|
|
|
// GOOD
|
|
await page.goto('/dashboard');
|
|
await expect(page.getByText('Welcome')).toBeVisible();
|
|
```
|
|
|
|
## 4. Hardcoded URLs
|
|
|
|
**Symptom:** Tests break in different environments.
|
|
|
|
```typescript
|
|
// BAD
|
|
await page.goto('http://localhost:3000/login');
|
|
|
|
// GOOD — uses baseURL from config
|
|
await page.goto('/login');
|
|
```
|
|
|
|
## 5. CSS Selectors Instead of Roles
|
|
|
|
**Symptom:** Tests break after CSS refactors.
|
|
|
|
```typescript
|
|
// BAD
|
|
await page.click('#submit-btn');
|
|
|
|
// GOOD
|
|
await page.getByRole('button', { name: 'Submit' }).click();
|
|
```
|
|
|
|
## 6. Shared State Between Tests
|
|
|
|
**Symptom:** Tests pass alone, fail in suite.
|
|
|
|
```typescript
|
|
// BAD — test B depends on test A
|
|
let userId: string;
|
|
test('create user', async () => { userId = '123'; });
|
|
test('edit user', async () => { /* uses userId */ });
|
|
|
|
// GOOD — each test is independent
|
|
test('edit user', async ({ request }) => {
|
|
const res = await request.post('/api/users', { data: { name: 'Test' } });
|
|
const { id } = await res.json();
|
|
// ...
|
|
});
|
|
```
|
|
|
|
## 7. Using networkidle
|
|
|
|
**Symptom:** Tests hang or timeout unpredictably.
|
|
|
|
```typescript
|
|
// BAD — waits for all network activity to stop
|
|
await page.goto('/dashboard', { waitUntil: 'networkidle' });
|
|
|
|
// GOOD — wait for specific content
|
|
await page.goto('/dashboard');
|
|
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
```
|
|
|
|
## 8. Not Waiting for Navigation
|
|
|
|
**Symptom:** Assertions run on wrong page.
|
|
|
|
```typescript
|
|
// BAD — click navigates but we don't wait
|
|
await page.getByRole('link', { name: 'Settings' }).click();
|
|
await expect(page.getByRole('heading')).toHaveText('Settings');
|
|
|
|
// GOOD — wait for URL change
|
|
await page.getByRole('link', { name: 'Settings' }).click();
|
|
await expect(page).toHaveURL('/settings');
|
|
await expect(page.getByRole('heading')).toHaveText('Settings');
|
|
```
|
|
|
|
## 9. Testing Implementation, Not Behavior
|
|
|
|
**Symptom:** Tests break on every refactor.
|
|
|
|
```typescript
|
|
// BAD — tests CSS class (implementation detail)
|
|
await expect(page.locator('.btn')).toHaveClass('btn-primary active');
|
|
|
|
// GOOD — tests what the user sees
|
|
await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled();
|
|
```
|
|
|
|
## 10. No Error Case Tests
|
|
|
|
**Symptom:** App breaks on errors but all tests pass.
|
|
|
|
```typescript
|
|
// Missing: what happens when the API fails?
|
|
test('should handle API error', async ({ page }) => {
|
|
await page.route('**/api/data', (route) =>
|
|
route.fulfill({ status: 500 })
|
|
);
|
|
await page.goto('/dashboard');
|
|
await expect(page.getByText(/error|try again/i)).toBeVisible();
|
|
});
|
|
```
|