| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | import { acquireDebugger, DEBUGGER_IDLE_MS, disposeDebugger, sendDebuggerCommand } from "./debuggerLease.js"; |
| 4 | import { FakeDebugger } from "./fakeGuestViews.js"; |
| 5 | |
| 6 | test("frame, screenshot and upload keep adjacent calls on one root connection", t => { |
| 7 | t.mock.timers.enable({ apis: ["setTimeout"] }); |
| 8 | const debuggerAPI = new FakeDebugger(); |
| 9 | const first = acquireDebugger(debuggerAPI), second = acquireDebugger(debuggerAPI); |
| 10 | first(); first(); |
| 11 | assert.equal(debuggerAPI.isAttached(), true); |
| 12 | second(); |
| 13 | assert.equal(debuggerAPI.isAttached(), true); |
| 14 | const next = acquireDebugger(debuggerAPI); |
| 15 | t.mock.timers.tick(DEBUGGER_IDLE_MS); |
| 16 | assert.equal(debuggerAPI.isAttached(), true); |
| 17 | next(); |
| 18 | t.mock.timers.tick(DEBUGGER_IDLE_MS); |
| 19 | assert.equal(debuggerAPI.isAttached(), false); |
| 20 | debuggerAPI.attach(); |
| 21 | acquireDebugger(debuggerAPI)(); |
| 22 | t.mock.timers.tick(DEBUGGER_IDLE_MS); |
| 23 | assert.equal(debuggerAPI.isAttached(), true, "externally owned debugger remains attached"); |
| 24 | }); |
| 25 | |
| 26 | test("an abandoned waiter cannot detach a pending command or a replacement lease", async t => { |
| 27 | t.mock.timers.enable({ apis: ["setTimeout"] }); |
| 28 | const debuggerAPI = new FakeDebugger(); |
| 29 | let settle!: () => void; |
| 30 | debuggerAPI.respond = () => new Promise<void>(resolve => { settle = resolve; }); |
| 31 | const release = acquireDebugger(debuggerAPI); |
| 32 | const command = sendDebuggerCommand(debuggerAPI, "Page.createIsolatedWorld"); |
| 33 | release(); |
| 34 | t.mock.timers.tick(DEBUGGER_IDLE_MS * 2); |
| 35 | assert.equal(debuggerAPI.isAttached(), true, "a live reply owns the connection"); |
| 36 | disposeDebugger(debuggerAPI); |
| 37 | const replacement = acquireDebugger(debuggerAPI); |
| 38 | await assert.rejects(release.send("Input.dispatchMouseEvent"), /lease is unavailable/); |
| 39 | settle(); await command; |
| 40 | t.mock.timers.tick(DEBUGGER_IDLE_MS * 2); |
| 41 | assert.equal(debuggerAPI.isAttached(), true, "old completion cannot detach the replacement"); |
| 42 | replacement(); disposeDebugger(debuggerAPI); |
| 43 | assert.equal(debuggerAPI.isAttached(), false); |
| 44 | }); |
| 45 | |
| 46 | test("destroyed notification drops timers without touching a destroyed native debugger", t => { |
| 47 | t.mock.timers.enable({ apis: ["setTimeout"] }); |
| 48 | const debuggerAPI = new FakeDebugger(); |
| 49 | acquireDebugger(debuggerAPI)(); |
| 50 | debuggerAPI.isAttached = () => { throw new Error("Object has been destroyed"); }; |
| 51 | disposeDebugger(debuggerAPI, false); |
| 52 | t.mock.timers.tick(DEBUGGER_IDLE_MS * 2); |
| 53 | }); |
| 54 |