aboutsummaryrefslogtreecommitdiff
path: root/playwright-js/API_REFERENCE.md
diff options
context:
space:
mode:
Diffstat (limited to 'playwright-js/API_REFERENCE.md')
-rw-r--r--playwright-js/API_REFERENCE.md653
1 files changed, 653 insertions, 0 deletions
diff --git a/playwright-js/API_REFERENCE.md b/playwright-js/API_REFERENCE.md
new file mode 100644
index 0000000..9ee2975
--- /dev/null
+++ b/playwright-js/API_REFERENCE.md
@@ -0,0 +1,653 @@
+# Playwright Skill - Complete API Reference
+
+This document contains the comprehensive Playwright API reference and advanced patterns. For quick-start execution patterns, see [SKILL.md](SKILL.md).
+
+## Table of Contents
+
+- [Installation & Setup](#installation--setup)
+- [Core Patterns](#core-patterns)
+- [Selectors & Locators](#selectors--locators)
+- [Common Actions](#common-actions)
+- [Waiting Strategies](#waiting-strategies)
+- [Assertions](#assertions)
+- [Page Object Model](#page-object-model-pom)
+- [Network & API Testing](#network--api-testing)
+- [Authentication & Session Management](#authentication--session-management)
+- [Visual Testing](#visual-testing)
+- [Mobile Testing](#mobile-testing)
+- [Debugging](#debugging)
+- [Performance Testing](#performance-testing)
+- [Parallel Execution](#parallel-execution)
+- [Data-Driven Testing](#data-driven-testing)
+- [Accessibility Testing](#accessibility-testing)
+- [CI/CD Integration](#cicd-integration)
+- [Best Practices](#best-practices)
+- [Common Patterns & Solutions](#common-patterns--solutions)
+- [Troubleshooting](#troubleshooting)
+
+## Installation & Setup
+
+### Prerequisites
+
+Before using this skill, ensure Playwright is available:
+
+```bash
+# Check if Playwright is installed
+npm list playwright 2>/dev/null || echo "Playwright not installed"
+
+# Install (if needed)
+cd ~/.claude/skills/playwright-skill
+npm run setup
+```
+
+### Basic Configuration
+
+Create `playwright.config.ts`:
+
+```typescript
+import { defineConfig, devices } from '@playwright/test';
+
+export default defineConfig({
+ testDir: './tests',
+ fullyParallel: true,
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 2 : 0,
+ workers: process.env.CI ? 1 : undefined,
+ reporter: 'html',
+ use: {
+ baseURL: 'http://localhost:3000',
+ trace: 'on-first-retry',
+ screenshot: 'only-on-failure',
+ video: 'retain-on-failure',
+ },
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ ],
+ webServer: {
+ command: 'npm run start',
+ url: 'http://localhost:3000',
+ reuseExistingServer: !process.env.CI,
+ },
+});
+```
+
+## Core Patterns
+
+### Basic Browser Automation
+
+```javascript
+const { chromium } = require('playwright');
+
+(async () => {
+ // Launch browser
+ const browser = await chromium.launch({
+ headless: false, // Set to true for headless mode
+ slowMo: 50 // Slow down operations by 50ms
+ });
+
+ const context = await browser.newContext({
+ viewport: { width: 1280, height: 720 },
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
+ });
+
+ const page = await context.newPage();
+
+ // Navigate
+ await page.goto('https://example.com', {
+ waitUntil: 'networkidle' // Wait for network to be idle
+ });
+
+ // Your automation here
+
+ await browser.close();
+})();
+```
+
+### Test Structure
+
+```typescript
+import { test, expect } from '@playwright/test';
+
+test.describe('Feature Name', () => {
+ test.beforeEach(async ({ page }) => {
+ await page.goto('/');
+ });
+
+ test('should do something', async ({ page }) => {
+ // Arrange
+ const button = page.locator('button[data-testid="submit"]');
+
+ // Act
+ await button.click();
+
+ // Assert
+ await expect(page).toHaveURL('/success');
+ await expect(page.locator('.message')).toHaveText('Success!');
+ });
+});
+```
+
+## Selectors & Locators
+
+### Best Practices for Selectors
+
+```javascript
+// PREFERRED: Data attributes (most stable)
+await page.locator('[data-testid="submit-button"]').click();
+await page.locator('[data-cy="user-input"]').fill('text');
+
+// GOOD: Role-based selectors (accessible)
+await page.getByRole('button', { name: 'Submit' }).click();
+await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
+await page.getByRole('heading', { level: 1 }).click();
+
+// GOOD: Text content (for unique text)
+await page.getByText('Sign in').click();
+await page.getByText(/welcome back/i).click();
+
+// OK: Semantic HTML
+await page.locator('button[type="submit"]').click();
+await page.locator('input[name="email"]').fill('test@test.com');
+
+// AVOID: Classes and IDs (can change frequently)
+await page.locator('.btn-primary').click(); // Avoid
+await page.locator('#submit').click(); // Avoid
+
+// LAST RESORT: Complex CSS/XPath
+await page.locator('div.container > form > button').click(); // Fragile
+```
+
+### Advanced Locator Patterns
+
+```javascript
+// Filter and chain locators
+const row = page.locator('tr').filter({ hasText: 'John Doe' });
+await row.locator('button').click();
+
+// Nth element
+await page.locator('button').nth(2).click();
+
+// Combining conditions
+await page.locator('button').and(page.locator('[disabled]')).count();
+
+// Parent/child navigation
+const cell = page.locator('td').filter({ hasText: 'Active' });
+const row = cell.locator('..');
+await row.locator('button.edit').click();
+```
+
+## Common Actions
+
+### Form Interactions
+
+```javascript
+// Text input
+await page.getByLabel('Email').fill('user@example.com');
+await page.getByPlaceholder('Enter your name').fill('John Doe');
+
+// Clear and type
+await page.locator('#username').clear();
+await page.locator('#username').type('newuser', { delay: 100 });
+
+// Checkbox
+await page.getByLabel('I agree').check();
+await page.getByLabel('Subscribe').uncheck();
+
+// Radio button
+await page.getByLabel('Option 2').check();
+
+// Select dropdown
+await page.selectOption('select#country', 'usa');
+await page.selectOption('select#country', { label: 'United States' });
+await page.selectOption('select#country', { index: 2 });
+
+// Multi-select
+await page.selectOption('select#colors', ['red', 'blue', 'green']);
+
+// File upload
+await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');
+await page.setInputFiles('input[type="file"]', [
+ 'file1.pdf',
+ 'file2.pdf'
+]);
+```
+
+### Mouse Actions
+
+```javascript
+// Click variations
+await page.click('button'); // Left click
+await page.click('button', { button: 'right' }); // Right click
+await page.dblclick('button'); // Double click
+await page.click('button', { position: { x: 10, y: 10 } }); // Click at position
+
+// Hover
+await page.hover('.menu-item');
+
+// Drag and drop
+await page.dragAndDrop('#source', '#target');
+
+// Manual drag
+await page.locator('#source').hover();
+await page.mouse.down();
+await page.locator('#target').hover();
+await page.mouse.up();
+```
+
+### Keyboard Actions
+
+```javascript
+// Type with delay
+await page.keyboard.type('Hello World', { delay: 100 });
+
+// Key combinations
+await page.keyboard.press('Control+A');
+await page.keyboard.press('Control+C');
+await page.keyboard.press('Control+V');
+
+// Special keys
+await page.keyboard.press('Enter');
+await page.keyboard.press('Tab');
+await page.keyboard.press('Escape');
+await page.keyboard.press('ArrowDown');
+```
+
+## Waiting Strategies
+
+### Smart Waiting
+
+```javascript
+// Wait for element states
+await page.locator('button').waitFor({ state: 'visible' });
+await page.locator('.spinner').waitFor({ state: 'hidden' });
+await page.locator('button').waitFor({ state: 'attached' });
+await page.locator('button').waitFor({ state: 'detached' });
+
+// Wait for specific conditions
+await page.waitForURL('**/success');
+await page.waitForURL(url => url.pathname === '/dashboard');
+
+// Wait for network
+await page.waitForLoadState('networkidle');
+await page.waitForLoadState('domcontentloaded');
+
+// Wait for function
+await page.waitForFunction(() => document.querySelector('.loaded'));
+await page.waitForFunction(
+ text => document.body.innerText.includes(text),
+ 'Content loaded'
+);
+
+// Wait for response
+const responsePromise = page.waitForResponse('**/api/users');
+await page.click('button#load-users');
+const response = await responsePromise;
+
+// Wait for request
+await page.waitForRequest(request =>
+ request.url().includes('/api/') && request.method() === 'POST'
+);
+
+// Custom timeout
+await page.locator('.slow-element').waitFor({
+ state: 'visible',
+ timeout: 10000 // 10 seconds
+});
+```
+
+## Assertions
+
+### Common Assertions
+
+```javascript
+import { expect } from '@playwright/test';
+
+// Page assertions
+await expect(page).toHaveTitle('My App');
+await expect(page).toHaveURL('https://example.com/dashboard');
+await expect(page).toHaveURL(/.*dashboard/);
+
+// Element visibility
+await expect(page.locator('.message')).toBeVisible();
+await expect(page.locator('.spinner')).toBeHidden();
+await expect(page.locator('button')).toBeEnabled();
+await expect(page.locator('input')).toBeDisabled();
+
+// Text content
+await expect(page.locator('h1')).toHaveText('Welcome');
+await expect(page.locator('.message')).toContainText('success');
+await expect(page.locator('.items')).toHaveText(['Item 1', 'Item 2']);
+
+// Input values
+await expect(page.locator('input')).toHaveValue('test@example.com');
+await expect(page.locator('input')).toBeEmpty();
+
+// Attributes
+await expect(page.locator('button')).toHaveAttribute('type', 'submit');
+await expect(page.locator('img')).toHaveAttribute('src', /.*\.png/);
+
+// CSS properties
+await expect(page.locator('.error')).toHaveCSS('color', 'rgb(255, 0, 0)');
+
+// Count
+await expect(page.locator('.item')).toHaveCount(5);
+
+// Checkbox/Radio state
+await expect(page.locator('input[type="checkbox"]')).toBeChecked();
+```
+
+## Page Object Model (POM)
+
+### Basic Page Object
+
+```javascript
+// pages/LoginPage.js
+class LoginPage {
+ constructor(page) {
+ this.page = page;
+ this.usernameInput = page.locator('input[name="username"]');
+ this.passwordInput = page.locator('input[name="password"]');
+ this.submitButton = page.locator('button[type="submit"]');
+ this.errorMessage = page.locator('.error-message');
+ }
+
+ async navigate() {
+ await this.page.goto('/login');
+ }
+
+ async login(username, password) {
+ await this.usernameInput.fill(username);
+ await this.passwordInput.fill(password);
+ await this.submitButton.click();
+ }
+
+ async getErrorMessage() {
+ return await this.errorMessage.textContent();
+ }
+}
+
+// Usage in test
+test('login with valid credentials', async ({ page }) => {
+ const loginPage = new LoginPage(page);
+ await loginPage.navigate();
+ await loginPage.login('user@example.com', 'password123');
+ await expect(page).toHaveURL('/dashboard');
+});
+```
+
+## Network & API Testing
+
+### Intercepting Requests
+
+```javascript
+// Mock API responses
+await page.route('**/api/users', route => {
+ route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify([
+ { id: 1, name: 'John' },
+ { id: 2, name: 'Jane' }
+ ])
+ });
+});
+
+// Modify requests
+await page.route('**/api/**', route => {
+ const headers = {
+ ...route.request().headers(),
+ 'X-Custom-Header': 'value'
+ };
+ route.continue({ headers });
+});
+
+// Block resources
+await page.route('**/*.{png,jpg,jpeg,gif}', route => route.abort());
+```
+
+### Custom Headers via Environment Variables
+
+The skill supports automatic header injection via environment variables:
+
+```bash
+# Single header (simple)
+PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill
+
+# Multiple headers (JSON)
+PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Request-ID":"123"}'
+```
+
+These headers are automatically applied to all requests when using:
+- `helpers.createContext(browser)` - headers merged automatically
+- `getContextOptionsWithHeaders(options)` - utility injected by run.js wrapper
+
+**Precedence (highest to lowest):**
+1. Headers passed directly in `options.extraHTTPHeaders`
+2. Environment variable headers
+3. Playwright defaults
+
+**Use case:** Identify automated traffic so your backend can return LLM-optimized responses (e.g., plain text errors instead of styled HTML).
+
+## Visual Testing
+
+### Screenshots
+
+```javascript
+// Full page screenshot
+await page.screenshot({
+ path: 'screenshot.png',
+ fullPage: true
+});
+
+// Element screenshot
+await page.locator('.chart').screenshot({
+ path: 'chart.png'
+});
+
+// Visual comparison
+await expect(page).toHaveScreenshot('homepage.png');
+```
+
+## Mobile Testing
+
+```javascript
+// Device emulation
+const { devices } = require('playwright');
+const iPhone = devices['iPhone 12'];
+
+const context = await browser.newContext({
+ ...iPhone,
+ locale: 'en-US',
+ permissions: ['geolocation'],
+ geolocation: { latitude: 37.7749, longitude: -122.4194 }
+});
+```
+
+## Debugging
+
+### Debug Mode
+
+```bash
+# Run with inspector
+npx playwright test --debug
+
+# Headed mode
+npx playwright test --headed
+
+# Slow motion
+npx playwright test --headed --slowmo=1000
+```
+
+### In-Code Debugging
+
+```javascript
+// Pause execution
+await page.pause();
+
+// Console logs
+page.on('console', msg => console.log('Browser log:', msg.text()));
+page.on('pageerror', error => console.log('Page error:', error));
+```
+
+## Performance Testing
+
+```javascript
+// Measure page load time
+const startTime = Date.now();
+await page.goto('https://example.com');
+const loadTime = Date.now() - startTime;
+console.log(`Page loaded in ${loadTime}ms`);
+```
+
+## Parallel Execution
+
+```javascript
+// Run tests in parallel
+test.describe.parallel('Parallel suite', () => {
+ test('test 1', async ({ page }) => {
+ // Runs in parallel with test 2
+ });
+
+ test('test 2', async ({ page }) => {
+ // Runs in parallel with test 1
+ });
+});
+```
+
+## Data-Driven Testing
+
+```javascript
+// Parameterized tests
+const testData = [
+ { username: 'user1', password: 'pass1', expected: 'Welcome user1' },
+ { username: 'user2', password: 'pass2', expected: 'Welcome user2' },
+];
+
+testData.forEach(({ username, password, expected }) => {
+ test(`login with ${username}`, async ({ page }) => {
+ await page.goto('/login');
+ await page.fill('#username', username);
+ await page.fill('#password', password);
+ await page.click('button[type="submit"]');
+ await expect(page.locator('.message')).toHaveText(expected);
+ });
+});
+```
+
+## Accessibility Testing
+
+```javascript
+import { injectAxe, checkA11y } from 'axe-playwright';
+
+test('accessibility check', async ({ page }) => {
+ await page.goto('/');
+ await injectAxe(page);
+ await checkA11y(page);
+});
+```
+
+## CI/CD Integration
+
+### GitHub Actions
+
+```yaml
+name: Playwright Tests
+on:
+ push:
+ branches: [main, master]
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ - name: Install dependencies
+ run: npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run tests
+ run: npx playwright test
+```
+
+## Best Practices
+
+1. **Test Organization** - Use descriptive test names, group related tests
+2. **Selector Strategy** - Prefer data-testid attributes, use role-based selectors
+3. **Waiting** - Use Playwright's auto-waiting, avoid hard-coded delays
+4. **Error Handling** - Add proper error messages, take screenshots on failure
+5. **Performance** - Run tests in parallel, reuse authentication state
+
+## Common Patterns & Solutions
+
+### Handling Popups
+
+```javascript
+const [popup] = await Promise.all([
+ page.waitForEvent('popup'),
+ page.click('button.open-popup')
+]);
+await popup.waitForLoadState();
+```
+
+### File Downloads
+
+```javascript
+const [download] = await Promise.all([
+ page.waitForEvent('download'),
+ page.click('button.download')
+]);
+await download.saveAs(`./downloads/${download.suggestedFilename()}`);
+```
+
+### iFrames
+
+```javascript
+const frame = page.frameLocator('#my-iframe');
+await frame.locator('button').click();
+```
+
+### Infinite Scroll
+
+```javascript
+async function scrollToBottom(page) {
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
+ await page.waitForTimeout(500);
+}
+```
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Element not found** - Check if element is in iframe, verify visibility
+2. **Timeout errors** - Increase timeout, check network conditions
+3. **Flaky tests** - Use proper waiting strategies, mock external dependencies
+4. **Authentication issues** - Verify auth state is properly saved
+
+## Quick Reference Commands
+
+```bash
+# Run tests
+npx playwright test
+
+# Run in headed mode
+npx playwright test --headed
+
+# Debug tests
+npx playwright test --debug
+
+# Generate code
+npx playwright codegen https://example.com
+
+# Show report
+npx playwright show-report
+```
+
+## Additional Resources
+
+- [Playwright Documentation](https://playwright.dev/docs/intro)
+- [API Reference](https://playwright.dev/docs/api/class-playwright)
+- [Best Practices](https://playwright.dev/docs/best-practices)