// Phase 29 unit coverage for the Phase 29 fog overlay's layered // overpaint logic. `fogPaintOps` lives in `src/map/render.ts` next // to its sole consumer (`RendererHandle.setVisibilityFog`) — the // renderer dispatches each op straight onto a Pixi `Graphics`, so // the unit test exercises the public ordering contract: a single // fog-coloured rectangle followed by one background-coloured // circle per visibility entry. The natural rendering order unions // overlapping circles for free, replacing the earlier `cut()` // implementation that produced disconnected arc segments. import { describe, expect, test } from "vitest"; import { FOG_COLOR, fogPaintOps } from "../src/map/render"; const BG_COLOR = 0x0a0e1a; const WORLD = { width: 1000, height: 800 }; describe("fogPaintOps", () => { test("empty input returns no ops", () => { expect(fogPaintOps(WORLD, [], FOG_COLOR, BG_COLOR)).toEqual([]); }); test("single circle emits fog rect + one bg circle in that order", () => { const ops = fogPaintOps( WORLD, [{ x: 100, y: 200, radius: 50 }], FOG_COLOR, BG_COLOR, ); expect(ops).toEqual([ { kind: "fillRect", x: 0, y: 0, width: 1000, height: 800, color: FOG_COLOR, alpha: 1, }, { kind: "fillCircle", x: 100, y: 200, radius: 50, color: BG_COLOR, alpha: 1, }, ]); }); test("multiple circles produce one fog rect followed by N bg circles", () => { const ops = fogPaintOps( WORLD, [ { x: 100, y: 100, radius: 50 }, { x: 300, y: 200, radius: 80 }, { x: 500, y: 600, radius: 30 }, ], FOG_COLOR, BG_COLOR, ); expect(ops.length).toBe(4); expect(ops[0].kind).toBe("fillRect"); for (let i = 1; i < ops.length; i++) { expect(ops[i].kind).toBe("fillCircle"); // Background-coloured circles paint on top of the fog rect. const op = ops[i]; if (op.kind === "fillCircle") { expect(op.color).toBe(BG_COLOR); expect(op.alpha).toBe(1); } } }); test("overlapping circles are emitted independently — the rendering order unions them", () => { // Two overlapping circles around adjacent LOCAL planets — the // op list keeps both circles. The renderer relies on the // overpaint to merge them visually; `cut()` (the previous // implementation) miscomputed the union. const ops = fogPaintOps( WORLD, [ { x: 200, y: 200, radius: 100 }, { x: 250, y: 200, radius: 100 }, ], FOG_COLOR, BG_COLOR, ); expect(ops.length).toBe(3); expect(ops[1]).toMatchObject({ x: 200, y: 200, radius: 100 }); expect(ops[2]).toMatchObject({ x: 250, y: 200, radius: 100 }); }); test("the fog rect always covers the full world rectangle", () => { const ops = fogPaintOps( { width: 3200, height: 1600 }, [{ x: 0, y: 0, radius: 10 }], FOG_COLOR, BG_COLOR, ); expect(ops[0]).toEqual({ kind: "fillRect", x: 0, y: 0, width: 3200, height: 1600, color: FOG_COLOR, alpha: 1, }); }); test("zero or negative world dimensions return no ops", () => { expect( fogPaintOps( { width: 0, height: 800 }, [{ x: 0, y: 0, radius: 10 }], FOG_COLOR, BG_COLOR, ), ).toEqual([]); expect( fogPaintOps( { width: 1000, height: -1 }, [{ x: 0, y: 0, radius: 10 }], FOG_COLOR, BG_COLOR, ), ).toEqual([]); }); });