/** * Render smoke test: runs the canvas renderer with a stub 2D context and * one enemy of every kind on screen, so every drawEnemy code path * (including new enemy types) executes without throwing. * Run: npx tsx scripts/test-render-smoke.mts */ import { GameEngine } from '../src/game/engine.ts' import type { EnemyKind } from '../src/game/types.ts' const gradient = { addColorStop: () => undefined } const noop = () => undefined const makeCanvas = (): Record => ({ width: 960, height: 528, style: {}, getContext: () => makeCtx(), addEventListener: noop, removeEventListener: noop, }) const makeCtx = (): Record => ({ canvas: {}, createLinearGradient: () => gradient, createRadialGradient: () => gradient, measureText: () => ({ width: 10 }), addColorStop: noop, arc: noop, arcTo: noop, beginPath: noop, clearRect: noop, closePath: noop, drawImage: noop, ellipse: noop, fill: noop, fillRect: noop, fillText: noop, lineTo: noop, moveTo: noop, quadraticCurveTo: noop, bezierCurveTo: noop, rect: noop, restore: noop, rotate: noop, roundRect: noop, save: noop, scale: noop, setLineDash: noop, setTransform: noop, stroke: noop, strokeRect: noop, strokeText: noop, translate: noop, clip: noop, }) // the renderer creates an offscreen background canvas via document ;(globalThis as Record).document = { createElement: () => makeCanvas(), } const fakeCanvas = makeCanvas() as unknown as HTMLCanvasElement const eng = new GameEngine() eng.startGame('normal', 'meadow') ;(eng as unknown as { attach(c: HTMLCanvasElement): void }).attach(fakeCanvas) const kinds: EnemyKind[] = ['normal', 'fast', 'tank', 'flyer', 'boss', 'brute', 'phantom', 'scorpion', 'golem', 'dragon'] const engAny = eng as unknown as { spawnEnemy(k: EnemyKind): void } kinds.forEach((k) => engAny.spawnEnemy(k)) // spread enemies along the path so overlays, hp bars & effects also draw eng.enemies.forEach((e, i) => { e.x = 120 + i * 70 e.y = 100 + (i % 5) * 60 e.maxHp = e.hp = 50 // partially damaged -> hp bars render }) const renderer = (eng as unknown as { renderer: { render(e: unknown): void } }).renderer renderer.render(eng) // second frame with slow/dot effects active on all enemies eng.enemies.forEach((e) => { e.slowUntil = eng.time + 1 e.dotUntil = eng.time + 1 e.dotDps = 5 e.dotColor = '#0f0' e.flash = 0.5 }) renderer.render(eng) console.log('Render-Smoke: alle 10 Gegnertypen (2 Frames, mit Slow/DoT/Flash) fehlerfrei gezeichnet ✓') process.exit(0)