Zum Inhalt

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>

generieren

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: true in 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(); }