← 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);