| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | import { build } from "esbuild"; |
| 4 | import { resolve } from "node:path"; |
| 5 | |
| 6 | const bundle = await build({ entryPoints: [resolve("src/main/browser/recorderRenderer.ts")], bundle: true, platform: "browser", format: "iife", target: "es2022", write: false, logLevel: "silent" }); |
| 7 | |
| 8 | // A static page emits only the capture's first frame and the refresh the video |
| 9 | // sink requests; a frame-rate-limited track can drop that refresh, and then the |
| 10 | // recording never receives a frame. |
| 11 | test("the capture track is not frame-rate limited", async () => { |
| 12 | const requested: MediaStreamConstraints[] = []; |
| 13 | const posted: { type: string }[] = []; |
| 14 | let onMessage!: (event: unknown) => void; |
| 15 | const window = { addEventListener: (_type: string, listener: (event: unknown) => void) => { onMessage = listener; } }; |
| 16 | const navigator = { mediaDevices: { getDisplayMedia: async (constraints: MediaStreamConstraints) => { requested.push(constraints); throw new Error("capture denied"); } } }; |
| 17 | new Function("window", "navigator", bundle.outputFiles[0].text)(window, navigator); |
| 18 | const port = { onmessage: null as null | ((event: { data: unknown }) => Promise<void>), postMessage: (message: { type: string }) => { posted.push(message); } }; |
| 19 | onMessage({ source: window, data: "reasonix-recorder-port", ports: [port] }); |
| 20 | await port.onmessage!({ data: { type: "start", width: 1280, height: 720, cropWidth: 1, cropHeight: 1 } }); |
| 21 | assert.equal(requested.length, 1); |
| 22 | assert.equal(requested[0].audio, false); |
| 23 | assert.ok(requested[0].video === true || (typeof requested[0].video === "object" && !("frameRate" in requested[0].video)), JSON.stringify(requested[0])); |
| 24 | assert.deepEqual(posted.map(message => message.type), ["ready", "error"]); |
| 25 | }); |
| 26 |