# Playwright E2e Testing

> End-to-end, API, and responsive testing for web applications using Playwright with TypeScript. Use when asked to write, run, debug, or maintain Playwright (@playwright/test) TypeScript tests for UI behavior, form submissions, user flows, API validation, responsive design, or visual regression. Covers browser automation, network interception, mocking, Page Object Model, fixtures, and parallel execution. Use when this capability is needed.

- Skill: `tomevault-io/playwright-e2e-testing-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/playwright-e2e-testing-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/playwright-e2e-testing-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/playwright-e2e-testing-2

---


# Playwright E2E Testing (TypeScript)

Comprehensive toolkit for end-to-end testing of web applications using Playwright with TypeScript. Enables robust UI testing, API validation, and responsive design verification following best practices.

> **Activation:** This skill is triggered when working with Playwright tests, browser automation, E2E testing, API testing with Playwright, or test infrastructure setup.

## When to Use This Skill

- **Write E2E tests** for user flows, forms, navigation, and authentication
- **API testing** via `request` fixture or network interception during UI tests
- **Responsive testing** across mobile, tablet, and desktop viewports
- **Debug flaky tests** using traces, screenshots, videos, and Playwright Inspector
- **Setup test infrastructure** with Page Object Model and fixtures
- **Mock/intercept APIs** for isolated, deterministic testing
- **Visual regression testing** with screenshot comparisons

## Prerequisites

| Requirement | Details |
|-------------|---------|
| Node.js | v18+ recommended |
| Package Manager | npm, yarn, or pnpm |
| Playwright | `@playwright/test` package |
| TypeScript | `typescript` + `ts-node` (optional but recommended) |
| Browsers | Installed via `npx playwright install` |

### Quick Setup

```bash
# Initialize new project
npm init playwright@latest

# Or add to existing project
npm install -D @playwright/test
npx playwright install
```

## First Questions to Ask

Before writing tests, clarify:

1. **App URL**: Local dev server command + port, or staging URL?
2. **Critical flows**: Which user journeys must be covered (happy path + error states)?
3. **Browsers/devices**: Chrome, Firefox, Safari? Mobile viewports?
4. **API strategy**: Real backend, mocked responses, or hybrid?
5. **Test data**: Seed data available? Reset/cleanup strategy?

---

## Core Principles

### 1. Test Runner & TypeScript

Always use `@playwright/test` with TypeScript for type safety and better IDE support.

```typescript
import { test, expect } from '@playwright/test';

test('user can login', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page).toHaveURL(/.*dashboard/);
});
```

### 2. Locator Strategy (Priority Order)

| Priority | Locator | Example |
|----------|---------|---------|
| 1 | Role + accessible name | `getByRole('button', { name: 'Submit' })` |
| 2 | Label | `getByLabel('Email')` |
| 3 | Placeholder | `getByPlaceholder('Enter email')` |
| 4 | Text | `getByText('Welcome')` |
| 5 | Test ID | `getByTestId('submit-btn')` |
| 6 | CSS (avoid) | `locator('.btn-primary')` |

See [Locator Strategies Guide](./references/locator_strategies.md) for detailed patterns.

### 3. Auto-Waiting & Web-First Assertions

Playwright auto-waits for elements. Never use `sleep()` or arbitrary timeouts.

```typescript
// ✅ Web-first assertions (auto-retry)
await expect(page.getByRole('alert')).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId('status')).toHaveText('Success!');

// ❌ Avoid manual waits
await page.waitForTimeout(2000); // Bad practice
```

### 4. Test Structure with Steps

Use `test.step()` for readable reports and failure localization:

```typescript
test('checkout flow', async ({ page }) => {
  await test.step('Add item to cart', async () => {
    await page.goto('/products/1');
    await page.getByRole('button', { name: 'Add to Cart' }).click();
  });

  await test.step('Complete checkout', async () => {
    await page.goto('/checkout');
    await page.getByRole('button', { name: 'Pay Now' }).click();
  });

  await test.step('Verify confirmation', async () => {
    await expect(page.getByRole('heading')).toContainText('Order Confirmed');
  });
});
```

---

## Key Workflows

### Forms & Navigation

```typescript
// Form submit and wait for navigation (auto-waiting)
await page.getByRole('button', { name: 'Login' }).click();
await expect(page).toHaveURL(/.*dashboard/);

