Files
cv-site/tests/visual-comparison.spec.js
T

354 lines
12 KiB
JavaScript
Raw Normal View History

/**
* Visual Comparison Test Suite
2025-11-06 09:19:44 +00:00
* Compares new Go + HTMX CV (localhost:1999) vs old React CV (localhost:3000)
*/
const { test, expect } = require('@playwright/test');
const fs = require('fs');
const path = require('path');
const OLD_SITE = 'http://localhost:3000';
const NEW_SITE = 'http://localhost:1999';
const SCREENSHOTS_DIR = path.join(__dirname, 'screenshots');
// Ensure screenshots directory exists
if (!fs.existsSync(SCREENSHOTS_DIR)) {
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
}
test.describe('Visual Comparison: New vs Old CV', () => {
test('Full page screenshots', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
// Load both sites
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
// Take full page screenshots
await pageOld.screenshot({
path: path.join(SCREENSHOTS_DIR, 'old-fullpage.png'),
fullPage: true
});
await pageNew.screenshot({
path: path.join(SCREENSHOTS_DIR, 'new-fullpage.png'),
fullPage: true
});
console.log('✓ Full page screenshots saved');
await contextOld.close();
await contextNew.close();
});
test('Header section comparison', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
// Screenshot header sections
const headerOld = await pageOld.locator('.cv-title-badges-header, [class*="header"]').first();
const headerNew = await pageNew.locator('.cv-title-badges-header').first();
if (await headerOld.count() > 0) {
await headerOld.screenshot({ path: path.join(SCREENSHOTS_DIR, 'old-header.png') });
}
if (await headerNew.count() > 0) {
await headerNew.screenshot({ path: path.join(SCREENSHOTS_DIR, 'new-header.png') });
}
console.log('✓ Header screenshots saved');
await contextOld.close();
await contextNew.close();
});
test('Badge measurements comparison', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
// Measure badge elements
const measurements = {
old: {},
new: {}
};
// New site badge measurements
const badgeNew = pageNew.locator('.title-badge').first();
if (await badgeNew.count() > 0) {
const box = await badgeNew.boundingBox();
const styles = await badgeNew.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
height: computed.height,
padding: computed.padding,
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
color: computed.color,
backgroundColor: computed.backgroundColor,
borderRadius: computed.borderRadius,
display: computed.display,
alignItems: computed.alignItems
};
});
measurements.new.badge = { box, styles };
}
// Old site badge measurements
const badgeOld = pageOld.locator('.title-badge, [class*="badge"]').first();
if (await badgeOld.count() > 0) {
const box = await badgeOld.boundingBox();
const styles = await badgeOld.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
height: computed.height,
padding: computed.padding,
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
color: computed.color,
backgroundColor: computed.backgroundColor,
borderRadius: computed.borderRadius,
display: computed.display,
alignItems: computed.alignItems
};
});
measurements.old.badge = { box, styles };
}
// Save measurements
fs.writeFileSync(
path.join(SCREENSHOTS_DIR, 'badge-measurements.json'),
JSON.stringify(measurements, null, 2)
);
console.log('✓ Badge measurements saved');
console.log('\nBadge Comparison:');
console.log('OLD:', JSON.stringify(measurements.old.badge?.styles, null, 2));
console.log('NEW:', JSON.stringify(measurements.new.badge?.styles, null, 2));
await contextOld.close();
await contextNew.close();
});
test('Typography comparison', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
const typography = {
old: {},
new: {}
};
// Selectors to compare
const selectors = {
name: '.cv-name',
sidebarTitle: '.sidebar-title',
sectionTitle: '.section-title',
badge: '.title-badge'
};
// Measure new site typography
for (const [key, selector] of Object.entries(selectors)) {
const element = pageNew.locator(selector).first();
if (await element.count() > 0) {
typography.new[key] = await element.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
fontFamily: computed.fontFamily,
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
lineHeight: computed.lineHeight,
color: computed.color,
letterSpacing: computed.letterSpacing
};
});
}
}
// Measure old site typography
for (const [key, selector] of Object.entries(selectors)) {
const element = pageOld.locator(selector).first();
if (await element.count() > 0) {
typography.old[key] = await element.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
fontFamily: computed.fontFamily,
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
lineHeight: computed.lineHeight,
color: computed.color,
letterSpacing: computed.letterSpacing
};
});
}
}
// Save typography comparison
fs.writeFileSync(
path.join(SCREENSHOTS_DIR, 'typography-comparison.json'),
JSON.stringify(typography, null, 2)
);
console.log('✓ Typography comparison saved');
console.log('\nTypography Comparison:');
console.log('OLD:', JSON.stringify(typography.old, null, 2));
console.log('NEW:', JSON.stringify(typography.new, null, 2));
await contextOld.close();
await contextNew.close();
});
test('Sidebar comparison', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
// Screenshot sidebars
const sidebarOld = pageOld.locator('.cv-sidebar, [class*="sidebar"]').first();
const sidebarNew = pageNew.locator('.cv-sidebar').first();
if (await sidebarOld.count() > 0) {
await sidebarOld.screenshot({ path: path.join(SCREENSHOTS_DIR, 'old-sidebar.png') });
}
if (await sidebarNew.count() > 0) {
await sidebarNew.screenshot({ path: path.join(SCREENSHOTS_DIR, 'new-sidebar.png') });
}
// Measure sidebar styles
const sidebarComparison = {
old: {},
new: {}
};
if (await sidebarNew.count() > 0) {
sidebarComparison.new = await sidebarNew.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
backgroundColor: computed.backgroundColor,
padding: computed.padding,
width: computed.width,
minWidth: computed.minWidth
};
});
}
if (await sidebarOld.count() > 0) {
sidebarComparison.old = await sidebarOld.evaluate(el => {
const computed = window.getComputedStyle(el);
return {
backgroundColor: computed.backgroundColor,
padding: computed.padding,
width: computed.width,
minWidth: computed.minWidth
};
});
}
fs.writeFileSync(
path.join(SCREENSHOTS_DIR, 'sidebar-comparison.json'),
JSON.stringify(sidebarComparison, null, 2)
);
console.log('✓ Sidebar comparison saved');
console.log('\nSidebar Comparison:');
console.log('OLD:', JSON.stringify(sidebarComparison.old, null, 2));
console.log('NEW:', JSON.stringify(sidebarComparison.new, null, 2));
await contextOld.close();
await contextNew.close();
});
test('Critical elements style extraction', async ({ browser }) => {
const contextOld = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const contextNew = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
const pageOld = await contextOld.newPage();
const pageNew = await contextNew.newPage();
await pageOld.goto(OLD_SITE, { waitUntil: 'networkidle' });
await pageNew.goto(NEW_SITE, { waitUntil: 'networkidle' });
const criticalElements = [
'.cv-title-badges-header',
'.title-badge',
'.badge-separator',
'.sidebar-title',
'.section-title',
'.cv-name'
];
const styleComparison = {
old: {},
new: {}
};
// Extract from new site
for (const selector of criticalElements) {
const element = pageNew.locator(selector).first();
if (await element.count() > 0) {
styleComparison.new[selector] = await element.evaluate(el => {
const computed = window.getComputedStyle(el);
const styles = {};
for (let i = 0; i < computed.length; i++) {
const prop = computed[i];
styles[prop] = computed.getPropertyValue(prop);
}
return styles;
});
}
}
// Extract from old site
for (const selector of criticalElements) {
const element = pageOld.locator(selector).first();
if (await element.count() > 0) {
styleComparison.old[selector] = await element.evaluate(el => {
const computed = window.getComputedStyle(el);
const styles = {};
for (let i = 0; i < computed.length; i++) {
const prop = computed[i];
styles[prop] = computed.getPropertyValue(prop);
}
return styles;
});
}
}
fs.writeFileSync(
path.join(SCREENSHOTS_DIR, 'critical-elements-full-styles.json'),
JSON.stringify(styleComparison, null, 2)
);
console.log('✓ Critical elements styles extracted');
await contextOld.close();
await contextNew.close();
});
});