88adc4f623
Adds a 4th Phaser primitive to drawGate: a faint vertical wall band at the gate's column (x=880) spanning the full 768px canvas height with alpha=0.18 (mid of the 0.15-0.20 fix_shape range). Closes G4 first-impression UX gap from 2026-05-09 live UAT — the gate now reads as part of a wall rather than a floating gray rectangle, honoring the bible's "walled garden" framing. Z-order: wall (behind) → body → glow → hit. The wall band shares the GATE_COLOR hue (0x6e6e75); the low alpha is what visually distinguishes the wall (structural context) from the body (the load-bearing element). WALL_BAND_WIDTH = GATE_HIT_W * 0.55 = 44px — narrower than the gate so the gate body still reads as the focal point. The wall does NOT pulse; updateGateIndicator continues to manage only the glow's alpha tween. Phase 3 watercolor deferral preserved: this is a single Phaser primitive, no painted texture, no animation. GateGameObjects interface gains a `wall` field — additive, so the existing Garden.ts consumer continues to work unchanged (it stores the whole returned object in this.gate). Vitest: 4 new cases green via Phaser-Scene-mock pattern (constants in range, first rectangle call has wall geometry, 4 total rectangles, gate exposes the wall handle). npm run ci exits 0; 333/333 total green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
|
|
/**
|
|
* G4 (gap closure 02-06) — assert gate-renderer adds a faint vertical
|
|
* wall band primitive at the gate's column.
|
|
*
|
|
* Phaser-Scene-mock pattern from Plan 02-06 Task 3 (avoids the Phaser 4 /
|
|
* happy-dom canvas.getContext incompatibility per Plan 02-02 SUMMARY).
|
|
* vi.mock('phaser') short-circuits the Phaser bundle import so this test
|
|
* can exercise drawGate's call surface in isolation; BlendModes.ADD is
|
|
* mocked as a sentinel value.
|
|
*/
|
|
vi.mock('phaser', () => ({
|
|
default: { BlendModes: { ADD: 1 } },
|
|
BlendModes: { ADD: 1 },
|
|
}));
|
|
|
|
const {
|
|
drawGate,
|
|
WALL_BAND_X,
|
|
WALL_BAND_WIDTH,
|
|
WALL_BAND_HEIGHT,
|
|
WALL_BAND_ALPHA,
|
|
WALL_BAND_COLOR,
|
|
} = await import('./gate-renderer');
|
|
type Scene = Parameters<typeof drawGate>[0];
|
|
|
|
function makeRectangleMock(): {
|
|
setInteractive: ReturnType<typeof vi.fn>;
|
|
on: ReturnType<typeof vi.fn>;
|
|
setData: ReturnType<typeof vi.fn>;
|
|
setBlendMode: ReturnType<typeof vi.fn>;
|
|
setAlpha: ReturnType<typeof vi.fn>;
|
|
} {
|
|
const r = {
|
|
setInteractive: vi.fn().mockReturnThis(),
|
|
on: vi.fn().mockReturnThis(),
|
|
setData: vi.fn().mockReturnThis(),
|
|
setBlendMode: vi.fn().mockReturnThis(),
|
|
setAlpha: vi.fn().mockReturnThis(),
|
|
};
|
|
return r;
|
|
}
|
|
|
|
function makeScene(): Scene {
|
|
const rectangle = makeRectangleMock();
|
|
return {
|
|
add: {
|
|
rectangle: vi.fn(() => rectangle),
|
|
},
|
|
tweens: { add: vi.fn() },
|
|
} as unknown as Scene;
|
|
}
|
|
|
|
describe('gate-renderer (Plan 02-06 G4 closure)', () => {
|
|
it('exports the wall band geometry constants with expected values', () => {
|
|
expect(WALL_BAND_X).toBe(880); // matches GATE_X
|
|
expect(WALL_BAND_HEIGHT).toBe(768); // matches Phaser canvas height
|
|
expect(WALL_BAND_ALPHA).toBeGreaterThanOrEqual(0.15); // fix_shape range
|
|
expect(WALL_BAND_ALPHA).toBeLessThanOrEqual(0.2); // fix_shape range
|
|
expect(WALL_BAND_COLOR).toBe(0x6e6e75); // same hue as GATE_COLOR
|
|
expect(WALL_BAND_WIDTH).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('drawGate adds the wall primitive at the gate column with low alpha', () => {
|
|
const scene = makeScene();
|
|
drawGate(scene);
|
|
|
|
// First scene.add.rectangle call is the wall band (per drawGate
|
|
// implementation order — wall is drawn behind everything else).
|
|
const firstCall = (scene.add.rectangle as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
// Signature: (x, y, width, height, fillColor, fillAlpha)
|
|
expect(firstCall[0]).toBe(WALL_BAND_X); // x
|
|
expect(firstCall[1]).toBe(WALL_BAND_HEIGHT / 2); // y-centered
|
|
expect(firstCall[2]).toBe(WALL_BAND_WIDTH); // width
|
|
expect(firstCall[3]).toBe(WALL_BAND_HEIGHT); // height = canvas height (full vertical span)
|
|
expect(firstCall[4]).toBe(WALL_BAND_COLOR); // color
|
|
expect(firstCall[5]).toBe(WALL_BAND_ALPHA); // alpha — low (0.18)
|
|
});
|
|
|
|
it('drawGate creates 4 rectangles total (wall + body + glow + hit)', () => {
|
|
const scene = makeScene();
|
|
drawGate(scene);
|
|
expect(scene.add.rectangle as ReturnType<typeof vi.fn>).toHaveBeenCalledTimes(4);
|
|
});
|
|
|
|
it('returned GateGameObjects exposes the wall handle', () => {
|
|
const scene = makeScene();
|
|
const gate = drawGate(scene);
|
|
expect(gate.wall).toBeDefined();
|
|
expect(gate.body).toBeDefined();
|
|
expect(gate.glow).toBeDefined();
|
|
expect(gate.hit).toBeDefined();
|
|
expect(gate.glowTween).toBeNull();
|
|
});
|
|
});
|