feat(test): implement comprehensive Playwright test suite

- Add complete E2E test coverage for authentication flows
- Implement component interaction and navigation testing
- Create responsive design validation across viewports
- Add theme switching and visual regression testing
- Include smoke tests for critical user paths
- Configure Playwright with proper test setup

Test suite ensures application reliability with automated validation
of user flows, accessibility, and visual consistency.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-16 12:44:32 -06:00
parent 3452f02afc
commit 48b9b680e3
11 changed files with 2710 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
import { test, expect, Page } from '@playwright/test';
import path from 'path';
const DEMO_ACCOUNTS = {
admin: { email: 'admin@example.com', password: 'demo123' },
organizer: { email: 'organizer@example.com', password: 'demo123' },
staff: { email: 'staff@example.com', password: 'demo123' },
};
async function takeScreenshot(page: Page, name: string) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `auth_${name}_${timestamp}.png`;
await page.screenshot({
path: path.join('screenshots', fileName),
fullPage: true
});
return fileName;
}
async function clearAuthStorage(page: Page) {
await page.evaluate(() => {
localStorage.removeItem('bct_auth_user');
localStorage.removeItem('bct_auth_remember');
});
}
test.describe('Authentication Flows (Realistic)', () => {
test.beforeEach(async ({ page }) => {
// Clear any existing auth state
await clearAuthStorage(page);
await page.goto('/');
});
test('should redirect to login when accessing protected route', async ({ page }) => {
await page.goto('/dashboard');
// Should redirect to login page
await expect(page).toHaveURL('/login');
await expect(page.locator('h1')).toContainText('Black Canyon Tickets');
await expect(page.locator('text=Sign in to your account')).toBeVisible();
await takeScreenshot(page, 'protected-route-redirect');
});
test('should login successfully with valid admin credentials', async ({ page }) => {
await page.goto('/login');
await takeScreenshot(page, 'login-page-initial');
// Fill in admin credentials using form elements
await page.fill('input[name="email"]', DEMO_ACCOUNTS.admin.email);
await page.fill('input[name="password"]', DEMO_ACCOUNTS.admin.password);
await takeScreenshot(page, 'login-form-filled');
// Submit login form
await page.click('button[type="submit"]');
// Wait for loading state
await expect(page.locator('text=Signing in...')).toBeVisible();
await takeScreenshot(page, 'login-loading-state');
// Should redirect to dashboard
await expect(page).toHaveURL('/', { timeout: 10000 });
// Verify user is logged in by checking for user name in sidebar
await expect(page.locator('text=Sarah Admin')).toBeVisible();
await takeScreenshot(page, 'dashboard-logged-in-admin');
});
test('should login successfully with organizer credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', DEMO_ACCOUNTS.organizer.email);
await page.fill('input[name="password"]', DEMO_ACCOUNTS.organizer.password);
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/', { timeout: 10000 });
await expect(page.locator('text=John Organizer')).toBeVisible();
await takeScreenshot(page, 'dashboard-logged-in-organizer');
});
test('should login successfully with staff credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', DEMO_ACCOUNTS.staff.email);
await page.fill('input[name="password"]', DEMO_ACCOUNTS.staff.password);
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/', { timeout: 10000 });
await expect(page.locator('text=Emma Staff')).toBeVisible();
await takeScreenshot(page, 'dashboard-logged-in-staff');
});
test('should show error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'invalid@example.com');
await page.fill('input[name="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
// Should show error message
await expect(page.locator('[role="alert"]')).toBeVisible();
await expect(page.locator('text=Invalid email or password')).toBeVisible();
// Should remain on login page
await expect(page).toHaveURL('/login');
await takeScreenshot(page, 'login-error-state');
});
test('should toggle password visibility', async ({ page }) => {
await page.goto('/login');
const passwordInput = page.locator('input[name="password"]');
const toggleButton = page.locator('button[type="button"]').filter({ hasText: '' }); // Eye icon button
// Password should be hidden initially
await expect(passwordInput).toHaveAttribute('type', 'password');
await page.fill('input[name="password"]', 'testpassword');
await takeScreenshot(page, 'password-hidden');
// Click toggle to show password
await toggleButton.click();
await expect(passwordInput).toHaveAttribute('type', 'text');
await takeScreenshot(page, 'password-visible');
// Click toggle to hide password again
await toggleButton.click();
await expect(passwordInput).toHaveAttribute('type', 'password');
});
test('should remember login when remember me is checked', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', DEMO_ACCOUNTS.admin.email);
await page.fill('input[name="password"]', DEMO_ACCOUNTS.admin.password);
// Check remember me
await page.check('input[name="rememberMe"]');
await takeScreenshot(page, 'remember-me-checked');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/', { timeout: 10000 });
// Verify localStorage has auth data
const authData = await page.evaluate(() => localStorage.getItem('bct_auth_user'));
const rememberMe = await page.evaluate(() => localStorage.getItem('bct_auth_remember'));
expect(authData).toBeTruthy();
expect(rememberMe).toBe('true');
// Refresh page - should stay logged in
await page.reload();
await expect(page).toHaveURL('/');
await expect(page.locator('text=Sarah Admin')).toBeVisible();
await takeScreenshot(page, 'persistent-login-after-refresh');
});
test('should use demo account buttons', async ({ page }) => {
await page.goto('/login');
// Click admin demo account button
await page.click('text=Sarah Admin');
// Form should be filled
await expect(page.locator('input[name="email"]')).toHaveValue(DEMO_ACCOUNTS.admin.email);
await expect(page.locator('input[name="password"]')).toHaveValue('demo123');
await takeScreenshot(page, 'demo-account-filled');
// Submit and verify login
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/', { timeout: 10000 });
await expect(page.locator('text=Sarah Admin')).toBeVisible();
await takeScreenshot(page, 'demo-account-login-success');
});
test('should validate empty form submission', async ({ page }) => {
await page.goto('/login');
// Try to submit empty form
await page.click('button[type="submit"]');
// Should show validation errors
await expect(page.locator('text=Email is required')).toBeVisible();
await takeScreenshot(page, 'form-validation-errors');
});
test('should validate password requirement', async ({ page }) => {
await page.goto('/login');
// Fill email but not password
await page.fill('input[name="email"]', DEMO_ACCOUNTS.admin.email);
await page.click('button[type="submit"]');
await expect(page.locator('text=Password is required')).toBeVisible();
await takeScreenshot(page, 'password-required-error');
});
test('should handle redirect after login', async ({ page }) => {
// Try to access events page directly
await page.goto('/events');
// Should redirect to login
await expect(page).toHaveURL('/login');
// Login
await page.fill('input[name="email"]', DEMO_ACCOUNTS.admin.email);
await page.fill('input[name="password"]', DEMO_ACCOUNTS.admin.password);
await page.click('button[type="submit"]');
// Should redirect back to events page (or dashboard for now)
await expect(page).toHaveURL(/\/(events|dashboard|$)/, { timeout: 10000 });
await takeScreenshot(page, 'redirect-after-login');
});
test('should show loading screen during initial auth check', async ({ page }) => {
// Set up a user in localStorage to trigger auth loading
await page.evaluate(() => {
localStorage.setItem('bct_auth_user', JSON.stringify({
id: 'test',
email: 'admin@example.com',
name: 'Test User'
}));
localStorage.setItem('bct_auth_remember', 'true');
});
await page.goto('/');
// Should briefly show loading state
const loading = page.locator('text=Loading...');
if (await loading.isVisible()) {
await takeScreenshot(page, 'auth-loading-screen');
}
// Should eventually show dashboard or redirect to login
await page.waitForTimeout(3000);
await takeScreenshot(page, 'auth-check-complete');
});
test('should handle demo account role display', async ({ page }) => {
await page.goto('/login');
// Verify all demo accounts are shown with correct roles
await expect(page.locator('text=Sarah Admin')).toBeVisible();
await expect(page.locator('text=admin').first()).toBeVisible();
await expect(page.locator('text=John Organizer')).toBeVisible();
await expect(page.locator('text=organizer')).toBeVisible();
await expect(page.locator('text=Emma Staff')).toBeVisible();
await expect(page.locator('text=staff')).toBeVisible();
await takeScreenshot(page, 'demo-accounts-display');
});
});