// The replay viewer (replay.html) on every track, judged against the pure model. The recording is // fixtures/replay-sample.json, which the pages fetch and the oracle reads from disk. import { test, expect, TRACKS } from '../lib/app.mjs' import { FRAME } from '../lib/clock.mjs' import { recording, REPLAY_MS } from '../lib/oracle.mjs' const LAST = recording.actions.length const lastScene = (shapes) => shapes.slice(Math.max(0, shapes.findLastIndex((s) => s.tag === 'Rect' && s.x === 0 && s.y === 0 && s.w === 400))) const stepOf = (shapes) => { const t = lastScene(shapes).find((s) => s.tag === 'Text' && s.text.startsWith('step ')) return Number(/^step (\d+) \//.exec(t.text)[1]) } const textsOf = (shapes) => lastScene(shapes).filter((s) => s.tag === 'Text').map((s) => s.text) // Multiples of `ms` in (a, b]: how many beats of a `ms` interval fall between two frames at times a and b. const beats = (ms, a, b) => Math.floor(b / ms) - Math.floor(a / ms) // Time of the last animation frame so far, in the page's own clock (ms since its first frame). const lastFrameTime = (a) => Math.floor(a.clock.now / FRAME) * FRAME - a.clock.t0 for (const track of TRACKS) { test.describe(`${track}: replay`, () => { test('Right, Left, Home and End show the model\'s frames', async ({ app }) => { const a = await (await app(track, 'replay')).start() expect(stepOf(await a.expectInSync())).toBe(0) const seen = [] for (const key of ['ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowLeft', 'End', 'ArrowRight', 'ArrowLeft', 'Home', 'ArrowLeft']) { await a.press(key) seen.push(stepOf(await a.expectInSync(`after ${key}`))) await a.advance(50) } await a.expectInSync() expect(seen).toEqual([1, 2, 3, 2, LAST, LAST, LAST - 1, 0, 0]) expect(textsOf(a.lastPageFrame)).toContain(`step 0 / ${LAST}`) }) test('autoplay advances one frame per 100 ms of fake time and stops on the last frame', async ({ app }) => { const a = await (await app(track, 'replay')).start() await a.advance(130) await a.press(' ') const pressedAt = lastFrameTime(a) expect(textsOf(await a.expectInSync('play'))).toContain('Space: pause') // For a while, check every 100 ms: the step shown is the number of 100 ms beats since the key. for (let i = 0; i < 12; i++) { await a.advance(REPLAY_MS) expect(stepOf(await a.expectInSync(`playing, ${i}`))).toBe(beats(REPLAY_MS, pressedAt, lastFrameTime(a))) } // Then run to the end and beyond, a second at a time; it stops on the last frame. while (a.state.playing) { await a.advance(1000) await a.expectInSync('playing on') } await a.advance(1000) const end = await a.expectInSync('stopped') expect(stepOf(end)).toBe(LAST) expect(textsOf(end)).toContain('Left/Right: step Home/End: jump Space: play') expect(beats(REPLAY_MS, pressedAt, lastFrameTime(a))).toBeGreaterThan(LAST) }) }) }