- Create modern toast notification system replacing browser alerts - Add mobile-responsive layouts with touch-friendly elements - Implement loading states and progress indicators for all AJAX operations - Add mobile navigation with collapsible menus - Create enhanced form validation with inline error messages - Add accessibility features (keyboard navigation, ARIA labels) - Build comprehensive mobile testing suite - Optimize for 320px to 1024px+ screen sizes - Include progressive enhancement and fallback support 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			285 lines
		
	
	
		
			No EOL
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			No EOL
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * Mobile Responsiveness Test for HVAC Plugin UX Enhancements
 | |
|  * 
 | |
|  * Tests the mobile experience including:
 | |
|  * - Touch-friendly interface elements
 | |
|  * - Responsive layouts
 | |
|  * - Mobile navigation
 | |
|  * - Toast notifications on mobile
 | |
|  * - Form usability on mobile devices
 | |
|  */
 | |
| 
 | |
| import { test, expect, devices } from '@playwright/test';
 | |
| 
 | |
| const STAGING_URL = 'https://upskill-staging.measurequick.com';
 | |
| 
 | |
| // Test with different mobile viewports
 | |
| const mobileDevices = [
 | |
|     devices['iPhone 12'],
 | |
|     devices['iPhone 12 Pro'],
 | |
|     devices['Samsung Galaxy S21'],
 | |
|     devices['iPad Mini']
 | |
| ];
 | |
| 
 | |
