ab48c7ef30
Brightens OUTLINE_COLOR 0x4d4d52 → 0x5a5a60 and OUTLINE_HOVER
0x6e6e75 → 0x7a7a82, plus adds a subtle HOVER_FILL_ALPHA=0.06 fill bump
on the hit rectangle. Closes G3 first-impression UX gap from 2026-05-09
live UAT — the 4×4 grid now reads as legible interactive surfaces
against the #1a1a1a canvas background.
The hover state is pointer-driven steady-state (color + fill alpha
swap), not animation — reduced-motion-safe per Phase 8 ownership of
global motion preferences. Phase 3 watercolor deferral preserved: this
is color values + a fill alpha; no painted assets, no new sprites.
Constants OUTLINE_COLOR / OUTLINE_HOVER are exported so the test pins
the brightened values directly. drawTiles function signature unchanged —
Garden.ts continues to work without modification.
Vitest: 5 new cases green via Phaser-Scene-mock pattern (vi.mock('phaser')
short-circuits the Phaser 4 / happy-dom checkInverseAlpha boot crash;
the mocked scene's add.graphics / add.rectangle capture the call args).
npm run ci exits 0; 329/329 total green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
107 lines
4.0 KiB
TypeScript
107 lines
4.0 KiB
TypeScript
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<typeof drawTiles>[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<typeof vi.fn>; lineStyle: ReturnType<typeof vi.fn>; strokeRoundedRect: ReturnType<typeof vi.fn> };
|
|
rectangle: {
|
|
setInteractive: ReturnType<typeof vi.fn>;
|
|
on: ReturnType<typeof vi.fn>;
|
|
setData: ReturnType<typeof vi.fn>;
|
|
setFillStyle: ReturnType<typeof vi.fn>;
|
|
};
|
|
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<typeof vi.fn>)).toHaveBeenCalledTimes(16);
|
|
expect((scene.add.rectangle as ReturnType<typeof vi.fn>)).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
|
|
});
|
|
});
|