/** * Generate test fixtures by taking screenshots of static HTML fixture pages. * This creates controlled test images for template matching verification. */ import puppeteer from 'puppeteer'; import * as path from 'path'; import * as fs from 'fs'; const FIXTURE_HTML_DIR = path.join(__dirname, '../resources/iracing-hosted-sessions'); const OUTPUT_DIR = path.join(__dirname, '../resources/test-fixtures'); async function generateFixtures(): Promise { console.log('šŸš€ Starting fixture generation...'); // Ensure output directory exists if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); console.log(`šŸ“ Created output directory: ${OUTPUT_DIR}`); } const browser = await puppeteer.launch({ headless: true, }); try { const page = await browser.newPage(); // Set viewport to match typical screen size (Retina 2x) await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 2, // Retina display }); // List of HTML fixtures to screenshot const fixtures = [ { file: '01-hosted-racing.html', name: 'hosted-racing' }, { file: '02-create-a-race.html', name: 'create-race' }, { file: '03-race-information.html', name: 'race-information' }, ]; for (const fixture of fixtures) { const htmlPath = path.join(FIXTURE_HTML_DIR, fixture.file); if (!fs.existsSync(htmlPath)) { console.log(`āš ļø Skipping ${fixture.file} - file not found`); continue; } console.log(`šŸ“ø Processing ${fixture.file}...`); // Load the HTML file await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0', timeout: 30000, }); // Take screenshot const outputPath = path.join(OUTPUT_DIR, `${fixture.name}-screenshot.png`); await page.screenshot({ path: outputPath, fullPage: false, // Just the viewport }); console.log(`āœ… Saved: ${outputPath}`); } console.log('\nšŸŽ‰ Fixture generation complete!'); console.log(`šŸ“ Screenshots saved to: ${OUTPUT_DIR}`); } finally { await browser.close(); } } // Also create a simple synthetic test pattern for algorithm verification async function createSyntheticTestPattern(): Promise { const sharp = (await import('sharp')).default; console.log('\nšŸ”§ Creating synthetic test patterns...'); // Ensure output directory exists if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); console.log(`šŸ“ Created output directory: ${OUTPUT_DIR}`); } // Create a simple test image (red square on white background) const width = 200; const height = 200; const channels = 4; // White background with a distinct blue rectangle in the center const imageData = Buffer.alloc(width * height * channels); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const idx = (y * width + x) * channels; // Create a blue rectangle from (50,50) to (150,150) if (x >= 50 && x < 150 && y >= 50 && y < 150) { imageData[idx] = 0; // R imageData[idx + 1] = 0; // G imageData[idx + 2] = 255; // B imageData[idx + 3] = 255; // A } else { // White background imageData[idx] = 255; // R imageData[idx + 1] = 255; // G imageData[idx + 2] = 255; // B imageData[idx + 3] = 255; // A } } } const testImagePath = path.join(OUTPUT_DIR, 'synthetic-test-image.png'); await sharp(imageData, { raw: { width, height, channels }, }) .png() .toFile(testImagePath); console.log(`āœ… Saved synthetic test image: ${testImagePath}`); // Create a template (the blue rectangle portion) const templateWidth = 100; const templateHeight = 100; const templateData = Buffer.alloc(templateWidth * templateHeight * channels); for (let y = 0; y < templateHeight; y++) { for (let x = 0; x < templateWidth; x++) { const idx = (y * templateWidth + x) * channels; // Blue fill templateData[idx] = 0; // R templateData[idx + 1] = 0; // G templateData[idx + 2] = 255; // B templateData[idx + 3] = 255; // A } } const templatePath = path.join(OUTPUT_DIR, 'synthetic-template.png'); await sharp(templateData, { raw: { width: templateWidth, height: templateHeight, channels }, }) .png() .toFile(templatePath); console.log(`āœ… Saved synthetic template: ${templatePath}`); // Create a more realistic pattern with gradients (better for NCC) const gradientWidth = 400; const gradientHeight = 300; const gradientData = Buffer.alloc(gradientWidth * gradientHeight * channels); for (let y = 0; y < gradientHeight; y++) { for (let x = 0; x < gradientWidth; x++) { const idx = (y * gradientWidth + x) * channels; // Create gradient background const bgGray = Math.floor((x / gradientWidth) * 128 + 64); // Add a distinct pattern in the center (button-like) if (x >= 150 && x < 250 && y >= 100 && y < 150) { // Darker rectangle with slight gradient const buttonGray = 50 + Math.floor((x - 150) / 100 * 30); gradientData[idx] = buttonGray; gradientData[idx + 1] = buttonGray; gradientData[idx + 2] = buttonGray + 20; // Slight blue tint gradientData[idx + 3] = 255; } else { gradientData[idx] = bgGray; gradientData[idx + 1] = bgGray; gradientData[idx + 2] = bgGray; gradientData[idx + 3] = 255; } } } const gradientImagePath = path.join(OUTPUT_DIR, 'gradient-test-image.png'); await sharp(gradientData, { raw: { width: gradientWidth, height: gradientHeight, channels }, }) .png() .toFile(gradientImagePath); console.log(`āœ… Saved gradient test image: ${gradientImagePath}`); // Extract the button region as a template const buttonTemplateWidth = 100; const buttonTemplateHeight = 50; const buttonTemplateData = Buffer.alloc(buttonTemplateWidth * buttonTemplateHeight * channels); for (let y = 0; y < buttonTemplateHeight; y++) { for (let x = 0; x < buttonTemplateWidth; x++) { const idx = (y * buttonTemplateWidth + x) * channels; const buttonGray = 50 + Math.floor(x / 100 * 30); buttonTemplateData[idx] = buttonGray; buttonTemplateData[idx + 1] = buttonGray; buttonTemplateData[idx + 2] = buttonGray + 20; buttonTemplateData[idx + 3] = 255; } } const buttonTemplatePath = path.join(OUTPUT_DIR, 'gradient-button-template.png'); await sharp(buttonTemplateData, { raw: { width: buttonTemplateWidth, height: buttonTemplateHeight, channels }, }) .png() .toFile(buttonTemplatePath); console.log(`āœ… Saved gradient button template: ${buttonTemplatePath}`); } // Run both async function main(): Promise { try { await createSyntheticTestPattern(); await generateFixtures(); } catch (error) { console.error('āŒ Error generating fixtures:', error); process.exit(1); } } main();