| name | write-e2e-test |
| description | 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. |
Write E2E Test
Create Playwright E2E tests for agentconfig.org following project patterns.
Test File Location
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
Test File Structure
import { test, expect } from '@playwright/test'
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
})
test('should do something specific', async ({ page }) => {
const element = page.getByRole('button', { name: 'Click me' })
await element.click()
await expect(page.getByText('Success')).toBeVisible()
})
})
Locator Strategy (Priority Order)
Use the most resilient locators, in this order of preference:
1. Role + Name (most resilient)
page.getByRole('button', { name: 'Submit' })
page.getByRole('heading', { name: 'Welcome' })
page.getByRole('navigation')
2. Label/Placeholder (for form elements)
page.getByLabel('Email address')
page.getByPlaceholder('Enter your email')
3. Text content (for static text)
page.getByText('Learn more')
page.getByText(/welcome/i)
4. Test ID (when others don't work)
page.getByTestId('file-tree-node')
5. CSS selectors (last resort)
page.locator('.custom-component')
Common Test Patterns
Navigation
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()
})
Theme Toggle
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')
})
Expandable Content
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()
})
Copy to Clipboard
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')
})
Responsive Testing
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()
})
})
Theme Testing
Test both light and dark modes:
test.describe('Dark mode', () => {
test.use({ colorScheme: 'dark' })
test('should render with dark theme colors', async ({ page }) => {
})
})
test.describe('Light mode', () => {
test.use({ colorScheme: 'light' })
test('should render with light theme colors', async ({ page }) => {
})
})
Test Independence
Each test must be independent:
- Don't rely on state from previous tests
- Use
beforeEach for common setup
- Clean up any side effects
Assertions
Use Playwright's built-in assertions (auto-waiting):
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')
const text = await page.textContent('.item')
expect(text).toBe('Hello')
Test Naming
Use descriptive names that explain the expected behavior:
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', ...)
test('click test', ...)
test('tree works', ...)
test('test 1', ...)
Running Tests
cd site
bun run test
bun run test:ui
Checklist
Before considering tests complete: