con un clic
e2e-testing
Writing, running, and debugging Playwright E2E tests in coding-tool.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Writing, running, and debugging Playwright E2E tests in coding-tool.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Single entry point for all implementation work. Triages tasks, manages beads issues, delegates to implementer skill, runs reviewers, creates PRs.
Pure development workflow with test-first development and coverage review. Used by coordinator as a subagent. Commits to its own worktree branch, but never pushes, manages beads issues, or opens PRs.
Review PR diff for bugs, error handling gaps, security issues, and API contract mismatches. Spawned by coordinator before PR creation.
Review PR test quality — meaningful coverage, edge cases, integration tests, and test accuracy. Spawned by coordinator before PR creation.
Day-2 training tutor for the agent-friendly-project exercise. Answers student questions about the process, the exercise, the workflow, the concepts from the training, or anything about this repo. Invoked via /tutorial.
Process open PRs — merge when CI passes, handle rebases, file issues for failures. Run in a dedicated window.
| name | e2e-testing |
| description | Writing, running, and debugging Playwright E2E tests in coding-tool. |
Guide for writing, running, and debugging Playwright E2E tests. Tests run against
the real Next.js app (auto-started via webServer) and a local Supabase stack.
helpers/setup.ts also clears test data before each test.When a test fails, in order:
Playwright says exactly which selector failed and why. Start there.
Failed tests write test-results/<test-name>/error-context.md — a YAML snapshot of the DOM at failure time (roles, labels, disabled states). Often more useful than a screenshot for seeing what actually rendered.
Config captures screenshot only-on-failure, video retain-on-failure, and trace on-first-retry. Open the report:
npx playwright show-report
Look for non-200s or unexpected bodies in the captured console logs. For auth/MFA/invitation flows, inspect sent email in the local Mailpit UI at http://localhost:54324 (the helpers/inbucket-client.ts helpers read from it).
Common patterns:
getSupabaseAdmin() insert or sign-in helper threw; check Supabase is running and migrations are applied (npx supabase db reset).npx playwright test e2e/your-test.spec.ts --headed # watch the browser
npx playwright test e2e/your-test.spec.ts --debug # Playwright Inspector, step through
Prerequisite: local Supabase running and .env.local present (the devcontainer does this on boot; otherwise npx supabase start). SUPABASE_SECRET_KEY must be set — specs are skipped without it (hasSupabaseCredentials()).
npm run test:e2e # full suite (auto-starts the app)
npx playwright test e2e/your-test.spec.ts # single file
npx playwright test -g "substring" # single test by name
Import the extended test/expect from helpers/setup (it clears test data before each test):
import { test, expect } from './helpers/setup';
import { hasSupabaseCredentials, getTestUserEmail } from './helpers/db-helpers';
import {
loginAsInstructor, loginAsStudent,
generateTestNamespaceId, createTestNamespace, cleanupNamespace,
} from './fixtures/auth-helpers';
import { getSupabaseAdmin, createTestClass, createTestSection } from './helpers/test-data';
const describeE2E = hasSupabaseCredentials() ? test.describe : test.describe.skip;
describeE2E('Feature', () => {
test('does the thing', async ({ page, browser }) => {
const namespaceId = generateTestNamespaceId();
await createTestNamespace(namespaceId);
try {
// 1. DATA SETUP via Supabase service role (fast, no UI)
const supabase = getSupabaseAdmin();
// ... createTestClass / createTestSection / createTestProblem ...
// 2. AUTH + UI
await loginAsInstructor(page, `instructor-${namespaceId}`, namespaceId);
await page.goto('/instructor');
// 3. ASSERT
await expect(page.locator('h2:has-text("Dashboard")')).toBeVisible();
} finally {
await cleanupNamespace(namespaceId);
}
});
});
getSupabaseAdmin() + the helpers/test-data.ts helpers (createTestClass, createTestSection, createTestProblem, createInstructorForSection, setupTestNamespaceWithSection, createTestInvitation). Only drive the UI for the flow you're actually verifying.namespaceId (generateTestNamespaceId()); pass it to the login/data helpers and cleanupNamespace() in finally.loginAsInstructor/loginAsStudent/loginAsSystemAdmin, or signInAs(page, username, role, namespaceId). system-admin triggers an MFA OTP — the helper reads it from Mailpit automatically; if it hangs, check http://localhost:54324.browser.newContext() per user; close extra contexts in finally.expect(...).toBeVisible(), page.waitForURL()). Avoid waitForTimeout except a brief settle for the debounced code sync.The code editor is Monaco. There is no test hook — interact through the rendered editor:
const editor = page.locator('.monaco-editor').first();
await editor.click();
await page.keyboard.press('ControlOrMeta+a');
await page.keyboard.press('Backspace');
await page.keyboard.type('print("hello")', { delay: 50 }); // delay lets Monaco capture keystrokes
await page.waitForTimeout(300); // settle for debounced sync
Monaco splits text across DOM nodes — assert on the .monaco-editor text content (partial match), not a single element.
| File | Purpose |
|---|---|
e2e/helpers/setup.ts | Extended test/expect; clears test data before each test |
e2e/helpers/db-helpers.ts | createTestUser, namespace create/cleanup, hasSupabaseCredentials, test email/password |
e2e/helpers/test-data.ts | getSupabaseAdmin() + class/section/problem/invitation setup helpers |
e2e/helpers/inbucket-client.ts | Read email/OTP from Mailpit (localhost:54324) |
e2e/fixtures/auth-helpers.ts | signInAs, loginAs*, sidebar navigation helpers |
playwright.config.ts | testDir: e2e/, Chromium, 2 workers (non-CI), retries 0, 30s timeout, auto webServer, baseURL localhost:3000 |