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:
300
reactrebuild0825/tests/navigation.spec.ts
Normal file
300
reactrebuild0825/tests/navigation.spec.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user