Files
blackcanyontickets/reactrebuild0825/tests/responsive.spec.ts
dzinesco 48b9b680e3 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>
2025-08-16 12:44:32 -06:00

354 lines
12 KiB
TypeScript

import { test, expect, Page } from '@playwright/test';
import path from 'path';
const DEMO_ACCOUNTS = {
admin: { email: 'admin@example.com', password: 'demo123' },
};
const VIEWPORTS = {
mobile: { width: 375, height: 667 },
mobileLarge: { width: 414, height: 896 },
tablet: { width: 768, height: 1024 },
tabletLarge: { width: 1024, height: 768 },
desktop: { width: 1280, height: 720 },
desktopLarge: { width: 1920, height: 1080 },
};
async function takeScreenshot(page: Page, name: string) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `responsive_${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('Responsive Design', () => {
test.beforeEach(async ({ page }) => {
// Clear auth storage
await page.evaluate(() => {
localStorage.removeItem('bct_auth_user');
localStorage.removeItem('bct_auth_remember');
});
});
test('should display correctly on mobile devices', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.mobile);
await loginAsAdmin(page);
// Mobile menu button should be visible
await expect(page.locator('[data-testid="mobile-menu-button"]')).toBeVisible();
// Sidebar should be hidden initially
await expect(page.locator('[data-testid="sidebar"]')).not.toBeVisible();
// Main content should take full width
const mainContent = page.locator('[data-testid="main-content"]');
await expect(mainContent).toBeVisible();
await takeScreenshot(page, 'mobile-dashboard');
// Test mobile navigation
await page.click('[data-testid="mobile-menu-button"]');
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
await takeScreenshot(page, 'mobile-menu-open');
// Navigate to events
await page.click('[data-testid="nav-events"]');
await expect(page).toHaveURL('/events');
await takeScreenshot(page, 'mobile-events-page');
});
test('should display correctly on tablet devices', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.tablet);
await loginAsAdmin(page);
// Should still show mobile menu on smaller tablets
await expect(page.locator('[data-testid="mobile-menu-button"]')).toBeVisible();
await takeScreenshot(page, 'tablet-dashboard');
// Test tablet navigation
await page.click('[data-testid="mobile-menu-button"]');
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
await takeScreenshot(page, 'tablet-menu-open');
});
test('should display correctly on large tablets', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.tabletLarge);
await loginAsAdmin(page);
// Large tablets should show desktop layout
await expect(page.locator('[data-testid="mobile-menu-button"]')).not.toBeVisible();
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
await takeScreenshot(page, 'tablet-large-dashboard');
});
test('should display correctly on desktop', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.desktop);
await loginAsAdmin(page);
// Desktop should show full sidebar
await expect(page.locator('[data-testid="mobile-menu-button"]')).not.toBeVisible();
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
// Sidebar should be properly sized
const sidebar = page.locator('[data-testid="sidebar"]');
const sidebarBox = await sidebar.boundingBox();
expect(sidebarBox?.width).toBeGreaterThan(200);
expect(sidebarBox?.width).toBeLessThan(300);
await takeScreenshot(page, 'desktop-dashboard');
// Navigate to events
await page.click('[data-testid="nav-events"]');
await expect(page).toHaveURL('/events');
await takeScreenshot(page, 'desktop-events-page');
});
test('should handle touch interactions on mobile', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.mobile);
await loginAsAdmin(page);
// Test touch tap on mobile menu
await page.tap('[data-testid="mobile-menu-button"]');
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
// Test touch tap on navigation
await page.tap('[data-testid="nav-events"]');
await expect(page).toHaveURL('/events');
await takeScreenshot(page, 'mobile-touch-navigation');
});
test('should handle swipe gestures for menu', async ({ page }) => {
await page.setViewportSize(VIEWPORTS.mobile);
await loginAsAdmin(page);
// Test swipe to open menu (if implemented)
const viewport = page.viewportSize();
if (viewport) {
// Swipe from left edge
await page.touchscreen.tap(10, viewport.height / 2);
await page.mouse.move(10, viewport.height / 2);
await page.mouse.down();
await page.mouse.move(200, viewport.height / 2);
await page.mouse.up();
await takeScreenshot(page, 'mobile-swipe-gesture');
}
});
test('should adapt card layouts across breakpoints', async ({ page }) => {
await loginAsAdmin(page);
// Test mobile card layout
await page.setViewportSize(VIEWPORTS.mobile);
await page.click('[data-testid="nav-events"]');
await expect(page).toHaveURL('/events');
// Cards should stack on mobile
const cards = page.locator('[data-testid^="event-card"]');
const cardCount = await cards.count();
if (cardCount > 0) {
// Check that cards are stacked (full width)
const firstCard = cards.first();
const cardBox = await firstCard.boundingBox();
const viewportWidth = VIEWPORTS.mobile.width;
if (cardBox) {
expect(cardBox.width).toBeGreaterThan(viewportWidth * 0.8);
}
}
await takeScreenshot(page, 'mobile-card-layout');
// Test desktop card layout
await page.setViewportSize(VIEWPORTS.desktop);
await page.waitForTimeout(500); // Allow layout to adjust
await takeScreenshot(page, 'desktop-card-layout');
});
test('should handle text scaling across devices', async ({ page }) => {
await loginAsAdmin(page);
// Test mobile text scaling
await page.setViewportSize(VIEWPORTS.mobile);
const mobileHeading = page.locator('h1').first();
const mobileFontSize = await mobileHeading.evaluate((el) =>
getComputedStyle(el).fontSize
);
await takeScreenshot(page, 'mobile-text-scaling');
// Test desktop text scaling
await page.setViewportSize(VIEWPORTS.desktop);
const desktopHeading = page.locator('h1').first();
const desktopFontSize = await desktopHeading.evaluate((el) =>
getComputedStyle(el).fontSize
);
await takeScreenshot(page, 'desktop-text-scaling');
// Desktop font should generally be larger or equal
const mobileSize = parseFloat(mobileFontSize);
const desktopSize = parseFloat(desktopFontSize);
expect(desktopSize).toBeGreaterThanOrEqual(mobileSize);
});
test('should handle form layouts responsively', async ({ page }) => {
await page.goto('/login');
// Test mobile form layout
await page.setViewportSize(VIEWPORTS.mobile);
const form = page.locator('[data-testid="login-form"]');
await expect(form).toBeVisible();
// Form should take most of the width on mobile
const formBox = await form.boundingBox();
if (formBox) {
expect(formBox.width).toBeGreaterThan(VIEWPORTS.mobile.width * 0.8);
}
await takeScreenshot(page, 'mobile-form-layout');
// Test desktop form layout
await page.setViewportSize(VIEWPORTS.desktop);
// Form should be centered and more constrained on desktop
const desktopFormBox = await form.boundingBox();
if (desktopFormBox) {
expect(desktopFormBox.width).toBeLessThan(VIEWPORTS.desktop.width * 0.6);
}
await takeScreenshot(page, 'desktop-form-layout');
});
test('should handle button sizes across devices', async ({ page }) => {
await page.goto('/login');
// Test mobile button sizes
await page.setViewportSize(VIEWPORTS.mobile);
const button = page.locator('[data-testid="login-button"]');
const mobileButtonBox = await button.boundingBox();
// Buttons should be touch-friendly on mobile (min 44px height)
if (mobileButtonBox) {
expect(mobileButtonBox.height).toBeGreaterThanOrEqual(40);
}
await takeScreenshot(page, 'mobile-button-sizes');
// Test desktop button sizes
await page.setViewportSize(VIEWPORTS.desktop);
const desktopButtonBox = await button.boundingBox();
await takeScreenshot(page, 'desktop-button-sizes');
// Compare button sizes
if (mobileButtonBox && desktopButtonBox) {
console.log('Mobile button height:', mobileButtonBox.height);
console.log('Desktop button height:', desktopButtonBox.height);
}
});
test('should handle navigation consistency across devices', async ({ page }) => {
await loginAsAdmin(page);
// Test that navigation items are consistent across devices
const navItems = ['Dashboard', 'Events'];
for (const viewport of Object.values(VIEWPORTS)) {
await page.setViewportSize(viewport);
await page.waitForTimeout(500); // Allow layout to adjust
// Open mobile menu if needed
const mobileMenuButton = page.locator('[data-testid="mobile-menu-button"]');
if (await mobileMenuButton.isVisible()) {
await mobileMenuButton.click();
}
// Check that all navigation items are present
for (const item of navItems) {
await expect(page.locator(`[data-testid="nav-${item.toLowerCase()}"]`)).toBeVisible();
}
await takeScreenshot(page, `navigation-${viewport.width}x${viewport.height}`);
// Close mobile menu if opened
if (await mobileMenuButton.isVisible()) {
await page.click('body');
}
}
});
test('should handle image scaling and loading', async ({ page }) => {
await loginAsAdmin(page);
// Test mobile image handling
await page.setViewportSize(VIEWPORTS.mobile);
// Check user avatar scaling
const avatar = page.locator('[data-testid="user-avatar"]');
if (await avatar.isVisible()) {
const avatarBox = await avatar.boundingBox();
if (avatarBox) {
// Avatar should be reasonably sized for mobile
expect(avatarBox.width).toBeGreaterThan(20);
expect(avatarBox.width).toBeLessThan(80);
}
}
await takeScreenshot(page, 'mobile-image-scaling');
// Test desktop image handling
await page.setViewportSize(VIEWPORTS.desktop);
if (await avatar.isVisible()) {
const desktopAvatarBox = await avatar.boundingBox();
await takeScreenshot(page, 'desktop-image-scaling');
if (desktopAvatarBox) {
console.log('Desktop avatar size:', desktopAvatarBox.width, 'x', desktopAvatarBox.height);
}
}
});
test('should handle orientation changes on mobile', async ({ page }) => {
await loginAsAdmin(page);
// Test portrait orientation
await page.setViewportSize({ width: 375, height: 667 });
await takeScreenshot(page, 'mobile-portrait');
// Test landscape orientation
await page.setViewportSize({ width: 667, height: 375 });
await takeScreenshot(page, 'mobile-landscape');
// Navigation should still work in landscape
const mobileMenuButton = page.locator('[data-testid="mobile-menu-button"]');
if (await mobileMenuButton.isVisible()) {
await mobileMenuButton.click();
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
await takeScreenshot(page, 'mobile-landscape-menu-open');
}
});
});