/** * 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 { if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } } async function setTheme(page: Page, theme: 'light' | 'dark'): Promise { // 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 { 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 { 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);