- 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>
300 lines
11 KiB
TypeScript
300 lines
11 KiB
TypeScript
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 = `navigation_${name}_${timestamp}.png`;
|
|
await page.screenshot({
|
|
path: path.join('screenshots', fileName),
|
|
fullPage: true
|
|
});
|
|
return fileName;
|
|
}
|
|
|
|
async function loginAsAdmin(page: Page) {
|
|
await page.goto('/login');
|
|
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
|
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
|
await page.click('[data-testid="login-button"]');
|
|
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
|
}
|
|
|
|
test.describe('Navigation and Routing', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Clear auth storage
|
|
await page.evaluate(() => {
|
|
localStorage.removeItem('bct_auth_user');
|
|
localStorage.removeItem('bct_auth_remember');
|
|
});
|
|
});
|
|
|
|
test('should navigate through all main sections', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Take screenshot of dashboard
|
|
await takeScreenshot(page, 'dashboard-page');
|
|
|
|
// Navigate to Events
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
await expect(page.locator('h1')).toContainText('Events');
|
|
await takeScreenshot(page, 'events-page');
|
|
|
|
// Check active navigation state
|
|
await expect(page.locator('[data-testid="nav-events"]')).toHaveClass(/active|bg-/);
|
|
|
|
// Navigate to Dashboard
|
|
await page.click('[data-testid="nav-dashboard"]');
|
|
await expect(page).toHaveURL('/dashboard');
|
|
await expect(page.locator('h1')).toContainText('Dashboard');
|
|
await takeScreenshot(page, 'dashboard-page-return');
|
|
|
|
// Check active navigation state
|
|
await expect(page.locator('[data-testid="nav-dashboard"]')).toHaveClass(/active|bg-/);
|
|
});
|
|
|
|
test('should show different navigation options based on user role', async ({ page }) => {
|
|
// Test admin navigation
|
|
await page.goto('/login');
|
|
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
|
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
|
await page.click('[data-testid="login-button"]');
|
|
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
|
|
|
// Admin should see all navigation options
|
|
await expect(page.locator('[data-testid="nav-dashboard"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="nav-events"]')).toBeVisible();
|
|
await takeScreenshot(page, 'admin-navigation');
|
|
|
|
// Logout and login as staff
|
|
await page.click('[data-testid="user-menu"]');
|
|
await page.click('[data-testid="logout-button"]');
|
|
await expect(page).toHaveURL('/login', { timeout: 10000 });
|
|
|
|
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.staff.email);
|
|
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.staff.password);
|
|
await page.click('[data-testid="login-button"]');
|
|
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
|
|
|
// Staff should see limited navigation options
|
|
await expect(page.locator('[data-testid="nav-dashboard"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="nav-events"]')).toBeVisible();
|
|
await takeScreenshot(page, 'staff-navigation');
|
|
});
|
|
|
|
test('should handle mobile navigation menu', async ({ page }) => {
|
|
// Set mobile viewport
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
await loginAsAdmin(page);
|
|
|
|
await takeScreenshot(page, 'mobile-dashboard-closed-menu');
|
|
|
|
// Mobile menu button should be visible
|
|
await expect(page.locator('[data-testid="mobile-menu-button"]')).toBeVisible();
|
|
|
|
// Sidebar should be hidden on mobile
|
|
await expect(page.locator('[data-testid="sidebar"]')).not.toBeVisible();
|
|
|
|
// Open mobile menu
|
|
await page.click('[data-testid="mobile-menu-button"]');
|
|
|
|
// Sidebar should now be visible
|
|
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
|
|
await takeScreenshot(page, 'mobile-menu-open');
|
|
|
|
// Navigate to events via mobile menu
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
// Menu should close after navigation
|
|
await expect(page.locator('[data-testid="sidebar"]')).not.toBeVisible();
|
|
await takeScreenshot(page, 'mobile-events-page');
|
|
|
|
// Test menu overlay close
|
|
await page.click('[data-testid="mobile-menu-button"]');
|
|
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
|
|
|
|
// Click overlay to close
|
|
await page.click('[data-testid="mobile-overlay"]');
|
|
await expect(page.locator('[data-testid="sidebar"]')).not.toBeVisible();
|
|
await takeScreenshot(page, 'mobile-menu-overlay-close');
|
|
});
|
|
|
|
test('should handle desktop navigation properly', async ({ page }) => {
|
|
// Set desktop viewport
|
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
await loginAsAdmin(page);
|
|
|
|
// Mobile menu button should not be visible on desktop
|
|
await expect(page.locator('[data-testid="mobile-menu-button"]')).not.toBeVisible();
|
|
|
|
// Sidebar should be visible on desktop
|
|
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
|
|
|
|
await takeScreenshot(page, 'desktop-navigation');
|
|
|
|
// Test navigation without mobile menu
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
|
|
|
|
await takeScreenshot(page, 'desktop-events-navigation');
|
|
});
|
|
|
|
test('should display user menu and profile information', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// User menu should show user name and avatar
|
|
await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="user-name"]')).toContainText('Sarah Admin');
|
|
await expect(page.locator('[data-testid="user-avatar"]')).toBeVisible();
|
|
|
|
// Click user menu to open dropdown
|
|
await page.click('[data-testid="user-menu"]');
|
|
|
|
// Dropdown should show profile options
|
|
await expect(page.locator('[data-testid="user-dropdown"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="profile-link"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="settings-link"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="logout-button"]')).toBeVisible();
|
|
|
|
await takeScreenshot(page, 'user-menu-dropdown');
|
|
|
|
// Click outside to close dropdown
|
|
await page.click('body');
|
|
await expect(page.locator('[data-testid="user-dropdown"]')).not.toBeVisible();
|
|
});
|
|
|
|
test('should show breadcrumb navigation', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Navigate to events
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
// Should show breadcrumb
|
|
await expect(page.locator('[data-testid="breadcrumb"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="breadcrumb"]')).toContainText('Events');
|
|
|
|
await takeScreenshot(page, 'breadcrumb-events');
|
|
|
|
// Navigate back to dashboard
|
|
await page.click('[data-testid="nav-dashboard"]');
|
|
await expect(page).toHaveURL('/dashboard');
|
|
|
|
await expect(page.locator('[data-testid="breadcrumb"]')).toContainText('Dashboard');
|
|
await takeScreenshot(page, 'breadcrumb-dashboard');
|
|
});
|
|
|
|
test('should handle keyboard navigation', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Test tab navigation through main navigation
|
|
await page.keyboard.press('Tab');
|
|
|
|
// Skip to content link should be focused first
|
|
await expect(page.locator('[data-testid="skip-to-content"]')).toBeFocused();
|
|
|
|
await page.keyboard.press('Tab');
|
|
// Navigation items should be focusable
|
|
await expect(page.locator('[data-testid="nav-dashboard"]')).toBeFocused();
|
|
|
|
await takeScreenshot(page, 'keyboard-nav-dashboard-focus');
|
|
|
|
await page.keyboard.press('Tab');
|
|
await expect(page.locator('[data-testid="nav-events"]')).toBeFocused();
|
|
|
|
await takeScreenshot(page, 'keyboard-nav-events-focus');
|
|
|
|
// Test Enter key navigation
|
|
await page.keyboard.press('Enter');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
await takeScreenshot(page, 'keyboard-nav-events-activated');
|
|
});
|
|
|
|
test('should handle skip to content functionality', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Focus skip to content link
|
|
await page.keyboard.press('Tab');
|
|
await expect(page.locator('[data-testid="skip-to-content"]')).toBeFocused();
|
|
|
|
await takeScreenshot(page, 'skip-to-content-focused');
|
|
|
|
// Activate skip to content
|
|
await page.keyboard.press('Enter');
|
|
|
|
// Main content should be focused
|
|
await expect(page.locator('[data-testid="main-content"]')).toBeFocused();
|
|
|
|
await takeScreenshot(page, 'skip-to-content-activated');
|
|
});
|
|
|
|
test('should maintain navigation state across page refreshes', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Navigate to events
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
// Refresh page
|
|
await page.reload();
|
|
|
|
// Should stay on events page with correct navigation state
|
|
await expect(page).toHaveURL('/events');
|
|
await expect(page.locator('[data-testid="nav-events"]')).toHaveClass(/active|bg-/);
|
|
|
|
await takeScreenshot(page, 'navigation-state-after-refresh');
|
|
});
|
|
|
|
test('should handle navigation with browser back/forward buttons', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Navigate to events
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
// Use browser back button
|
|
await page.goBack();
|
|
await expect(page).toHaveURL('/dashboard');
|
|
await expect(page.locator('[data-testid="nav-dashboard"]')).toHaveClass(/active|bg-/);
|
|
|
|
await takeScreenshot(page, 'browser-back-navigation');
|
|
|
|
// Use browser forward button
|
|
await page.goForward();
|
|
await expect(page).toHaveURL('/events');
|
|
await expect(page.locator('[data-testid="nav-events"]')).toHaveClass(/active|bg-/);
|
|
|
|
await takeScreenshot(page, 'browser-forward-navigation');
|
|
});
|
|
|
|
test('should show loading states during navigation', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Slow down network to see loading states
|
|
await page.route('**/*', (route) => {
|
|
setTimeout(() => route.continue(), 500);
|
|
});
|
|
|
|
// Navigate to events
|
|
await page.click('[data-testid="nav-events"]');
|
|
|
|
// Should show loading state
|
|
await expect(page.locator('[data-testid="loading-spinner"]')).toBeVisible();
|
|
await takeScreenshot(page, 'navigation-loading-state');
|
|
|
|
// Wait for navigation to complete
|
|
await expect(page).toHaveURL('/events');
|
|
await expect(page.locator('[data-testid="loading-spinner"]')).not.toBeVisible();
|
|
|
|
await takeScreenshot(page, 'navigation-complete');
|
|
});
|
|
}); |