Files
headroom/openspec/templates/PlaywrightE2ETest.spec.ts

138 lines
4.2 KiB
TypeScript

import { test, expect } from '@playwright/test';
/*
* Test Template for Playwright E2E Tests
*
* Copy this file and replace:
* - {{capability}} with the capability name (e.g., 'authentication')
* - {{scenario_description}} with the spec scenario description
*
* Mark pending tests with test.fixme() during Red Phase
* Remove test.fixme() and implement during Green Phase
*/
test.describe('{{capability}} Flow', () => {
test.beforeEach(async ({ page }) => {
// Setup: Navigate to base URL, login, etc.
await page.goto('/');
});
/*
* Scenario: {{scenario_description}}
*
* Spec Reference: specs/{{capability}}/spec.md
* Scenario: {{scenario_number}}
*/
test('{{scenario_description}}', async ({ page }) => {
// Arrange (GIVEN)
// Set up initial state
await page.goto('/{{route}}');
// Act (WHEN)
// Perform user actions
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'password');
await page.click('button[type="submit"]');
// Assert (THEN)
// Verify expected outcome
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('[data-testid="success-message"]')).toBeVisible();
await expect(page.locator('[data-testid="user-name"]')).toContainText('Test User');
});
/*
* Example: Error scenario
*/
test('displays error when {{error_condition}}', async ({ page }) => {
// Arrange
await page.goto('/{{route}}');
// Act
await page.fill('[name="email"]', 'invalid@example.com');
await page.fill('[name="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
// Assert
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
await expect(page.locator('[data-testid="error-message"]')).toContainText('Invalid credentials');
await expect(page).toHaveURL('/{{route}}');
});
/*
* Example: Form validation
*/
test('validates required fields', async ({ page }) => {
// Arrange
await page.goto('/{{route}}');
// Act
await page.click('button[type="submit"]');
// Assert
await expect(page.locator('[data-testid="error-email"]')).toContainText('Email is required');
await expect(page.locator('[data-testid="error-password"]')).toContainText('Password is required');
});
/*
* Example: CRUD operation
*/
test('completes full {{resource}} CRUD flow', async ({ page }) => {
// Create
await page.goto('/{{resource}}/new');
await page.fill('[name="name"]', 'Test {{resource}}');
await page.click('button[type="submit"]');
await expect(page.locator('[data-testid="success-message"]')).toBeVisible();
// Read
await page.goto('/{{resource}}');
await expect(page.locator('text=Test {{resource}}')).toBeVisible();
// Update
await page.click('[data-testid="edit-{{resource}}"]');
await page.fill('[name="name"]', 'Updated {{resource}}');
await page.click('button[type="submit"]');
await expect(page.locator('[data-testid="success-message"]')).toBeVisible();
// Delete
await page.click('[data-testid="delete-{{resource}}"]');
await page.click('[data-testid="confirm-delete"]');
await expect(page.locator('text=Updated {{resource}}')).not.toBeVisible();
});
/*
* Example: RBAC test
*/
test('restricts access based on role', async ({ page }) => {
// Login as developer (limited permissions)
await loginAs(page, 'developer');
// Try to access admin page
await page.goto('/admin/{{resource}}');
// Should be redirected or show access denied
await expect(page.locator('[data-testid="access-denied"]')).toBeVisible();
});
});
/*
* Helper functions
*/
async function loginAs(page, role: string) {
await page.goto('/login');
await page.fill('[name="email"]', `${role}@example.com`);
await page.fill('[name="password"]', 'password');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
}
async function create{{model}}(page, data: object) {
await page.goto('/{{resource}}/new');
for (const [key, value] of Object.entries(data)) {
await page.fill(`[name="${key}"]`, value);
}
await page.click('button[type="submit"]');
}