| for (const device of mobileDevices) {
 | |
|     test.describe(`Mobile Responsiveness - ${device.name}`, () => {
 | |
|         test.use({ ...device });
 | |
| 
 | |
|         test(`Login page mobile layout - ${device.name}`, async ({ page }) => {
 | |
|             test.setTimeout(20000);
 | |
| 
 | |
|             await page.goto(`${STAGING_URL}/community-login`);
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Check login form is properly sized for mobile
 | |
|             const loginForm = page.locator('#hvac_community_loginform');
 | |
|             await expect(loginForm).toBeVisible();
 | |
| 
 | |
|             // Verify mobile-optimized form elements
 | |
|             const usernameField = page.locator('#user_login');
 | |
|             const passwordField = page.locator('#user_pass');
 | |
|             const submitButton = page.locator('#wp-submit');
 | |
| 
 | |
|             await expect(usernameField).toBeVisible();
 | |
|             await expect(passwordField).toBeVisible();
 | |
|             await expect(submitButton).toBeVisible();
 | |
| 
 | |
|             // Check touch target sizes (minimum 44px for iOS)
 | |
|             const submitBox = await submitButton.boundingBox();
 | |
|             expect(submitBox?.height).toBeGreaterThanOrEqual(44);
 | |
| 
 | |
|             // Test form field focus states work on mobile
 | |
|             await usernameField.focus();
 | |
|             await passwordField.focus();
 | |
| 
 | |
|             // Take screenshot for visual verification
 | |
|             await page.screenshot({ path: `test-results/mobile-login-${device.name?.replace(/\s+/g, '-')}.png` });
 | |
|         });
 | |
| 
 | |
|         test(`Dashboard mobile layout - ${device.name}`, async ({ page }) => {
 | |
|             test.setTimeout(30000);
 | |
| 
 | |
|             // Login first
 | |
|             await page.goto(`${STAGING_URL}/community-login`);
 | |
|             await page.fill('#user_login', 'test_trainer');
 | |
|             await page.fill('#user_pass', 'Test123!');
 | |
|             await page.click('#wp-submit');
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Verify we're on dashboard
 | |
|             await expect(page).toHaveURL(/hvac-dashboard/);
 | |
| 
 | |
|             // Check dashboard layout adapts to mobile
 | |
|             const dashboard = page.locator('.hvac-dashboard-wrapper');
 | |
|             await expect(dashboard).toBeVisible();
 | |
| 
 | |
|             // Test mobile navigation if present
 | |
|             const mobileNavToggle = page.locator('.hvac-mobile-nav-toggle');
 | |
|             if (await mobileNavToggle.isVisible()) {
 | |
|                 await mobileNavToggle.click();
 | |
|                 
 | |
|                 const mobileNav = page.locator('.hvac-mobile-nav');
 | |
|                 await expect(mobileNav).toHaveClass(/open/);
 | |
|                 
 | |
|                 // Test navigation links work
 | |
|                 const navLinks = page.locator('.hvac-mobile-nav a');
 | |
|                 const navCount = await navLinks.count();
 | |
|                 expect(navCount).toBeGreaterThan(0);
 | |
|             }
 | |
| 
 | |
|             // Check stats cards stack properly on mobile
 | |
|             const statCards = page.locator('.hvac-stat-card');
 | |
|             const statCount = await statCards.count();
 | |
|             if (statCount > 0) {
 | |
|                 // Verify cards are visible and properly sized
 | |
|                 for (let i = 0; i < Math.min(statCount, 3); i++) {
 | |
|                     await expect(statCards.nth(i)).toBeVisible();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Test touch scrolling works
 | |
|             await page.evaluate(() => window.scrollTo(0, 200));
 | |
|             await page.waitForTimeout(500);
 | |
| 
 | |
|             // Take screenshot
 | |
|             await page.screenshot({ path: `test-results/mobile-dashboard-${device.name?.replace(/\s+/g, '-')}.png` });
 | |
|         });
 | |
| 
 | |
|         test(`Toast notifications on mobile - ${device.name}`, async ({ page }) => {
 | |
|             test.setTimeout(25000);
 | |
| 
 | |
|             // Login
 | |
|             await page.goto(`${STAGING_URL}/community-login`);
 | |
|             await page.fill('#user_login', 'test_trainer');
 | |
|             await page.fill('#user_pass', 'Test123!');
 | |
|             await page.click('#wp-submit');
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Go to certificate reports to trigger potential notifications
 | |
|             await page.goto(`${STAGING_URL}/certificate-reports`);
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Check if toast container exists and is positioned correctly for mobile
 | |
|             const toastContainer = page.locator('.hvac-toast-container');
 | |
|             
 | |
|             // Inject test toast to verify mobile positioning
 | |
|             await page.evaluate(() => {
 | |
|                 if (window.HVACToast) {
 | |
|                     window.HVACToast.success('This is a test toast notification for mobile testing');
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             await page.waitForTimeout(1000);
 | |
| 
 | |
|             // Check if toast is visible and properly positioned
 | |
|             const toast = page.locator('.hvac-toast');
 | |
|             if (await toast.count() > 0) {
 | |
|                 await expect(toast.first()).toBeVisible();
 | |
|                 
 | |
|                 // Verify toast doesn't overflow viewport
 | |
|                 const toastBox = await toast.first().boundingBox();
 | |
|                 const viewport = page.viewportSize();
 | |
|                 
 | |
|                 if (toastBox && viewport) {
 | |
|                     expect(toastBox.x + toastBox.width).toBeLessThanOrEqual(viewport.width);
 | |
|                     expect(toastBox.y).toBeGreaterThanOrEqual(0);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Take screenshot
 | |
|             await page.screenshot({ path: `test-results/mobile-toast-${device.name?.replace(/\s+/g, '-')}.png` });
 | |
|         });
 | |
| 
 | |
|         test(`Form interaction on mobile - ${device.name}`, async ({ page }) => {
 | |
|             test.setTimeout(25000);
 | |
| 
 | |
|             // Login
 | |
|             await page.goto(`${STAGING_URL}/community-login`);
 | |
|             await page.fill('#user_login', 'test_trainer');
 | |
|             await page.fill('#user_pass', 'Test123!');
 | |
|             await page.click('#wp-submit');
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Go to create event page to test forms
 | |
|             await page.goto(`${STAGING_URL}/manage-event`);
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Test form field interactions
 | |
|             const titleField = page.locator('#event_title');
 | |
|             if (await titleField.isVisible()) {
 | |
|                 // Check field is properly sized for mobile
 | |
|                 const fieldBox = await titleField.boundingBox();
 | |
|                 expect(fieldBox?.height).toBeGreaterThanOrEqual(44);
 | |
| 
 | |
|                 // Test touch interaction
 | |
|                 await titleField.tap();
 | |
|                 await titleField.fill('Mobile Test Event');
 | |
|                 
 | |
|                 const value = await titleField.inputValue();
 | |
|                 expect(value).toBe('Mobile Test Event');
 | |
|             }
 | |
| 
 | |
|             // Check form buttons are touch-friendly
 | |
|             const buttons = page.locator('button, input[type="submit"]');
 | |
|             const buttonCount = await buttons.count();
 | |
|             
 | |
|             for (let i = 0; i < Math.min(buttonCount, 3); i++) {
 | |
|                 const button = buttons.nth(i);
 | |
|                 if (await button.isVisible()) {
 | |
|                     const buttonBox = await button.boundingBox();
 | |
|                     expect(buttonBox?.height).toBeGreaterThanOrEqual(44);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // Take screenshot
 | |
|             await page.screenshot({ path: `test-results/mobile-form-${device.name?.replace(/\s+/g, '-')}.png` });
 | |
|         });
 | |
|     });
 | |
| }
 | |
| 
 | |
| // Test responsive breakpoints
 | |
| test.describe('Responsive Breakpoints', () => {
 | |
|     const breakpoints = [
 | |
|         { name: 'Mobile Small', width: 320, height: 568 },
 | |
|         { name: 'Mobile Medium', width: 375, height: 667 },
 | |
|         { name: 'Mobile Large', width: 414, height: 896 },
 | |
|         { name: 'Tablet Portrait', width: 768, height: 1024 },
 | |
|         { name: 'Tablet Landscape', width: 1024, height: 768 }
 | |
|     ];
 | |
| 
 | |
|     for (const breakpoint of breakpoints) {
 | |
|         test(`Dashboard layout at ${breakpoint.name} (${breakpoint.width}x${breakpoint.height})`, async ({ page }) => {
 | |
|             test.setTimeout(25000);
 | |
| 
 | |
|             // Set viewport
 | |
|             await page.setViewportSize({ width: breakpoint.width, height: breakpoint.height });
 | |
| 
 | |
|             // Login
 | |
|             await page.goto(`${STAGING_URL}/community-login`);
 | |
|             await page.fill('#user_login', 'test_trainer');
 | |
|             await page.fill('#user_pass', 'Test123!');
 | |
|             await page.click('#wp-submit');
 | |
|             await page.waitForLoadState('networkidle');
 | |
| 
 | |
|             // Check dashboard layout
 | |
|             await expect(page).toHaveURL(/hvac-dashboard/);
 | |
|             
 | |
|             // Verify content is visible and accessible
 | |
|             const mainContent = page.locator('.hvac-dashboard-wrapper, .entry-content');
 | |
|             await expect(mainContent).toBeVisible();
 | |
| 
 | |
|             // Check navigation is appropriate for screen size
 | |
|             if (breakpoint.width <= 767) {
 | |
|                 // Mobile: should see mobile nav or stacked nav
 | |
|                 const mobileNav = page.locator('.hvac-mobile-nav-toggle, .hvac-dashboard-nav');
 | |
|                 await expect(mobileNav).toBeVisible();
 | |
|             } else {
 | |
|                 // Tablet/Desktop: should see regular nav
 | |
|                 const desktopNav = page.locator('.hvac-dashboard-nav');
 | |
|                 await expect(desktopNav).toBeVisible();
 | |
|             }
 | |
| 
 | |
|             // Take screenshot
 | |
|             await page.screenshot({ 
 | |
|                 path: `test-results/responsive-${breakpoint.name.replace(/\s+/g, '-')}-${breakpoint.width}x${breakpoint.height}.png`,
 | |
|                 fullPage: true 
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| });
 | |
| 
 | |
| // Test accessibility on mobile
 | |
| test.describe('Mobile Accessibility', () => {
 | |
|     test.use(devices['iPhone 12']);
 | |
| 
 | |
|     test('Touch accessibility and keyboard navigation', async ({ page }) => {
 | |
|         test.setTimeout(25000);
 | |
| 
 | |
|         // Login
 | |
|         await page.goto(`${STAGING_URL}/community-login`);
 | |
|         await page.fill('#user_login', 'test_trainer');
 | |
|         await page.fill('#user_pass', 'Test123!');
 | |
|         await page.click('#wp-submit');
 | |
|         await page.waitForLoadState('networkidle');
 | |
| 
 | |
|         // Test keyboard navigation with tab
 | |
|         await page.keyboard.press('Tab');
 | |
|         await page.keyboard.press('Tab');
 | |
|         await page.keyboard.press('Tab');
 | |
| 
 | |
|         // Check focus states are visible
 | |
|         const focusedElement = page.locator(':focus');
 | |
|         await expect(focusedElement).toBeVisible();
 | |
| 
 | |
|         // Test escape key closes mobile elements
 | |
|         await page.keyboard.press('Escape');
 | |
| 
 | |
|         // Verify aria labels exist on interactive elements
 | |
|         const buttons = page.locator('button[aria-label], input[aria-label]');
 | |
|         const buttonCount = await buttons.count();
 | |
|         console.log(`Found ${buttonCount} buttons with aria-labels`);
 | |
| 
 | |
|         // Take screenshot
 | |
|         await page.screenshot({ path: `test-results/mobile-accessibility.png` });
 | |
|     });
 | |
| }); |