mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { CURSOR_TELEMETRY_VERSION } from "../constants";
|
|
|
|
const { writeFile, rm } = vi.hoisted(() => ({
|
|
writeFile: vi.fn(),
|
|
rm: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("node:fs/promises", () => ({
|
|
default: {
|
|
writeFile,
|
|
rm,
|
|
},
|
|
}));
|
|
|
|
vi.mock("electron", () => ({
|
|
app: {
|
|
getPath: vi.fn(() => "/tmp"),
|
|
},
|
|
}));
|
|
|
|
vi.mock("../utils", () => ({
|
|
getTelemetryPathForVideo: vi.fn(() => "/tmp/recording.cursor.json"),
|
|
getScreen: vi.fn(() => ({
|
|
getCursorScreenPoint: () => ({ x: 0, y: 0 }),
|
|
getPrimaryDisplay: () => ({ scaleFactor: 1 }),
|
|
getDisplayNearestPoint: () => ({ bounds: { x: 0, y: 0, width: 1, height: 1 } }),
|
|
getAllDisplays: () => [],
|
|
})),
|
|
}));
|
|
|
|
import { activeCursorSamples, setActiveCursorSamples, setCursorCaptureStartTimeMs } from "../state";
|
|
import {
|
|
getCursorCaptureElapsedMs,
|
|
normalizeCursorTelemetrySamples,
|
|
pauseCursorCapture,
|
|
pauseCursorCaptureAtBoundary,
|
|
pushCursorSample,
|
|
resetCursorCaptureClock,
|
|
resumeCursorCapture,
|
|
writeCursorTelemetry,
|
|
} from "./telemetry";
|
|
|
|
describe("cursor telemetry pause clock", () => {
|
|
beforeEach(() => {
|
|
writeFile.mockReset();
|
|
rm.mockReset();
|
|
setCursorCaptureStartTimeMs(1_000);
|
|
setActiveCursorSamples([]);
|
|
resetCursorCaptureClock();
|
|
});
|
|
|
|
it("subtracts paused time from elapsed cursor timestamps", () => {
|
|
expect(getCursorCaptureElapsedMs(1_120)).toBe(120);
|
|
|
|
pauseCursorCapture(1_200);
|
|
expect(getCursorCaptureElapsedMs(1_450)).toBe(200);
|
|
|
|
resumeCursorCapture(1_700);
|
|
expect(getCursorCaptureElapsedMs(1_900)).toBe(400);
|
|
});
|
|
|
|
it("ignores duplicate pause or resume transitions", () => {
|
|
pauseCursorCapture(1_150);
|
|
pauseCursorCapture(1_250);
|
|
resumeCursorCapture(1_500);
|
|
resumeCursorCapture(1_650);
|
|
|
|
expect(getCursorCaptureElapsedMs(1_900)).toBe(550);
|
|
});
|
|
|
|
it("drops cursor samples captured after the renderer pause boundary", () => {
|
|
pushCursorSample(0.1, 0.1, 120, "move");
|
|
pushCursorSample(0.2, 0.2, 205, "move");
|
|
pushCursorSample(0.3, 0.3, 260, "move");
|
|
|
|
pauseCursorCaptureAtBoundary(1_200);
|
|
|
|
expect(getCursorCaptureElapsedMs(1_500)).toBe(200);
|
|
expect(activeCursorSamples.map((sample) => sample.timeMs)).toEqual([120]);
|
|
|
|
resumeCursorCapture(1_700);
|
|
expect(getCursorCaptureElapsedMs(1_900)).toBe(400);
|
|
});
|
|
|
|
it("normalizes cursor telemetry samples before persisting them", async () => {
|
|
const samples = normalizeCursorTelemetrySamples([
|
|
{ timeMs: 30, cx: 2, cy: -1, interactionType: "click", cursorType: "pointer" },
|
|
{ timeMs: -10, cx: Number.NaN, cy: 0.2, interactionType: "drag", cursorType: "ibeam" },
|
|
{ timeMs: 10, cx: 0.25, cy: 0.75, interactionType: "move", cursorType: "text" },
|
|
]);
|
|
|
|
expect(samples).toEqual([
|
|
{ timeMs: 0, cx: 0.5, cy: 0.2, interactionType: undefined, cursorType: undefined },
|
|
{ timeMs: 10, cx: 0.25, cy: 0.75, interactionType: "move", cursorType: "text" },
|
|
{ timeMs: 30, cx: 1, cy: 0, interactionType: "click", cursorType: "pointer" },
|
|
]);
|
|
|
|
await writeCursorTelemetry("/tmp/recording.mp4", samples);
|
|
|
|
expect(writeFile).toHaveBeenCalledWith(
|
|
"/tmp/recording.cursor.json",
|
|
JSON.stringify(
|
|
{
|
|
version: CURSOR_TELEMETRY_VERSION,
|
|
samples,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
"utf-8",
|
|
);
|
|
expect(rm).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("removes the sidecar when saving an empty cursor telemetry payload", async () => {
|
|
await writeCursorTelemetry("/tmp/recording.mp4", []);
|
|
|
|
expect(rm).toHaveBeenCalledWith("/tmp/recording.cursor.json", { force: true });
|
|
expect(writeFile).not.toHaveBeenCalled();
|
|
});
|
|
});
|