// Form with API response validation
const responsePromise = page.waitForResponse(
  r => r.url().includes('/api/login') && r.status() === 200
);
await page.getByRole('button', { name: 'Login' }).click();
const response = await responsePromise;
```

### API Testing (Request Fixture)

```typescript
test('API health check', async ({ request }) => {
  const response = await request.get('/api/health');
  expect(response.ok()).toBeTruthy();
  expect(await response.json()).toMatchObject({ status: 'ok' });
});
```

### API Mocking & Interception

```typescript
test('handles API error', async ({ page }) => {
  await page.route('**/api/users', route =>
    route.fulfill({ status: 500, body: JSON.stringify({ error: 'Server error' }) })
  );
  await page.goto('/users');
  await expect(page.getByRole('alert')).toContainText('Something went wrong');
});
```

### Responsive Testing

```typescript
const viewports = [
  { width: 375, height: 667, name: 'mobile' },
  { width: 768, height: 1024, name: 'tablet' },
  { width: 1280, height: 720, name: 'desktop' },
];

for (const vp of viewports) {
  test(`navigation works on ${vp.name}`, async ({ page }) => {
    await page.setViewportSize(vp);
    await page.goto('/');
    // Mobile: hamburger menu
    if (vp.width < 768) {
      await page.getByRole('button', { name: /menu/i }).click();
    }
    await page.getByRole('link', { name: 'About' }).click();
    await expect(page).toHaveURL(/about/);
  });
}
```

---

## Configuration

Use `playwright.config.ts` for project-wide settings:

```typescript
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  retries: process.env.CI ? 2 : 0,
  reporter: [['html'], ['junit', { outputFile: 'results.xml' }]],
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    { name: 'chromium', use: devices['Desktop Chrome'] },
    { name: 'mobile', use: devices['Pixel 5'] },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});
```

---

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| Element not found | Wrong locator or not rendered | Use `PWDEBUG=1` to inspect, verify with `getByRole` |
| Timeout waiting | Element hidden or slow load | Check for overlays, increase timeout, use `waitFor()` |
| Flaky tests | Race conditions, animations | Add `test.step()`, use proper waits, disable animations |
| Strict mode violation | Multiple elements match | Use `.first()`, `.filter()`, or more specific locator |
| Screenshots differ | Dynamic content | Mask dynamic areas, use deterministic data |
| CI fails, local passes | Environment differences | Check `baseURL`, timeouts, `webServer` config |
| API mock not working | Route pattern mismatch | Use `**/api/...` glob, verify with `page.on('request')` |

---

## CLI Quick Reference

| Command | Description |
|---------|-------------|
| `npx playwright test` | Run all tests headless |
| `npx playwright test --ui` | Open UI mode (interactive) |
| `npx playwright test --headed` | Run with visible browser |
| `npx playwright test --debug` | Run with Playwright Inspector |
| `npx playwright test -g "login"` | Run tests matching pattern |
| `npx playwright test --project=chromium` | Run specific project |
| `npx playwright show-report` | Open HTML report |
| `npx playwright codegen` | Generate tests by recording |
| `PWDEBUG=1 npx playwright test` | Debug with Inspector |
| `DEBUG=pw:api npx playwright test` | Verbose API logging |

---

## References

| Document | Content |
|----------|---------|
| [Snippets](./references/snippets.md) | Ready-to-use code patterns |
| [Locator Strategies](./references/locator_strategies.md) | Complete locator guide |
| [Page Object Model](./references/page_object_model.md) | POM implementation patterns |
| [Debugging Guide](./references/debugging.md) | Troubleshooting & debugging techniques |

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/fugazi) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

