Initial commit
This commit is contained in:
378
skills/test-debugger/resources/common-errors.md
Normal file
378
skills/test-debugger/resources/common-errors.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# Common Playwright Errors and Quick Fixes
|
||||
|
||||
## Timeout Errors
|
||||
|
||||
### Error: "Timeout 30000ms exceeded waiting for selector"
|
||||
|
||||
**Cause:** Element not found within timeout period
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Add explicit wait
|
||||
await page.waitForSelector('[data-testid="element"]', { state: 'visible' });
|
||||
|
||||
// 2. Verify data-testid is correct
|
||||
const count = await page.locator('[data-testid="element"]').count();
|
||||
console.log('Found', count, 'elements');
|
||||
|
||||
// 3. Wait for page to load first
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// 4. Increase timeout if legitimately slow
|
||||
await page.locator('[data-testid="element"]').waitFor({
|
||||
state: 'visible',
|
||||
timeout: 60000
|
||||
});
|
||||
```
|
||||
|
||||
### Error: "Timeout 30000ms exceeded waiting for navigation"
|
||||
|
||||
**Cause:** Page navigation taking too long or not happening
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for specific URL
|
||||
await page.waitForURL('/expected-path', { timeout: 45000 });
|
||||
|
||||
// 2. Wait for load state
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 3. Check if navigation actually occurs
|
||||
console.log('Current URL:', page.url());
|
||||
```
|
||||
|
||||
## Selector Errors
|
||||
|
||||
### Error: "strict mode violation: locator resolved to X elements"
|
||||
|
||||
**Cause:** Multiple elements match the selector
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Use .first() or .last()
|
||||
await page.locator('[data-testid="item"]').first().click();
|
||||
|
||||
// 2. Use .nth() for specific element
|
||||
await page.locator('[data-testid="item"]').nth(2).click();
|
||||
|
||||
// 3. Make selector more specific (avoid if possible)
|
||||
await page.locator('[data-testid="item"][aria-selected="true"]').click();
|
||||
|
||||
// 4. Filter locators
|
||||
await page.locator('[data-testid="item"]').filter({ hasText: 'Active' }).click();
|
||||
```
|
||||
|
||||
### Error: "Element not found"
|
||||
|
||||
**Cause:** Element doesn't exist or wrong selector
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Verify element exists
|
||||
const exists = await page.locator('[data-testid="element"]').count() > 0;
|
||||
console.log('Element exists:', exists);
|
||||
|
||||
// 2. Check for typos in data-testid
|
||||
// Verify in HTML: <button data-testid="correct-id">
|
||||
|
||||
// 3. Wait for element to be added to DOM
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'attached' });
|
||||
|
||||
// 4. Check if in iframe
|
||||
const frame = page.frameLocator('[data-testid="app-frame"]');
|
||||
await frame.locator('[data-testid="element"]').click();
|
||||
```
|
||||
|
||||
## Assertion Errors
|
||||
|
||||
### Error: "expect(received).toBeVisible() - Expected visible but received hidden"
|
||||
|
||||
**Cause:** Element exists but is not visible
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for element to become visible
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('[data-testid="element"]')).toBeVisible();
|
||||
|
||||
// 2. Check visibility with timeout
|
||||
await expect(page.locator('[data-testid="element"]')).toBeVisible({
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 3. Debug visibility
|
||||
const isVisible = await page.locator('[data-testid="element"]').isVisible();
|
||||
console.log('Is visible:', isVisible);
|
||||
```
|
||||
|
||||
### Error: "expect(received).toContainText() - Expected substring not found"
|
||||
|
||||
**Cause:** Element doesn't contain expected text or not loaded yet
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for text to appear
|
||||
await expect(page.locator('[data-testid="element"]')).toContainText('Expected', {
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 2. Wait for network before checking
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.locator('[data-testid="element"]')).toContainText('Expected');
|
||||
|
||||
// 3. Check actual text content
|
||||
const text = await page.locator('[data-testid="element"]').textContent();
|
||||
console.log('Actual text:', text);
|
||||
```
|
||||
|
||||
## Click Errors
|
||||
|
||||
### Error: "locator.click: Target closed"
|
||||
|
||||
**Cause:** Element or page disappeared before click completed
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for element to be stable
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
await page.waitForTimeout(100); // Allow animations to settle
|
||||
await page.locator('[data-testid="element"]').click();
|
||||
|
||||
// 2. Use force click if element is covered
|
||||
await page.locator('[data-testid="element"]').click({ force: true });
|
||||
|
||||
// 3. Check if element is being removed
|
||||
const count = await page.locator('[data-testid="element"]').count();
|
||||
console.log('Element count before click:', count);
|
||||
```
|
||||
|
||||
### Error: "Element is not clickable"
|
||||
|
||||
**Cause:** Element is disabled, covered, or not in viewport
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Check if element is enabled
|
||||
await expect(page.locator('[data-testid="button"]')).toBeEnabled();
|
||||
await page.locator('[data-testid="button"]').click();
|
||||
|
||||
// 2. Scroll into view
|
||||
await page.locator('[data-testid="element"]').scrollIntoViewIfNeeded();
|
||||
await page.locator('[data-testid="element"]').click();
|
||||
|
||||
// 3. Wait for element to be clickable
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
await expect(page.locator('[data-testid="element"]')).toBeEnabled();
|
||||
await page.locator('[data-testid="element"]').click();
|
||||
```
|
||||
|
||||
## Network Errors
|
||||
|
||||
### Error: "net::ERR_CONNECTION_REFUSED"
|
||||
|
||||
**Cause:** Server is not running or wrong URL
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Verify server is running
|
||||
// Check if dev server is started: npm run dev
|
||||
|
||||
// 2. Check baseURL in playwright.config.ts
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000', // Correct port?
|
||||
}
|
||||
|
||||
// 3. Use full URL if baseURL not set
|
||||
await page.goto('http://localhost:3000/path');
|
||||
```
|
||||
|
||||
### Error: "Navigation failed because page was closed"
|
||||
|
||||
**Cause:** Page closed prematurely during navigation
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Don't close page too early
|
||||
// Remove premature page.close() calls
|
||||
|
||||
// 2. Wait for navigation to complete
|
||||
await page.goto('/path');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// 3. Check for popup blockers or redirects
|
||||
```
|
||||
|
||||
## Fill/Type Errors
|
||||
|
||||
### Error: "Cannot type into a non-editable element"
|
||||
|
||||
**Cause:** Trying to fill a non-input element
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Verify element is an input/textarea
|
||||
const tagName = await page.locator('[data-testid="element"]').evaluate(
|
||||
el => el.tagName
|
||||
);
|
||||
console.log('Tag name:', tagName); // Should be INPUT or TEXTAREA
|
||||
|
||||
// 2. Check if element is contenteditable
|
||||
const isEditable = await page.locator('[data-testid="element"]').evaluate(
|
||||
el => el.contentEditable
|
||||
);
|
||||
|
||||
// 3. Use correct element selector
|
||||
// Ensure data-testid is on the input, not its container
|
||||
```
|
||||
|
||||
### Error: "Element is disabled"
|
||||
|
||||
**Cause:** Trying to interact with disabled element
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for element to be enabled
|
||||
await expect(page.locator('[data-testid="input"]')).toBeEnabled();
|
||||
await page.locator('[data-testid="input"]').fill('value');
|
||||
|
||||
// 2. Check why element is disabled
|
||||
const isDisabled = await page.locator('[data-testid="input"]').isDisabled();
|
||||
console.log('Is disabled:', isDisabled);
|
||||
|
||||
// 3. Ensure prerequisites are met (e.g., terms accepted)
|
||||
await page.locator('[data-testid="terms-checkbox"]').check();
|
||||
await page.locator('[data-testid="submit"]').click();
|
||||
```
|
||||
|
||||
## Frame/Iframe Errors
|
||||
|
||||
### Error: "Frame was detached"
|
||||
|
||||
**Cause:** Interacting with iframe that was removed
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Re-acquire frame reference
|
||||
const frame = page.frameLocator('[data-testid="app-frame"]');
|
||||
await frame.locator('[data-testid="element"]').click();
|
||||
|
||||
// 2. Wait for frame to be ready
|
||||
await page.frameLocator('[data-testid="app-frame"]').locator('body').waitFor();
|
||||
|
||||
// 3. Check if frame exists
|
||||
const frameCount = await page.frames().length;
|
||||
console.log('Frame count:', frameCount);
|
||||
```
|
||||
|
||||
## Race Condition Errors
|
||||
|
||||
### Error: Test passes sometimes, fails other times
|
||||
|
||||
**Cause:** Race condition / flaky test
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Wait for network to settle
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 2. Wait for specific API calls
|
||||
await page.waitForResponse('**/api/data');
|
||||
|
||||
// 3. Add explicit waits
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
|
||||
// 4. Increase timeouts
|
||||
await expect(page.locator('[data-testid="result"]')).toBeVisible({
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
// 5. Enable retries in playwright.config.ts
|
||||
retries: 2,
|
||||
```
|
||||
|
||||
## Context/State Errors
|
||||
|
||||
### Error: "localStorage is not defined"
|
||||
|
||||
**Cause:** Accessing localStorage before page is loaded
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Navigate to page first
|
||||
await page.goto('/');
|
||||
await page.evaluate(() => localStorage.setItem('key', 'value'));
|
||||
|
||||
// 2. Use context.addInitScript for early setup
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem('key', 'value');
|
||||
});
|
||||
```
|
||||
|
||||
### Error: "Test passes in isolation but fails in suite"
|
||||
|
||||
**Cause:** Test pollution / shared state
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// 1. Clear state in beforeEach
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.context().clearCookies();
|
||||
await page.evaluate(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
});
|
||||
|
||||
// 2. Use test.describe.serial for dependent tests
|
||||
test.describe.serial('Login flow', () => {
|
||||
// Tests run in order
|
||||
});
|
||||
|
||||
// 3. Create fresh context for each test
|
||||
test.use({ storageState: undefined });
|
||||
```
|
||||
|
||||
## Configuration Errors
|
||||
|
||||
### Error: "Cannot find module '@playwright/test'"
|
||||
|
||||
**Cause:** Playwright not installed
|
||||
|
||||
**Quick Fixes:**
|
||||
```bash
|
||||
# Install Playwright
|
||||
npm install -D @playwright/test
|
||||
|
||||
# Install browsers
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
### Error: "No tests found"
|
||||
|
||||
**Cause:** Test files not matching pattern
|
||||
|
||||
**Quick Fixes:**
|
||||
```typescript
|
||||
// In playwright.config.ts
|
||||
testDir: './tests',
|
||||
testMatch: '**/*.spec.ts', // Or *.test.ts
|
||||
```
|
||||
|
||||
## Debugging Commands
|
||||
|
||||
```bash
|
||||
# Run with UI mode
|
||||
npx playwright test --ui
|
||||
|
||||
# Run in headed mode
|
||||
npx playwright test --headed
|
||||
|
||||
# Debug specific test
|
||||
npx playwright test --debug test-file.spec.ts
|
||||
|
||||
# Show trace
|
||||
npx playwright show-trace trace.zip
|
||||
|
||||
# Generate code
|
||||
npx playwright codegen localhost:3000
|
||||
```
|
||||
250
skills/test-debugger/resources/debugging-checklist.md
Normal file
250
skills/test-debugger/resources/debugging-checklist.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Playwright Test Debugging Checklist
|
||||
|
||||
## Initial Assessment
|
||||
|
||||
- [ ] Read the error message carefully
|
||||
- [ ] Note the failing test name and location
|
||||
- [ ] Check if test fails consistently or intermittently
|
||||
- [ ] Identify which browser(s) are failing
|
||||
- [ ] Check if failure is local only or also in CI
|
||||
|
||||
## Error Type Identification
|
||||
|
||||
### Timeout Errors
|
||||
- [ ] Check if element exists with correct data-testid
|
||||
- [ ] Verify page has loaded completely
|
||||
- [ ] Add explicit wait before interaction
|
||||
- [ ] Check if network requests are pending
|
||||
- [ ] Increase timeout if operation is legitimately slow
|
||||
|
||||
### Selector Errors
|
||||
- [ ] Verify data-testid in HTML matches test code
|
||||
- [ ] Check if multiple elements have the same testid
|
||||
- [ ] Ensure element is not in an iframe
|
||||
- [ ] Confirm element is added to DOM (not removed)
|
||||
- [ ] Use Playwright Inspector to test selector
|
||||
|
||||
### Assertion Errors
|
||||
- [ ] Verify expected value is correct
|
||||
- [ ] Check if element exists and is visible
|
||||
- [ ] Add wait before assertion
|
||||
- [ ] Check if content is populated asynchronously
|
||||
- [ ] Increase assertion timeout if needed
|
||||
|
||||
### Navigation Errors
|
||||
- [ ] Check if URL is correct
|
||||
- [ ] Verify server is running
|
||||
- [ ] Check for network issues
|
||||
- [ ] Ensure proper wait after navigation
|
||||
- [ ] Look for redirects or authentication requirements
|
||||
|
||||
## Element Investigation
|
||||
|
||||
- [ ] Count elements matching selector (should be 1)
|
||||
```typescript
|
||||
const count = await page.locator('[data-testid="element"]').count();
|
||||
console.log('Element count:', count);
|
||||
```
|
||||
|
||||
- [ ] Check element visibility
|
||||
```typescript
|
||||
const isVisible = await page.locator('[data-testid="element"]').isVisible();
|
||||
console.log('Is visible:', isVisible);
|
||||
```
|
||||
|
||||
- [ ] Check element state
|
||||
```typescript
|
||||
const isEnabled = await page.locator('[data-testid="element"]').isEnabled();
|
||||
console.log('Is enabled:', isEnabled);
|
||||
```
|
||||
|
||||
- [ ] Get element attributes
|
||||
```typescript
|
||||
const testId = await page.locator('[data-testid="element"]').getAttribute('data-testid');
|
||||
console.log('data-testid:', testId);
|
||||
```
|
||||
|
||||
## Timing Investigation
|
||||
|
||||
- [ ] Add waits before interactions
|
||||
```typescript
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
```
|
||||
|
||||
- [ ] Wait for page load
|
||||
```typescript
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForLoadState('networkidle');
|
||||
```
|
||||
|
||||
- [ ] Wait for specific API calls
|
||||
```typescript
|
||||
await page.waitForResponse('**/api/endpoint');
|
||||
```
|
||||
|
||||
- [ ] Check if animations/transitions are in progress
|
||||
```typescript
|
||||
await page.waitForTimeout(500); // Only for testing, remove later
|
||||
```
|
||||
|
||||
## Interactive Debugging
|
||||
|
||||
- [ ] Run test in headed mode
|
||||
```bash
|
||||
npx playwright test --headed
|
||||
```
|
||||
|
||||
- [ ] Use debug mode
|
||||
```bash
|
||||
npx playwright test --debug
|
||||
```
|
||||
|
||||
- [ ] Add `page.pause()` in test
|
||||
```typescript
|
||||
await page.pause(); // Pauses execution for manual inspection
|
||||
```
|
||||
|
||||
- [ ] Enable slow motion
|
||||
```typescript
|
||||
// In playwright.config.ts
|
||||
use: {
|
||||
launchOptions: {
|
||||
slowMo: 1000, // Slow down by 1 second
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Take screenshots for inspection
|
||||
```typescript
|
||||
await page.screenshot({ path: 'debug-screenshot.png', fullPage: true });
|
||||
```
|
||||
|
||||
## Trace Analysis
|
||||
|
||||
- [ ] Enable trace recording
|
||||
```typescript
|
||||
// In playwright.config.ts
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] Open trace viewer
|
||||
```bash
|
||||
npx playwright show-trace trace.zip
|
||||
```
|
||||
|
||||
- [ ] Check trace for:
|
||||
- Network requests
|
||||
- Console logs
|
||||
- Screenshots at each step
|
||||
- DOM snapshots
|
||||
- Action timeline
|
||||
|
||||
## Test Isolation Check
|
||||
|
||||
- [ ] Run test in isolation (comment out other tests)
|
||||
- [ ] Check if test passes when run alone
|
||||
- [ ] Verify no shared state between tests
|
||||
- [ ] Ensure proper cleanup in `afterEach`
|
||||
- [ ] Check for global state mutations
|
||||
|
||||
## Environment Differences
|
||||
|
||||
### Local vs CI
|
||||
- [ ] Run test in headless mode locally
|
||||
```bash
|
||||
npx playwright test --headed=false
|
||||
```
|
||||
|
||||
- [ ] Check viewport size matches CI
|
||||
```typescript
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
```
|
||||
|
||||
- [ ] Compare timeouts (CI may need longer)
|
||||
- [ ] Check if CI has different environment variables
|
||||
|
||||
### Browser Differences
|
||||
- [ ] Test in all configured browsers
|
||||
```bash
|
||||
npx playwright test --project=chromium
|
||||
npx playwright test --project=firefox
|
||||
npx playwright test --project=webkit
|
||||
```
|
||||
|
||||
- [ ] Check for browser-specific issues
|
||||
- [ ] Verify CSS compatibility
|
||||
- [ ] Test JavaScript feature support
|
||||
|
||||
## Common Quick Fixes
|
||||
|
||||
### Fix #1: Add Explicit Wait
|
||||
```typescript
|
||||
// Before
|
||||
await page.locator('[data-testid="element"]').click();
|
||||
|
||||
// After
|
||||
await page.locator('[data-testid="element"]').waitFor({ state: 'visible' });
|
||||
await page.locator('[data-testid="element"]').click();
|
||||
```
|
||||
|
||||
### Fix #2: Wait for Network
|
||||
```typescript
|
||||
// Before
|
||||
await page.locator('[data-testid="submit"]').click();
|
||||
await expect(page.locator('[data-testid="result"]')).toBeVisible();
|
||||
|
||||
// After
|
||||
await page.locator('[data-testid="submit"]').click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.locator('[data-testid="result"]')).toBeVisible();
|
||||
```
|
||||
|
||||
### Fix #3: Handle Multiple Elements
|
||||
```typescript
|
||||
// Before
|
||||
await page.locator('[data-testid="item"]').click(); // Error if multiple
|
||||
|
||||
// After
|
||||
await page.locator('[data-testid="item"]').first().click();
|
||||
```
|
||||
|
||||
### Fix #4: Increase Timeout
|
||||
```typescript
|
||||
// Before
|
||||
await expect(page.locator('[data-testid="slow-element"]')).toBeVisible();
|
||||
|
||||
// After
|
||||
await expect(page.locator('[data-testid="slow-element"]')).toBeVisible({
|
||||
timeout: 15000
|
||||
});
|
||||
```
|
||||
|
||||
### Fix #5: Check Element State
|
||||
```typescript
|
||||
// Before
|
||||
await page.locator('[data-testid="button"]').click();
|
||||
|
||||
// After
|
||||
await expect(page.locator('[data-testid="button"]')).toBeEnabled();
|
||||
await page.locator('[data-testid="button"]').click();
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
- [ ] Run test 5+ times to verify consistency
|
||||
- [ ] Test in all browsers
|
||||
- [ ] Run full test suite to check for regressions
|
||||
- [ ] Test in CI environment
|
||||
- [ ] Document the fix for future reference
|
||||
|
||||
## Prevention
|
||||
|
||||
- [ ] Add explicit waits where needed
|
||||
- [ ] Use only data-testid locators
|
||||
- [ ] Ensure test isolation
|
||||
- [ ] Add retry logic in config
|
||||
- [ ] Review and refactor flaky tests regularly
|
||||
- [ ] Keep tests simple and focused
|
||||
- [ ] Maintain good test hygiene (cleanup, fixtures)
|
||||
480
skills/test-debugger/resources/playwright-commands.md
Normal file
480
skills/test-debugger/resources/playwright-commands.md
Normal file
@@ -0,0 +1,480 @@
|
||||
# Useful Playwright Debugging Commands
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npx playwright test
|
||||
|
||||
# Run specific test file
|
||||
npx playwright test login.spec.ts
|
||||
|
||||
# Run tests matching pattern
|
||||
npx playwright test --grep "login"
|
||||
|
||||
# Run tests in specific browser
|
||||
npx playwright test --project=chromium
|
||||
npx playwright test --project=firefox
|
||||
npx playwright test --project=webkit
|
||||
|
||||
# Run tests in headed mode (see browser)
|
||||
npx playwright test --headed
|
||||
|
||||
# Run specific test by line number
|
||||
npx playwright test login.spec.ts:42
|
||||
|
||||
# Run tests in parallel
|
||||
npx playwright test --workers=4
|
||||
|
||||
# Run tests sequentially
|
||||
npx playwright test --workers=1
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
```bash
|
||||
# Debug mode (opens inspector)
|
||||
npx playwright test --debug
|
||||
|
||||
# Debug specific test
|
||||
npx playwright test login.spec.ts --debug
|
||||
|
||||
# UI Mode (interactive)
|
||||
npx playwright test --ui
|
||||
|
||||
# Step through tests
|
||||
npx playwright test --debug --headed
|
||||
|
||||
# Playwright Inspector
|
||||
PWDEBUG=1 npx playwright test
|
||||
```
|
||||
|
||||
## Code Generation
|
||||
|
||||
```bash
|
||||
# Generate test code by recording actions
|
||||
npx playwright codegen
|
||||
|
||||
# Generate code for specific URL
|
||||
npx playwright codegen https://example.com
|
||||
|
||||
# Generate with specific device
|
||||
npx playwright codegen --device="iPhone 12"
|
||||
|
||||
# Generate with authentication
|
||||
npx playwright codegen --save-storage=auth.json
|
||||
```
|
||||
|
||||
## Trace Viewing
|
||||
|
||||
```bash
|
||||
# Show trace file
|
||||
npx playwright show-trace trace.zip
|
||||
|
||||
# Open trace from test results
|
||||
npx playwright show-trace test-results/login-test/trace.zip
|
||||
|
||||
# View trace with network data
|
||||
npx playwright show-trace trace.zip --network
|
||||
```
|
||||
|
||||
## Report Viewing
|
||||
|
||||
```bash
|
||||
# Open HTML report
|
||||
npx playwright show-report
|
||||
|
||||
# Open specific report
|
||||
npx playwright show-report ./playwright-report
|
||||
```
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
```bash
|
||||
# Install Playwright
|
||||
npm init playwright@latest
|
||||
|
||||
# Install Playwright Test
|
||||
npm install -D @playwright/test
|
||||
|
||||
# Install browsers
|
||||
npx playwright install
|
||||
|
||||
# Install specific browser
|
||||
npx playwright install chromium
|
||||
npx playwright install firefox
|
||||
npx playwright install webkit
|
||||
|
||||
# Install with dependencies (Linux)
|
||||
npx playwright install --with-deps
|
||||
|
||||
# Update Playwright
|
||||
npm install -D @playwright/test@latest
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
# Run with custom config
|
||||
npx playwright test --config=custom.config.ts
|
||||
|
||||
# List all projects
|
||||
npx playwright test --list
|
||||
|
||||
# Show configuration
|
||||
npx playwright show-config
|
||||
```
|
||||
|
||||
## Useful Test Options
|
||||
|
||||
```bash
|
||||
# Run tests with retries
|
||||
npx playwright test --retries=3
|
||||
|
||||
# Set timeout
|
||||
npx playwright test --timeout=60000
|
||||
|
||||
# Run tests with maximum failures
|
||||
npx playwright test --max-failures=5
|
||||
|
||||
# Run only failed tests
|
||||
npx playwright test --last-failed
|
||||
|
||||
# Update snapshots
|
||||
npx playwright test --update-snapshots
|
||||
|
||||
# Ignore snapshots
|
||||
npx playwright test --ignore-snapshots
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Enable debug logs
|
||||
DEBUG=pw:api npx playwright test
|
||||
|
||||
# Enable verbose logging
|
||||
DEBUG=* npx playwright test
|
||||
|
||||
# Set browser
|
||||
BROWSER=firefox npx playwright test
|
||||
|
||||
# Set headed mode
|
||||
HEADED=1 npx playwright test
|
||||
|
||||
# Disable parallel execution
|
||||
PWTEST_PARALLEL=0 npx playwright test
|
||||
```
|
||||
|
||||
## In-Test Debugging Commands
|
||||
|
||||
### Console Logging
|
||||
|
||||
```typescript
|
||||
// Log to console
|
||||
console.log('Debug message:', value);
|
||||
|
||||
// Log page URL
|
||||
console.log('Current URL:', page.url());
|
||||
|
||||
// Log element count
|
||||
const count = await page.locator('[data-testid="item"]').count();
|
||||
console.log('Element count:', count);
|
||||
|
||||
// Log element text
|
||||
const text = await page.locator('[data-testid="element"]').textContent();
|
||||
console.log('Element text:', text);
|
||||
|
||||
// Log all text contents
|
||||
const allText = await page.locator('[data-testid="item"]').allTextContents();
|
||||
console.log('All texts:', allText);
|
||||
```
|
||||
|
||||
### Page Pause
|
||||
|
||||
```typescript
|
||||
// Pause test execution (opens inspector)
|
||||
await page.pause();
|
||||
|
||||
// Pause on specific condition
|
||||
if (someCondition) {
|
||||
await page.pause();
|
||||
}
|
||||
```
|
||||
|
||||
### Screenshots
|
||||
|
||||
```typescript
|
||||
// Take screenshot
|
||||
await page.screenshot({ path: 'screenshot.png' });
|
||||
|
||||
// Full page screenshot
|
||||
await page.screenshot({ path: 'screenshot.png', fullPage: true });
|
||||
|
||||
// Element screenshot
|
||||
await page.locator('[data-testid="element"]').screenshot({ path: 'element.png' });
|
||||
|
||||
// Screenshot with timestamp
|
||||
const timestamp = Date.now();
|
||||
await page.screenshot({ path: `debug-${timestamp}.png` });
|
||||
```
|
||||
|
||||
### Video Recording
|
||||
|
||||
```typescript
|
||||
// In playwright.config.ts
|
||||
use: {
|
||||
video: 'on', // or 'retain-on-failure'
|
||||
}
|
||||
|
||||
// In test
|
||||
const path = await page.video()?.path();
|
||||
console.log('Video saved at:', path);
|
||||
```
|
||||
|
||||
### Trace Recording
|
||||
|
||||
```typescript
|
||||
// Start tracing
|
||||
await context.tracing.start({ screenshots: true, snapshots: true });
|
||||
|
||||
// Stop tracing and save
|
||||
await context.tracing.stop({ path: 'trace.zip' });
|
||||
|
||||
// Or in playwright.config.ts
|
||||
use: {
|
||||
trace: 'on-first-retry', // or 'on' for all tests
|
||||
}
|
||||
```
|
||||
|
||||
### Evaluate JavaScript
|
||||
|
||||
```typescript
|
||||
// Execute JavaScript in page context
|
||||
const result = await page.evaluate(() => {
|
||||
return document.title;
|
||||
});
|
||||
console.log('Page title:', result);
|
||||
|
||||
// With parameters
|
||||
const result = await page.evaluate((text) => {
|
||||
return document.body.textContent?.includes(text);
|
||||
}, 'search term');
|
||||
|
||||
// Get element properties
|
||||
const value = await page.locator('[data-testid="input"]').evaluate(
|
||||
(el: HTMLInputElement) => el.value
|
||||
);
|
||||
```
|
||||
|
||||
### Wait Commands
|
||||
|
||||
```typescript
|
||||
// Wait for timeout (avoid in production)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Wait for selector
|
||||
await page.waitForSelector('[data-testid="element"]');
|
||||
|
||||
// Wait for URL
|
||||
await page.waitForURL('/dashboard');
|
||||
await page.waitForURL(/\/user\/\d+/);
|
||||
|
||||
// Wait for load state
|
||||
await page.waitForLoadState('load');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Wait for response
|
||||
await page.waitForResponse('**/api/data');
|
||||
await page.waitForResponse(response =>
|
||||
response.url().includes('/api/') && response.status() === 200
|
||||
);
|
||||
|
||||
// Wait for request
|
||||
await page.waitForRequest('**/api/users');
|
||||
|
||||
// Wait for function
|
||||
await page.waitForFunction(() => document.querySelectorAll('.item').length > 5);
|
||||
|
||||
// Wait for event
|
||||
await page.waitForEvent('response');
|
||||
await page.waitForEvent('request');
|
||||
```
|
||||
|
||||
### Network Debugging
|
||||
|
||||
```typescript
|
||||
// Listen to all requests
|
||||
page.on('request', request =>
|
||||
console.log('>>', request.method(), request.url())
|
||||
);
|
||||
|
||||
// Listen to all responses
|
||||
page.on('response', response =>
|
||||
console.log('<<', response.status(), response.url())
|
||||
);
|
||||
|
||||
// Listen to console messages
|
||||
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
|
||||
|
||||
// Listen to page errors
|
||||
page.on('pageerror', error => console.log('PAGE ERROR:', error));
|
||||
|
||||
// Intercept and log requests
|
||||
await page.route('**/api/**', route => {
|
||||
console.log('API Request:', route.request().url());
|
||||
route.continue();
|
||||
});
|
||||
```
|
||||
|
||||
### State Inspection
|
||||
|
||||
```typescript
|
||||
// Get all cookies
|
||||
const cookies = await context.cookies();
|
||||
console.log('Cookies:', cookies);
|
||||
|
||||
// Get localStorage
|
||||
const localStorage = await page.evaluate(() => {
|
||||
return JSON.stringify(window.localStorage);
|
||||
});
|
||||
console.log('LocalStorage:', localStorage);
|
||||
|
||||
// Get sessionStorage
|
||||
const sessionStorage = await page.evaluate(() => {
|
||||
return JSON.stringify(window.sessionStorage);
|
||||
});
|
||||
console.log('SessionStorage:', sessionStorage);
|
||||
|
||||
// Get viewport size
|
||||
const viewport = page.viewportSize();
|
||||
console.log('Viewport:', viewport);
|
||||
```
|
||||
|
||||
### Element State Checks
|
||||
|
||||
```typescript
|
||||
// Check visibility
|
||||
const isVisible = await page.locator('[data-testid="element"]').isVisible();
|
||||
console.log('Is visible:', isVisible);
|
||||
|
||||
// Check if enabled
|
||||
const isEnabled = await page.locator('[data-testid="button"]').isEnabled();
|
||||
console.log('Is enabled:', isEnabled);
|
||||
|
||||
// Check if checked
|
||||
const isChecked = await page.locator('[data-testid="checkbox"]').isChecked();
|
||||
console.log('Is checked:', isChecked);
|
||||
|
||||
// Get bounding box
|
||||
const box = await page.locator('[data-testid="element"]').boundingBox();
|
||||
console.log('Bounding box:', box);
|
||||
|
||||
// Get attribute
|
||||
const value = await page.locator('[data-testid="input"]').getAttribute('value');
|
||||
console.log('Value:', value);
|
||||
|
||||
// Get inner text
|
||||
const text = await page.locator('[data-testid="element"]').innerText();
|
||||
console.log('Inner text:', text);
|
||||
|
||||
// Get input value
|
||||
const inputValue = await page.locator('[data-testid="input"]').inputValue();
|
||||
console.log('Input value:', inputValue);
|
||||
```
|
||||
|
||||
## Performance Testing
|
||||
|
||||
```bash
|
||||
# Run with tracing
|
||||
npx playwright test --trace=on
|
||||
|
||||
# Run with video
|
||||
npx playwright test --video=on
|
||||
|
||||
# Slow down execution
|
||||
npx playwright test --slow-mo=1000
|
||||
```
|
||||
|
||||
## CI/CD Commands
|
||||
|
||||
```bash
|
||||
# Run in CI mode
|
||||
CI=1 npx playwright test
|
||||
|
||||
# Run with JUnit reporter
|
||||
npx playwright test --reporter=junit
|
||||
|
||||
# Run with multiple reporters
|
||||
npx playwright test --reporter=html,json,junit
|
||||
|
||||
# Run with sharding
|
||||
npx playwright test --shard=1/3
|
||||
npx playwright test --shard=2/3
|
||||
npx playwright test --shard=3/3
|
||||
```
|
||||
|
||||
## Test Filtering
|
||||
|
||||
```bash
|
||||
# Run tests with specific tag
|
||||
npx playwright test --grep @smoke
|
||||
|
||||
# Skip tests with tag
|
||||
npx playwright test --grep-invert @skip
|
||||
|
||||
# Run tests in specific describe block
|
||||
npx playwright test --grep "Login tests"
|
||||
|
||||
# Run only tests marked with test.only
|
||||
# (Automatically done if test.only exists)
|
||||
```
|
||||
|
||||
## Maintenance Commands
|
||||
|
||||
```bash
|
||||
# Clean test output
|
||||
rm -rf test-results/
|
||||
rm -rf playwright-report/
|
||||
|
||||
# List installed browsers
|
||||
npx playwright --version
|
||||
|
||||
# Check for browser updates
|
||||
npx playwright install --dry-run
|
||||
|
||||
# Uninstall browsers
|
||||
npx playwright uninstall
|
||||
|
||||
# Clear browser cache
|
||||
npx playwright install --force
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Use `--headed` for visual debugging**
|
||||
```bash
|
||||
npx playwright test --headed --project=chromium
|
||||
```
|
||||
|
||||
2. **Use `--debug` to step through test**
|
||||
```bash
|
||||
npx playwright test login.spec.ts --debug
|
||||
```
|
||||
|
||||
3. **Use `--ui` for interactive mode**
|
||||
```bash
|
||||
npx playwright test --ui
|
||||
```
|
||||
|
||||
4. **Use `show-trace` to analyze failures**
|
||||
```bash
|
||||
npx playwright show-trace trace.zip
|
||||
```
|
||||
|
||||
5. **Use `codegen` to learn selectors**
|
||||
```bash
|
||||
npx playwright codegen http://localhost:3000
|
||||
```
|
||||
Reference in New Issue
Block a user