Write Playwright E2E tests following project patterns for components and user interactions. Use when adding tests for new features, testing accessibility, or validating responsive behavior.
Create Playwright E2E tests for agentconfig.org following project patterns.
All E2E tests live in site/tests/e2e/:
site/tests/e2e/
├── app.spec.ts
├── comparison.spec.ts
├── fileTree.spec.ts
├── navigation.spec.ts
├── primitiveCards.spec.ts
└── theme.spec.ts
import { test, expect } from '@playwright/test'
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
})
test('should do something specific', async ({ page }) => {
// Arrange
const element = page.getByRole('button', { name: 'Click me' })
// Act
await element.click()
// Assert
await expect(page.getByText('Success')).toBeVisible()
})
})
Use the most resilient locators, in this order of preference:
page.getByRole('button', { name: 'Submit' })
page.getByRole('heading', { name: 'Welcome' })
page.getByRole('navigation')
page.getByLabel('Email address')
page.getByPlaceholder('Enter your email')
page.getByText('Learn more')
page.getByText(/welcome/i) // regex for flexible matching
page.getByTestId('file-tree-node')
page.locator('.custom-component')
test('should scroll to section when nav link is clicked', async ({ page }) => {
const navLink = page.getByRole('link', { name: 'File Tree' })
const section = page.getByRole('region', { name: 'File Tree' })
await navLink.click()
await expect(section).toBeInViewport()
})
test('should toggle between light and dark mode', async ({ page }) => {
const toggle = page.getByRole('button', { name: /theme/i })
await expect(page.locator('html')).toHaveAttribute('data-theme', 'light')
await toggle.click()
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark')
})
test('should expand tree node on click', async ({ page }) => {
const treeNode = page.getByRole('button', { name: '.github/' })
const childNode = page.getByRole('button', { name: 'copilot-instructions.md' })
await expect(childNode).not.toBeVisible()
await treeNode.click()
await expect(childNode).toBeVisible()
})
test('should copy template to clipboard', async ({ page, context }) => {
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
const copyButton = page.getByRole('button', { name: 'Copy template' })
await copyButton.click()
const clipboardText = await page.evaluate(() => navigator.clipboard.readText())
expect(clipboardText).toContain('expected content')
})
Test at multiple viewport sizes:
test.describe('Mobile viewport', () => {
test.use({ viewport: { width: 375, height: 667 } })
test('should show mobile navigation', async ({ page }) => {
await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible()
})
})
test.describe('Desktop viewport', () => {
test.use({ viewport: { width: 1280, height: 720 } })
test('should show full navigation', async ({ page }) => {
await expect(page.getByRole('navigation')).toBeVisible()
})
})
Test both light and dark modes:
test.describe('Dark mode', () => {
test.use({ colorScheme: 'dark' })
test('should render with dark theme colors', async ({ page }) => {
// Verify dark mode specific styling
})
})
test.describe('Light mode', () => {
test.use({ colorScheme: 'light' })
test('should render with light theme colors', async ({ page }) => {
// Verify light mode specific styling
})
})
Each test must be independent:
beforeEach for common setupUse Playwright's built-in assertions (auto-waiting):
// Good - auto-waits for element
await expect(page.getByText('Hello')).toBeVisible()
await expect(page.getByRole('button')).toBeEnabled()
await expect(page.locator('.item')).toHaveCount(3)
await expect(page.getByRole('link')).toHaveAttribute('href', '/about')
// Bad - doesn't auto-wait
const text = await page.textContent('.item')
expect(text).toBe('Hello')
Use descriptive names that explain the expected behavior:
// Good
test('should expand tree node when clicked', ...)
test('should scroll to File Tree section when nav link is clicked', ...)
test('should display error message when copy fails', ...)
// Bad
test('click test', ...)
test('tree works', ...)
test('test 1', ...)
cd site
bun run test # Run all tests
bun run test:ui # Interactive test UI
Before considering tests complete:
page.waitForTimeout)Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer