Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Playwright testing guide covering E2E, component, API, visual, accessibility, and security tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
core/test-suite-structure.md
1# Test Suite Structure23## Table of Contents451. [Configuration](#configuration)62. [E2E Tests](#e2e-tests)73. [Component Tests](#component-tests)84. [API Tests](#api-tests)95. [Visual Regression Tests](#visual-regression-tests)106. [Directory Structure](#directory-structure)117. [Tagging & Filtering](#tagging--filtering)1213### Project Setup1415```bash16npm init playwright@latest17```1819## Configuration2021### Essential Configuration2223```typescript24// playwright.config.ts25import { defineConfig, devices } from "@playwright/test";2627export default defineConfig({28testDir: "./tests",29fullyParallel: true,30forbidOnly: !!process.env.CI,31retries: process.env.CI ? 2 : 0,32workers: process.env.CI ? 1 : undefined,33reporter: [["html"], ["list"]],34use: {35baseURL: "http://localhost:3000",36trace: "on-first-retry",37screenshot: "only-on-failure",38},39projects: [40{ name: "setup", testMatch: /.*\.setup\.ts/ },41{42name: "chromium",43use: { ...devices["Desktop Chrome"] },44dependencies: ["setup"],45},46],47webServer: {48command: "npm run dev",49url: "http://localhost:3000",50reuseExistingServer: !process.env.CI,51},52});53```5455## E2E Tests5657Full user journey tests through the browser.5859### Structure6061```typescript62// tests/e2e/checkout.spec.ts63import { test, expect } from "@playwright/test";6465test.describe("Checkout Flow", () => {66test.beforeEach(async ({ page }) => {67await page.goto("/products");68});6970test("complete purchase as guest", async ({ page }) => {71// Add to cart72await page.getByRole("button", { name: "Add to Cart" }).first().click();73await expect(page.getByTestId("cart-count")).toHaveText("1");7475// Go to checkout76await page.getByRole("link", { name: "Cart" }).click();77await page.getByRole("button", { name: "Checkout" }).click();7879// Fill shipping80await page.getByLabel("Email").fill("[email protected]");81await page.getByLabel("Address").fill("123 Test St");82await page.getByRole("button", { name: "Continue" }).click();8384// Payment85await page.getByLabel("Card Number").fill("4242424242424242");86await page.getByRole("button", { name: "Pay Now" }).click();8788// Confirmation89await expect(page.getByRole("heading")).toHaveText("Order Confirmed");90});9192test("apply discount code", async ({ page }) => {93await page.getByRole("button", { name: "Add to Cart" }).first().click();94await page.getByRole("link", { name: "Cart" }).click();9596await page.getByLabel("Discount Code").fill("SAVE10");97await page.getByRole("button", { name: "Apply" }).click();9899await expect(page.getByText("10% discount applied")).toBeVisible();100});101});102```103104### Best Practices105106- Test critical user journeys107- Keep tests independent108- Use realistic data109- Clean up test data in teardown110111## Component Tests112113Test individual components in isolation using Playwright Component Testing.114115```bash116npm init playwright@latest -- --ct117```118119For comprehensive component testing patterns including mounting, props, events, slots, mocking, and framework-specific examples (React, Vue, Svelte), see **[component-testing.md](../testing-patterns/component-testing.md)**.120121## API Tests122123Test backend APIs without browser.124125### API Mocking Patterns126127For E2E tests that need to mock API responses:128129```typescript130// Mock single endpoint131test("displays mocked users", async ({ page }) => {132await page.route("**/api/users", (route) =>133route.fulfill({134status: 200,135json: [{ id: 1, name: "Test User" }],136})137);138139await page.goto("/users");140await expect(page.getByText("Test User")).toBeVisible();141});142143// Mock with different responses144test("handles API errors", async ({ page }) => {145await page.route("**/api/users", (route) =>146route.fulfill({147status: 500,148json: { error: "Server error" },149})150);151152await page.goto("/users");153await expect(page.getByText("Server error")).toBeVisible();154});155156// Conditional mocking157test("mocks based on request", async ({ page }) => {158await page.route("**/api/users", (route, request) => {159if (request.method() === "GET") {160route.fulfill({ json: [{ id: 1, name: "User" }] });161} else {162route.continue();163}164});165});166167// Mock with delay (simulate slow network)168test("handles slow API", async ({ page }) => {169await page.route("**/api/data", (route) =>170route.fulfill({171json: { data: "test" },172delay: 2000, // 2 second delay173})174);175176await page.goto("/dashboard");177await expect(page.getByText("Loading...")).toBeVisible();178await expect(page.getByText("test")).toBeVisible();179});180```181182For advanced patterns (GraphQL mocking, HAR recording, request modification, network throttling), see **[network-advanced.md](../advanced/network-advanced.md)**.183184## Visual Regression Tests185186Compare screenshots to detect visual changes.187188### Basic Visual Test189190```typescript191// tests/visual/homepage.spec.ts192import { test, expect } from "@playwright/test";193194test("homepage visual", async ({ page }) => {195await page.goto("/");196await expect(page).toHaveScreenshot("homepage.png");197});198199test("component visual", async ({ page }) => {200await page.goto("/components");201202const button = page.getByRole("button", { name: "Primary" });203await expect(button).toHaveScreenshot("primary-button.png");204});205```206207### Visual Test Options208209```typescript210test("dashboard visual", async ({ page }) => {211await page.goto("/dashboard");212213await expect(page).toHaveScreenshot("dashboard.png", {214fullPage: true, // Capture entire scrollable page215maxDiffPixels: 100, // Allow up to 100 different pixels216maxDiffPixelRatio: 0.01, // Or 1% difference217threshold: 0.2, // Pixel comparison threshold218animations: "disabled", // Disable animations219mask: [page.getByTestId("date")], // Mask dynamic content220});221});222```223224### Handling Dynamic Content225226```typescript227test("page with dynamic content", async ({ page }) => {228await page.goto("/profile");229230// Mask elements that change231await expect(page).toHaveScreenshot("profile.png", {232mask: [233page.getByTestId("timestamp"),234page.getByTestId("avatar"),235page.getByRole("img"),236],237});238});239240// Or hide elements via CSS241test("page hiding dynamic elements", async ({ page }) => {242await page.goto("/profile");243244await page.addStyleTag({245content: `246.dynamic-content { visibility: hidden !important; }247[data-testid="ad-banner"] { display: none !important; }248`,249});250251await expect(page).toHaveScreenshot("profile-stable.png");252});253```254255### Visual Test Configuration256257```typescript258// playwright.config.ts259export default defineConfig({260expect: {261toHaveScreenshot: {262maxDiffPixels: 50,263animations: "disabled",264},265},266projects: [267{268name: "visual-chrome",269use: {270...devices["Desktop Chrome"],271viewport: { width: 1280, height: 720 },272},273testMatch: /.*visual.*\.spec\.ts/,274},275],276});277```278279### Update Snapshots280281```bash282# Update all snapshots283npx playwright test --update-snapshots284285# Update specific test286npx playwright test homepage.spec.ts --update-snapshots287```288289## Directory Structure290291```292tests/293├── e2e/ # End-to-end tests294│ ├── auth.spec.ts295│ ├── checkout.spec.ts296│ └── dashboard.spec.ts297├── component/ # Component tests298│ ├── Button.spec.tsx299│ └── Modal.spec.tsx300├── api/ # API tests301│ ├── users.spec.ts302│ └── products.spec.ts303├── visual/ # Visual regression tests304│ └── homepage.spec.ts305├── fixtures/ # Custom fixtures306│ ├── auth.fixture.ts307│ └── api.fixture.ts308└── pages/ # Page objects309├── login.page.ts310└── dashboard.page.ts311```312313## Anti-Patterns to Avoid314315| Anti-Pattern | Problem | Solution |316| ------------------------------------- | ---------------------------------- | ------------------------- |317| Long test files | Hard to maintain, slow to navigate | Split by feature, use POM |318| Tests depend on execution order | Flaky, hard to debug | Keep tests independent |319| Testing multiple features in one test | Hard to debug failures | One feature per test |320321## Related References322323- **Component Testing**: See [component-testing.md](../testing-patterns/component-testing.md) for comprehensive CT patterns324- **Projects**: See [projects-dependencies.md](projects-dependencies.md) for project-based filtering325- **Page Objects**: See [page-object-model.md](page-object-model.md) for organizing page interactions326- **Test Data**: See [fixtures-hooks.md](fixtures-hooks.md) for managing test data327328## Tagging & Filtering329330### Using Tags331332```typescript333test("user login @smoke @auth", async ({ page }) => {334// ...335});336337test("checkout flow @e2e @critical", async ({ page }) => {338// ...339});340341test.describe("API tests @api", () => {342test("create user", async ({ request }) => {343// ...344});345});346```347348### Running Tagged Tests349350```bash351# Run smoke tests352npx playwright test --grep @smoke353354# Run all except slow tests355npx playwright test --grep-invert @slow356357# Combine tags358npx playwright test --grep "@smoke|@critical"359```360361For project-based filtering and advanced project configuration, see **[projects-dependencies.md](projects-dependencies.md)**.362