Agent skill
playwright-testing-frankxai-arcanea
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/playwright-testing-frankxai-arcanea
SKILL.md
Arcanea Playwright Testing
"Alera guards the Voice Gate at 528 Hz — Truth, expression. Tests are truth-tellers. They reveal what is real, not what we hope."
Quick Start
Decision Tree
Test need → Is it a pure unit (function/hook)?
├─ Yes → Jest in packages/
└─ No → Playwright E2E
Playwright task → Is the dev server running?
├─ No → Use with_server.py helper OR next dev directly
└─ Yes → Reconnaissance-then-action pattern
Run Arcanea Dev Server for Tests
# From monorepo root
pnpm --filter web dev # starts on :3000
# Or in test scripts — use playwright's webServer config
Setup
playwright.config.ts (apps/web)
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [['html', { outputFolder: 'playwright-report' }]],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
// Auto-start dev server for local testing
webServer: {
command: 'pnpm --filter web dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'Mobile Safari', use: { ...devices['iPhone 14'] } },
],
})
Reconnaissance-Then-Action Pattern
Always inspect before acting on dynamic Next.js pages:
import { test, expect } from '@playwright/test'
test('lore page loads', async ({ page }) => {
// 1. Navigate and wait
await page.goto('/lore')
await page.waitForLoadState('networkidle') // CRITICAL for Next.js hydration
// 2. Reconnaissance — take screenshot to understand DOM state
await page.screenshot({ path: 'tests/screenshots/lore-debug.png', fullPage: true })
// 3. Identify selectors from rendered state
const guardianCards = page.locator('[data-testid="guardian-card"]')
const count = await guardianCards.count()
console.log(`Found ${count} guardian cards`)
// 4. Assert
expect(count).toBeGreaterThanOrEqual(1)
})
Arcanea Test Patterns
Academy Gate Quiz Flow
test('gate quiz — foundation gate completion', async ({ page }) => {
// Auth: sign in as test user
await page.goto('/auth/sign-in')
await page.waitForLoadState('networkidle')
await page.fill('[data-testid="email-input"]', process.env.TEST_USER_EMAIL!)
await page.fill('[data-testid="password-input"]', process.env.TEST_USER_PASSWORD!)
await page.click('[data-testid="sign-in-btn"]')
await page.waitForURL('/academy')
// Navigate to Foundation Gate quiz
await page.goto('/academy/gate-quiz?gate=foundation')
await page.waitForLoadState('networkidle')
// Answer questions (10 questions for Foundation gate)
for (let i = 0; i < 10; i++) {
const question = page.locator('[data-testid="quiz-question"]')
await expect(question).toBeVisible()
// Select first answer (test data has correct first answers)
await page.locator('[data-testid="answer-option"]').first().click()
await page.locator('[data-testid="next-question-btn"]').click()
}
// Verify completion screen
await expect(page.locator('[data-testid="gate-complete"]')).toBeVisible()
await expect(page.locator('[data-testid="guardian-name"]')).toContainText('Lyssandria')
})
Lore Navigation Test
test('lore explore — elements navigation', async ({ page }) => {
await page.goto('/lore')
await page.waitForLoadState('networkidle')
// Test all five elements navigation
const elements = ['fire', 'water', 'earth', 'wind', 'void']
for (const element of elements) {
await page.goto(`/lore/elements/${element}`)
await page.waitForLoadState('networkidle')
// Verify element page loads with correct title
const title = page.locator('h1, [data-testid="element-title"]')
await expect(title).toBeVisible()
await expect(title).toContainText(element, { ignoreCase: true })
}
})
Prompt Books CRUD Flow
test('prompt books — create and view prompt', async ({ page }) => {
await authenticateTestUser(page)
await page.goto('/prompt-books')
await page.waitForLoadState('networkidle')
// Create new prompt
await page.click('[data-testid="new-prompt-btn"]')
await page.waitForLoadState('networkidle')
// Fill editor
await page.fill('[data-testid="prompt-title"]', 'Test Guardian Invocation')
await page.locator('[data-testid="prompt-editor"]').click()
await page.keyboard.type('Channel the essence of {element} through {gate}')
await page.click('[data-testid="save-prompt-btn"]')
// Verify saved
await expect(page.locator('[data-testid="save-success"]')).toBeVisible()
// Navigate back and verify prompt appears
await page.goto('/prompt-books')
await expect(page.locator('text=Test Guardian Invocation')).toBeVisible()
})
Visual Regression — Glass Design System
test('guardian card visual snapshot', async ({ page }) => {
await page.goto('/lore/guardians')
await page.waitForLoadState('networkidle')
// Wait for glassmorphism animations to settle
await page.waitForTimeout(500)
// Snapshot the guardian grid
const grid = page.locator('[data-testid="guardian-grid"]')
await expect(grid).toHaveScreenshot('guardian-grid.png', {
maxDiffPixelRatio: 0.02, // 2% tolerance for antialiasing
})
})
Auth Helper
// tests/helpers/auth.ts — reuse across tests
import { type Page } from '@playwright/test'
export async function authenticateTestUser(page: Page) {
await page.goto('/auth/sign-in')
await page.waitForLoadState('networkidle')
await page.fill('[name="email"]', process.env.TEST_USER_EMAIL!)
await page.fill('[name="password"]', process.env.TEST_USER_PASSWORD!)
await page.click('[type="submit"]')
await page.waitForURL(/\/(academy|dashboard|lore)/)
}
// Use storage state for faster auth across tests
// playwright.config.ts: use: { storageState: 'playwright/.auth/user.json' }
Save auth state once
// tests/auth.setup.ts — runs before test suite
import { test as setup } from '@playwright/test'
import { authenticateTestUser } from './helpers/auth'
const authFile = 'playwright/.auth/user.json'
setup('authenticate', async ({ page }) => {
await authenticateTestUser(page)
await page.context().storageState({ path: authFile })
})
Common Patterns
Wait for Next.js hydration
// Always after navigation to dynamic Next.js pages
await page.goto('/path')
await page.waitForLoadState('networkidle') // waits for all network requests
Capture screenshots for debugging
// Inline debug screenshot (delete after fixing)
await page.screenshot({ path: `/tmp/debug-${Date.now()}.png`, fullPage: true })
Test data attributes — add to Arcanea components
// In components: always add data-testid for critical interactive elements
<button data-testid="unlock-gate-btn" onClick={handleUnlock}>
Unlock Gate
</button>
// In tests: prefer data-testid over CSS selectors (resilient to style changes)
await page.click('[data-testid="unlock-gate-btn"]')
Intercept API calls
// Mock Supabase/API calls in tests
await page.route('/api/guardians', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(MOCK_GUARDIANS),
})
})
Running Tests
# From apps/web directory
cd apps/web
# Run all E2E tests
npx playwright test
# Run specific test file
npx playwright test tests/e2e/lore.spec.ts
# Run with UI mode (visual debugger)
npx playwright test --ui
# Debug specific test
npx playwright test --debug tests/e2e/gate-quiz.spec.ts
# Update snapshots
npx playwright test --update-snapshots
# View report
npx playwright show-report
Quick Checklist
Before shipping any Playwright test in Arcanea:
-
waitForLoadState('networkidle')after every navigation - Screenshots taken during reconnaissance, deleted before commit
-
data-testidattributes used for selectors (not CSS classes) - Auth handled via storage state for speed
- Environment variables for test credentials (TEST_USER_EMAIL, TEST_USER_PASSWORD)
- API mocks in place for flaky external calls
- Tests are independent — no shared state between tests
- Visual snapshots have 2% tolerance for antialiasing
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?