playwright
Use to write or set up Playwright / @playwright/test E2E tests — test specs, locators, assertions, fixtures, config. Focuses on structuring tests with high-precision locator strategies. Not for live page exploration or debugging (use agent-browser or web-test).
What this skill does
# Playwright Test Writing
## Overview
Write browser tests using `@playwright/test`, the official Playwright test runner. It provides auto-waiting, test isolation, built-in web assertions with auto-retry, and parallel execution out of the box.
Prefer `@playwright/test` for all testing scenarios. Use library mode (`playwright`) only for direct browser scripting needs: console error capture, network inspection, or custom automation outside a test context.
## Project Setup
```bash
# Initialize Playwright in a project
npm init playwright@latest
# Or add to existing project
npm install -D @playwright/test
npx playwright install
```
Essential `playwright.config.ts` (for full multi-browser, reporter, and webServer config, see `references/api-patterns.md`):
```typescript
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
},
});
```
## Test File Structure
Tests follow the pattern: `tests/<feature>.spec.ts`
```typescript
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('should perform expected behavior', async ({ page }) => {
// Arrange
const submitButton = page.getByRole('button', { name: 'Submit' });
// Act
await submitButton.click();
// Assert
await expect(page.getByText('Success')).toBeVisible();
});
});
```
Always use the AAA pattern: **Arrange** (locate elements), **Act** (interact), **Assert** (verify outcome).
## Locator Strategy (Precision Order)
Apply locators in strict priority order — always prefer the highest-precision option available:
| Priority | Locator | When to Use | Stability |
|----------|---------|-------------|-----------|
| 1 | `getByTestId('id')` | Element has `data-testid` attribute | Highest — immune to text/structure changes |
| 2 | `getByRole('role', { name })` | Element has clear ARIA role and accessible name | Very high — maps from accessibility tree |
| 3 | `getByLabel('label')` | Form inputs with associated `<label>` | High — tied to label text |
| 4 | `getByPlaceholder('text')` | Input with placeholder text | Medium-high — placeholder may change |
| 5 | `getByText('text', { exact: true })` | Visible text content | Medium — text may change |
| 6 | `page.locator('[data-attr="val"]')` | Custom data attributes | Medium — depends on attribute stability |
| 7 | `page.locator('css')` | Last resort — specific CSS selector | Low — fragile to DOM restructuring |
**Rules:**
- Never use index-based locators (`nth(0)`) unless testing a list where index is semantically meaningful.
- Never use XPath unless no other option exists.
- Always pass `{ exact: true }` to `getByText()` to prevent partial matches.
- Prefer accessibility-based locators (`getByRole`, `getByLabel`) — they match what users see and interact with.
- When multiple elements share the same locator, narrow with `.filter({ hasText: 'unique' })` or scope to a parent.
For locator disambiguation patterns (filter, chaining, scoping), see `references/api-patterns.md`.
## Web Assertions (Auto-retry)
Playwright's `expect` API auto-retries assertions until timeout (default 5s):
```typescript
// Visibility
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();
// Text content
await expect(locator).toHaveText('exact text');
await expect(locator).toContainText('partial');
// Input values
await expect(locator).toHaveValue('input value');
// Attributes
await expect(locator).toHaveAttribute('href', '/path');
// Count
await expect(locator).toHaveCount(3);
// Element state
await expect(locator).toBeEnabled();
await expect(locator).toBeDisabled();
await expect(locator).toBeChecked();
// Page-level
await expect(page).toHaveURL(/\/dashboard/);
await expect(page).toHaveTitle('Dashboard');
```
**Critical**: Always use `await expect(locator).toHaveText()` — not `expect(await locator.textContent()).toBe()`. Web assertions auto-retry; manual extraction does not.
## Page Interactions
```typescript
// Click
await page.getByRole('button', { name: 'Save' }).click();
// Fill (clears existing content first)
await page.getByLabel('Email').fill('[email protected]');
// Type sequentially (appends, triggers key events)
await page.getByLabel('Search').pressSequentially('query');
// Select dropdown
await page.getByLabel('Country').selectOption('US');
// Check / uncheck
await page.getByLabel('Agree to terms').check();
// Keyboard
await page.keyboard.press('Enter');
await page.keyboard.press('Escape');
// File upload
await page.getByLabel('Upload').setInputFiles('path/to/file.pdf');
// Wait for navigation
await page.waitForURL('/dashboard');
```
## Test Lifecycle
```typescript
test.describe('Suite', () => {
test.beforeAll(async () => {
// Run once before all tests in this suite
});
test.beforeEach(async ({ page }) => {
// Run before each test — common setup (e.g., navigation)
});
test.afterEach(async ({ page }) => {
// Run after each test — cleanup
});
test.afterAll(async () => {
// Run once after all tests in this suite
});
});
```
### Custom Fixtures
Extend the `test` object with reusable setup logic:
```typescript
import { test as base, Page } from '@playwright/test';
const test = base.extend<{ authenticatedPage: Page }>({
authenticatedPage: async ({ page }, use) => {
await page.goto('/login');
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign In' }).click();
await page.waitForURL('/dashboard');
await use(page);
},
});
test('should show user profile', async ({ authenticatedPage }) => {
await authenticatedPage.goto('/profile');
await expect(authenticatedPage.getByRole('heading', { name: 'Profile' })).toBeVisible();
});
```
## Running Tests
```bash
npx playwright test # Run all tests
npx playwright test login.spec.ts # Run specific file
npx playwright test --headed # Visible browser
npx playwright test --ui # Interactive UI mode
npx playwright test --debug # Step-through debugger
npx playwright show-report # View HTML report
```
## Library Mode (Advanced)
Use `playwright` (not `@playwright/test`) when direct browser control is needed outside a test runner:
```typescript
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// Console error capture
page.on('console', msg => {
if (msg.type() === 'error') console.log('Console error:', msg.text());
});
// Network request inspection
page.on('response', response => {
if (response.status() >= 400)
console.log(`Failed: ${response.status()} ${response.url()}`);
});
// JavaScript execution in page context
const title = await page.evaluate(() => document.title);
// Cookie inspection
const cookies = await context.cookies();
await browser.close();
```
Use library mode for: console error capture, network inspection, `page.evaluate()`, iframe/shadow DOM exploration, cookie/localStorage checks.
## Additional Resources
### Reference Files
For detailed locator mapping, assertion catalog, and advanced configuration:
- **`references/api-patterns.md`** — Locator disambiguation patterns, assertion quick reference, advanced fixtures and configuration
Related in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.