| name | e2e-tests |
| description | Create and maintain Playwright E2E tests following project patterns. Use when adding new tests, debugging test failures, or understanding test structure. |
E2E Test Creation Skill
This skill helps you write and maintain Playwright end-to-end tests following the established patterns in this codebase.
When to Use This Skill
Invoke this skill when:
- Creating a new e2e test file
- Adding tests to an existing test file
- Debugging flaky or failing tests
- User asks about e2e test patterns or best practices
- User wants to test a new feature end-to-end
Framework Overview
| Property | Value |
|---|---|
| Framework | Playwright (@playwright/test v1.56.1) |
| Location | /e2e/ directory |
| Config | playwright.config.ts |
| Run command | npm run test:e2e |
| Total tests | 80+ tests across 13 files |
Two Authentication Modes
The project uses a two-tier authentication system:
1. Authenticated Tests (Default - Most Tests)
- Run with project
chromium-authenticated - Use cached auth state from
.auth/student.json - No login needed - tests start authenticated
- Excludes:
auth.spec.ts,registration.spec.ts
2. Unauthenticated Tests
- Run with project
chromium-unauthenticated - For testing login/registration flows
- Files:
auth.spec.ts,registration.spec.ts - Must handle cookie banner dismissal
File Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Test file | {feature}.spec.ts |
practice.spec.ts |
| Setup file | *.setup.ts |
auth.setup.ts |
| Helper file | helpers/{name}.ts |
helpers/auth.ts |
Creating a New Test File
Step 1: Determine Authentication Mode
Ask: Does this test need to start from a logged-out state?
- YES → Use unauthenticated pattern, add to
testMatchin config - NO → Use authenticated pattern (default)
Step 2: Create File Structure
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
// Authentication handled via storageState in playwright.config.ts
// No beforeEach login needed for authenticated tests
test('should describe expected behavior', async ({ page }) => {
await page.goto('/feature-path', { waitUntil: 'domcontentloaded' });
// Test implementation
});
});
Step 3: Apply Selector Strategy
Priority order:
data-testidattributes (most reliable)getByRole(semantic, accessible)getByText(user-visible text)- CSS selectors (last resort)
→ See selectors-guide.md for detailed guidance.
Step 4: Apply Wait Patterns
NEVER use waitForTimeout for synchronization.
Use event-based waits:
// Wait for element visibility
await expect(element).toBeVisible({ timeout: 5000 });
// Wait for URL change
await page.waitForURL(/\/dashboard/, { timeout: 5000 });
// Wait for network
await page.waitForLoadState('networkidle');
→ See anti-patterns.md for common mistakes.
Selector Strategy Quick Reference
// PREFERRED: data-testid
await page.getByTestId('mode-zen').click();
// GOOD: Role-based
await page.getByRole('button', { name: /Siguiente/i }).click();
await page.getByRole('heading', { name: /Dashboard/i });
// ACCEPTABLE: Text-based
await page.getByText(/Práctica PAES/i).toBeVisible();
// LAST RESORT: CSS selectors
await page.locator('.specific-class').click();
→ See selectors-guide.md for comprehensive guidance.
Test Configuration Quick Reference
// Extend timeout for long tests
test('long flow', async ({ page }) => {
test.setTimeout(60000); // 60 seconds
});
// Retry flaky tests
test('registration', { retries: 2 }, async ({ page }) => {});
// Scope auth to describe block
test.describe('Admin features', () => {
test.use({ storageState: '.auth/admin.json' });
});
// Skip in CI
test.skip(process.env.CI === 'true', 'Local only');
→ See advanced-patterns.md for complete configuration options.
Common Test Patterns
Pattern 1: Navigation Test
test('should navigate to feature page', async ({ page }) => {
await page.goto('/feature', { waitUntil: 'domcontentloaded' });
await expect(page.getByRole('heading', { name: /Feature Title/i })).toBeVisible();
await expect(page.getByTestId('main-content')).toBeVisible();
});
Pattern 2: Form Interaction
test('should submit form successfully', async ({ page }) => {
await page.goto('/form-page', { waitUntil: 'domcontentloaded' });
await page.getByTestId('input-field').fill('value');
await page.getByRole('button', { name: /Submit/i }).click();
await expect(page.getByText(/Success/i)).toBeVisible();
});
Pattern 3: API Interception
test('should handle API errors gracefully', async ({ page }) => {
await page.route('**/api/endpoint', route => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Server error' }),
});
});
await page.goto('/page');
await expect(page.getByText(/error/i)).toBeVisible();
});
Pattern 4: Cookie Banner Dismissal
// For unauthenticated tests - add at start of test or beforeEach
await page.evaluate(() => {
localStorage.setItem('cookie-consent', 'accepted');
});
→ See templates.md for 12 complete copy-paste templates.
→ See advanced-patterns.md for resilience, loops, and advanced waits.
Quality Gates
Before completing any test file:
- Uses
data-testidfor critical elements - No
waitForTimeoutfor synchronization (only for animations) - All waits are event-based
- Test names start with "should"
- Uses
test.describe()for grouping - Handles both success and error cases
- Cookie banner dismissed if needed (unauthenticated tests)
- No hardcoded credentials (use test users from db-setup)
Running Tests
# Run all e2e tests
npm run test:e2e
# Run specific test file
npx playwright test e2e/practice.spec.ts
# Run with UI mode (interactive)
npx playwright test --ui
# Run with headed browser (visible)
npx playwright test --headed
# Debug a specific test
npx playwright test --debug -g "test name"
# Run tests matching pattern
npx playwright test -g "should login"
Test Categories
Current test file organization:
| Category | Files | Purpose |
|---|---|---|
| Auth | auth.spec.ts, registration.spec.ts |
Login, registration flows |
| Practice | practice.spec.ts, adaptive-practice.spec.ts |
Quiz modes (Zen, Rapid Fire) |
| Progress | progress.spec.ts |
Analytics, history, skills |
| Profile | profile.spec.ts |
User profile, stats |
| Curriculum | curriculum.spec.ts |
M1/M2 curriculum pages |
| Lessons | mini-lesson-*.spec.ts |
Interactive mini-lessons |
| Subscription | subscription-trial.spec.ts |
Pricing, trials, paywalls |
| Live | live-practice.spec.ts |
Real-time sessions |
| AI | ai-tutor.spec.ts |
AI tutor functionality |
| Knowledge | knowledge-declarations.spec.ts |
Skill declarations |
Test Credentials
| Role | Password | |
|---|---|---|
| Student | student@test.com |
StudentTest123! |
| Admin | admin@test.com |
AdminTest123! |
→ See helpers-reference.md for complete helper documentation.
Related Files
- →
advanced-patterns.md- Test config, waits, assertions, resilience patterns - →
anti-patterns.md- Common mistakes to avoid - →
templates.md- 12 copy-paste test templates - →
selectors-guide.md- Selector strategy deep-dive - →
helpers-reference.md- Auth and DB helper documentation