Test E2E
This skill provides a smart E2E test runner with targeting by app/feature and the ability to generate new test files for uncovered areas (especially Creator app).
When to Use This Skill
- •Running E2E tests before deployment
- •Testing specific features in isolation
- •Generating new E2E tests for Creator app
- •Verifying staging/production deployments
- •Debugging test failures
Current Test Coverage
Dashboard App (3 test files)
- •
auth.e2e.ts- Authentication flows (signup, signin, protected routes, OAuth) - •
comps.e2e.ts- Comps navigator functionality - •
trial.e2e.ts- Trial mode features
Creator App (0 test files)
No E2E tests exist - Use --generate to create tests
Website App (0 test files)
Marketing pages, minimal testing needed
Commands
Running Tests
/test-e2e # Run all tests /test-e2e dashboard # Run dashboard tests only /test-e2e dashboard auth # Run dashboard auth tests /test-e2e creator # Run creator tests (if any exist) /test-e2e --staging # Run against staging environment /test-e2e --production # Run against production (read-only tests) /test-e2e --ui # Open Playwright UI mode /test-e2e --debug # Run in headed debug mode
Generating Tests
/test-e2e --generate creator auth # Generate creator auth tests /test-e2e --generate creator titles # Generate creator title management tests /test-e2e --generate creator profile # Generate creator profile tests /test-e2e --generate dashboard mandates # Generate mandate search tests
Test Running Workflow
Step 1: Environment Setup
Configure test environment:
# Local (default) export TEST_ENV=local export BASE_URL=http://localhost:8081 # or 8083 for creator # Staging export TEST_ENV=staging export BASE_URL=https://dashboard-staging.kstorybridge.com # Production (read-only tests only!) export TEST_ENV=production export BASE_URL=https://dashboard.kstorybridge.com
Step 2: Start Dev Server (for local)
If testing locally, ensure dev server is running:
# For dashboard tests npm run dev:dashboard # For creator tests npm run dev:creator
Or use Playwright's webServer config (auto-starts).
Step 3: Run Tests
# All tests npm run test:e2e # Specific test file npx playwright test auth.e2e.ts # Specific test by name npx playwright test -g "should show validation errors" # With UI npm run test:e2e:ui # With debugging npm run test:e2e:debug
Step 4: Analyze Results
# Show HTML report npm run test:e2e:report # View test artifacts ls test-results/ ls playwright-report/
Test Generation Workflow
Step 1: Identify Test Scope
Determine what to test:
| App | Feature | Priority | Complexity |
|---|---|---|---|
| Creator | Auth (signup/signin) | HIGH | Medium |
| Creator | Title CRUD | HIGH | High |
| Creator | Profile editing | MEDIUM | Low |
| Creator | OAuth | MEDIUM | Medium |
| Dashboard | Mandate search | HIGH | Medium |
| Dashboard | AI chatbot | LOW | High (flaky) |
Step 2: Generate Test File
Create test file from template:
# Create test directory if needed mkdir -p apps/creator/e2e # Generate file (using template) # See templates/auth.template.ts for structure
Step 3: Customize Tests
Adjust selectors and assertions for specific app:
// Creator app uses different routes
await page.goto('/signin'); // Not /buyers/signin
// Creator app has different form fields
await page.getByLabel(/pen name/i).fill('Test Author');
Step 4: Run and Verify
# Run new tests npx playwright test apps/creator/e2e/auth.e2e.ts # Debug if failures npx playwright test --debug apps/creator/e2e/auth.e2e.ts
Test Patterns
Authentication Tests
import { test, expect } from '@playwright/test';
test.describe('Creator Auth', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/signup');
});
test('should show signup form', async ({ page }) => {
await expect(page.getByRole('heading', { name: /create.*account/i })).toBeVisible();
await expect(page.getByLabel(/email/i)).toBeVisible();
await expect(page.getByLabel(/password/i)).toBeVisible();
});
test('should validate required fields', async ({ page }) => {
await page.getByRole('button', { name: /create account/i }).click();
await expect(page.getByText(/required/i)).toBeVisible();
});
test('should redirect to home after signup', async ({ page }) => {
// Fill form
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByLabel(/password/i).fill('Password123!');
// ... fill other fields
await page.getByRole('button', { name: /create account/i }).click();
// Wait for redirect
await expect(page).toHaveURL('/home');
});
});
Protected Route Tests
test.describe('Protected Routes', () => {
test('should redirect unauthenticated users to signin', async ({ page }) => {
await page.goto('/home');
await expect(page).toHaveURL('/signin');
});
test('should allow authenticated users to access protected routes', async ({ page }) => {
// Login first
await page.goto('/signin');
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByLabel(/password/i).fill('Password123!');
await page.getByRole('button', { name: /sign in/i }).click();
// Access protected route
await page.goto('/home');
await expect(page).toHaveURL('/home');
});
});
CRUD Operation Tests
test.describe('Title Management', () => {
test.beforeEach(async ({ page }) => {
// Login as creator
await loginAsCreator(page);
});
test('should create a new title', async ({ page }) => {
await page.goto('/titles/new');
await page.getByLabel(/title name/i).fill('Test Title');
await page.getByLabel(/synopsis/i).fill('A test synopsis...');
// ... fill other fields
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/title created/i)).toBeVisible();
});
test('should edit an existing title', async ({ page }) => {
await page.goto('/titles');
await page.getByText('Test Title').click();
await page.getByRole('button', { name: /edit/i }).click();
await page.getByLabel(/title name/i).fill('Updated Title');
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/updated/i)).toBeVisible();
});
test('should delete a title', async ({ page }) => {
await page.goto('/titles');
await page.getByText('Test Title').click();
await page.getByRole('button', { name: /delete/i }).click();
await page.getByRole('button', { name: /confirm/i }).click();
await expect(page.getByText('Test Title')).not.toBeVisible();
});
});
Environment-Specific Testing
Staging Tests
Run tests against staging environment:
TEST_ENV=staging npm run test:e2e
Considerations:
- •Use test accounts (not production data)
- •Tests may take longer (network latency)
- •Some tests may need adjustment for staging data
Production Tests
Run read-only tests against production:
TEST_ENV=production npm run test:e2e:production
CRITICAL: Production tests must be READ-ONLY:
- •NO signup tests (creates real accounts)
- •NO data modification tests
- •Only test public pages and read operations
Safe production tests:
- •Page load verification
- •Public route accessibility
- •Static content presence
Test Reports
Console Output
Running E2E tests...
Dashboard Tests
auth.e2e.ts
✓ should show signup form (1.2s)
✓ should validate required fields (0.8s)
✓ should redirect to signin for protected routes (0.5s)
✗ should complete OAuth flow (timeout)
comps.e2e.ts
✓ should show comps navigator (1.5s)
✓ should search for comps (2.3s)
Summary
Tests: 5 passed, 1 failed
Duration: 8.3s
Failed Tests:
- auth.e2e.ts: should complete OAuth flow
Error: Timeout waiting for OAuth callback
HTML Report
After test run:
npm run test:e2e:report # Opens browser with detailed report
Slack Notification
{
"text": "E2E Test Results",
"attachments": [
{
"color": "warning",
"fields": [
{"title": "Passed", "value": "5", "short": true},
{"title": "Failed", "value": "1", "short": true},
{"title": "Duration", "value": "8.3s", "short": true},
{"title": "Environment", "value": "local", "short": true}
]
}
]
}
Debugging Tests
Interactive Mode
# Open Playwright UI npm run test:e2e:ui # Run specific test in debug mode npx playwright test -g "should show signup form" --debug
Trace Viewer
# Enable trace for all tests npx playwright test --trace on # View trace npx playwright show-trace test-results/auth-should-show-signup-form/trace.zip
Screenshots and Videos
Test artifacts are saved automatically on failure:
- •
test-results/[test-name]/screenshot.png - •
test-results/[test-name]/video.webm
Tips
- •Flaky tests: Add
test.retry(2)for network-dependent tests - •Slow tests: Use
test.slow()for long operations - •Test isolation: Each test should start with clean state
- •Selectors: Prefer
getByRole,getByLabel,getByTextover CSS selectors - •Timeouts: Increase for slow operations with
{ timeout: 10000 } - •Skip: Use
test.skip()for temporarily broken tests
Test Helpers
Create shared helpers in tests/helpers/:
// tests/helpers/auth.ts
export async function loginAsCreator(page: Page) {
await page.goto('/signin');
await page.getByLabel(/email/i).fill(process.env.TEST_CREATOR_EMAIL!);
await page.getByLabel(/password/i).fill(process.env.TEST_CREATOR_PASSWORD!);
await page.getByRole('button', { name: /sign in/i }).click();
await expect(page).toHaveURL('/home');
}
export async function loginAsBuyer(page: Page) {
await page.goto('/buyers/signin');
await page.getByLabel(/email/i).fill(process.env.TEST_BUYER_EMAIL!);
await page.getByLabel(/password/i).fill(process.env.TEST_BUYER_PASSWORD!);
await page.getByRole('button', { name: /sign in/i }).click();
await expect(page).toHaveURL('/buyers/home');
}
Related Skills
- •
/deploy-staging- Deploy to staging before running tests - •
/pr-production- Create production PR after tests pass - •
/health-check- Quick verification of app health