feat(control-station): add atomic recorded-session playback
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { after, before, test } from "node:test";
|
||||
|
||||
import { createServer } from "vite";
|
||||
|
||||
let server;
|
||||
let attemptRecordedAutoplay;
|
||||
let createLatestAnimationFrameEmitter;
|
||||
|
||||
before(async () => {
|
||||
server = await createServer({
|
||||
appType: "custom",
|
||||
logLevel: "silent",
|
||||
server: { middlewareMode: true },
|
||||
});
|
||||
({ attemptRecordedAutoplay, createLatestAnimationFrameEmitter } =
|
||||
await server.ssrLoadModule("/src/components/RerunViewport.tsx"));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await server?.close();
|
||||
});
|
||||
|
||||
test("recorded autoplay reports success only after seek and play both succeed", () => {
|
||||
let seekAttempts = 0;
|
||||
let playAttempts = 0;
|
||||
const seekToStart = () => {
|
||||
seekAttempts += 1;
|
||||
};
|
||||
const startPlaying = () => {
|
||||
playAttempts += 1;
|
||||
if (playAttempts === 1) throw new Error("receiver is not ready yet");
|
||||
};
|
||||
|
||||
assert.equal(attemptRecordedAutoplay(seekToStart, startPlaying), false);
|
||||
assert.equal(attemptRecordedAutoplay(seekToStart, startPlaying), true);
|
||||
assert.equal(seekAttempts, 2);
|
||||
assert.equal(playAttempts, 2);
|
||||
});
|
||||
|
||||
test("recorded autoplay does not try play when the seek itself fails", () => {
|
||||
let playAttempts = 0;
|
||||
|
||||
assert.equal(attemptRecordedAutoplay(
|
||||
() => {
|
||||
throw new Error("timeline is not ready yet");
|
||||
},
|
||||
() => {
|
||||
playAttempts += 1;
|
||||
},
|
||||
), false);
|
||||
assert.equal(playAttempts, 0);
|
||||
});
|
||||
|
||||
test("time updates coalesce to the latest value once per animation frame", () => {
|
||||
let nextHandle = 1;
|
||||
const frames = new Map();
|
||||
const cancelled = [];
|
||||
const emitted = [];
|
||||
const emitter = createLatestAnimationFrameEmitter({
|
||||
emit(value) {
|
||||
emitted.push(value);
|
||||
},
|
||||
requestFrame(callback) {
|
||||
const handle = nextHandle;
|
||||
nextHandle += 1;
|
||||
frames.set(handle, callback);
|
||||
return handle;
|
||||
},
|
||||
cancelFrame(handle) {
|
||||
cancelled.push(handle);
|
||||
frames.delete(handle);
|
||||
},
|
||||
});
|
||||
|
||||
emitter.push(1);
|
||||
emitter.push(2);
|
||||
emitter.push(3);
|
||||
assert.equal(frames.size, 1);
|
||||
assert.deepEqual(emitted, []);
|
||||
|
||||
const firstFrame = frames.get(1);
|
||||
frames.delete(1);
|
||||
firstFrame(0);
|
||||
assert.deepEqual(emitted, [3]);
|
||||
|
||||
emitter.push(4);
|
||||
assert.equal(frames.size, 1);
|
||||
emitter.cancel();
|
||||
assert.deepEqual(cancelled, [2]);
|
||||
assert.deepEqual(emitted, [3]);
|
||||
|
||||
emitter.push(5);
|
||||
assert.equal(frames.size, 0);
|
||||
assert.deepEqual(emitted, [3]);
|
||||
});
|
||||
Reference in New Issue
Block a user