import { describe, it, expect, vi } from 'vitest'; /** * G3 (gap closure 02-06) — assert tile-renderer uses the brightened * outline colors and the hover fill bump. * * Phaser cannot import under happy-dom — its boot probe `checkInverseAlpha` * calls `canvas.getContext('2d')` which returns null and the call into * `context.fillStyle = '...'` then crashes (Plan 02-02 SUMMARY documents * this). We mock the `phaser` module entirely so importing tile-renderer.ts * does not pull the real Phaser bundle. * * The rest of the test mocks the Scene API surface that drawTiles uses. */ vi.mock('phaser', () => ({ default: {}, // No named exports needed — tile-renderer uses only Phaser types and // the runtime call surface comes from the mocked scene argument below. })); const { drawTiles, OUTLINE_COLOR, OUTLINE_HOVER } = await import('./tile-renderer'); type Scene = Parameters[0]; describe('tile-renderer (Plan 02-06 G3 closure)', () => { it('exports OUTLINE_COLOR=0x5a5a60 (brightened from 0x4d4d52)', () => { expect(OUTLINE_COLOR).toBe(0x5a5a60); }); it('exports OUTLINE_HOVER=0x7a7a82 (brightened from 0x6e6e75)', () => { expect(OUTLINE_HOVER).toBe(0x7a7a82); }); function makeMocks(): { graphics: { clear: ReturnType; lineStyle: ReturnType; strokeRoundedRect: ReturnType }; rectangle: { setInteractive: ReturnType; on: ReturnType; setData: ReturnType; setFillStyle: ReturnType; }; scene: Scene; pointerOverHandlers: Array<() => void>; } { const graphics = { clear: vi.fn(), lineStyle: vi.fn(), strokeRoundedRect: vi.fn(), }; const pointerOverHandlers: Array<() => void> = []; const rectangle = { setInteractive: vi.fn().mockReturnThis(), on: vi.fn().mockReturnThis(), setData: vi.fn().mockReturnThis(), setFillStyle: vi.fn().mockReturnThis(), }; rectangle.on.mockImplementation((evt: string, fn: () => void) => { if (evt === 'pointerover') pointerOverHandlers.push(fn); return rectangle; }); const scene = { add: { graphics: vi.fn(() => graphics), rectangle: vi.fn(() => rectangle), }, } as unknown as Scene; return { graphics, rectangle, scene, pointerOverHandlers }; } it('drawTiles creates 16 tile groups with outline graphics + hit rectangles', () => { const { scene } = makeMocks(); const tiles = drawTiles(scene); expect(tiles).toHaveLength(16); expect((scene.add.graphics as ReturnType)).toHaveBeenCalledTimes(16); expect((scene.add.rectangle as ReturnType)).toHaveBeenCalledTimes(16); }); it('initial draw uses OUTLINE_COLOR (resting state)', () => { const { graphics, scene } = makeMocks(); drawTiles(scene); const calls = graphics.lineStyle.mock.calls; expect(calls.length).toBeGreaterThan(0); // Every initial call uses OUTLINE_COLOR; assert the first. expect(calls[0][1]).toBe(OUTLINE_COLOR); }); it('pointerover handler swaps to OUTLINE_HOVER and adds fill alpha bump', () => { const { graphics, rectangle, scene, pointerOverHandlers } = makeMocks(); drawTiles(scene); expect(pointerOverHandlers.length).toBeGreaterThan(0); // Fire the first tile's pointerover handler. pointerOverHandlers[0]!(); // After pointerover, the most recent lineStyle call uses OUTLINE_HOVER. const lineStyleCalls = graphics.lineStyle.mock.calls; const lastLineCall = lineStyleCalls[lineStyleCalls.length - 1]; expect(lastLineCall[1]).toBe(OUTLINE_HOVER); // setFillStyle was called with the hover alpha bump (>0, ≤0.1). const fillCalls = rectangle.setFillStyle.mock.calls; const fillBumpCall = fillCalls.find((c) => c[1] && c[1] > 0); expect(fillBumpCall).toBeDefined(); expect(fillBumpCall![1]).toBeGreaterThan(0); expect(fillBumpCall![1]).toBeLessThanOrEqual(0.1); // sanity: subtle bump, not a flash }); });