import { describe, expect, it } from 'vitest'; import { TUTORIAL_FADE_MS, TUTORIAL_MS, tutorialAlpha } from '../src/scenes/door/tutorialFade'; // Regression guard for the door-tutorial fade. The original implementation was // `if (alpha > 0) tweens.add({ alpha: 0, duration: 800 })` inside update(): // each frame's new tween re-captured the still-positive alpha, so the guard // never closed and DoorScene accumulated one live 800ms tween per frame // (~1,100 by the end of a night). The fade is now this pure function; these // tests pin the property that killed the tween version — it must actually // reach 0 and stay there. describe('tutorialAlpha', () => { it('holds at full alpha through the tutorial window', () => { expect(tutorialAlpha(0)).toBe(1); expect(tutorialAlpha(TUTORIAL_MS)).toBe(1); }); it('fades linearly once the hold expires', () => { expect(tutorialAlpha(TUTORIAL_MS + TUTORIAL_FADE_MS / 2)).toBeCloseTo(0.5); expect(tutorialAlpha(TUTORIAL_MS + TUTORIAL_FADE_MS / 4)).toBeCloseTo(0.75); }); it('reaches exactly 0 at fade end and never goes negative after', () => { expect(tutorialAlpha(TUTORIAL_MS + TUTORIAL_FADE_MS)).toBe(0); expect(tutorialAlpha(TUTORIAL_MS + TUTORIAL_FADE_MS + 1)).toBe(0); // A whole night later it is still pinned at 0 — the condition the old // tween-spawning guard could never satisfy. expect(tutorialAlpha(TUTORIAL_MS + 4 * 60 * 60 * 1000)).toBe(0); }); it('is monotonically non-increasing frame over frame', () => { let prev = Infinity; for (let ms = 0; ms < TUTORIAL_MS + 2 * TUTORIAL_FADE_MS; ms += 16.67) { const a = tutorialAlpha(ms); expect(a).toBeLessThanOrEqual(prev); expect(a).toBeGreaterThanOrEqual(0); prev = a; } }); });