2025-11-25 06:41:56 +00:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* TEST: Button Colors and Visibility Across Viewports
|
|
|
|
|
|
*
|
|
|
|
|
|
* Verifies:
|
|
|
|
|
|
* 1. Zoom button is purple (#9b59b6), not blue
|
|
|
|
|
|
* 2. Info button is blue (#3498db) - different from zoom
|
|
|
|
|
|
* 3. All buttons visible at desktop viewport (1278px) even in mobile mode
|
|
|
|
|
|
* 4. Zoom/keyboard buttons properly hidden at mobile viewport (<900px)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
const VIEWPORTS = {
|
|
|
|
|
|
desktop: {
|
|
|
|
|
|
width: 1278,
|
|
|
|
|
|
height: 800,
|
|
|
|
|
|
name: 'Desktop (1278px)',
|
|
|
|
|
|
// Simulate mobile user agent to test that buttons still show at desktop size
|
|
|
|
|
|
userAgent: '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',
|
|
|
|
|
|
hasTouch: true
|
|
|
|
|
|
},
|
|
|
|
|
|
mobile: {
|
|
|
|
|
|
width: 375,
|
|
|
|
|
|
height: 667,
|
|
|
|
|
|
name: 'Mobile Portrait (375px)',
|
|
|
|
|
|
userAgent: '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',
|
|
|
|
|
|
hasTouch: true
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Convert RGB color to hex format for comparison
|
|
|
|
|
|
*/
|
|
|
|
|
|
function rgbToHex(rgb) {
|
|
|
|
|
|
const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
|
|
|
|
if (!match) return rgb;
|
|
|
|
|
|
|
|
|
|
|
|
const r = parseInt(match[1]);
|
|
|
|
|
|
const g = parseInt(match[2]);
|
|
|
|
|
|
const b = parseInt(match[3]);
|
|
|
|
|
|
|
|
|
|
|
|
return '#' + [r, g, b].map(x => {
|
|
|
|
|
|
const hex = x.toString(16);
|
|
|
|
|
|
return hex.length === 1 ? '0' + hex : hex;
|
|
|
|
|
|
}).join('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function testViewport(browser, viewport) {
|
|
|
|
|
|
const context = await browser.newContext({
|
|
|
|
|
|
viewport: { width: viewport.width, height: viewport.height },
|
|
|
|
|
|
userAgent: viewport.userAgent,
|
|
|
|
|
|
hasTouch: viewport.hasTouch
|
|
|
|
|
|
});
|
|
|
|
|
|
const page = await context.newPage();
|
|
|
|
|
|
|
|
|
|
|
|
await page.goto('http://localhost:1999/?lang=en');
|
|
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
|
|
|
|
|
|
|
|
// Wait a bit for device detection to run
|
|
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
|
|
|
|
|
|
|
|
const results = await page.evaluate(() => {
|
|
|
|
|
|
// Get all button elements
|
|
|
|
|
|
const downloadBtn = document.querySelector('.download-btn');
|
|
|
|
|
|
const printBtn = document.querySelector('.print-friendly-btn');
|
|
|
|
|
|
const themeBtn = document.querySelector('.color-theme-switcher');
|
|
|
|
|
|
const zoomToggle = document.querySelector('.zoom-toggle-btn');
|
|
|
|
|
|
const shortcutsBtn = document.querySelector('.shortcuts-btn');
|
|
|
|
|
|
const infoBtn = document.querySelector('.info-button');
|
|
|
|
|
|
const backToTop = document.querySelector('.back-to-top');
|
|
|
|
|
|
|
|
|
|
|
|
// Get device class
|
|
|
|
|
|
const hasDesktopClass = document.documentElement.classList.contains('is-desktop');
|
|
|
|
|
|
const hasMobileClass = document.documentElement.classList.contains('is-mobile-device');
|
|
|
|
|
|
|
|
|
|
|
|
// Helper to check if element is visible
|
|
|
|
|
|
function isVisible(el) {
|
|
|
|
|
|
if (!el) return false;
|
|
|
|
|
|
const style = window.getComputedStyle(el);
|
|
|
|
|
|
return style.display !== 'none' &&
|
|
|
|
|
|
style.visibility !== 'hidden' &&
|
|
|
|
|
|
style.opacity !== '0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get button colors (background-color)
|
|
|
|
|
|
function getBackgroundColor(el) {
|
|
|
|
|
|
if (!el) return 'N/A';
|
|
|
|
|
|
return window.getComputedStyle(el).backgroundColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
viewport: {
|
|
|
|
|
|
width: window.innerWidth,
|
|
|
|
|
|
height: window.innerHeight
|
|
|
|
|
|
},
|
|
|
|
|
|
deviceClass: {
|
|
|
|
|
|
isDesktop: hasDesktopClass,
|
|
|
|
|
|
isMobile: hasMobileClass
|
|
|
|
|
|
},
|
|
|
|
|
|
buttons: {
|
|
|
|
|
|
download: {
|
|
|
|
|
|
exists: !!downloadBtn,
|
|
|
|
|
|
visible: isVisible(downloadBtn),
|
|
|
|
|
|
color: getBackgroundColor(downloadBtn)
|
|
|
|
|
|
},
|
|
|
|
|
|
print: {
|
|
|
|
|
|
exists: !!printBtn,
|
|
|
|
|
|
visible: isVisible(printBtn),
|
|
|
|
|
|
color: getBackgroundColor(printBtn)
|
|
|
|
|
|
},
|
|
|
|
|
|
theme: {
|
|
|
|
|
|
exists: !!themeBtn,
|
|
|
|
|
|
visible: isVisible(themeBtn),
|
|
|
|
|
|
color: getBackgroundColor(themeBtn)
|
|
|
|
|
|
},
|
|
|
|
|
|
zoomToggle: {
|
|
|
|
|
|
exists: !!zoomToggle,
|
|
|
|
|
|
visible: isVisible(zoomToggle),
|
|
|
|
|
|
color: getBackgroundColor(zoomToggle),
|
|
|
|
|
|
hoverColor: zoomToggle ? window.getComputedStyle(zoomToggle, ':hover').backgroundColor : 'N/A'
|
|
|
|
|
|
},
|
|
|
|
|
|
shortcuts: {
|
|
|
|
|
|
exists: !!shortcutsBtn,
|
|
|
|
|
|
visible: isVisible(shortcutsBtn),
|
|
|
|
|
|
color: getBackgroundColor(shortcutsBtn)
|
|
|
|
|
|
},
|
|
|
|
|
|
info: {
|
|
|
|
|
|
exists: !!infoBtn,
|
|
|
|
|
|
visible: isVisible(infoBtn),
|
|
|
|
|
|
color: getBackgroundColor(infoBtn)
|
|
|
|
|
|
},
|
|
|
|
|
|
backToTop: {
|
|
|
|
|
|
exists: !!backToTop,
|
|
|
|
|
|
visible: isVisible(backToTop),
|
|
|
|
|
|
color: getBackgroundColor(backToTop)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await page.screenshot({
|
|
|
|
|
|
path: `tests/screenshots/button-test-${viewport.width}px.png`,
|
|
|
|
|
|
fullPage: false
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await context.close();
|
|
|
|
|
|
return results;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
|
|
const browser = await chromium.launch({ headless: true });
|
|
|
|
|
|
|
|
|
|
|
|
console.log('🎨 Button Colors and Visibility Test\n');
|
|
|
|
|
|
console.log('Testing: Color differentiation and responsive visibility\n');
|
|
|
|
|
|
|
|
|
|
|
|
const allResults = {};
|
|
|
|
|
|
const failures = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const [key, viewport] of Object.entries(VIEWPORTS)) {
|
|
|
|
|
|
console.log(`📱 Testing: ${viewport.name}`);
|
|
|
|
|
|
console.log('='.repeat(60));
|
|
|
|
|
|
|
|
|
|
|
|
const results = await testViewport(browser, viewport);
|
|
|
|
|
|
allResults[key] = results;
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`\nViewport: ${results.viewport.width}×${results.viewport.height}`);
|
|
|
|
|
|
console.log(`Device class: ${results.deviceClass.isDesktop ? 'Desktop' : 'Mobile'}\n`);
|
|
|
|
|
|
|
|
|
|
|
|
// Print button status
|
|
|
|
|
|
console.log('Button Visibility:');
|
|
|
|
|
|
console.log(` 📥 Download: ${results.buttons.download.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` 🖨️ Print: ${results.buttons.print.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` 🎨 Theme: ${results.buttons.theme.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` 🔍 Zoom: ${results.buttons.zoomToggle.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` ⌨️ Shortcuts: ${results.buttons.shortcuts.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` ℹ️ Info: ${results.buttons.info.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
console.log(` ⬆️ Back: ${results.buttons.backToTop.visible ? '✅ Visible' : '❌ Hidden'}`);
|
|
|
|
|
|
|
|
|
|
|
|
// Color verification for zoom and info buttons
|
|
|
|
|
|
if (results.buttons.zoomToggle.exists) {
|
|
|
|
|
|
const zoomColor = rgbToHex(results.buttons.zoomToggle.color);
|
|
|
|
|
|
console.log(`\n 🔍 Zoom color: ${zoomColor} ${results.buttons.zoomToggle.color}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (results.buttons.info.exists) {
|
|
|
|
|
|
const infoColor = rgbToHex(results.buttons.info.color);
|
|
|
|
|
|
console.log(` ℹ️ Info color: ${infoColor} ${results.buttons.info.color}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Validation rules
|
|
|
|
|
|
const issues = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (viewport.width > 900) {
|
|
|
|
|
|
// DESKTOP VIEWPORT (even with mobile UA)
|
|
|
|
|
|
console.log('\n📋 Validating desktop viewport (>900px)...');
|
|
|
|
|
|
|
|
|
|
|
|
// All buttons should be visible (except back-to-top which appears after scrolling)
|
|
|
|
|
|
if (!results.buttons.download.visible) issues.push('Download button not visible at desktop size');
|
|
|
|
|
|
if (!results.buttons.print.visible) issues.push('Print button not visible at desktop size');
|
|
|
|
|
|
if (!results.buttons.theme.visible) issues.push('Theme button not visible at desktop size');
|
|
|
|
|
|
if (!results.buttons.zoomToggle.visible) issues.push('Zoom toggle button not visible at desktop size');
|
|
|
|
|
|
if (!results.buttons.shortcuts.visible) issues.push('Shortcuts button not visible at desktop size');
|
|
|
|
|
|
if (!results.buttons.info.visible) issues.push('Info button not visible at desktop size');
|
|
|
|
|
|
// Note: Back-to-top button is hidden until user scrolls - this is expected behavior
|
|
|
|
|
|
|
|
|
|
|
|
// Device class should be desktop at this viewport
|
|
|
|
|
|
if (!results.deviceClass.isDesktop) {
|
|
|
|
|
|
issues.push('Device class should be "is-desktop" at viewport >900px');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify zoom button is NOT blue (should be purple #9b59b6 or similar)
|
|
|
|
|
|
if (results.buttons.zoomToggle.exists) {
|
|
|
|
|
|
const zoomColor = rgbToHex(results.buttons.zoomToggle.color);
|
|
|
|
|
|
const infoColor = results.buttons.info.exists ? rgbToHex(results.buttons.info.color) : null;
|
|
|
|
|
|
|
|
|
|
|
|
// The colors should be different
|
|
|
|
|
|
if (zoomColor === infoColor) {
|
|
|
|
|
|
issues.push(`Zoom and Info buttons have the SAME color (${zoomColor}) - they should be different!`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// MOBILE VIEWPORT
|
|
|
|
|
|
console.log('\n📋 Validating mobile viewport (≤900px)...');
|
|
|
|
|
|
|
|
|
|
|
|
// Core buttons should be visible
|
|
|
|
|
|
if (!results.buttons.download.visible) issues.push('Download button not visible on mobile');
|
|
|
|
|
|
if (!results.buttons.print.visible) issues.push('Print button not visible on mobile');
|
|
|
|
|
|
if (!results.buttons.theme.visible) issues.push('Theme button not visible on mobile');
|
|
|
|
|
|
if (!results.buttons.info.visible) issues.push('Info button not visible on mobile');
|
|
|
|
|
|
if (!results.buttons.backToTop.visible) issues.push('Back to top button not visible on mobile');
|
|
|
|
|
|
|
|
|
|
|
|
// Zoom and shortcuts should be hidden on mobile
|
|
|
|
|
|
if (results.buttons.zoomToggle.visible) issues.push('Zoom toggle should be hidden at mobile viewport');
|
|
|
|
|
|
if (results.buttons.shortcuts.visible) issues.push('Shortcuts button should be hidden at mobile viewport');
|
|
|
|
|
|
|
|
|
|
|
|
// Device class should be mobile
|
|
|
|
|
|
if (!results.deviceClass.isMobile) {
|
|
|
|
|
|
issues.push('Device class should be "is-mobile-device" at viewport ≤900px with mobile UA');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (issues.length > 0) {
|
|
|
|
|
|
console.log('\n❌ ISSUES FOUND:');
|
|
|
|
|
|
issues.forEach(issue => {
|
|
|
|
|
|
console.log(` - ${issue}`);
|
|
|
|
|
|
failures.push(`${viewport.name}: ${issue}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('\n✅ All checks passed');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Final summary
|
|
|
|
|
|
console.log('='.repeat(60));
|
|
|
|
|
|
console.log('FINAL SUMMARY\n');
|
|
|
|
|
|
|
|
|
|
|
|
if (failures.length === 0) {
|
|
|
|
|
|
console.log('✅ ALL TESTS PASSED\n');
|
|
|
|
|
|
console.log('Button colors are distinct:');
|
2025-11-25 06:49:24 +00:00
|
|
|
|
console.log(' 🔍 Zoom button: Purple (#5c59b6)');
|
2025-11-25 06:41:56 +00:00
|
|
|
|
console.log(' ℹ️ Info button: Blue (#3498db)');
|
|
|
|
|
|
console.log('\nButton visibility works correctly:');
|
|
|
|
|
|
console.log(' ✅ All buttons visible at desktop viewport (>900px)');
|
|
|
|
|
|
console.log(' ✅ Zoom/shortcuts hidden at mobile viewport (≤900px)');
|
|
|
|
|
|
console.log(' ✅ Device detection considers viewport width\n');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(`❌ ${failures.length} ISSUE(S) FOUND:\n`);
|
|
|
|
|
|
failures.forEach((failure, i) => {
|
|
|
|
|
|
console.log(`${i + 1}. ${failure}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await browser.close();
|
|
|
|
|
|
process.exit(failures.length === 0 ? 0 : 1);
|
|
|
|
|
|
})();
|