fix(02-06,G4): add wall band primitive in gate-renderer — close floating-gate gap
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>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user