← back to Wine Finder Next

test-mobile.js

197 lines

const { chromium, devices } = require('playwright');

async function testWineFinderMobile() {
  const browser = await chromium.launch({ headless: true });
  const iPhone = devices['iPhone 13 Pro'];
  const context = await browser.newContext({
    ...iPhone,
    locale: 'en-US',
    timezoneId: 'America/New_York',
  });

  const page = await context.newPage();
  const results = {
    tests: [],
    passed: 0,
    failed: 0,
    timestamp: new Date().toISOString()
  };

  console.log('\n🧪 WINE FINDER NEXT.JS - MOBILE TEST SUITE\n');
  console.log('Device: iPhone 13 Pro (390x844)');
  console.log('URL: http://45.61.58.125:7201\n');
  console.log('═'.repeat(60));

  try {
    // Test 1: Homepage loads
    console.log('\n✓ Test 1: Homepage Loading...');
    await page.goto('http://45.61.58.125:7201', { waitUntil: 'networkidle', timeout: 30000 });
    const title = await page.title();
    const test1 = title.includes('Red Thunder Wine Tracker');
    results.tests.push({ name: 'Homepage loads', passed: test1, details: `Title: ${title}` });
    if (test1) results.passed++; else results.failed++;
    console.log(test1 ? '  ✅ PASSED - Title correct' : '  ❌ FAILED - Title incorrect');

    // Test 2: Mobile viewport
    console.log('\n✓ Test 2: Mobile Viewport...');
    const viewport = page.viewportSize();
    const test2 = viewport.width === 390 && viewport.height === 844;
    results.tests.push({ name: 'Mobile viewport', passed: test2, details: `${viewport.width}x${viewport.height}` });
    if (test2) results.passed++; else results.failed++;
    console.log(test2 ? `  ✅ PASSED - ${viewport.width}x${viewport.height}` : '  ❌ FAILED - Viewport incorrect');

    // Test 3: Hero section visible
    console.log('\n✓ Test 3: Hero Section Visibility...');
    const heroVisible = await page.locator('h1:has-text("Red Thunder Wine Tracker")').isVisible();
    const test3 = heroVisible;
    results.tests.push({ name: 'Hero section visible', passed: test3 });
    if (test3) results.passed++; else results.failed++;
    console.log(test3 ? '  ✅ PASSED - Hero visible' : '  ❌ FAILED - Hero not visible');

    // Test 4: Search bar renders
    console.log('\n✓ Test 4: Search Bar...');
    const searchInput = await page.locator('input[placeholder*="Search for wines"]').isVisible();
    const test4 = searchInput;
    results.tests.push({ name: 'Search bar renders', passed: test4 });
    if (test4) results.passed++; else results.failed++;
    console.log(test4 ? '  ✅ PASSED - Search bar visible' : '  ❌ FAILED - Search bar missing');

    // Test 5: White text in search (mobile fix)
    console.log('\n✓ Test 5: Search Text Color...');
    const searchColor = await page.locator('input[placeholder*="Search for wines"]').evaluate(el => {
      return window.getComputedStyle(el).color;
    });
    const test5 = searchColor.includes('255, 255, 255') || searchColor.includes('rgb(255, 255, 255)');
    results.tests.push({ name: 'Search text is white', passed: test5, details: `Color: ${searchColor}` });
    if (test5) results.passed++; else results.failed++;
    console.log(test5 ? '  ✅ PASSED - Text is white' : `  ⚠️  WARNING - Text color: ${searchColor}`);

    // Test 6: Wine cards load
    console.log('\n✓ Test 6: Wine Cards Loading...');
    await page.waitForTimeout(3000); // Wait for API
    const wineCards = await page.locator('.wine-card').count();
    const test6 = wineCards > 0;
    results.tests.push({ name: 'Wine cards load', passed: test6, details: `${wineCards} cards found` });
    if (test6) results.passed++; else results.failed++;
    console.log(test6 ? `  ✅ PASSED - ${wineCards} wine cards loaded` : '  ❌ FAILED - No cards found');

    // Test 7: Mobile responsive grid
    console.log('\n✓ Test 7: Responsive Grid...');
    const gridCols = await page.locator('.grid').first().evaluate(el => {
      return window.getComputedStyle(el).gridTemplateColumns;
    });
    const test7 = gridCols.split(' ').length <= 2; // Should be 1-2 columns on mobile
    results.tests.push({ name: 'Mobile responsive grid', passed: test7, details: `Columns: ${gridCols}` });
    if (test7) results.passed++; else results.failed++;
    console.log(test7 ? '  ✅ PASSED - Grid is mobile-optimized' : '  ❌ FAILED - Too many columns');

    // Test 8: Touch interactions
    console.log('\n✓ Test 8: Touch Interactions...');
    if (wineCards > 0) {
      const firstCard = page.locator('.wine-card').first();
      await firstCard.scrollIntoViewIfNeeded();

      // Try to click price history button
      const priceButton = firstCard.locator('button:has-text("Show Price History")');
      const buttonVisible = await priceButton.isVisible();

      if (buttonVisible) {
        await priceButton.click();
        await page.waitForTimeout(2000);
        const chartVisible = await firstCard.locator('.animate-fadeIn').isVisible();
        const test8 = chartVisible;
        results.tests.push({ name: 'Touch interactions work', passed: test8, details: 'Price chart opens on tap' });
        if (test8) results.passed++; else results.failed++;
        console.log(test8 ? '  ✅ PASSED - Chart opens on tap' : '  ❌ FAILED - Chart didnt open');
      } else {
        results.tests.push({ name: 'Touch interactions work', passed: false, details: 'Button not found' });
        results.failed++;
        console.log('  ❌ FAILED - Price history button not found');
      }
    }

    // Test 9: Font size prevents iOS zoom
    console.log('\n✓ Test 9: iOS Zoom Prevention...');
    const inputFontSize = await page.locator('input[placeholder*="Search"]').evaluate(el => {
      return parseInt(window.getComputedStyle(el).fontSize);
    });
    const test9 = inputFontSize >= 16;
    results.tests.push({ name: 'iOS zoom prevention', passed: test9, details: `Font size: ${inputFontSize}px` });
    if (test9) results.passed++; else results.failed++;
    console.log(test9 ? `  ✅ PASSED - ${inputFontSize}px (prevents zoom)` : `  ❌ FAILED - ${inputFontSize}px (too small)`);

    // Test 10: Wine details display
    console.log('\n✓ Test 10: Wine Details Display...');
    if (wineCards > 0) {
      const firstCard = page.locator('.wine-card').first();
      const hasWinery = await firstCard.locator('text=/🏛️/').isVisible();
      const hasRegion = await firstCard.locator('text=/📍/').isVisible();
      const hasPrice = await firstCard.locator('text=/\\$/').first().isVisible();
      const test10 = hasWinery || hasRegion || hasPrice;
      results.tests.push({
        name: 'Wine details display',
        passed: test10,
        details: `Winery: ${hasWinery}, Region: ${hasRegion}, Price: ${hasPrice}`
      });
      if (test10) results.passed++; else results.failed++;
      console.log(test10 ? '  ✅ PASSED - Details visible' : '  ❌ FAILED - Details missing');
    }

    // Test 11: Performance check
    console.log('\n✓ Test 11: Performance...');
    const performanceMetrics = await page.evaluate(() => JSON.stringify(window.performance.timing));
    const timing = JSON.parse(performanceMetrics);
    const loadTime = timing.loadEventEnd - timing.navigationStart;
    const test11 = loadTime < 5000;
    results.tests.push({ name: 'Page load performance', passed: test11, details: `${loadTime}ms` });
    if (test11) results.passed++; else results.failed++;
    console.log(test11 ? `  ✅ PASSED - ${loadTime}ms` : `  ⚠️  SLOW - ${loadTime}ms`);

    // Test 12: Screenshot
    console.log('\n✓ Test 12: Taking Screenshots...');
    await page.screenshot({ path: '/tmp/wine-finder-mobile.png', fullPage: true });
    console.log('  📸 Screenshot saved: /tmp/wine-finder-mobile.png');

    // Summary
    console.log('\n' + '═'.repeat(60));
    console.log('\n📊 TEST SUMMARY\n');
    console.log(`Total Tests: ${results.tests.length}`);
    console.log(`✅ Passed: ${results.passed}`);
    console.log(`❌ Failed: ${results.failed}`);
    console.log(`Success Rate: ${((results.passed / results.tests.length) * 100).toFixed(1)}%`);
    console.log('\n' + '═'.repeat(60) + '\n');

    // Detailed results
    if (results.failed > 0) {
      console.log('❌ FAILED TESTS:\n');
      results.tests.filter(t => !t.passed).forEach(t => {
        console.log(`  • ${t.name}`);
        if (t.details) console.log(`    ${t.details}`);
      });
      console.log('');
    }

    console.log('✅ PASSED TESTS:\n');
    results.tests.filter(t => t.passed).forEach(t => {
      console.log(`  • ${t.name}`);
      if (t.details) console.log(`    ${t.details}`);
    });

  } catch (error) {
    console.error('\n❌ TEST ERROR:', error.message);
    results.tests.push({ name: 'Test execution', passed: false, details: error.message });
    results.failed++;
  } finally {
    await browser.close();
  }

  return results;
}

testWineFinderMobile().then(results => {
  process.exit(results.failed > 0 ? 1 : 0);
}).catch(err => {
  console.error(err);
  process.exit(1);
});