// What a controller's input waits for: a pointer moving waits for the next refresh, and anything // else goes at once. Run with `node --test e2e/remote_input.test.mjs`. import test from "node:test"; import assert from "node:assert/strict"; globalThis.requestAnimationFrame ??= () => 0; const { RemoteViewer } = await import("../web/room/remote.js"); function viewer() { let t = 0; const v = { batch: [], scheduled: 0, flushed: 0, roomNow: () => ++t, log() {}, schedule() { this.scheduled++; }, flush() { this.flushed++; return Promise.resolve(); }, }; v.queue = RemoteViewer.prototype.queue; v.moveTo = RemoteViewer.prototype.moveTo; return v; } test("a move waits for the next refresh", () => { const v = viewer(); v.moveTo({ x: 1, y: 1 }); assert.deepEqual([v.scheduled, v.flushed], [1, 0]); }); for (const a of [{ type: "key", usage: 4, down: true }, { type: "button", button: 0, down: true }, { type: "wheel", dx: 0, dy: 1 }, { type: "text", text: "a" }]) { test(`a ${a.type} goes at once, with the moves before it`, () => { const v = viewer(); v.moveTo({ x: 1, y: 1 }); v.queue(a); assert.equal(v.flushed, 1); assert.deepEqual(v.batch.map((x) => x.type), ["move", a.type]); }); } test("moves between refreshes are one action, at the latest place", () => { const v = viewer(); v.moveTo({ x: 1, y: 1 }); const at = v.batch[0].at; v.moveTo({ x: 2, y: 3 }); v.moveTo({ x: 5, y: 8 }); assert.equal(v.batch.length, 1); assert.deepEqual([v.batch[0].x, v.batch[0].y], [5, 8]); assert.ok(v.batch[0].at > at, "when it was last there"); }); test("a move after a key is a new action, so the order they happened in stands", () => { const v = viewer(); v.moveTo({ x: 1, y: 1 }); v.queue({ type: "key", usage: 4, down: true }); v.moveTo({ x: 2, y: 2 }); assert.deepEqual(v.batch.map((x) => x.type), ["move", "key", "move"]); assert.equal(v.batch[0].x, 1, "the first move keeps its place"); assert.equal(v.batch[2].x, 2); });