diff --git a/src/client/match-view.ts b/src/client/match-view.ts index f2da776..c4bf977 100644 --- a/src/client/match-view.ts +++ b/src/client/match-view.ts @@ -179,8 +179,8 @@ export function createMatchView(container: HTMLElement, onAction: (action: Actio if (state.reveal && !state.aborted && (!state.finished || state.finished.reason === 'hit')) renderer.play(state.reveal, Date.now() + offset); else renderer.reset(); } - tick(); cutscene.update(state); + tick(); }, destroy() { submission++; clearInterval(interval); cutscene.destroy(); renderer.destroy(); sound.stop(); container.innerHTML = ''; current = undefined; }, }; } diff --git a/tests/equipment-art.test.ts b/tests/equipment-art.test.ts index 768677a..2a3bd21 100644 --- a/tests/equipment-art.test.ts +++ b/tests/equipment-art.test.ts @@ -363,6 +363,25 @@ test('result sound waits for all three cutscene stages', t => { } finally { view.destroy(); } }); +test('a delayed finishing snapshot plays the remaining cutscene before result audio', t => { + const { container, state } = domHarness(t, false); + const started = state.clockNow, cues: string[] = []; + t.mock.timers.enable({ apis: ['Date', 'setTimeout', 'setInterval'], now: started }); + t.mock.method(performance, 'now', () => Date.now() - started); + t.mock.method(sound, 'play', (id: string) => { if (!cues.includes(id)) cues.push(id); }); + state.matchId = 'audio-delayed-snapshot'; state.phase = 'finished'; + state.reveal = { matchId: state.matchId, turnId: 1, actions: ['blast', 'charge'], energyBefore: [1, 1], energy: [0, 1], hitIndex: 1, winnerIndex: 0, + revealedAt: started - 1500, revealEndsAt: started - 300 }; + state.finished = { matchId: state.matchId, winnerIndex: 0, reason: 'hit', stats: emptyPlayerStats(), revealEndsAt: started - 300, saved: true }; + const view = createMatchView(container as unknown as HTMLElement, async () => true, () => {}, () => {}); + try { + view.update(state); + assert.deepEqual(cues, ['cutscene2']); + t.mock.timers.tick(900); t.mock.timers.tick(1200); + assert.deepEqual(cues, ['cutscene2', 'cutscene3', 'win']); + } finally { view.destroy(); } +}); + for (const reducedMotion of [false, true]) test(`cutscene timer automatically opens results once (reduced motion: ${reducedMotion})`, t => { const { state } = domHarness(t, reducedMotion); t.mock.timers.enable({ apis: ['setTimeout'] });