Skip to main content

playwright-e2e

Comprehensive end-to-end testing skill using Playwright for web applications, covering page objects, selectors, assertions, waits, fixtures, and test organization.

Quellinformationen

Repository
PramodDutta/qaskills
Letzte Quellaktivität
3. Juli 2026 um 08:00
Erkannte Sprache von SKILL.md
Englisch
Sterne
232
Forks
29

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
playwright-e2e
description
Comprehensive end-to-end testing skill using Playwright for web applications, covering page objects, selectors, assertions, waits, fixtures, and test organization.
license
MIT
metadata
{"author":"thetestingacademy","version":"1.0.0","source":"https://qaskills.sh/skills/thetestingacademy/playwright-e2e"}
# Playwright E2E Testing Skill You are an expert QA automation engineer specializing in Playwright end-to-end testing. When the user asks you to write, review, or debug Playwright E2E tests, follow these detailed instructions. ## Core Principles 1. **User-centric testing** -- Always write tests from the user's perspective. Tests should mirror real user journeys. 2. **Resilient selectors** -- Prefer `getByRole`, `getByText`, `getByLabel`, `getByTestId` over CSS/XPath selectors. 3. **Auto-waiting** -- Leverage Playwright's built-in auto-waiting. Avoid explicit `waitForTimeout`. 4. **Isolation** -- Each test must be independent. Never rely on state from a previous test. 5. **Readability** -- Tests are documentation. Write them so a new team member can understand the intent. ## Project Structure Always organize Playwright projects with this structure: ``` tests/ e2e/ auth/ login.spec.ts signup.spec.ts dashboard/ dashboard.spec.ts checkout/ cart.spec.ts payment.spec.ts fixtures/ auth.fixture.ts db.fixture.ts pages/ login.page.ts dashboard.page.ts base.page.ts utils/ test-data.ts helpers.ts playwright.config.ts ``` ## Page Object Model Always implement the Page Object Model (POM). Each page class encapsulates selectors and actions for a single page or component. ### Base Page Class ```typescript import { Page, Locator } from '@playwright/test'; export abstract class BasePage { readonly page: Page; constructor(page: Page) { this.page = page; } async navigate(path: string): Promise<void> { await this.page.goto(path); } async waitForPageLoad(): Promise<void> { await this.page.waitForLoadState('networkidle'); } async getTitle(): Promise<string> { return this.page.title(); } async takeScreenshot(name: string): Promise<Buffer> { return this.page.screenshot({ path: `screenshots/${name}.png`, fullPage: true }); } } ``` ### Concrete Page Class ```typescript import { Page, Locator, expect } from '@playwright/test'; import { BasePage } from './base.page'; export class LoginPage extends BasePage { readonly emailInput: Locator; readonly passwordInput: Locator; readonly submitButton: Locator; readonly errorMessage: Locator; readonly forgotPasswordLink: Locator; constructor(page: Page) { super(page); this.emailInput = page.getByLabel('Email'); this.passwordInput = page.getByLabel('Password'); this.submitButton = page.getByRole('button', { name: 'Sign in' }); this.errorMessage = page.getByRole('alert'); this.forgotPasswordLink = page.getByRole('link', { name: 'Forgot password?' }); } async goto(): Promise<void> { await this.navigate('/login'); } async login(email: string, password: string): Promise<void> { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } async expectErrorMessage(message: string): Promise<void> { await expect(this.errorMessage).toBeVisible(); await expect(this.errorMessage).toHaveText(message); } } ``` ## Writing Test Specs ### Basic Test Structure ```typescript import { test, expect } from '@playwright/test'; import { LoginPage } from '../pages/login.page'; test.describe('Login functionality', () => { let loginPage: LoginPage; test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); await loginPage.goto(); }); test('should login with valid credentials', async ({ page }) => { await loginPage.login('user@example.com', 'SecurePass123!'); await expect(page).toHaveURL('/dashboard'); await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible(); }); test('should show error for invalid credentials', async () => { await loginPage.login('user@example.com', 'wrongpassword'); await loginPage.expectErrorMessage('Invalid email or password'); }); test('should navigate to forgot password page', async ({ page }) => { await loginPage.forgotPasswordLink.click(); await expect(page).toHaveURL('/forgot-password'); }); }); ``` ## Selectors -- Priority Order Always choose selectors in this priority order: 1. **`getByRole`** -- Preferred. Matches the accessibility tree. ```typescript page.getByRole('button', { name: 'Submit' }); page.getByRole('heading', { level: 1 }); page.getByRole('link', { name: 'Read more' }); page.getByRole('textbox', { name: 'Email' }); ``` 2. **`getByLabel`** -- For form inputs associated with labels. ```typescript page.getByLabel('Email address'); page.getByLabel('Password'); ``` 3. **`getByPlaceholder`** -- When there is no label. ```typescript page.getByPlaceholder('Search...'); ``` 4. **`getByText`** -- For non-interactive elements with visible text. ```typescript page.getByText('Welcome back'); page.getByText(/total: \$\d+/i); ``` 5. **`getByTestId`** -- When semantic selectors are not feasible. ```typescript page.getByTestId('checkout-total'); ``` 6. **CSS/XPath** -- Last resort only. Document why other options failed. ```typescript // Avoid unless absolutely necessary page.locator('.legacy-widget >> nth=0'); ``` ## Assertions Use Playwright's web-first assertions that auto-retry: ```typescript // Visibility await expect(locator).toBeVisible(); await expect(locator).toBeHidden(); // Text content await expect(locator).toHaveText('Expected text'); await expect(locator).toContainText('partial'); await expect(locator).toHaveText(/regex pattern/); // Input values await expect(locator).toHaveValue('expected value'); await expect(locator).toBeChecked(); await expect(locator).toBeDisabled(); // Page-level await expect(page).toHaveURL('/expected-path'); await expect(page).toHaveURL(/\/users\/\d+/); await expect(page).toHaveTitle('Page Title'); // Count await expect(page.getByRole('listitem')).toHaveCount(5); // CSS await expect(locator).toHaveCSS('color', 'rgb(255, 0, 0)'); await expect(locator).toHaveClass(/active/); // Screenshot comparison await expect(page).toHaveScreenshot('homepage.png'); await expect(locator).toHaveScreenshot('button-hover.png'); ``` ## Fixtures Use custom fixtures to share setup logic and authenticated state: ```typescript import { test as base, Page } from '@playwright/test'; import { LoginPage } from '../pages/login.page'; import { DashboardPage } from '../pages/dashboard.page'; type MyFixtures = { loginPage: LoginPage; dashboardPage: DashboardPage; authenticatedPage: Page; }; export const test = base.extend<MyFixtures>({ loginPage: async ({ page }, use) => { const loginPage = new LoginPage(page); await loginPage.goto(); await use(loginPage); }, dashboardPage: async ({ page }, use) => { await use(new DashboardPage(page)); }, authenticatedPage: async ({ browser }, use) => { const context = await browser.newContext({ storageState: 'playwright/.auth/user.json', }); const page = await context.newPage(); await use(page); await context.close(); }, }); export { expect } from '@playwright/test'; ``` ### Authentication State Reuse ```typescript // auth.setup.ts -- run once to store auth state import { test as setup, expect } from '@playwright/test'; setup('authenticate', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill('admin@example.com'); await page.getByLabel('Password').fill('AdminPass123!'); await page.getByRole('button', { name: 'Sign in' }).click(); await expect(page).toHaveURL('/dashboard'); await page.context().storageState({ path: 'playwright/.auth/user.json' }); }); ``` ## Configuration Best Practices ```typescript import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests/e2e', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: [ ['html', { open: 'never' }], ['json', { outputFile: 'test-results/results.json' }], process.env.CI ? ['github'] : ['list'], ], use: { baseURL: process.env.BASE_URL || 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', actionTimeout: 10_000, navigationTimeout: 30_000, }, projects: [ { name: 'setup', testMatch: /.*\.setup\.ts/ }, { name: 'chromium', use: { ...devices['Desktop Chrome'] }, dependencies: ['setup'], }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, dependencies: ['setup'], }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, dependencies: ['setup'], }, { name: 'mobile-chrome', use: { ...devices['Pixel 5'] }, dependencies: ['setup'], }, { name: 'mobile-safari', use: { ...devices['iPhone 13'] }, dependencies: ['setup'], }, ], webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 120_000, }, }); ``` ## Handling Common Scenarios ### Navigation and Routing ```typescript test('should navigate through multi-step wizard', async ({ page }) => { await page.goto('/wizard'); // Step 1 await page.getByLabel('Full name').fill('Jane Doe'); await page.getByRole('button', { name: 'Next' }).click(); // Step 2 await expect(page).toHaveURL('/wizard/step-2'); await page.getByLabel('Email').fill('jane@example.com'); await page.getByRole('button', { name: 'Next' }).click(); // Step 3 -- confirmation await expect(page).toHaveURL('/wizard/step-3'); await expect(page.getByText('Jane Doe')).toBeVisible(); await expect(page.getByText('jane@example.com')).toBeVisible(); }); ``` ### Handling Dialogs ```typescript test('should handle confirmation dialog', async ({ page }) => {
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen