Files
frontend/scripts/capture-youtube-reference.ts
2026-01-29 21:05:41 -03:00

191 lines
5.0 KiB
TypeScript

/**
* YouTube Reference Screenshot Capture Script
*
* Captures reference screenshots from live YouTube for visual comparison testing.
* Run weekly to keep baselines current with YouTube's design changes.
*
* Usage: npx ts-node scripts/capture-youtube-reference.ts
*/
import { chromium, Browser, Page } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
const OUTPUT_DIR = path.join(__dirname, '../specs/002-youtube-design-preview/reference');
const VIEWPORT = { width: 1920, height: 1080 };
interface CaptureConfig {
name: string;
url: string;
selector: string;
waitFor?: string;
theme?: 'light' | 'dark';
}
const CAPTURES: CaptureConfig[] = [
{
name: 'youtube-desktop-homepage',
url: 'https://www.youtube.com',
selector: 'ytd-rich-item-renderer',
waitFor: 'ytd-rich-item-renderer',
theme: 'light',
},
{
name: 'youtube-search-results',
url: 'https://www.youtube.com/results?search_query=tutorial',
selector: 'ytd-video-renderer',
waitFor: 'ytd-video-renderer',
theme: 'light',
},
{
name: 'youtube-watch-sidebar',
url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
selector: 'ytd-compact-video-renderer, yt-lockup-view-model',
waitFor: 'ytd-watch-flexy',
theme: 'light',
},
{
name: 'youtube-trending',
url: 'https://www.youtube.com/feed/trending',
selector: 'ytd-video-renderer',
waitFor: 'ytd-video-renderer',
theme: 'light',
},
];
async function ensureOutputDir(): Promise<void> {
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
}
async function setTheme(page: Page, theme: 'light' | 'dark'): Promise<void> {
// YouTube uses document.documentElement attributes for theming
if (theme === 'dark') {
await page.evaluate(() => {
document.documentElement.setAttribute('dark', 'true');
});
}
// Light is default
}
async function captureScreenshot(
browser: Browser,
config: CaptureConfig
): Promise<string> {
const context = await browser.newContext({
viewport: VIEWPORT,
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
});
const page = await context.newPage();
try {
console.log(`Capturing: ${config.name}`);
console.log(` URL: ${config.url}`);
await page.goto(config.url, { waitUntil: 'networkidle' });
if (config.waitFor) {
await page.waitForSelector(config.waitFor, { timeout: 30000 });
}
if (config.theme) {
await setTheme(page, config.theme);
}
// Wait for images to load
await page.waitForTimeout(2000);
// Generate timestamp for versioning
const timestamp = new Date().toISOString().split('T')[0];
const filename = `${config.name}-${timestamp}.png`;
const filepath = path.join(OUTPUT_DIR, filename);
// Full page screenshot
await page.screenshot({
path: filepath,
fullPage: false,
});
console.log(` Saved: ${filename}`);
// Also capture just the video card element if possible
try {
const element = await page.locator(config.selector).first();
if (await element.isVisible()) {
const elementFilename = `${config.name}-element-${timestamp}.png`;
await element.screenshot({
path: path.join(OUTPUT_DIR, elementFilename),
});
console.log(` Saved element: ${elementFilename}`);
}
} catch (e) {
console.log(` Could not capture element screenshot`);
}
return filepath;
} finally {
await context.close();
}
}
async function main(): Promise<void> {
console.log('YouTube Reference Screenshot Capture');
console.log('====================================');
console.log(`Output directory: ${OUTPUT_DIR}`);
console.log(`Viewport: ${VIEWPORT.width}x${VIEWPORT.height}`);
console.log('');
await ensureOutputDir();
const browser = await chromium.launch({
headless: true,
});
try {
for (const config of CAPTURES) {
try {
await captureScreenshot(browser, config);
} catch (error) {
console.error(`Error capturing ${config.name}:`, error);
}
}
// Also capture dark mode versions
console.log('\nCapturing dark mode versions...');
for (const config of CAPTURES.slice(0, 2)) {
try {
await captureScreenshot(browser, {
...config,
name: `${config.name}-dark`,
theme: 'dark',
});
} catch (error) {
console.error(`Error capturing dark mode ${config.name}:`, error);
}
}
} finally {
await browser.close();
}
console.log('\nCapture complete!');
console.log(`Screenshots saved to: ${OUTPUT_DIR}`);
// Generate manifest
const manifest = {
capturedAt: new Date().toISOString(),
viewport: VIEWPORT,
screenshots: fs.readdirSync(OUTPUT_DIR).filter((f) => f.endsWith('.png')),
};
fs.writeFileSync(
path.join(OUTPUT_DIR, 'manifest.json'),
JSON.stringify(manifest, null, 2)
);
console.log('Manifest saved: manifest.json');
}
main().catch(console.error);