← back to Handbag Auth Nextjs

mobile-analysis.js

428 lines

const puppeteer = require('puppeteer');
const fs = require('fs').promises;

const SITE_URL = 'http://45.61.58.125:7991';

async function analyzeMobileUI() {
  console.log('Mobile UI Analysis\n' + '='.repeat(50));

  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
  });

  const results = {
    home: { issues: [], warnings: [], good: [] },
    camera: { issues: [], warnings: [], good: [] },
    priceTracker: { issues: [], warnings: [], good: [] }
  };

  try {
    const page = await browser.newPage();

    // Mobile iPhone SE viewport (smallest common)
    await page.setViewport({
      width: 375,
      height: 667,
      isMobile: true,
      hasTouch: true,
      deviceScaleFactor: 2
    });

    // Set mobile user agent
    await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1');

    // Test Home Page
    console.log('\n📱 Testing HOME page (375px width)...');
    try {
      await page.goto(SITE_URL, {
        waitUntil: 'domcontentloaded',
        timeout: 15000
      });

      const homeAnalysis = await page.evaluate(() => {
        const analysis = { issues: [], warnings: [], good: [] };

        // Check viewport meta tag
        const viewportMeta = document.querySelector('meta[name="viewport"]');
        if (viewportMeta) {
          const content = viewportMeta.getAttribute('content');
          if (content.includes('width=device-width')) {
            analysis.good.push('✅ Proper viewport meta tag found');
          }
        } else {
          analysis.issues.push('❌ Missing viewport meta tag');
        }

        // Check horizontal scrolling
        if (document.documentElement.scrollWidth > window.innerWidth) {
          analysis.issues.push(`❌ Horizontal scroll detected (${document.documentElement.scrollWidth}px > ${window.innerWidth}px)`);
        } else {
          analysis.good.push('✅ No horizontal overflow');
        }

        // Check navigation menu
        const nav = document.querySelector('nav, [class*="nav"]');
        if (nav) {
          const navDisplay = window.getComputedStyle(nav).display;
          const hasHamburger = document.querySelector('[class*="menu"], button svg, [aria-label*="menu"]');
          if (hasHamburger) {
            analysis.good.push('✅ Mobile menu button found');
          } else if (navDisplay === 'none' || navDisplay === 'hidden') {
            analysis.warnings.push('⚠️  Navigation hidden but no mobile menu button found');
          }
        }

        // Check input elements
        const inputs = document.querySelectorAll('input, textarea, select');
        let inputIssues = 0;
        inputs.forEach(input => {
          const fontSize = parseInt(window.getComputedStyle(input).fontSize);
          if (fontSize < 16) {
            inputIssues++;
          }
        });
        if (inputIssues > 0) {
          analysis.issues.push(`❌ ${inputIssues} input(s) with font-size < 16px (causes iOS zoom)`);
        } else if (inputs.length > 0) {
          analysis.good.push('✅ All inputs have proper font size (≥16px)');
        }

        // Check touch targets
        const clickables = document.querySelectorAll('button, a, [role="button"]');
        let smallTargets = 0;
        clickables.forEach(el => {
          const rect = el.getBoundingClientRect();
          if (rect.width > 0 && rect.height > 0 && (rect.width < 44 || rect.height < 44)) {
            smallTargets++;
          }
        });
        if (smallTargets > 0) {
          analysis.warnings.push(`⚠️  ${smallTargets} touch targets < 44px (Apple HIG recommendation)`);
        }

        // Check grid responsiveness
        const grids = document.querySelectorAll('[class*="grid"]');
        grids.forEach(grid => {
          const classes = grid.className;
          if (classes.includes('sm:') || classes.includes('md:') || classes.includes('lg:')) {
            analysis.good.push('✅ Responsive grid classes detected');
            return;
          }
        });

        // Check text readability
        const textElements = document.querySelectorAll('p, span, h1, h2, h3, h4, h5, h6');
        let tinyText = 0;
        textElements.forEach(el => {
          if (el.textContent && el.textContent.trim()) {
            const fontSize = parseInt(window.getComputedStyle(el).fontSize);
            if (fontSize > 0 && fontSize < 14) {
              tinyText++;
            }
          }
        });
        if (tinyText > 0) {
          analysis.warnings.push(`⚠️  ${tinyText} text elements < 14px`);
        }

        // Check images
        const images = document.querySelectorAll('img');
        let overflowImages = 0;
        images.forEach(img => {
          const rect = img.getBoundingClientRect();
          if (rect.right > window.innerWidth) {
            overflowImages++;
          }
        });
        if (overflowImages > 0) {
          analysis.issues.push(`❌ ${overflowImages} image(s) overflow viewport`);
        }

        // Check flexbox/grid usage
        const containers = document.querySelectorAll('div, section, main');
        let responsiveContainers = 0;
        containers.forEach(container => {
          const display = window.getComputedStyle(container).display;
          if (display === 'flex' || display === 'grid') {
            responsiveContainers++;
          }
        });
        if (responsiveContainers > 5) {
          analysis.good.push(`✅ Modern layout (${responsiveContainers} flex/grid containers)`);
        }

        return analysis;
      });

      results.home = homeAnalysis;

      // Take screenshot
      await page.screenshot({
        path: 'mobile-screenshots/analysis-home-375px.png',
        fullPage: false
      });

    } catch (error) {
      results.home.issues.push(`❌ Failed to load: ${error.message}`);
    }

    // Test Camera Page
    console.log('\n📱 Testing CAMERA page (375px width)...');
    try {
      await page.goto(`${SITE_URL}/camera`, {
        waitUntil: 'domcontentloaded',
        timeout: 15000
      });

      // Wait a bit for any dynamic content
      await new Promise(resolve => setTimeout(resolve, 2000));

      const cameraAnalysis = await page.evaluate(() => {
        const analysis = { issues: [], warnings: [], good: [] };

        // Check for camera-specific elements
        const cameraElements = document.querySelectorAll('video, canvas, [class*="camera"], [id*="camera"]');
        if (cameraElements.length > 0) {
          analysis.good.push('✅ Camera elements found');

          // Check if camera view is responsive
          cameraElements.forEach(el => {
            const rect = el.getBoundingClientRect();
            if (rect.width > window.innerWidth) {
              analysis.issues.push('❌ Camera view overflows viewport');
            }
          });
        }

        // Check horizontal scrolling
        if (document.documentElement.scrollWidth > window.innerWidth) {
          analysis.issues.push(`❌ Horizontal scroll detected`);
        } else {
          analysis.good.push('✅ No horizontal overflow');
        }

        // Check button sizes
        const buttons = document.querySelectorAll('button');
        let goodButtons = 0;
        buttons.forEach(btn => {
          const rect = btn.getBoundingClientRect();
          if (rect.width >= 44 && rect.height >= 44) {
            goodButtons++;
          }
        });
        if (goodButtons === buttons.length && buttons.length > 0) {
          analysis.good.push('✅ All buttons are touch-friendly (≥44px)');
        }

        return analysis;
      });

      results.camera = cameraAnalysis;

      await page.screenshot({
        path: 'mobile-screenshots/analysis-camera-375px.png',
        fullPage: false
      });

    } catch (error) {
      results.camera.issues.push(`❌ Failed to load: ${error.message}`);
    }

    // Test Price Tracker Page
    console.log('\n📱 Testing PRICE TRACKER page (375px width)...');
    try {
      await page.goto(`${SITE_URL}/price-tracker`, {
        waitUntil: 'domcontentloaded',
        timeout: 15000
      });

      const priceTrackerAnalysis = await page.evaluate(() => {
        const analysis = { issues: [], warnings: [], good: [] };

        // Check horizontal scrolling
        if (document.documentElement.scrollWidth > window.innerWidth) {
          analysis.issues.push(`❌ Horizontal scroll detected`);
        } else {
          analysis.good.push('✅ No horizontal overflow');
        }

        // Check form elements
        const forms = document.querySelectorAll('form');
        if (forms.length > 0) {
          analysis.good.push('✅ Forms found');

          // Check form inputs
          const formInputs = document.querySelectorAll('form input, form select, form textarea');
          let properInputs = 0;
          formInputs.forEach(input => {
            const fontSize = parseInt(window.getComputedStyle(input).fontSize);
            if (fontSize >= 16) {
              properInputs++;
            }
          });
          if (properInputs === formInputs.length && formInputs.length > 0) {
            analysis.good.push('✅ All form inputs have proper font size');
          }
        }

        // Check tables/lists for mobile friendliness
        const tables = document.querySelectorAll('table');
        if (tables.length > 0) {
          tables.forEach(table => {
            const rect = table.getBoundingClientRect();
            if (rect.width > window.innerWidth) {
              analysis.warnings.push('⚠️  Table may not be mobile-optimized');
            }
          });
        }

        // Check cards/list items
        const cards = document.querySelectorAll('[class*="card"], [class*="list"] > *');
        if (cards.length > 0) {
          let overflowCards = 0;
          cards.forEach(card => {
            const rect = card.getBoundingClientRect();
            if (rect.right > window.innerWidth) {
              overflowCards++;
            }
          });
          if (overflowCards === 0) {
            analysis.good.push('✅ Cards/lists fit within viewport');
          } else {
            analysis.issues.push(`❌ ${overflowCards} card(s) overflow viewport`);
          }
        }

        return analysis;
      });

      results.priceTracker = priceTrackerAnalysis;

      await page.screenshot({
        path: 'mobile-screenshots/analysis-price-tracker-375px.png',
        fullPage: false
      });

    } catch (error) {
      results.priceTracker.issues.push(`❌ Failed to load: ${error.message}`);
    }

    // Test tablet view
    console.log('\n📱 Testing iPad view (768px width)...');
    await page.setViewport({
      width: 768,
      height: 1024,
      isMobile: true,
      hasTouch: true
    });

    await page.goto(SITE_URL, {
      waitUntil: 'domcontentloaded',
      timeout: 15000
    });

    await page.screenshot({
      path: 'mobile-screenshots/analysis-home-768px.png',
      fullPage: false
    });

  } finally {
    await browser.close();
  }

  // Print results
  console.log('\n' + '='.repeat(50));
  console.log('MOBILE UI/UX TEST RESULTS');
  console.log('='.repeat(50));

  const pages = [
    { name: 'HOME PAGE', data: results.home },
    { name: 'CAMERA PAGE', data: results.camera },
    { name: 'PRICE TRACKER PAGE', data: results.priceTracker }
  ];

  let totalIssues = 0;
  let totalWarnings = 0;

  pages.forEach(page => {
    console.log(`\n### ${page.name}`);

    if (page.data.issues.length > 0) {
      console.log('\nCritical Issues:');
      page.data.issues.forEach(issue => console.log(`  ${issue}`));
      totalIssues += page.data.issues.length;
    }

    if (page.data.warnings.length > 0) {
      console.log('\nWarnings:');
      page.data.warnings.forEach(warning => console.log(`  ${warning}`));
      totalWarnings += page.data.warnings.length;
    }

    if (page.data.good.length > 0) {
      console.log('\nWorking Well:');
      page.data.good.forEach(good => console.log(`  ${good}`));
    }
  });

  // Overall assessment
  console.log('\n' + '='.repeat(50));
  console.log('OVERALL ASSESSMENT');
  console.log('='.repeat(50));

  if (totalIssues === 0 && totalWarnings < 3) {
    console.log('\n✅ MOBILE-FRIENDLY: The site appears to be well-optimized for mobile devices!');
  } else if (totalIssues === 0) {
    console.log('\n⚠️  MOSTLY MOBILE-FRIENDLY: The site works on mobile but has some minor issues.');
  } else {
    console.log('\n❌ NEEDS IMPROVEMENT: The site has critical mobile usability issues.');
  }

  console.log(`\nTotal Critical Issues: ${totalIssues}`);
  console.log(`Total Warnings: ${totalWarnings}`);

  // Recommendations
  console.log('\n' + '='.repeat(50));
  console.log('RECOMMENDATIONS');
  console.log('='.repeat(50));

  const recommendations = [];

  // Check for common issues across all results
  const allIssues = [...results.home.issues, ...results.camera.issues, ...results.priceTracker.issues];
  const allWarnings = [...results.home.warnings, ...results.camera.warnings, ...results.priceTracker.warnings];

  if (allIssues.some(i => i.includes('font-size < 16px'))) {
    recommendations.push('1. Set all input, textarea, and select elements to minimum 16px font-size to prevent iOS zoom');
  }

  if (allIssues.some(i => i.includes('Horizontal scroll'))) {
    recommendations.push('2. Fix horizontal overflow issues - ensure all content fits within viewport width');
  }

  if (allWarnings.some(w => w.includes('touch targets < 44px'))) {
    recommendations.push('3. Increase button/link sizes to at least 44x44px for better touch interaction');
  }

  if (allWarnings.some(w => w.includes('text elements < 14px'))) {
    recommendations.push('4. Increase text size for better readability on mobile devices');
  }

  if (allIssues.some(i => i.includes('overflow viewport'))) {
    recommendations.push('5. Ensure images and cards use responsive sizing (max-width: 100%)');
  }

  if (recommendations.length === 0) {
    console.log('\n✅ No critical recommendations - the site is mobile-optimized!');
  } else {
    recommendations.forEach(rec => console.log(`\n${rec}`));
  }

  // Save detailed report
  await fs.writeFile('mobile-analysis-report.json', JSON.stringify(results, null, 2));
  console.log('\n📊 Detailed report saved to mobile-analysis-report.json');
  console.log('📸 Screenshots saved in mobile-screenshots/');
}

analyzeMobileUI().catch(console.error);