← back to Dear Bubbe Nextjs

archived/tests/mobile-test.js

222 lines

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

async function testMobileExperience() {
  const browser = await puppeteer.launch({ 
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  
  const results = {
    timestamp: new Date().toISOString(),
    tests: [],
    issues: [],
    recommendations: []
  };

  try {
    // Test iPhone SE (375x667) - smallest modern viewport
    const page = await browser.newPage();
    await page.setViewport({ width: 375, height: 667, isMobile: true });
    await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1');
    
    console.log('🧪 Testing mobile experience on iPhone SE viewport (375x667)...');
    
    await page.goto('http://45.61.58.125:3011', { waitUntil: 'networkidle0' });
    
    // Test 1: Page loads and is responsive
    const title = await page.title();
    results.tests.push({
      test: 'Page Load',
      status: title.includes('Bubbe') ? 'PASS' : 'FAIL',
      details: `Title: ${title}`
    });
    
    // Test 2: Viewport meta tag
    const viewport = await page.$('meta[name="viewport"]');
    const viewportContent = viewport ? await page.evaluate(el => el.getAttribute('content'), viewport) : null;
    results.tests.push({
      test: 'Viewport Meta Tag',
      status: viewportContent && viewportContent.includes('width=device-width') ? 'PASS' : 'FAIL',
      details: `Content: ${viewportContent}`
    });
    
    // Test 3: Talk Button visibility and size
    const talkButton = await page.$('button[aria-label="Talk to Bubbe"]');
    if (talkButton) {
      const buttonBox = await talkButton.boundingBox();
      const isVisible = buttonBox && buttonBox.width > 0 && buttonBox.height > 0;
      const isLargeEnough = buttonBox && buttonBox.width >= 44 && buttonBox.height >= 44; // iOS minimum
      
      results.tests.push({
        test: 'Talk Button Visibility',
        status: isVisible ? 'PASS' : 'FAIL',
        details: `Size: ${buttonBox ? `${buttonBox.width}x${buttonBox.height}` : 'Not found'}`
      });
      
      results.tests.push({
        test: 'Talk Button Touch Target Size',
        status: isLargeEnough ? 'PASS' : 'FAIL',
        details: `Size: ${buttonBox ? `${buttonBox.width}x${buttonBox.height}` : 'Not found'} (iOS minimum: 44x44px)`
      });
      
      if (!isLargeEnough) {
        results.issues.push('Talk Button too small for mobile touch targets (iOS requires 44x44px minimum)');
        results.recommendations.push('Increase Talk Button size to at least 44x44px for iOS compliance');
      }
    } else {
      results.tests.push({
        test: 'Talk Button Presence',
        status: 'FAIL',
        details: 'Talk Button not found'
      });
      results.issues.push('Talk Button not found on page');
    }
    
    // Test 4: Click/Tap the Talk Button to open modal
    if (talkButton) {
      await talkButton.click();
      await page.waitForTimeout(1000); // Wait for modal to open
      
      const modal = await page.$('.fixed.inset-0.bg-black.bg-opacity-50');
      const modalVisible = modal && await modal.isIntersectingViewport();
      
      results.tests.push({
        test: 'Chat Modal Opens on Mobile',
        status: modalVisible ? 'PASS' : 'FAIL',
        details: `Modal ${modalVisible ? 'visible' : 'not visible'} after button click`
      });
      
      if (modalVisible) {
        // Test 5: Modal responsiveness
        const modalContent = await page.$('.bg-white.rounded-2xl');
        if (modalContent) {
          const modalBox = await modalContent.boundingBox();
          const fitsViewport = modalBox && modalBox.width <= 375 && modalBox.height <= 667;
          
          results.tests.push({
            test: 'Modal Fits Viewport',
            status: fitsViewport ? 'PASS' : 'FAIL',
            details: `Modal size: ${modalBox ? `${modalBox.width}x${modalBox.height}` : 'Not measured'}, Viewport: 375x667`
          });
          
          if (!fitsViewport) {
            results.issues.push('Chat Modal may overflow viewport on small screens');
            results.recommendations.push('Make modal fullscreen or near-fullscreen on mobile devices');
          }
        }
        
        // Test 6: Voice button in modal
        const voiceButton = await page.$('button[title="Voice On"], button[title="Voice Off"]');
        if (voiceButton) {
          const voiceButtonBox = await voiceButton.boundingBox();
          const voiceButtonLargeEnough = voiceButtonBox && voiceButtonBox.width >= 44 && voiceButtonBox.height >= 44;
          
          results.tests.push({
            test: 'Modal Voice Button Touch Target',
            status: voiceButtonLargeEnough ? 'PASS' : 'FAIL',
            details: `Size: ${voiceButtonBox ? `${voiceButtonBox.width}x${voiceButtonBox.height}` : 'Not found'}`
          });
          
          if (!voiceButtonLargeEnough) {
            results.issues.push('Voice button in modal too small for touch targets');
            results.recommendations.push('Increase voice button size in modal');
          }
        }
        
        // Test 7: Input field sizing
        const inputField = await page.$('textarea[placeholder*="Type your message"]');
        if (inputField) {
          const inputBox = await inputField.boundingBox();
          const fontSize = await page.evaluate(el => window.getComputedStyle(el).fontSize, inputField);
          const fontSizeNum = parseInt(fontSize);
          
          results.tests.push({
            test: 'Input Field Font Size (iOS Auto-zoom Prevention)',
            status: fontSizeNum >= 16 ? 'PASS' : 'FAIL',
            details: `Font size: ${fontSize} (iOS requires 16px minimum to prevent auto-zoom)`
          });
          
          if (fontSizeNum < 16) {
            results.issues.push('Input field font size too small, will trigger iOS auto-zoom');
            results.recommendations.push('Set input field font-size to 16px to prevent iOS auto-zoom');
          }
        }
      }
    }
    
    // Test 8: Android Chrome viewport (360x640)
    await page.setViewport({ width: 360, height: 640, isMobile: true });
    await page.setUserAgent('Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36');
    
    await page.reload({ waitUntil: 'networkidle0' });
    
    const androidTalkButton = await page.$('button[aria-label="Talk to Bubbe"]');
    if (androidTalkButton) {
      const androidButtonBox = await androidTalkButton.boundingBox();
      const androidIsLargeEnough = androidButtonBox && androidButtonBox.width >= 48 && androidButtonBox.height >= 48; // Android minimum
      
      results.tests.push({
        test: 'Talk Button Touch Target Size (Android)',
        status: androidIsLargeEnough ? 'PASS' : 'FAIL',
        details: `Size: ${androidButtonBox ? `${androidButtonBox.width}x${androidButtonBox.height}` : 'Not found'} (Android minimum: 48x48dp)`
      });
      
      if (!androidIsLargeEnough) {
        results.issues.push('Talk Button too small for Android touch targets (Material Design requires 48x48dp minimum)');
        results.recommendations.push('Increase Talk Button size to at least 48x48dp for Android compliance');
      }
    }
    
  } catch (error) {
    results.tests.push({
      test: 'Mobile Testing',
      status: 'ERROR',
      details: error.message
    });
    results.issues.push(`Testing error: ${error.message}`);
  } finally {
    await browser.close();
  }
  
  // Generate summary
  const passCount = results.tests.filter(t => t.status === 'PASS').length;
  const failCount = results.tests.filter(t => t.status === 'FAIL').length;
  const totalCount = results.tests.filter(t => t.status !== 'INFO' && t.status !== 'ERROR').length;
  
  results.summary = {
    total: totalCount,
    passed: passCount,
    failed: failCount,
    score: totalCount > 0 ? Math.round((passCount / totalCount) * 100) : 0
  };
  
  // Output results
  console.log('\n📱 Mobile Experience Test Results');
  console.log('=================================');
  console.log(`Score: ${results.summary.score}% (${passCount}/${totalCount} tests passed)\n`);
  
  results.tests.forEach(test => {
    const emoji = test.status === 'PASS' ? '✅' : test.status === 'FAIL' ? '❌' : test.status === 'ERROR' ? '🚨' : 'ℹ️';
    console.log(`${emoji} ${test.test}: ${test.status}`);
    if (test.details) console.log(`   ${test.details}`);
  });
  
  if (results.issues.length > 0) {
    console.log('\n🔴 Issues Found:');
    results.issues.forEach((issue, i) => console.log(`${i + 1}. ${issue}`));
  }
  
  if (results.recommendations.length > 0) {
    console.log('\n💡 Recommendations:');
    results.recommendations.forEach((rec, i) => console.log(`${i + 1}. ${rec}`));
  }
  
  // Save detailed results
  fs.writeFileSync('mobile-test-results.json', JSON.stringify(results, null, 2));
  console.log('\n📄 Detailed results saved to mobile-test-results.json');
  
  return results;
}

testMobileExperience().catch(console.error);