- Add comprehensive analytics components with export functionality - Implement territory management with manager performance tracking - Add seatmap components for venue layout management - Create customer management features with modal interface - Add advanced hooks for dashboard flags and territory data - Implement seat selection and venue management utilities - Add type definitions for ticketing and seatmap systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
320 lines
10 KiB
TypeScript
320 lines
10 KiB
TypeScript
import path from 'path';
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
import type { Page } from '@playwright/test';
|
|
|
|
|
|
const DEMO_ACCOUNTS = {
|
|
admin: { email: 'admin@example.com', password: 'demo123' },
|
|
};
|
|
|
|
async function takeScreenshot(page: Page, name: string) {
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
const fileName = `theme_${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 });
|
|
}
|
|
|
|
async function getThemeFromLocalStorage(page: Page) {
|
|
return await page.evaluate(() => localStorage.getItem('bct_theme'));
|
|
}
|
|
|
|
async function getDocumentTheme(page: Page) {
|
|
return await page.evaluate(() => document.documentElement.classList.contains('dark'));
|
|
}
|
|
|
|
test.describe('Theme Switching', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Clear theme storage
|
|
await page.evaluate(() => {
|
|
localStorage.removeItem('bct_theme');
|
|
localStorage.removeItem('bct_auth_user');
|
|
localStorage.removeItem('bct_auth_remember');
|
|
});
|
|
});
|
|
|
|
test('should start with default light theme', async ({ page }) => {
|
|
await page.goto('/');
|
|
|
|
// Should start with light theme
|
|
const isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(false);
|
|
|
|
await takeScreenshot(page, 'default-light-theme');
|
|
|
|
// Check theme toggle button shows correct state
|
|
await expect(page.locator('[data-testid="theme-toggle"]')).toBeVisible();
|
|
});
|
|
|
|
test('should switch from light to dark theme', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Verify starting in light theme
|
|
let isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(false);
|
|
await takeScreenshot(page, 'before-dark-switch');
|
|
|
|
// Click theme toggle
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
|
|
// Should switch to dark theme
|
|
isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
// Verify theme is saved to localStorage
|
|
const savedTheme = await getThemeFromLocalStorage(page);
|
|
expect(savedTheme).toBe('dark');
|
|
|
|
await takeScreenshot(page, 'after-dark-switch');
|
|
|
|
// Check visual elements have dark theme classes
|
|
await expect(page.locator('body')).toHaveClass(/dark/);
|
|
});
|
|
|
|
test('should switch from dark to light theme', async ({ page }) => {
|
|
// Set dark theme initially
|
|
await page.evaluate(() => {
|
|
localStorage.setItem('bct_theme', 'dark');
|
|
document.documentElement.classList.add('dark');
|
|
});
|
|
|
|
await loginAsAdmin(page);
|
|
|
|
// Verify starting in dark theme
|
|
let isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
await takeScreenshot(page, 'before-light-switch');
|
|
|
|
// Click theme toggle
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
|
|
// Should switch to light theme
|
|
isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(false);
|
|
|
|
// Verify theme is saved to localStorage
|
|
const savedTheme = await getThemeFromLocalStorage(page);
|
|
expect(savedTheme).toBe('light');
|
|
|
|
await takeScreenshot(page, 'after-light-switch');
|
|
});
|
|
|
|
test('should persist theme across page refreshes', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
let isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
// Refresh the page
|
|
await page.reload();
|
|
|
|
// Theme should persist
|
|
isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
const savedTheme = await getThemeFromLocalStorage(page);
|
|
expect(savedTheme).toBe('dark');
|
|
|
|
await takeScreenshot(page, 'dark-theme-after-refresh');
|
|
});
|
|
|
|
test('should persist theme across navigation', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
await takeScreenshot(page, 'dark-theme-dashboard');
|
|
|
|
// Navigate to events page
|
|
await page.click('[data-testid="nav-events"]');
|
|
await expect(page).toHaveURL('/events');
|
|
|
|
// Theme should persist
|
|
const isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'dark-theme-events-page');
|
|
|
|
// Navigate back to dashboard
|
|
await page.click('[data-testid="nav-dashboard"]');
|
|
await expect(page).toHaveURL('/dashboard');
|
|
|
|
// Theme should still persist
|
|
const stillDark = await getDocumentTheme(page);
|
|
expect(stillDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'dark-theme-dashboard-return');
|
|
});
|
|
|
|
test('should apply theme to all components', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Take screenshot in light theme
|
|
await takeScreenshot(page, 'components-light-theme');
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
|
|
// Check that key components have theme applied
|
|
await expect(page.locator('[data-testid="sidebar"]')).toHaveClass(/dark/);
|
|
await expect(page.locator('[data-testid="header"]')).toHaveClass(/dark/);
|
|
await expect(page.locator('[data-testid="main-content"]')).toHaveClass(/dark/);
|
|
|
|
await takeScreenshot(page, 'components-dark-theme');
|
|
});
|
|
|
|
test('should handle system theme preference', async ({ page }) => {
|
|
// Mock system preference for dark theme
|
|
await page.emulateMedia({ colorScheme: 'dark' });
|
|
|
|
await page.goto('/');
|
|
|
|
// Should respect system preference
|
|
await takeScreenshot(page, 'system-dark-preference');
|
|
|
|
// Mock system preference for light theme
|
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
await page.reload();
|
|
|
|
await takeScreenshot(page, 'system-light-preference');
|
|
});
|
|
|
|
test('should show theme toggle with correct icon', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// In light theme, should show moon icon (to switch to dark)
|
|
let isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(false);
|
|
|
|
await expect(page.locator('[data-testid="theme-toggle"] [data-testid="moon-icon"]')).toBeVisible();
|
|
await takeScreenshot(page, 'theme-toggle-light-mode');
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
|
|
// In dark theme, should show sun icon (to switch to light)
|
|
isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
await expect(page.locator('[data-testid="theme-toggle"] [data-testid="sun-icon"]')).toBeVisible();
|
|
await takeScreenshot(page, 'theme-toggle-dark-mode');
|
|
});
|
|
|
|
test('should handle theme toggle keyboard interaction', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Focus the theme toggle button
|
|
await page.focus('[data-testid="theme-toggle"]');
|
|
|
|
// Press Enter to toggle theme
|
|
await page.keyboard.press('Enter');
|
|
|
|
// Should switch to dark theme
|
|
const isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'keyboard-theme-toggle');
|
|
|
|
// Press Space to toggle back
|
|
await page.keyboard.press('Space');
|
|
|
|
// Should switch back to light theme
|
|
const isLight = await getDocumentTheme(page);
|
|
expect(isLight).toBe(false);
|
|
});
|
|
|
|
test('should maintain proper contrast ratios in both themes', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Test light theme contrast
|
|
await takeScreenshot(page, 'contrast-light-theme');
|
|
|
|
// Check text is readable against background
|
|
const lightTextColor = await page.locator('h1').evaluate((el) =>
|
|
getComputedStyle(el).color
|
|
);
|
|
const lightBgColor = await page.locator('body').evaluate((el) =>
|
|
getComputedStyle(el).backgroundColor
|
|
);
|
|
|
|
console.log('Light theme - Text:', lightTextColor, 'Background:', lightBgColor);
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
await takeScreenshot(page, 'contrast-dark-theme');
|
|
|
|
// Check text is readable against background
|
|
const darkTextColor = await page.locator('h1').evaluate((el) =>
|
|
getComputedStyle(el).color
|
|
);
|
|
const darkBgColor = await page.locator('body').evaluate((el) =>
|
|
getComputedStyle(el).backgroundColor
|
|
);
|
|
|
|
console.log('Dark theme - Text:', darkTextColor, 'Background:', darkBgColor);
|
|
});
|
|
|
|
test('should handle rapid theme switching', async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
|
|
// Rapidly toggle theme multiple times
|
|
for (let i = 0; i < 5; i++) {
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
await page.waitForTimeout(100); // Small delay to see the transition
|
|
}
|
|
|
|
// Should end up in dark theme (odd number of clicks)
|
|
const isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'rapid-theme-switching-end');
|
|
|
|
// Theme should be saved correctly
|
|
const savedTheme = await getThemeFromLocalStorage(page);
|
|
expect(savedTheme).toBe('dark');
|
|
});
|
|
|
|
test('should handle theme on login page', async ({ page }) => {
|
|
await page.goto('/login');
|
|
|
|
// Theme toggle should be available on login page
|
|
await expect(page.locator('[data-testid="theme-toggle"]')).toBeVisible();
|
|
|
|
await takeScreenshot(page, 'login-page-light-theme');
|
|
|
|
// Switch to dark theme
|
|
await page.click('[data-testid="theme-toggle"]');
|
|
|
|
const isDark = await getDocumentTheme(page);
|
|
expect(isDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'login-page-dark-theme');
|
|
|
|
// Login should maintain theme
|
|
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 });
|
|
|
|
// Theme should persist after login
|
|
const stillDark = await getDocumentTheme(page);
|
|
expect(stillDark).toBe(true);
|
|
|
|
await takeScreenshot(page, 'dashboard-after-login-dark-theme');
|
|
});
|
|
}); |