mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
86 lines
2.5 KiB
JavaScript
86 lines
2.5 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createCameraMotion } from './cameraMotion.js';
|
|
const move = {
|
|
durationSec: 2,
|
|
easing: 'linear',
|
|
from: { lat: 0, lon: 0, alt: 100, heading: 0, pitch: 0, roll: 0 },
|
|
to: { lat: 2, lon: 2, alt: 200, heading: 0, pitch: 0, roll: 0 },
|
|
};
|
|
function harness(apply) {
|
|
let time = 0;
|
|
let next = 0;
|
|
const frames = new Map();
|
|
const poses = [];
|
|
const owner = createCameraMotion({
|
|
now: () => time,
|
|
requestFrame: (cb) => {
|
|
const id = ++next;
|
|
frames.set(id, cb);
|
|
return id;
|
|
},
|
|
cancelFrame: (id) => frames.delete(id),
|
|
applyPose: (pose) => {
|
|
poses.push(pose);
|
|
return apply?.(pose);
|
|
},
|
|
});
|
|
return {
|
|
owner,
|
|
frames,
|
|
poses,
|
|
tick(ms) {
|
|
time = ms;
|
|
const callbacks = [...frames.values()];
|
|
frames.clear();
|
|
callbacks.forEach((cb) => cb());
|
|
},
|
|
};
|
|
}
|
|
test('motion uses the authored curve and releases the frame on completion', async () => {
|
|
const h = harness();
|
|
const done = h.owner.play(move, { cancelled: false });
|
|
assert.deepEqual(h.poses[0], move.from);
|
|
h.tick(1000);
|
|
assert.equal(h.poses.at(-1).alt, 150);
|
|
h.tick(2000);
|
|
assert.equal(await done, true);
|
|
assert.deepEqual(h.poses.at(-1), move.to);
|
|
assert.equal(h.frames.size, 0);
|
|
assert.equal(h.owner.active, false);
|
|
});
|
|
test('abort and replacement settle immediately and revoke already queued callbacks', async () => {
|
|
const h = harness();
|
|
const abort = new AbortController();
|
|
const first = h.owner.play(move, { signal: abort.signal });
|
|
const stale = [...h.frames.values()][0];
|
|
abort.abort();
|
|
assert.equal(await first, false);
|
|
const second = h.owner.play(move, {});
|
|
const count = h.poses.length;
|
|
stale();
|
|
assert.equal(h.poses.length, count);
|
|
h.owner.destroy();
|
|
assert.equal(await second, false);
|
|
assert.equal(h.frames.size, 0);
|
|
assert.equal(await h.owner.play(move, {}), false);
|
|
});
|
|
test('synchronous cancellation from pose application schedules no abandoned frame', async () => {
|
|
let owner;
|
|
const h = harness(() => owner.cancel());
|
|
owner = h.owner;
|
|
assert.equal(await owner.play(move, {}), false);
|
|
assert.equal(h.frames.size, 0);
|
|
});
|
|
test('failed pose application settles and invalid durations cannot schedule a loop', async () => {
|
|
const h = harness(() => {
|
|
throw new Error('camera unavailable');
|
|
});
|
|
await assert.rejects(h.owner.play(move, {}), /camera unavailable/);
|
|
assert.equal(h.frames.size, 0);
|
|
assert.throws(
|
|
() => h.owner.play({ ...move, durationSec: 0 }, {}),
|
|
/duration/,
|
|
);
|
|
});
|