← back to Handbag Auth Nextjs
final-mobile-test.js
342 lines
const puppeteer = require('puppeteer');
const fs = require('fs').promises;
const SITE_URL = 'http://45.61.58.125:7991';
async function finalMobileTest() {
console.log('='.repeat(60));
console.log('MOBILE UI/UX TEST REPORT');
console.log('='.repeat(60));
console.log(`Site: ${SITE_URL}`);
console.log(`Date: ${new Date().toISOString()}\n`);
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
});
const page = await browser.newPage();
// Test different mobile viewports
const viewports = [
{ name: 'iPhone SE', width: 375, height: 667, device: 'Mobile' },
{ name: 'iPhone 12 Pro', width: 390, height: 844, device: 'Mobile' },
{ name: 'iPhone 14 Pro Max', width: 430, height: 932, device: 'Mobile' },
{ name: 'iPad', width: 768, height: 1024, device: 'Tablet' }
];
const pages = [
{ name: 'Home', path: '/', description: 'Listings grid and search' },
{ name: 'Camera', path: '/camera', description: 'Handbag scanner' },
{ name: 'Price Tracker', path: '/price-tracker', description: 'Price tracking tool' }
];
const issues = [];
const recommendations = new Set();
console.log('Testing each page across mobile viewports...\n');
for (const viewport of viewports) {
console.log(`\n${viewport.device} - ${viewport.name} (${viewport.width}x${viewport.height})`);
console.log('-'.repeat(50));
await page.setViewport({
width: viewport.width,
height: viewport.height,
isMobile: true,
hasTouch: true,
deviceScaleFactor: 2
});
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15');
for (const pageInfo of pages) {
try {
console.log(` Testing ${pageInfo.name}...`);
await page.goto(`${SITE_URL}${pageInfo.path}`, {
waitUntil: 'domcontentloaded',
timeout: 10000
});
await page.waitForTimeout(2000);
// Take screenshot
const screenshotName = `final-${viewport.name.replace(/\s/g, '-')}-${pageInfo.name.toLowerCase()}.png`;
await page.screenshot({
path: `mobile-screenshots/${screenshotName}`,
fullPage: false
});
// Run comprehensive tests
const testResults = await page.evaluate(() => {
const results = {
passed: [],
failed: [],
warnings: []
};
// 1. Check viewport meta tag
const viewportMeta = document.querySelector('meta[name="viewport"]');
if (viewportMeta && viewportMeta.content.includes('width=device-width')) {
results.passed.push('Viewport meta tag configured');
} else {
results.failed.push('Missing or incorrect viewport meta tag');
}
// 2. Check horizontal scrolling
const hasHorizontalScroll = document.documentElement.scrollWidth > window.innerWidth;
if (!hasHorizontalScroll) {
results.passed.push('No horizontal overflow');
} else {
results.failed.push(`Horizontal scrolling detected (${document.documentElement.scrollWidth}px > ${window.innerWidth}px)`);
}
// 3. Check input font sizes (iOS zoom prevention)
const inputs = document.querySelectorAll('input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]), textarea, select');
let smallInputs = [];
inputs.forEach(input => {
const styles = window.getComputedStyle(input);
const fontSize = parseInt(styles.fontSize);
if (fontSize < 16) {
const type = input.tagName.toLowerCase();
const placeholder = input.getAttribute('placeholder') || '';
smallInputs.push(`${type}: "${placeholder}" (${fontSize}px)`);
}
});
if (smallInputs.length === 0 && inputs.length > 0) {
results.passed.push(`All ${inputs.length} input fields have proper font size (≥16px)`);
} else if (smallInputs.length > 0) {
results.failed.push(`${smallInputs.length} input(s) with font < 16px will cause iOS zoom`);
results.warnings.push(`Small inputs: ${smallInputs.join(', ')}`);
}
// 4. Check touch target sizes
const touchTargets = document.querySelectorAll('button, a, [role="button"]');
let smallTargets = 0;
let tinyTargets = [];
touchTargets.forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
if (rect.width < 44 || rect.height < 44) {
smallTargets++;
if (rect.width < 30 || rect.height < 30) {
const text = el.textContent?.trim().substring(0, 20) || 'unnamed';
tinyTargets.push(`"${text}" (${Math.round(rect.width)}x${Math.round(rect.height)}px)`);
}
}
}
});
if (smallTargets === 0 && touchTargets.length > 0) {
results.passed.push('All touch targets meet 44px minimum');
} else if (tinyTargets.length > 0) {
results.failed.push(`${tinyTargets.length} critically small touch targets (<30px)`);
results.warnings.push(`Tiny targets: ${tinyTargets.join(', ')}`);
} else if (smallTargets > 0) {
results.warnings.push(`${smallTargets} touch targets below 44px recommendation`);
}
// 5. Check mobile navigation
const nav = document.querySelector('nav');
const mobileMenuButton = document.querySelector('[aria-label*="menu" i], button:has(svg), [class*="burger"], [class*="menu-toggle"]');
if (nav) {
const navDisplay = window.getComputedStyle(nav).display;
if (window.innerWidth < 768) {
if (navDisplay === 'none' || navDisplay === 'hidden') {
if (mobileMenuButton) {
results.passed.push('Mobile menu button present');
} else {
results.warnings.push('Navigation hidden on mobile without menu button');
}
} else if (navDisplay === 'flex' || navDisplay === 'block') {
results.passed.push('Navigation visible on mobile');
}
}
}
// 6. Check text readability
const textElements = document.querySelectorAll('p, span:not(:empty), h1, h2, h3, h4, h5, h6, a, button');
let unreadableText = 0;
textElements.forEach(el => {
const text = el.textContent?.trim();
if (text && text.length > 0) {
const styles = window.getComputedStyle(el);
const fontSize = parseInt(styles.fontSize);
if (fontSize > 0 && fontSize < 12) {
unreadableText++;
}
}
});
if (unreadableText > 0) {
results.warnings.push(`${unreadableText} text elements < 12px may be hard to read`);
} else {
results.passed.push('Text sizes are readable');
}
// 7. Check responsive images
const images = document.querySelectorAll('img');
let overflowingImages = 0;
images.forEach(img => {
const rect = img.getBoundingClientRect();
if (rect.right > window.innerWidth + 1) {
overflowingImages++;
}
});
if (overflowingImages > 0) {
results.failed.push(`${overflowingImages} image(s) overflow viewport`);
} else if (images.length > 0) {
results.passed.push('All images fit within viewport');
}
// 8. Check for responsive grid/flexbox
const containers = document.querySelectorAll('[class*="grid"], [class*="flex"]');
if (containers.length > 10) {
results.passed.push(`Modern responsive layout (${containers.length} flex/grid containers)`);
}
return results;
});
// Process results
if (testResults.failed.length > 0) {
issues.push({
viewport: viewport.name,
page: pageInfo.name,
failures: testResults.failed,
warnings: testResults.warnings
});
console.log(` ❌ ${testResults.failed.length} critical issue(s)`);
testResults.failed.forEach(f => {
console.log(` - ${f}`);
// Add recommendations based on failures
if (f.includes('font < 16px')) {
recommendations.add('Set all text inputs, textareas, and selects to font-size: 16px minimum');
}
if (f.includes('Horizontal scrolling')) {
recommendations.add('Fix horizontal overflow by ensuring all content uses max-width: 100%');
}
if (f.includes('critically small touch targets')) {
recommendations.add('Increase button and link sizes to minimum 44x44px for touch accessibility');
}
});
} else {
console.log(` ✅ No critical issues`);
}
if (testResults.warnings.length > 0) {
console.log(` ⚠️ ${testResults.warnings.length} warning(s)`);
}
if (testResults.passed.length > 0) {
console.log(` ✓ ${testResults.passed.length} checks passed`);
}
} catch (error) {
console.log(` ❌ Error: ${error.message}`);
issues.push({
viewport: viewport.name,
page: pageInfo.name,
error: error.message
});
}
}
}
await browser.close();
// Final Report
console.log('\n' + '='.repeat(60));
console.log('SUMMARY');
console.log('='.repeat(60));
const totalIssues = issues.reduce((sum, issue) => {
return sum + (issue.failures ? issue.failures.length : 1);
}, 0);
if (totalIssues === 0) {
console.log('\n✅ EXCELLENT! The site is fully mobile-optimized!');
console.log(' No critical issues found across all tested viewports.');
} else if (totalIssues <= 2) {
console.log('\n⚠️ GOOD - The site is mostly mobile-friendly with minor issues.');
} else {
console.log('\n❌ NEEDS IMPROVEMENT - Multiple mobile usability issues detected.');
}
console.log(`\nTotal Critical Issues: ${totalIssues}`);
console.log(`Pages with Issues: ${issues.length}`);
if (recommendations.size > 0) {
console.log('\n' + '='.repeat(60));
console.log('RECOMMENDATIONS');
console.log('='.repeat(60));
Array.from(recommendations).forEach((rec, i) => {
console.log(`\n${i + 1}. ${rec}`);
});
}
// Components needing fixes
if (issues.length > 0) {
console.log('\n' + '='.repeat(60));
console.log('COMPONENTS NEEDING FIXES');
console.log('='.repeat(60));
const componentsToFix = new Set();
issues.forEach(issue => {
if (issue.failures) {
issue.failures.forEach(failure => {
if (failure.includes('input')) {
componentsToFix.add('Input fields in forms');
}
if (failure.includes('touch targets')) {
componentsToFix.add('Buttons and links (increase size)');
}
if (failure.includes('Horizontal')) {
componentsToFix.add('Page layout containers');
}
});
}
});
if (componentsToFix.size > 0) {
console.log('\nFiles likely needing updates:');
componentsToFix.forEach(comp => {
console.log(` - ${comp}`);
});
}
}
console.log('\n' + '='.repeat(60));
console.log('MOBILE NAVIGATION');
console.log('='.repeat(60));
console.log('\n⚠️ Note: The site navigation is hidden on mobile devices');
console.log(' but there is no visible hamburger menu button.');
console.log(' Users can still navigate using the "Scan Handbag" button');
console.log(' and links within pages, but adding a mobile menu would');
console.log(' improve navigation accessibility.\n');
// Save report
const report = {
timestamp: new Date().toISOString(),
site: SITE_URL,
totalIssues,
issues,
recommendations: Array.from(recommendations),
mobileReadiness: totalIssues === 0 ? 'Excellent' : totalIssues <= 2 ? 'Good' : 'Needs Improvement'
};
await fs.writeFile('final-mobile-report.json', JSON.stringify(report, null, 2));
console.log('📊 Full report saved to: final-mobile-report.json');
console.log('📸 Screenshots saved to: mobile-screenshots/');
console.log('\n✅ Mobile UI/UX testing complete!');
}
finalMobileTest().catch(console.error);