import type { Workspace } from '../workspace/workspace.js'; declare global { interface WindowEventMap { 'test-result': CustomEvent; } interface Window { workspace: Workspace; } } export interface TestResult { success: boolean; messages: string[]; } const testResult: TestResult = { success: true, messages: [], }; interface TestCase { name: string; callback: () => Promise; } let testCases: TestCase[] = []; function reportTestResult() { const event = new CustomEvent('test-result', { detail: testResult, }); window.dispatchEvent(event); } function addMessage(message: string) { console.log(message); testResult.messages.push(message); } function reject(message: string) { testResult.success = false; addMessage(`❌ ${message}`); } export async function testSerial( name: string, callback: () => Promise ) { testCases.push({ name, callback }); } function wait(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } export async function runOnce() { await wait(50); // for correct event sequence for (const testCase of testCases) { const { name, callback } = testCase; const result = await callback(); if (result) addMessage(`✅ ${name}`); else reject(name); } reportTestResult(); testCases = []; } // XXX: workaround typing issue in blobs/__tests__/test-entry.ts export function assertExists(val: T | null | undefined): asserts val is T { if (val === null || val === undefined) { throw new Error('val does not exist'); } } export async function nextFrame() { return new Promise(resolve => requestAnimationFrame(resolve)); } // Test image source: https://en.wikipedia.org/wiki/Test_card export async function loadTestImageBlob(name: string): Promise { const resp = await fetch(`/${name}.png`); return await resp.blob(); } export async function loadImage(blobUrl: string) { const img = new Image(); img.src = blobUrl; return new Promise(resolve => { img.onload = () => resolve(img); }); } export function assertColor( img: HTMLImageElement, x: number, y: number, color: [number, number, number] ): boolean { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; ctx.drawImage(img, 0, 0); const data = ctx.getImageData(x, y, 1, 1).data; const r = data[0]; const g = data[1]; const b = data[2]; return r === color[0] && g === color[1] && b === color[2]; } // prevent redundant test runs export function disableButtonsAfterClick() { const buttons = document.querySelectorAll('button'); buttons.forEach(btn => { btn.addEventListener('click', () => { buttons.forEach(button => { button.disabled = true; }); }); }); }