Files
cv-site/tests/compare-rendered.js
T
juanatsap 2c372eee49 feat: add social links to footer and optional company logo toggle
**Social Links in Footer (Page 2):**
- Replace address/phone with LinkedIn, GitHub, and Behance links
- Maintain email@ link
- All links are clickable and open in new tabs
- Footer displays social media profiles prominently

**Company Logo Toggle Feature:**
- Add "Show logos" toggle switch in top action bar
- Toggle displays company logos (48x48px) to the left of each experience item
- LinkedIn-style layout when logos are shown
- Logos hidden by default, optional display via toggle
- Graceful fallback: missing logos don't break layout (onerror handler)
- Logos directory created at static/images/logos/ with README

**Technical Implementation:**
- New CSS file: logo-toggle.css for toggle switch and logo layout
- JavaScript: toggleLogos() function for show/hide functionality
- Template updates: experience items now support flex layout with logos
- Action bar grid updated to accommodate 4 columns
- Logo display uses CSS class `.show-logos` on `.cv-paper`
- Print CSS: logos hidden in PDF exports by default

**User Experience:**
- Clean toggle switch UI with smooth animations
- Mobile responsive design
- Accessibility: proper ARIA labels for toggle
- Optional feature that doesn't clutter default view
- Professional LinkedIn-style appearance when enabled

Logos can be added to static/images/logos/ directory using filenames
from the companyLogo field in CV JSON data.
2025-11-05 12:15:43 +00:00

193 lines
6.0 KiB
JavaScript

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
async function compareRendered() {
const browser = await chromium.launch({ headless: true });
console.log('\n=== COMPARING RENDERED SITES ===\n');
// OLD SITE
const pageOld = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
console.log('Loading OLD site (React)...');
await pageOld.goto('http://localhost:3000', { waitUntil: 'networkidle', timeout: 30000 });
// Wait for React to render
await pageOld.waitForTimeout(2000);
// Take screenshot
await pageOld.screenshot({
path: './tests/screenshots/old-full-rendered.png',
fullPage: true
});
// Get actual rendered content
const oldContent = await pageOld.evaluate(() => {
const app = document.getElementById('app') || document.body;
return {
innerHTML: app.innerHTML.substring(0, 2000),
hasContent: app.innerHTML.length > 100,
classes: Array.from(document.querySelectorAll('[class]')).map(el => el.className).filter(c => c).slice(0, 50)
};
});
console.log('OLD site content loaded:', oldContent.hasContent);
console.log('OLD site classes found:', oldContent.classes.length);
if (oldContent.classes.length > 0) {
console.log('Sample classes:', oldContent.classes.slice(0, 10));
}
// NEW SITE
const pageNew = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
console.log('\nLoading NEW site (Go+HTMX)...');
await pageNew.goto('http://localhost:1999', { waitUntil: 'networkidle' });
await pageNew.screenshot({
path: './tests/screenshots/new-full-rendered.png',
fullPage: true
});
// SIDE-BY-SIDE COMPARISON
console.log('\n=== HEADER BADGES COMPARISON ===\n');
// Try multiple selectors for old site
const oldBadgeSelectors = [
'[class*="badge"]',
'[class*="title"]',
'div[class*="cv"]',
'.badge',
'.title-badge'
];
let oldBadges = null;
for (const selector of oldBadgeSelectors) {
try {
const count = await pageOld.locator(selector).count();
if (count > 0) {
console.log(`Found ${count} elements with selector: ${selector}`);
oldBadges = await pageOld.$$eval(selector, elements =>
elements.slice(0, 5).map(el => {
const computed = window.getComputedStyle(el);
return {
tag: el.tagName,
class: el.className,
text: el.textContent?.substring(0, 50),
styles: {
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
color: computed.color,
backgroundColor: computed.backgroundColor,
padding: computed.padding,
height: computed.height
}
};
})
);
break;
}
} catch (e) {
// Try next selector
}
}
const newBadges = await pageNew.$$eval('.title-badge', elements =>
elements.slice(0, 5).map(el => {
const computed = window.getComputedStyle(el);
return {
tag: el.tagName,
class: el.className,
text: el.textContent?.trim(),
styles: {
fontSize: computed.fontSize,
fontWeight: computed.fontWeight,
color: computed.color,
backgroundColor: computed.backgroundColor,
padding: computed.padding,
height: computed.height
}
};
})
);
console.log('\nOLD site badges:');
console.log(JSON.stringify(oldBadges, null, 2));
console.log('\nNEW site badges:');
console.log(JSON.stringify(newBadges, null, 2));
// VISUAL PIXEL COMPARISON
console.log('\n=== VISUAL COMPARISON ===\n');
// Get dimensions
const oldDimensions = await pageOld.evaluate(() => ({
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight
}));
const newDimensions = await pageNew.evaluate(() => ({
width: document.documentElement.scrollWidth,
height: document.documentElement.scrollHeight
}));
console.log('OLD site dimensions:', oldDimensions);
console.log('NEW site dimensions:', newDimensions);
// Screenshot specific sections
try {
// Header comparison
const oldHeader = pageOld.locator('header, [class*="header"], div').first();
const newHeader = pageNew.locator('.cv-title-badges-header').first();
if (await oldHeader.count() > 0) {
await oldHeader.screenshot({ path: './tests/screenshots/old-header-section.png' });
}
await newHeader.screenshot({ path: './tests/screenshots/new-header-section.png' });
// Sidebar comparison
const oldSidebar = pageOld.locator('[class*="sidebar"], aside').first();
const newSidebar = pageNew.locator('.cv-sidebar').first();
if (await oldSidebar.count() > 0) {
await oldSidebar.screenshot({ path: './tests/screenshots/old-sidebar-section.png' });
}
await newSidebar.screenshot({ path: './tests/screenshots/new-sidebar-section.png' });
} catch (e) {
console.log('Error capturing sections:', e.message);
}
// CREATE COMPARISON REPORT
const report = {
timestamp: new Date().toISOString(),
oldSite: {
url: 'http://localhost:3000',
hasContent: oldContent.hasContent,
classesFound: oldContent.classes.length,
dimensions: oldDimensions,
badges: oldBadges
},
newSite: {
url: 'http://localhost:1999',
dimensions: newDimensions,
badges: newBadges
},
comparison: {
dimensionsMatch: Math.abs(oldDimensions.width - newDimensions.width) < 50 &&
Math.abs(oldDimensions.height - newDimensions.height) < 50,
pixelPerfect: null // To be determined by visual inspection
}
};
fs.writeFileSync(
'./tests/screenshots/comparison-report.json',
JSON.stringify(report, null, 2)
);
console.log('\n✓ Comparison complete!');
console.log('✓ Screenshots saved to tests/screenshots/');
console.log('✓ Report saved to comparison-report.json');
await browser.close();
}
compareRendered().catch(console.error);