Playwright Cheatsheet¶
Playwright - Modern Web Testing & Automation
Playwright ist ein Framework für Web Testing und Automation. Es ermöglicht Chromium, Firefox und WebKit mit einer einzigen API zu testen. Playwright ist gebaut, um Cross-Browser Web Automation zu ermöglichen, die schnell, zuverlässig und fähig ist. < p>
Inhaltsverzeichnis¶
- [Installation](#installation
- (#getting-started)
- (#browser-management_)
- (#page-navigation_)
- [Element Interaction](LINK_4__
- (#selectors)
- (#waiting-strategies_)
- [Form Handling](#form-handling
- Schüsse & Videos
- [Netzwerk-Interception](#network-interception_
- [Autorisierung](LINK_10__
- Mobiltest
- Testrahmen
- (Debugging)(LINK_13_)
- (#performance-testing_)
- API Testing
- (#visual-testing_)
- [CI/CD Integration](LINK_17_
- [Erweiterte Funktionen](LINK_18_
- Beste Praktiken
Installation¶
Einfache Installation¶
# Install Playwright
npm init playwright@latest
# Or install manually
npm install -D @playwright/test
# Install browsers
npx playwright install
# Install specific browsers
npx playwright install chromium
npx playwright install firefox
npx playwright install webkit
```_
### Systemabhängigkeiten
```bash
# Install system dependencies (Linux)
npx playwright install-deps
# Ubuntu/Debian specific
sudo npx playwright install-deps
# Docker installation
FROM mcr.microsoft.com/playwright:v1.40.0-focal
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
```_
### Projektaufbau
```bash
# Create new Playwright project
npm init playwright@latest my-tests
# Project structure
my-tests/
├── tests/
│ └── example.spec.js
├── playwright.config.js
├── package.json
└── package-lock.json
```_
### Datei konfigurieren
```javascript
// playwright.config.js
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Test directory
testDir: './tests',
// Run tests in files in parallel
fullyParallel: true,
// Fail the build on CI if you accidentally left test.only in the source code
forbidOnly: !!process.env.CI,
// Retry on CI only
retries: process.env.CI ? 2 : 0,
// Opt out of parallel tests on CI
workers: process.env.CI ? 1 : undefined,
// Reporter to use
reporter: 'html',
// Shared settings for all the projects below
use: {
// Base URL to use in actions like `await page.goto('/')`
baseURL: 'http://127.0.0.1:3000',
// Collect trace when retrying the failed test
trace: 'on-first-retry',
// Record video on failure
video: 'retain-on-failure',
// Take screenshot on failure
screenshot: 'only-on-failure',
},
// Configure projects for major browsers
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
},
],
// Run your local dev server before starting the tests
webServer: {
command: 'npm run start',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
},
});
```_
## Erste Schritte
### Grundteststruktur
```javascript
// tests/example.spec.js
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
// Navigate to page
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring
await expect(page).toHaveTitle(/Playwright/);
// Click the get started link
await page.getByRole('link', { name: 'Get started' }).click();
// Expects the URL to contain intro
await expect(page).toHaveURL(/.*intro/);
});
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Click the get started link
await page.getByRole('link', { name: 'Get started' }).click();
// Expects the URL to contain intro
await expect(page).toHaveURL(/.*intro/);
});
```_
### Laufende Prüfungen
```bash
# Run all tests
npx playwright test
# Run tests in headed mode
npx playwright test --headed
# Run tests in specific browser
npx playwright test --project=chromium
# Run specific test file
npx playwright test tests/example.spec.js
# Run tests matching pattern
npx playwright test --grep "login"
# Run tests in debug mode
npx playwright test --debug
# Run tests with UI mode
npx playwright test --ui
```_
### Prüfhaken
```javascript
import { test, expect } from '@playwright/test';
test.describe('User Authentication', () => {
test.beforeEach(async ({ page }) => {
// Go to the starting url before each test
await page.goto('https://example.com/login');
});
test.afterEach(async ({ page }) => {
// Clean up after each test
await page.evaluate(() => localStorage.clear());
});
test('should login successfully', async ({ page }) => {
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login-button');
await expect(page).toHaveURL(/dashboard/);
});
test('should show error for invalid credentials', async ({ page }) => {
await page.fill('#username', 'invalid');
await page.fill('#password', 'wrong');
await page.click('#login-button');
await expect(page.locator('.error-message')).toBeVisible();
});
});
```_
## Browser Management
### Browser Kontexte
```javascript
import { test, expect, chromium } from '@playwright/test';
test('multiple contexts', async () => {
// Launch browser
const browser = await chromium.launch();
// Create contexts
const context1 = await browser.newContext();
const context2 = await browser.newContext();
// Create pages in different contexts
const page1 = await context1.newPage();
const page2 = await context2.newPage();
// Navigate pages
await page1.goto('https://example.com');
await page2.goto('https://playwright.dev');
// Contexts are isolated
await page1.evaluate(() => localStorage.setItem('user', 'context1'));
await page2.evaluate(() => localStorage.setItem('user', 'context2'));
// Verify isolation
const storage1 = await page1.evaluate(() => localStorage.getItem('user'));
const storage2 = await page2.evaluate(() => localStorage.getItem('user'));
expect(storage1).toBe('context1');
expect(storage2).toBe('context2');
// Cleanup
await browser.close();
});
```_
### Browser-Startoptionen
```javascript
import { chromium, firefox, webkit } from '@playwright/test';
test('browser launch options', async () => {
// Chromium with custom options
const browser = await chromium.launch({
headless: false,
slowMo: 1000,
devtools: true,
args: [
'--start-maximized',
'--disable-web-security',
'--disable-features=VizDisplayCompositor'
]
});
// Context with custom options
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Custom User Agent',
locale: 'en-US',
timezoneId: 'America/New_York',
permissions: ['geolocation'],
geolocation: { latitude: 40.7128, longitude: -74.0060 },
colorScheme: 'dark',
reducedMotion: 'reduce'
});
const page = await context.newPage();
await page.goto('https://example.com');
await browser.close();
});
```_
### Mehrere Browser
```javascript
import { test, chromium, firefox, webkit } from '@playwright/test';
test('cross-browser testing', async () => {
const browsers = [
{ name: 'Chromium', browser: chromium },
{ name: 'Firefox', browser: firefox },
{ name: 'WebKit', browser: webkit }
];
for (const { name, browser } of browsers) {
console.log(`Testing with ${name}`);
const browserInstance = await browser.launch();
const context = await browserInstance.newContext();
const page = await context.newPage();
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example/);
await browserInstance.close();
}
});
```_
## Seite Navigation
### Hauptnavigation
```javascript
test('navigation', async ({ page }) => {
// Navigate to URL
await page.goto('https://example.com');
// Navigate with options
await page.goto('https://example.com', {
waitUntil: 'networkidle',
timeout: 30000
});
// Go back
await page.goBack();
// Go forward
await page.goForward();
// Reload page
await page.reload();
// Navigate and wait for specific event
await Promise.all([
page.waitForNavigation(),
page.click('a[href="/next-page"]')
]);
});
```_
### Wartestrategien
```javascript
test('wait strategies', async ({ page }) => {
await page.goto('https://example.com');
// Wait for load event
await page.goto('https://example.com', { waitUntil: 'load' });
// Wait for DOM content loaded
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// Wait for network idle
await page.goto('https://example.com', { waitUntil: 'networkidle' });
// Wait for specific element
await page.waitForSelector('#content');
// Wait for element to be visible
await page.waitForSelector('#modal', { state: 'visible' });
// Wait for element to be hidden
await page.waitForSelector('#loading', { state: 'hidden' });
// Wait for function to return true
await page.waitForFunction(() => window.jQuery !== undefined);
// Wait for response
await page.waitForResponse('**/api/data');
// Wait for request
await page.waitForRequest('**/api/submit');
});
```_
### URL und Titel Assertions
```javascript
test('URL and title checks', async ({ page }) => {
await page.goto('https://example.com');
// Check current URL
expect(page.url()).toBe('https://example.com/');
// Check URL contains
expect(page.url()).toContain('example.com');
// Check URL matches pattern
await expect(page).toHaveURL(/example\.com/);
// Check title
await expect(page).toHaveTitle('Example Domain');
// Check title contains
await expect(page).toHaveTitle(/Example/);
// Get title programmatically
const title = await page.title();
expect(title).toBe('Example Domain');
});
```_
## Element Interaktion
### Klicken Sie auf Elemente
```javascript
test('clicking elements', async ({ page }) => {
await page.goto('https://example.com');
// Click by selector
await page.click('#submit-button');
// Click with options
await page.click('#button', {
button: 'right',
clickCount: 2,
delay: 1000,
force: true,
modifiers: ['Shift']
});
// Click at position
await page.click('#element', { position: { x: 10, y: 10 } });
// Double click
await page.dblclick('#element');
// Right click
await page.click('#element', { button: 'right' });
// Click and wait for navigation
await Promise.all([
page.waitForNavigation(),
page.click('#link')
]);
});
```_
### Form Interaktionen
```javascript
test('form interactions', async ({ page }) => {
await page.goto('https://example.com/form');
// Fill input
await page.fill('#username', 'testuser');
// Type with delay
await page.type('#password', 'password123', { delay: 100 });
// Clear input
await page.fill('#field', '');
// Select option
await page.selectOption('#country', 'US');
// Select multiple options
await page.selectOption('#languages', ['en', 'es', 'fr']);
// Check checkbox
await page.check('#agree-terms');
// Uncheck checkbox
await page.uncheck('#newsletter');
// Check radio button
await page.check('#gender-male');
// Upload file
await page.setInputFiles('#file-upload', 'path/to/file.pdf');
// Upload multiple files
await page.setInputFiles('#files', ['file1.pdf', 'file2.jpg']);
});
```_
### Keyboard und Maus Aktionen
```javascript
test('keyboard and mouse actions', async ({ page }) => {
await page.goto('https://example.com');
// Keyboard actions
await page.keyboard.press('Enter');
await page.keyboard.press('Control+A');
await page.keyboard.press('Meta+C'); // Cmd+C on Mac
// Type text
await page.keyboard.type('Hello World');
// Key combinations
await page.keyboard.down('Shift');
await page.keyboard.press('ArrowLeft');
await page.keyboard.up('Shift');
// Mouse actions
await page.mouse.move(100, 100);
await page.mouse.click(100, 100);
await page.mouse.dblclick(100, 100);
// Drag and drop
await page.dragAndDrop('#source', '#target');
// Hover
await page.hover('#menu-item');
// Focus element
await page.focus('#input-field');
// Scroll
await page.mouse.wheel(0, 100);
});
```_
## Wähler
### CSS-Auswahlen
```javascript
test('CSS selectors', async ({ page }) => {
await page.goto('https://example.com');
// Basic selectors
await page.click('#submit'); // ID
await page.click('.button'); // Class
await page.click('button'); // Tag
await page.click('input[type="text"]'); // Attribute
// Combinators
await page.click('div > button'); // Child
await page.click('div button'); // Descendant
await page.click('div + button'); // Adjacent sibling
await page.click('div ~ button'); // General sibling
// Pseudo-selectors
await page.click('button:first-child');
await page.click('button:last-child');
await page.click('button:nth-child(2)');
await page.click('input:checked');
await page.click('button:not(.disabled)');
});
```_
### Textauswahl
```javascript
test('text selectors', async ({ page }) => {
await page.goto('https://example.com');
// Exact text match
await page.click('text=Submit');
// Partial text match
await page.click('text=Sub');
// Case insensitive
await page.click('text=submit >> i');
// Regular expression
await page.click('text=/submit/i');
// Text in specific element
await page.click('button:has-text("Submit")');
// Text with quotes
await page.click('text="Click here"');
});
```_
### Rollenbasierte Wähler
```javascript
test('role-based selectors', async ({ page }) => {
await page.goto('https://example.com');
// Button role
await page.click('role=button[name="Submit"]');
// Link role
await page.click('role=link[name="Home"]');
// Textbox role
await page.fill('role=textbox[name="Username"]', 'user');
// Checkbox role
await page.check('role=checkbox[name="Agree"]');
// Common roles
await page.click('role=button');
await page.click('role=link');
await page.fill('role=textbox', 'text');
await page.selectOption('role=combobox', 'option');
await page.check('role=checkbox');
await page.check('role=radio');
});
```_
### Schaufensterfiguren
```javascript
test('playwright locators', async ({ page }) => {
await page.goto('https://example.com');
// Get by role
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByRole('link', { name: 'Home' }).click();
await page.getByRole('textbox', { name: 'Username' }).fill('user');
// Get by text
await page.getByText('Submit').click();
await page.getByText(/submit/i).click();
// Get by label
await page.getByLabel('Username').fill('user');
await page.getByLabel('Password').fill('pass');
// Get by placeholder
await page.getByPlaceholder('Enter username').fill('user');
// Get by alt text
await page.getByAltText('Profile picture').click();
// Get by title
await page.getByTitle('Close dialog').click();
// Get by test id
await page.getByTestId('submit-button').click();
// Chaining locators
const form = page.locator('#login-form');
await form.getByLabel('Username').fill('user');
await form.getByLabel('Password').fill('pass');
await form.getByRole('button', { name: 'Login' }).click();
});
```_
### Erweiterte Auswahlmöglichkeiten
```javascript
test('advanced selectors', async ({ page }) => {
await page.goto('https://example.com');
// XPath
await page.click('xpath=//button[@id="submit"]');
// CSS with text
await page.click('css=button >> text=Submit');
// Multiple selectors (OR)
await page.click('#submit, .submit-button, button[type="submit"]');
// Selector with visibility
await page.click('button:visible');
// Selector with state
await page.click('button:enabled');
// Has selector
await page.click('article:has(h2:text("Title"))');
// Near selector
await page.click('button:near(:text("Submit"))');
// Nth match
await page.click('button >> nth=1');
// Filter by text
await page.locator('button').filter({ hasText: 'Submit' }).click();
// Filter by other locator
await page.locator('article').filter({ has: page.locator('h2') }).first().click();
});
```_
## Wartestrategien
### Element Warten
```javascript
test('element waiting', async ({ page }) => {
await page.goto('https://example.com');
// Wait for element to be attached to DOM
await page.waitForSelector('#element');
// Wait for element to be visible
await page.waitForSelector('#element', { state: 'visible' });
// Wait for element to be hidden
await page.waitForSelector('#element', { state: 'hidden' });
// Wait for element to be detached from DOM
await page.waitForSelector('#element', { state: 'detached' });
// Wait with timeout
await page.waitForSelector('#element', { timeout: 5000 });
// Wait for multiple elements
await Promise.all([
page.waitForSelector('#element1'),
page.waitForSelector('#element2'),
page.waitForSelector('#element3')
]);
});
```_
### Auto-Wartung
```javascript
test('auto-waiting', async ({ page }) => {
await page.goto('https://example.com');
// Playwright automatically waits for elements to be:
// - Attached to DOM
// - Visible
// - Stable (not animating)
// - Enabled
// - Editable (for input actions)
// These actions auto-wait
await page.click('#button'); // Waits for button to be clickable
await page.fill('#input', 'text'); // Waits for input to be editable
await page.selectOption('#select', 'option'); // Waits for select to be enabled
await page.check('#checkbox'); // Waits for checkbox to be checkable
// Assertions also auto-wait
await expect(page.locator('#element')).toBeVisible();
await expect(page.locator('#input')).toHaveValue('expected');
await expect(page.locator('#list li')).toHaveCount(5);
});
```_
### Kundenspezifisches Warten
```javascript
test('custom waiting', async ({ page }) => {
await page.goto('https://example.com');
// Wait for function to return truthy value
await page.waitForFunction(() => {
return document.querySelector('#dynamic-content') !== null;
});
// Wait for function with arguments
await page.waitForFunction(
(selector) => document.querySelector(selector) !== null,
'#dynamic-element'
);
// Wait for JavaScript condition
await page.waitForFunction(() => window.dataLoaded === true);
// Wait for network idle
await page.waitForLoadState('networkidle');
// Wait for DOM content loaded
await page.waitForLoadState('domcontentloaded');
// Wait for load event
await page.waitForLoadState('load');
// Wait for timeout
await page.waitForTimeout(1000);
// Wait for event
await page.waitForEvent('dialog');
await page.waitForEvent('download');
await page.waitForEvent('popup');
});
```_
## Form Handling
### Eingabefelder
```javascript
test('input field handling', async ({ page }) => {
await page.goto('https://example.com/form');
// Text input
await page.fill('#username', 'testuser');
await page.fill('#email', 'test@example.com');
// Password input
await page.fill('#password', 'secretpassword');
// Number input
await page.fill('#age', '25');
// Date input
await page.fill('#birthdate', '2000-01-01');
// Textarea
await page.fill('#comments', 'This is a long comment text');
// Clear field
await page.fill('#field', '');
// Type with delay (simulates human typing)
await page.type('#search', 'search term', { delay: 100 });
// Press keys
await page.press('#input', 'Control+A');
await page.press('#input', 'Backspace');
});
```_
### Wählen Sie Dropdowns
```javascript
test('select dropdown handling', async ({ page }) => {
await page.goto('https://example.com/form');
// Select by value
await page.selectOption('#country', 'US');
// Select by label
await page.selectOption('#country', { label: 'United States' });
// Select by index
await page.selectOption('#country', { index: 1 });
// Select multiple options
await page.selectOption('#languages', ['en', 'es', 'fr']);
// Select all options
const options = await page.locator('#languages option').allTextContents();
await page.selectOption('#languages', options);
// Get selected value
const selectedValue = await page.inputValue('#country');
expect(selectedValue).toBe('US');
// Get all selected values
const selectedValues = await page.evaluate(() => {
const select = document.querySelector('#languages');
return Array.from(select.selectedOptions).map(option => option.value);
});
});
```_
### Checkboxen und Radio Buttons
```javascript
test('checkbox and radio handling', async ({ page }) => {
await page.goto('https://example.com/form');
// Check checkbox
await page.check('#agree-terms');
await page.check('#newsletter');
// Uncheck checkbox
await page.uncheck('#newsletter');
// Toggle checkbox
const isChecked = await page.isChecked('#newsletter');
if (!isChecked) {
await page.check('#newsletter');
}
// Radio buttons
await page.check('#gender-male');
await page.check('#age-25-34');
// Verify checkbox state
expect(await page.isChecked('#agree-terms')).toBe(true);
expect(await page.isChecked('#newsletter')).toBe(false);
// Verify radio button state
expect(await page.isChecked('#gender-male')).toBe(true);
expect(await page.isChecked('#gender-female')).toBe(false);
});
```_
### Datei-Uploads
```javascript
test('file upload handling', async ({ page }) => {
await page.goto('https://example.com/upload');
// Single file upload
await page.setInputFiles('#file-upload', 'tests/fixtures/document.pdf');
// Multiple file upload
await page.setInputFiles('#multiple-files', [
'tests/fixtures/image1.jpg',
'tests/fixtures/image2.png',
'tests/fixtures/document.pdf'
]);
// Upload from buffer
await page.setInputFiles('#file-upload', {
name: 'test.txt',
mimeType: 'text/plain',
buffer: Buffer.from('Hello World')
});
// Remove files
await page.setInputFiles('#file-upload', []);
// Verify file upload
const fileName = await page.inputValue('#file-upload');
expect(fileName).toContain('document.pdf');
// Handle file chooser dialog
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.click('#upload-button')
]);
await fileChooser.setFiles('tests/fixtures/document.pdf');
});
```_
### Formular Einreichung
```javascript
test('form submission', async ({ page }) => {
await page.goto('https://example.com/form');
// Fill form
await page.fill('#username', 'testuser');
await page.fill('#email', 'test@example.com');
await page.fill('#password', 'password123');
await page.check('#agree-terms');
// Submit form by clicking submit button
await Promise.all([
page.waitForNavigation(),
page.click('#submit-button')
]);
// Submit form by pressing Enter
await Promise.all([
page.waitForNavigation(),
page.press('#username', 'Enter')
]);
// Submit form programmatically
await page.evaluate(() => {
document.querySelector('#form').submit();
});
// Verify form submission
await expect(page).toHaveURL(/success/);
await expect(page.locator('.success-message')).toBeVisible();
});
```_
## Screenshots & Videos
### Screenshots
```javascript
test('screenshots', async ({ page }) => {
await page.goto('https://example.com');
// Full page screenshot
await page.screenshot({ path: 'screenshots/fullpage.png' });
// Screenshot with options
await page.screenshot({
path: 'screenshots/page.png',
fullPage: true,
clip: { x: 0, y: 0, width: 800, height: 600 },
quality: 80,
type: 'jpeg'
});
// Element screenshot
await page.locator('#header').screenshot({ path: 'screenshots/header.png' });
// Screenshot to buffer
const buffer = await page.screenshot();
// Screenshot with mask
await page.screenshot({
path: 'screenshots/masked.png',
mask: [page.locator('.sensitive-data')]
});
// Mobile screenshot
await page.setViewportSize({ width: 375, height: 667 });
await page.screenshot({ path: 'screenshots/mobile.png' });
});
```_
### Videoaufnahme
```javascript
// playwright.config.js
export default defineConfig({
use: {
// Record video for all tests
video: 'on',
// Record video only on failure
video: 'retain-on-failure',
// Record video on first retry
video: 'on-first-retry',
},
});
test('video recording', async ({ page }) => {
// Video is automatically recorded based on config
await page.goto('https://example.com');
await page.click('#button');
await page.fill('#input', 'test');
// Video will be saved to test-results folder
});
```_
### Visual Vergleiche
```javascript
test('visual comparisons', async ({ page }) => {
await page.goto('https://example.com');
// Compare full page
await expect(page).toHaveScreenshot('homepage.png');
// Compare element
await expect(page.locator('#header')).toHaveScreenshot('header.png');
// Compare with threshold
await expect(page).toHaveScreenshot('page.png', { threshold: 0.2 });
// Compare with mask
await expect(page).toHaveScreenshot('page.png', {
mask: [page.locator('.dynamic-content')]
});
// Update screenshots
// npx playwright test --update-snapshots
});
```_
## Network Interception
### Interception anfordern
```javascript
test('request interception', async ({ page }) => {
// Intercept all requests
await page.route('**/*', route => {
console.log('Request:', route.request().url());
route.continue();
});
// Intercept specific requests
await page.route('**/api/users', route => {
// Block request
route.abort();
// Modify request
route.continue({
headers: {
...route.request().headers(),
'Authorization': 'Bearer token'
}
});
// Mock response
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ users: [] })
});
});
await page.goto('https://example.com');
});
```_
### Antwort Mocking
```javascript
test('response mocking', async ({ page }) => {
// Mock API response
await page.route('**/api/data', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
success: true,
data: [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
]
})
});
});
// Mock with file
await page.route('**/api/users', route => {
route.fulfill({
path: 'tests/fixtures/users.json'
});
});
// Mock error response
await page.route('**/api/error', route => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' })
});
});
await page.goto('https://example.com');
});
```_
### Netzwerküberwachung
```javascript
test('network monitoring', async ({ page }) => {
const requests = [];
const responses = [];
// Monitor requests
page.on('request', request => {
requests.push({
url: request.url(),
method: request.method(),
headers: request.headers(),
postData: request.postData()
});
});
// Monitor responses
page.on('response', response => {
responses.push({
url: response.url(),
status: response.status(),
headers: response.headers()
});
});
await page.goto('https://example.com');
// Wait for specific request
const [request] = await Promise.all([
page.waitForRequest('**/api/data'),
page.click('#load-data')
]);
// Wait for specific response
const [response] = await Promise.all([
page.waitForResponse('**/api/submit'),
page.click('#submit')
]);
expect(response.status()).toBe(200);
// Analyze network activity
const apiRequests = requests.filter(req => req.url.includes('/api/'));
expect(apiRequests).toHaveLength(3);
});
```_
## Authentication
### Grundlegende Authentifizierung
```javascript
test('basic authentication', async ({ page }) => {
// Set basic auth
await page.setExtraHTTPHeaders({
'Authorization': 'Basic ' + Buffer.from('username:password').toString('base64')
});
await page.goto('https://example.com/protected');
// Or use context-level auth
const context = await browser.newContext({
httpCredentials: {
username: 'testuser',
password: 'password123'
}
});
const page2 = await context.newPage();
await page2.goto('https://example.com/protected');
});
```_
### Sitzungsmanagement
```javascript
test('session management', async ({ page }) => {
// Login
await page.goto('https://example.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login');
// Save session
const cookies = await page.context().cookies();
const localStorage = await page.evaluate(() => {
return JSON.stringify(window.localStorage);
});
// Use session in another test
await page.context().addCookies(cookies);
await page.evaluate(storage => {
const data = JSON.parse(storage);
for (const [key, value] of Object.entries(data)) {
window.localStorage.setItem(key, value);
}
}, localStorage);
await page.goto('https://example.com/dashboard');
await expect(page.locator('.user-info')).toBeVisible();
});
```_
### OAuthentication und Token Authentication
```javascript
test('token authentication', async ({ page }) => {
// Set authorization header
await page.setExtraHTTPHeaders({
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
});
// Or intercept requests to add token
await page.route('**/api/**', route => {
route.continue({
headers: {
...route.request().headers(),
'Authorization': 'Bearer ' + process.env.API_TOKEN
}
});
});
await page.goto('https://example.com/app');
});
```_
### Multifaktor Authentication
```javascript
test('multi-factor authentication', async ({ page }) => {
// Step 1: Username and password
await page.goto('https://example.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login');
// Step 2: Wait for MFA prompt
await expect(page.locator('#mfa-code')).toBeVisible();
// Step 3: Enter MFA code (you might need to generate this)
const mfaCode = '123456'; // In real tests, this might come from a TOTP generator
await page.fill('#mfa-code', mfaCode);
await page.click('#verify-mfa');
// Step 4: Verify successful login
await expect(page).toHaveURL(/dashboard/);
await expect(page.locator('.welcome-message')).toBeVisible();
});
```_
## Mobile Testing
### Geräte-Emulation
```javascript
import { test, devices } from '@playwright/test';
test('mobile testing', async ({ browser }) => {
// iPhone 12
const iPhone = devices['iPhone 12'];
const context = await browser.newContext({
...iPhone
});
const page = await context.newPage();
await page.goto('https://example.com');
// Test mobile-specific features
await expect(page.locator('.mobile-menu')).toBeVisible();
await expect(page.locator('.desktop-menu')).toBeHidden();
await context.close();
});
// Using project configuration
test.describe('Mobile Tests', () => {
test.use({ ...devices['iPhone 12'] });
test('mobile navigation', async ({ page }) => {
await page.goto('https://example.com');
// Test hamburger menu
await page.click('.hamburger-menu');
await expect(page.locator('.mobile-nav')).toBeVisible();
});
});
```_
### Touch Interactions
```javascript
test('touch interactions', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('https://example.com');
// Tap (equivalent to click on mobile)
await page.tap('#button');
// Long press
await page.touchscreen.tap(100, 100, { duration: 1000 });
// Swipe
await page.touchscreen.tap(100, 300);
await page.mouse.move(300, 300);
await page.mouse.up();
// Pinch to zoom
await page.touchscreen.tap(200, 200);
await page.touchscreen.tap(250, 250);
// Scroll
await page.mouse.wheel(0, 100);
// Test responsive design
const isMobileMenuVisible = await page.isVisible('.mobile-menu');
expect(isMobileMenuVisible).toBe(true);
});
```_
### Geolocation Testing
```javascript
test('geolocation testing', async ({ context, page }) => {
// Grant geolocation permission
await context.grantPermissions(['geolocation']);
// Set geolocation
await context.setGeolocation({ latitude: 40.7128, longitude: -74.0060 });
await page.goto('https://example.com/map');
// Test location-based features
await page.click('#get-location');
await expect(page.locator('#location-display')).toContainText('New York');
// Change location
await context.setGeolocation({ latitude: 34.0522, longitude: -118.2437 });
await page.reload();
await page.click('#get-location');
await expect(page.locator('#location-display')).toContainText('Los Angeles');
});
```_
## Testrahmen
### Prüforganisation
```javascript
import { test, expect } from '@playwright/test';
// Test suite
test.describe('User Authentication', () => {
test.beforeAll(async () => {
// Setup before all tests in this describe block
console.log('Setting up test data');
});
test.beforeEach(async ({ page }) => {
// Setup before each test
await page.goto('https://example.com/login');
});
test.afterEach(async ({ page }) => {
// Cleanup after each test
await page.evaluate(() => localStorage.clear());
});
test.afterAll(async () => {
// Cleanup after all tests
console.log('Cleaning up test data');
});
test('should login with valid credentials', async ({ page }) => {
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login');
await expect(page).toHaveURL(/dashboard/);
});
test('should show error for invalid credentials', async ({ page }) => {
await page.fill('#username', 'invalid');
await page.fill('#password', 'wrong');
await page.click('#login');
await expect(page.locator('.error')).toBeVisible();
});
});
```_
### Parametertests
```javascript
const testData = [
{ username: 'user1', password: 'pass1', expected: 'dashboard' },
{ username: 'user2', password: 'pass2', expected: 'admin' },
{ username: 'user3', password: 'pass3', expected: 'profile' }
];
testData.forEach(({ username, password, expected }) => {
test(`login with ${username}`, async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('#username', username);
await page.fill('#password', password);
await page.click('#login');
await expect(page).toHaveURL(new RegExp(expected));
});
});
// Or using test.describe.parallel for parallel execution
test.describe.parallel('Parallel Tests', () => {
['chrome', 'firefox', 'safari'].forEach(browser => {
test(`test on ${browser}`, async ({ page }) => {
// Test logic here
});
});
});
```_
### Benutzerdefinierte Fixtures
```javascript
// fixtures.js
import { test as base } from '@playwright/test';
// Custom fixture for authenticated user
const test = base.extend({
authenticatedPage: async ({ page }, use) => {
// Login before each test
await page.goto('https://example.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login');
await page.waitForURL('**/dashboard');
await use(page);
// Logout after each test
await page.click('#logout');
}
});
// Use custom fixture
test('dashboard test', async ({ authenticatedPage }) => {
await expect(authenticatedPage.locator('.welcome')).toBeVisible();
});
export { test };
```_
### Testannotationen
```javascript
test('slow test', async ({ page }) => {
test.slow(); // Mark test as slow (3x timeout)
await page.goto('https://example.com');
// Long-running test logic
});
test('flaky test', async ({ page }) => {
test.fixme(); // Mark test as broken
await page.goto('https://example.com');
// Flaky test logic
});
test('skip on mobile', async ({ page, isMobile }) => {
test.skip(isMobile, 'Not supported on mobile');
await page.goto('https://example.com');
// Desktop-only test logic
});
test('conditional test', async ({ page, browserName }) => {
test.skip(browserName === 'webkit', 'Not supported in Safari');
await page.goto('https://example.com');
// Browser-specific test logic
});
```_
## Debugging
### Debug Mode
```bash
# Run tests in debug mode
npx playwright test --debug
# Debug specific test
npx playwright test tests/example.spec.js --debug
# Debug with headed browser
npx playwright test --headed --debug
# Debug with slow motion
npx playwright test --headed --slowMo=1000
```_
### Debugging im Code
```javascript
test('debugging example', async ({ page }) => {
await page.goto('https://example.com');
// Pause execution
await page.pause();
// Add console logs
console.log('Current URL:', page.url());
// Take screenshot for debugging
await page.screenshot({ path: 'debug-screenshot.png' });
// Evaluate JavaScript in browser context
const title = await page.evaluate(() => document.title);
console.log('Page title:', title);
// Get element information
const element = page.locator('#button');
console.log('Element visible:', await element.isVisible());
console.log('Element text:', await element.textContent());
await page.click('#button');
});
```_
### Trace Viewer
```javascript
// playwright.config.js
export default defineConfig({
use: {
// Collect trace when retrying the failed test
trace: 'on-first-retry',
// Or collect trace for all tests
trace: 'on',
},
});
// View traces
// npx playwright show-trace test-results/example-chromium/trace.zip
```_
### Browser-Tools
```javascript
test('debug with devtools', async ({ page }) => {
// Launch browser with devtools
await page.goto('https://example.com');
// Open devtools programmatically
await page.evaluate(() => {
debugger; // This will trigger debugger if devtools is open
});
// Inspect element
await page.locator('#element').highlight();
// Log to browser console
await page.evaluate(() => {
console.log('Debug message from browser');
});
});
```_
### Fehlerbehebung
```javascript
test('error handling', async ({ page }) => {
try {
await page.goto('https://example.com');
await page.click('#non-existent-element', { timeout: 5000 });
} catch (error) {
console.log('Error occurred:', error.message);
// Take screenshot on error
await page.screenshot({ path: 'error-screenshot.png' });
// Log page content for debugging
const content = await page.content();
console.log('Page content:', content);
throw error; // Re-throw to fail the test
}
});
```_
## Leistungsprüfung
### Performance Metrics
```javascript
test('performance metrics', async ({ page }) => {
await page.goto('https://example.com');
// Get performance metrics
const metrics = await page.evaluate(() => {
const navigation = performance.getEntriesByType('navigation')[0];
return {
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
firstPaint: performance.getEntriesByName('first-paint')[0]?.startTime,
firstContentfulPaint: performance.getEntriesByName('first-contentful-paint')[0]?.startTime
};
});
console.log('Performance metrics:', metrics);
// Assert performance thresholds
expect(metrics.domContentLoaded).toBeLessThan(2000);
expect(metrics.loadComplete).toBeLessThan(5000);
});
```_
### Integration von Gebäuden
```javascript
import { playAudit } from 'playwright-lighthouse';
test('lighthouse audit', async ({ page }) => {
await page.goto('https://example.com');
await playAudit({
page,
thresholds: {
performance: 90,
accessibility: 90,
'best-practices': 90,
seo: 90,
},
port: 9222,
});
});
```_
### Netzwerkleistung
```javascript
test('network performance', async ({ page }) => {
const responses = [];
page.on('response', response => {
responses.push({
url: response.url(),
status: response.status(),
size: response.headers()['content-length'],
timing: response.timing()
});
});
await page.goto('https://example.com');
// Analyze network performance
const slowRequests = responses.filter(r => r.timing.responseEnd > 1000);
const largeRequests = responses.filter(r => parseInt(r.size) > 1000000);
expect(slowRequests.length).toBeLessThan(3);
expect(largeRequests.length).toBeLessThan(2);
console.log('Total requests:', responses.length);
console.log('Slow requests:', slowRequests.length);
console.log('Large requests:', largeRequests.length);
});
```_
## API Testing
### REST API Testing
```javascript
import { test, expect, request } from '@playwright/test';
test('API testing', async ({ request }) => {
// GET request
const response = await request.get('https://api.example.com/users');
expect(response.status()).toBe(200);
const users = await response.json();
expect(users).toHaveLength(10);
// POST request
const newUser = {
name: 'John Doe',
email: 'john@example.com'
};
const createResponse = await request.post('https://api.example.com/users', {
data: newUser
});
expect(createResponse.status()).toBe(201);
const createdUser = await createResponse.json();
expect(createdUser.name).toBe(newUser.name);
// PUT request
const updateResponse = await request.put(`https://api.example.com/users/${createdUser.id}`, {
data: { name: 'Jane Doe' }
});
expect(updateResponse.status()).toBe(200);
// DELETE request
const deleteResponse = await request.delete(`https://api.example.com/users/${createdUser.id}`);
expect(deleteResponse.status()).toBe(204);
});
```_
### API Authentifizierung
```javascript
test('API with authentication', async ({ request }) => {
// Login to get token
const loginResponse = await request.post('https://api.example.com/auth/login', {
data: {
username: 'testuser',
password: 'password123'
}
});
const { token } = await loginResponse.json();
// Use token for authenticated requests
const response = await request.get('https://api.example.com/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
expect(response.status()).toBe(200);
});
```_
### GraphQL Testing
```javascript
test('GraphQL API testing', async ({ request }) => {
const query = `
query GetUsers {
users {
id
name
email
}
}
`;
const response = await request.post('https://api.example.com/graphql', {
data: { query }
});
expect(response.status()).toBe(200);
const result = await response.json();
expect(result.data.users).toBeDefined();
expect(result.data.users.length).toBeGreaterThan(0);
});
```_
## Visuelle Prüfung
### Visual Regression Testing
```javascript
test('visual regression', async ({ page }) => {
await page.goto('https://example.com');
// Take baseline screenshot
await expect(page).toHaveScreenshot('homepage.png');
// Test different states
await page.hover('#menu');
await expect(page).toHaveScreenshot('homepage-menu-hover.png');
await page.click('#toggle-theme');
await expect(page).toHaveScreenshot('homepage-dark-theme.png');
// Test responsive design
await page.setViewportSize({ width: 768, height: 1024 });
await expect(page).toHaveScreenshot('homepage-tablet.png');
await page.setViewportSize({ width: 375, height: 667 });
await expect(page).toHaveScreenshot('homepage-mobile.png');
});
```_
### Component Visual Testing
```javascript
test('component visual testing', async ({ page }) => {
await page.goto('https://example.com/components');
// Test individual components
await expect(page.locator('.button')).toHaveScreenshot('button.png');
await expect(page.locator('.card')).toHaveScreenshot('card.png');
await expect(page.locator('.modal')).toHaveScreenshot('modal.png');
// Test component states
await page.hover('.button');
await expect(page.locator('.button')).toHaveScreenshot('button-hover.png');
await page.click('.button');
await expect(page.locator('.button')).toHaveScreenshot('button-active.png');
});
```_
### Cross-Browser Visual Testing
```javascript
['chromium', 'firefox', 'webkit'].forEach(browserName => {
test(`visual test on ${browserName}`, async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot(`homepage-${browserName}.png`);
});
});
CI/CD Integration¶
GitHub Aktionen¶
```yaml
.github/workflows/playwright.yml¶
name: Playwright Tests on: push: branches: [ main, master ] pull_request: branches: [ main, master ] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v3 if: always() with: name: playwright-report path: playwright-report/ retention-days: 30 ```_
Docker Integration¶
```dockerfile
Dockerfile¶
FROM mcr.microsoft.com/playwright:v1.40.0-focal
WORKDIR /app
COPY package*.json ./ RUN npm ci
COPY . .
CMD ["npx", "playwright", "test"] ```_
```bash
Build and run tests in Docker¶
docker build -t playwright-tests . docker run --rm playwright-tests ```_
Jenkins Pipeline¶
```groovy pipeline { agent any
stages {
stage('Install Dependencies') {
steps {
sh 'npm ci'
sh 'npx playwright install --with-deps'
}
}
stage('Run Tests') {
steps {
sh 'npx playwright test'
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: 'playwright-report',
reportFiles: 'index.html',
reportName: 'Playwright Report'
])
}
}
}
}
} ```_
Erweiterte Funktionen¶
Kundenspezifische Spiele¶
```javascript // custom-matchers.js import { expect } from '@playwright/test';
expect.extend({ async toHaveLoadTime(page, maxTime) { const startTime = Date.now(); await page.waitForLoadState('networkidle'); const loadTime = Date.now() - startTime;
const pass = loadTime <= maxTime;
return {
message: () => `Expected load time to be <= ${maxTime}ms, but was ${loadTime}ms`,
pass
};
} });
// Usage test('custom matcher', async ({ page }) => { await page.goto('https://example.com'); await expect(page).toHaveLoadTime(3000); }); ```_
Seite Objektmodell¶
```javascript // pages/LoginPage.js export class LoginPage { constructor(page) { this.page = page; this.usernameInput = page.locator('#username'); this.passwordInput = page.locator('#password'); this.loginButton = page.locator('#login'); this.errorMessage = page.locator('.error'); }
async goto() { await this.page.goto('https://example.com/login'); }
async login(username, password) { await this.usernameInput.fill(username); await this.passwordInput.fill(password); await this.loginButton.click(); }
async getErrorMessage() { return await this.errorMessage.textContent(); } }
// Usage in test import { LoginPage } from './pages/LoginPage';
test('login with page object', async ({ page }) => { const loginPage = new LoginPage(page);
await loginPage.goto(); await loginPage.login('testuser', 'password123');
await expect(page).toHaveURL(/dashboard/); }); ```_
Global Setup und Teardown¶
```javascript // global-setup.js export default async function globalSetup() { // Start test server console.log('Starting test server...');
// Create test data console.log('Creating test data...');
// Any other global setup }
// global-teardown.js export default async function globalTeardown() { // Stop test server console.log('Stopping test server...');
// Clean up test data console.log('Cleaning up test data...'); }
// playwright.config.js export default defineConfig({ globalSetup: require.resolve('./global-setup'), globalTeardown: require.resolve('./global-teardown'), }); ```_
Best Practices¶
Prüforganisation¶
```javascript // Organize tests by feature/page test.describe('User Management', () => { test.describe('User Registration', () => { test('should register new user', async ({ page }) => { // Test implementation });
test('should validate email format', async ({ page }) => {
// Test implementation
});
});
test.describe('User Login', () => { test('should login with valid credentials', async ({ page }) => { // Test implementation }); }); }); ```_
Zuverlässige Wähler¶
```javascript // Good: Use data-testid attributes await page.click('[data-testid="submit-button"]');
// Good: Use role-based selectors await page.click('role=button[name="Submit"]');
// Good: Use text content await page.click('text=Submit');
// Avoid: CSS selectors that depend on styling await page.click('.btn.btn-primary.submit-btn');
// Avoid: XPath with position await page.click('//div[3]/button[2]'); ```_
Fehlerbehebung¶
```javascript test('robust error handling', async ({ page }) => { try { await page.goto('https://example.com');
// Use soft assertions for non-critical checks
await expect.soft(page.locator('.optional-element')).toBeVisible();
// Continue with critical test steps
await page.click('#important-button');
await expect(page.locator('#result')).toBeVisible();
} catch (error) { // Take screenshot on failure await page.screenshot({ path: 'failure-screenshot.png' });
// Log additional context
console.log('Current URL:', page.url());
console.log('Page title:', await page.title());
throw error;
} }); ```_
Performance Best Practices¶
- **Use auto-waiting*: Playwright wartet automatisch auf Elemente
- ** Überflüssige Wartezeiten*: Nicht verwenden
page.waitForTimeout(), es sei denn, absolut notwendig - ** Browser-Kontexte wiederverwenden*: Kontexte zwischen Tests teilen, wenn möglich
- **Parallelize-Tests*: Verwenden
fullyParallel: truein Konfiguration - ** Optimieren Sie die Auswahl**: Verwenden Sie effiziente Selektoren wie Data-Testid
Best Practices der Wartung¶
- **Keep-Tests unabhängig*: Jeder Test sollte isoliert laufen können
- **Use Page Object Model*: Verkapseln von Seiteninteraktionen in wiederverwendbaren Klassen
- **Implementieren Sie die richtige Reinigung*: Klarer Zustand zwischen den Tests
- **Bedeutungsvolle Testnamen*: Beschreiben Sie, was der Test überprüft
- **Regular-Updates*: Halten Sie Playwright und Browser aktualisiert
--
Zusammenfassung¶
Playwright ist ein leistungsstarkes, modernes Web-Test-Framework, das bietet:
- **Cross-Browser Support*: Test auf Chrom, Firefox und WebKit
- Auto-Wartung: Intelligentes Warten auf Elemente, die bereit sind
- Powerful Selectors: Mehrere Auswahlstrategien inklusive Rollenbasis
- ** Netzwerk-Interception**: Netzwerkanfragen erfassen und überwachen
- **Mobile Testing*: Geräte-Emulation und Berührungsinteraktionen
- **Visual Testing*: Screenshotvergleich und visuelle Regressionstests
- **Debugging Tools*: Trace Viewer, Debug-Modus und Entwickler-Tools Integration
- **CI/CD Ready*: Einfache Integration mit beliebten CI/CD-Plattformen
- **API Testing*: Integrierte Unterstützung für REST- und GraphQL-API-Tests
- **Performance Testing*: Eingebaute Performance Metriken und Leuchtturmintegration
Playwrights moderne Architektur und umfassendes Feature-Set machen es zu einer ausgezeichneten Wahl für End-to-End-Tests, Browser-Automatisierung und Web-Schrottaufgaben.
<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll('code'); alle Befehle = ''; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + '\n'); navigator.clipboard.writeText (allCommands); Alarm ('Alle Befehle, die in die Zwischenablage kopiert werden!'); }
Funktion generierenPDF() { Fenster.print(); }