93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
import { test, expect, Page } from '@playwright/test';
|
|
import { loginAsUser, TEST_CONFIG } from '../utils/test-helpers';
|
|
|
|
/**
|
|
* UI/UX Dynamic Audit Suite
|
|
* Scans pages for:
|
|
* - Interactive elements that are too small (< 44x44px)
|
|
* - Console errors
|
|
* - Broken images/links
|
|
* - Overflow issues
|
|
*/
|
|
|
|
const PAGES_TO_AUDIT = [
|
|
'/dashboard',
|
|
'/library',
|
|
'/marketplace',
|
|
'/settings',
|
|
'/profile/me',
|
|
'/studio',
|
|
'/messages' // Often problematic
|
|
];
|
|
|
|
test.describe('Dynamic UI/UX Audit', () => {
|
|
let consoleErrors: string[] = [];
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
// Capture console errors
|
|
page.on('console', msg => {
|
|
if (msg.type() === 'error') consoleErrors.push(`[${page.url()}] ${msg.text()}`);
|
|
});
|
|
|
|
// Login once
|
|
await loginAsUser(page);
|
|
});
|
|
|
|
for (const path of PAGES_TO_AUDIT) {
|
|
test(`Audit page: ${path}`, async ({ page }) => {
|
|
console.log(`\n🔍 Auditing ${path}...`);
|
|
await page.goto(`${TEST_CONFIG.FRONTEND_URL}${path}`);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(1000); // Allow animations/layout to settle
|
|
|
|
// 1. Check for Console Errors
|
|
if (consoleErrors.length > 0) {
|
|
console.log(`⚠️ Console Errors on ${path}:`, consoleErrors);
|
|
consoleErrors = []; // Reset for next check logic (though beforeeach resets too, strictly speaking this is shared scope in this loop impl if not careful, but playwright isolates tests)
|
|
}
|
|
|
|
// 2. Interactive Element Sizing (Mobile Friendly Check)
|
|
// Find all buttons and anchors
|
|
const interactiveElements = await page.locator('button:visible, a:visible, [role="button"]:visible').all();
|
|
|
|
let smallTargets = 0;
|
|
for (const el of interactiveElements) {
|
|
const box = await el.boundingBox();
|
|
if (box) {
|
|
// Check if smaller than 44px in either dimension (Apple guidelines)
|
|
// We allow smaller if it's strictly an icon-only button inside a toolbar, but warn generally
|
|
if (box.width < 32 || box.height < 32) { // 32 is lenient, 44 is ideal
|
|
const html = await el.evaluate(e => e.outerHTML);
|
|
// console.log(`⚠️ Small touch target (${Math.round(box.width)}x${Math.round(box.height)}):`, html.substring(0, 100));
|
|
smallTargets++;
|
|
}
|
|
}
|
|
}
|
|
if (smallTargets > 0) {
|
|
console.log(`⚠️ Found ${smallTargets} interactive elements smaller than 32x32px on ${path}`);
|
|
}
|
|
|
|
// 3. Overflow Detection
|
|
const hasHorizontalScroll = await page.evaluate(() => {
|
|
return document.body.scrollWidth > window.innerWidth;
|
|
});
|
|
if (hasHorizontalScroll) {
|
|
console.log(`🔴 Layout Issue: Horizontal scroll detected on ${path}`);
|
|
}
|
|
|
|
// 4. Broken Image Detection
|
|
const images = await page.locator('img').all();
|
|
for (const img of images) {
|
|
const isBroken = await img.evaluate((i: HTMLImageElement) => {
|
|
return !i.complete || i.naturalWidth === 0;
|
|
});
|
|
if (isBroken) {
|
|
const src = await img.getAttribute('src');
|
|
console.log(`🔴 Broken Image: ${src}`);
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Audit complete for ${path}`);
|
|
});
|
|
}
|
|
});
|