From dcb79fc62bdd4d9ec0cbdb2c97d3eb2b2dacdf22 Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Tue, 28 Jul 2026 22:41:18 +0530 Subject: refactor: merge nine files whose reason to exist was gone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/ goes from 84 files to 75 at the same line count — every merge below had exactly one consumer. cdp/event-queue.ts and cdp/pending-requests.ts become daemon/queue.ts. I split these in the daemon-internals commit on the grounds that they held two unrelated concerns, but the transport commit had already deleted the second transport, so both halves had one consumer and the split bought nothing. They also sat in cdp/ while only daemon/ used them. cdp/transport.ts held only the CdpTransport interface after the second transport went; five of its six consumers import it type-only, so it moves to cdp/types.ts. cdp/evaluate.ts moves into cdp/session.ts rather than into its lone consumer isolated-tab.ts — it reaches session.call directly, so under src/domains/ it would trip the raw-cdp-call rule. util/keycodes.ts and util/time.ts fold into their only callers (keyboard.ts, page.ts); util/bytes.ts into util/truncate.ts, which already owns output-size formatting; readpage/capture.ts and search/extract.ts into the tools beside them; profile/picker.ts into profile/command.ts. Kept deliberately: mutex.ts and domains/target.ts each have their own test suite, and folding target.ts into click.ts would make a tool file export a helper. sharp-shim.ts is an isolation boundary carrying two allowlisted casts. result.ts, guards.ts, js-template.ts and paths.ts are small but have 44, 12, 8 and 3 consumers. Co-Authored-By: Claude Opus 5 (1M context) --- test/cdp/evaluate-test.ts | 97 --------------------------- test/cdp/event-queue-test.ts | 137 -------------------------------------- test/cdp/session-call-test.ts | 2 +- test/cdp/session-evaluate-test.ts | 97 +++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 235 deletions(-) delete mode 100644 test/cdp/evaluate-test.ts delete mode 100644 test/cdp/event-queue-test.ts create mode 100644 test/cdp/session-evaluate-test.ts (limited to 'test/cdp') diff --git a/test/cdp/evaluate-test.ts b/test/cdp/evaluate-test.ts deleted file mode 100644 index cffd513..0000000 --- a/test/cdp/evaluate-test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { test, describe } from "node:test"; -import assert from "node:assert/strict"; -import { evaluateJson } from "../../src/cdp/evaluate"; -import type { CdpSession } from "../../src/cdp/session"; -import { err, ok } from "../../src/util/result"; - -const isNumberArray = (v: unknown): v is ReadonlyArray => - Array.isArray(v) && v.every((x) => typeof x === "number"); - -const fakeSession = (payload: unknown): CdpSession => - ({ - call: async () => ok(payload), - }) as unknown as CdpSession; - -type Call = { method: string; args: unknown[] }; - -const recordingSession = (result: unknown): { session: CdpSession; calls: Call[] } => { - const calls: Call[] = []; - const session = { - call: async (...args: unknown[]) => { - calls.push({ method: "call", args }); - return result; - }, - callOnTarget: async (...args: unknown[]) => { - calls.push({ method: "callOnTarget", args }); - return result; - }, - } as unknown as CdpSession; - return { session, calls }; -}; - -describe("evaluateJson", () => { - test("unwraps result.value and applies the guard", async () => { - const s = fakeSession({ result: { type: "object", value: [1, 2, 3] } }); - const r = await evaluateJson(s, "[1,2,3]", isNumberArray); - assert.equal(r.success, true); - if (r.success) assert.deepEqual(r.data, [1, 2, 3]); - }); - - test("surfaces a page exception as an error", async () => { - const s = fakeSession({ - result: { type: "undefined" }, - exceptionDetails: { text: "ReferenceError: x is not defined" }, - }); - const r = await evaluateJson(s, "x", isNumberArray); - assert.equal(r.success, false); - if (!r.success) { - assert.equal(r.error.kind, "remote_error"); - assert.match(r.error.message, /ReferenceError/); - } - }); - - test("rejects a value that fails the guard", async () => { - const s = fakeSession({ result: { type: "string", value: "nope" } }); - const r = await evaluateJson(s, "'nope'", isNumberArray); - assert.equal(r.success, false); - if (!r.success) assert.equal(r.error.kind, "invalid_response"); - }); - - test("dispatches via callOnTarget when opts.sessionId is set", async () => { - const { session, calls } = recordingSession(ok({ result: { type: "object", value: [4, 5] } })); - const r = await evaluateJson(session, "[4,5]", isNumberArray, { sessionId: "sess-1" }); - assert.equal(r.success, true); - if (r.success) assert.deepEqual(r.data, [4, 5]); - assert.equal(calls.length, 1); - assert.equal(calls[0]?.method, "callOnTarget"); - const [, , sessionIdArg] = calls[0]?.args ?? []; - assert.equal(sessionIdArg, "sess-1"); - }); - - test("dispatches via call when no opts are given", async () => { - const { session, calls } = recordingSession(ok({ result: { type: "object", value: [1] } })); - const r = await evaluateJson(session, "[1]", isNumberArray); - assert.equal(r.success, true); - assert.equal(calls.length, 1); - assert.equal(calls[0]?.method, "call"); - assert.equal(calls.some((c) => c.method === "callOnTarget"), false); - }); - - test("passes timeoutMs through to the transport options", async () => { - const { session, calls } = recordingSession(ok({ result: { type: "object", value: [1] } })); - await evaluateJson(session, "[1]", isNumberArray, { timeoutMs: 5000 }); - const [, , optsArg] = calls[0]?.args ?? []; - assert.deepEqual(optsArg, { timeoutMs: 5000 }); - }); - - test("passes through a transport failure unchanged", async () => { - const failure = err({ kind: "timeout" as const, message: "timed out" }); - const { session } = recordingSession(failure); - const r = await evaluateJson(session, "[1]", isNumberArray); - assert.equal(r.success, false); - if (!r.success) { - assert.equal(r.error.kind, "timeout"); - assert.equal(r.error.message, "timed out"); - } - }); -}); diff --git a/test/cdp/event-queue-test.ts b/test/cdp/event-queue-test.ts deleted file mode 100644 index 771ce8e..0000000 --- a/test/cdp/event-queue-test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { test, describe } from "node:test"; -import assert from "node:assert/strict"; -import { makeEventQueue } from "../../src/cdp/event-queue"; -import { rejectAllPending, sendWithTimeout, type Pending } from "../../src/cdp/pending-requests"; - -describe("event queue iteration", () => { - test("yields pushed events in order", async () => { - const q = makeEventQueue(); - q.push({ method: "Page.loadEventFired", params: {} }); - q.push({ method: "Page.frameNavigated", params: {} }); - const seen: string[] = []; - for await (const ev of q.iter) { - seen.push(ev.method); - if (seen.length === 2) break; - } - assert.deepEqual(seen, ["Page.loadEventFired", "Page.frameNavigated"]); - }); - - test("ending the queue terminates the iteration after draining the buffer", async () => { - const q = makeEventQueue(); - q.push({ method: "Page.loadEventFired", params: {} }); - q.end(); - const seen: string[] = []; - for await (const ev of q.iter) seen.push(ev.method); - assert.deepEqual(seen, ["Page.loadEventFired"]); - }); - - test("a waiter pending at end() is released", async () => { - const q = makeEventQueue(); - const done = (async () => { - const seen: string[] = []; - for await (const ev of q.iter) seen.push(ev.method); - return seen; - })(); - await Promise.resolve(); - q.end(); - assert.deepEqual(await done, []); - }); - - test("push after end() is dropped", async () => { - const q = makeEventQueue(); - q.end(); - q.push({ method: "Page.loadEventFired", params: {} }); - const seen: string[] = []; - for await (const ev of q.iter) seen.push(ev.method); - assert.deepEqual(seen, []); - }); - - test("a waiter is handed the next pushed event directly", async () => { - const q = makeEventQueue(); - const it = q.iter[Symbol.asyncIterator](); - const pending = it.next(); - await Promise.resolve(); - q.push({ method: "Runtime.consoleAPICalled", params: { a: 1 } }); - const r = await pending; - assert.equal(r.done, false); - assert.deepEqual(r.value, { method: "Runtime.consoleAPICalled", params: { a: 1 } }); - }); - - test("the terminal result reports done with an undefined value", async () => { - const q = makeEventQueue(); - q.end(); - const it = q.iter[Symbol.asyncIterator](); - const r = await it.next(); - assert.equal(r.done, true); - assert.equal(r.value, undefined); - }); - - test("sessionId is preserved on yielded events", async () => { - const q = makeEventQueue(); - q.push({ method: "Page.loadEventFired", params: {}, sessionId: "s1" }); - q.end(); - const seen: Array = []; - for await (const ev of q.iter) seen.push(ev.sessionId); - assert.deepEqual(seen, ["s1"]); - }); -}); - -describe("pending request bookkeeping", () => { - test("rejectAllPending settles every entry with transport_closed and clears the map", () => { - const pending = new Map(); - const settled: string[] = []; - for (const id of [1, 2]) { - pending.set(id, { - resolve: (r) => settled.push(r.success ? "ok" : r.error.kind), - timer: setTimeout(() => {}, 10_000), - method: `M.m${id}`, - }); - } - rejectAllPending(pending, "gone"); - assert.deepEqual(settled, ["transport_closed", "transport_closed"]); - assert.equal(pending.size, 0); - }); - - test("sendWithTimeout resolves transport_closed when send throws synchronously", async () => { - const pending = new Map(); - const r = await sendWithTimeout(pending, 7, "M.m", 10_000, "Test", () => { - throw new Error("socket gone"); - }); - assert.equal(r.success, false); - if (!r.success) { - assert.equal(r.error.kind, "transport_closed"); - assert.equal(r.error.message, "socket gone"); - assert.equal(r.error.method, "M.m"); - } - assert.equal(pending.size, 0); - }); - - test("sendWithTimeout registers a pending entry the caller can resolve", async () => { - const pending = new Map(); - let sent = false; - const p = sendWithTimeout(pending, 9, "M.m", 10_000, "Test", () => { - sent = true; - }); - assert.equal(sent, true); - const entry = pending.get(9); - assert.notEqual(entry, undefined); - if (entry) { - clearTimeout(entry.timer); - entry.resolve({ success: true, data: { hello: "world" } }); - } - const r = await p; - assert.equal(r.success, true); - if (r.success) assert.deepEqual(r.data, { hello: "world" }); - }); - - test("sendWithTimeout resolves a timeout error and drops the pending entry", async () => { - const pending = new Map(); - const r = await sendWithTimeout(pending, 11, "M.slow", 1, "Test", () => {}); - assert.equal(r.success, false); - if (!r.success) { - assert.equal(r.error.kind, "timeout"); - assert.match(r.error.message, /Test timeout after 1ms: M\.slow/); - } - assert.equal(pending.size, 0); - }); -}); diff --git a/test/cdp/session-call-test.ts b/test/cdp/session-call-test.ts index 6a14e17..ef9a725 100644 --- a/test/cdp/session-call-test.ts +++ b/test/cdp/session-call-test.ts @@ -2,7 +2,7 @@ import { test, describe } from "node:test"; import assert from "node:assert/strict"; import { decodeResult } from "../../src/cdp/commands"; import { createCdpSession } from "../../src/cdp/session"; -import type { CdpTransport } from "../../src/cdp/transport"; +import type { CdpTransport } from "../../src/cdp/types"; import type { CdpEvent } from "../../src/cdp/types"; import { type Result, ok, err } from "../../src/util/result"; import { type CdpError, cdpError } from "../../src/cdp/errors"; diff --git a/test/cdp/session-evaluate-test.ts b/test/cdp/session-evaluate-test.ts new file mode 100644 index 0000000..c03a4cb --- /dev/null +++ b/test/cdp/session-evaluate-test.ts @@ -0,0 +1,97 @@ +import { test, describe } from "node:test"; +import assert from "node:assert/strict"; +import { evaluateJson } from "../../src/cdp/session"; +import type { CdpSession } from "../../src/cdp/session"; +import { err, ok } from "../../src/util/result"; + +const isNumberArray = (v: unknown): v is ReadonlyArray => + Array.isArray(v) && v.every((x) => typeof x === "number"); + +const fakeSession = (payload: unknown): CdpSession => + ({ + call: async () => ok(payload), + }) as unknown as CdpSession; + +type Call = { method: string; args: unknown[] }; + +const recordingSession = (result: unknown): { session: CdpSession; calls: Call[] } => { + const calls: Call[] = []; + const session = { + call: async (...args: unknown[]) => { + calls.push({ method: "call", args }); + return result; + }, + callOnTarget: async (...args: unknown[]) => { + calls.push({ method: "callOnTarget", args }); + return result; + }, + } as unknown as CdpSession; + return { session, calls }; +}; + +describe("evaluateJson", () => { + test("unwraps result.value and applies the guard", async () => { + const s = fakeSession({ result: { type: "object", value: [1, 2, 3] } }); + const r = await evaluateJson(s, "[1,2,3]", isNumberArray); + assert.equal(r.success, true); + if (r.success) assert.deepEqual(r.data, [1, 2, 3]); + }); + + test("surfaces a page exception as an error", async () => { + const s = fakeSession({ + result: { type: "undefined" }, + exceptionDetails: { text: "ReferenceError: x is not defined" }, + }); + const r = await evaluateJson(s, "x", isNumberArray); + assert.equal(r.success, false); + if (!r.success) { + assert.equal(r.error.kind, "remote_error"); + assert.match(r.error.message, /ReferenceError/); + } + }); + + test("rejects a value that fails the guard", async () => { + const s = fakeSession({ result: { type: "string", value: "nope" } }); + const r = await evaluateJson(s, "'nope'", isNumberArray); + assert.equal(r.success, false); + if (!r.success) assert.equal(r.error.kind, "invalid_response"); + }); + + test("dispatches via callOnTarget when opts.sessionId is set", async () => { + const { session, calls } = recordingSession(ok({ result: { type: "object", value: [4, 5] } })); + const r = await evaluateJson(session, "[4,5]", isNumberArray, { sessionId: "sess-1" }); + assert.equal(r.success, true); + if (r.success) assert.deepEqual(r.data, [4, 5]); + assert.equal(calls.length, 1); + assert.equal(calls[0]?.method, "callOnTarget"); + const [, , sessionIdArg] = calls[0]?.args ?? []; + assert.equal(sessionIdArg, "sess-1"); + }); + + test("dispatches via call when no opts are given", async () => { + const { session, calls } = recordingSession(ok({ result: { type: "object", value: [1] } })); + const r = await evaluateJson(session, "[1]", isNumberArray); + assert.equal(r.success, true); + assert.equal(calls.length, 1); + assert.equal(calls[0]?.method, "call"); + assert.equal(calls.some((c) => c.method === "callOnTarget"), false); + }); + + test("passes timeoutMs through to the transport options", async () => { + const { session, calls } = recordingSession(ok({ result: { type: "object", value: [1] } })); + await evaluateJson(session, "[1]", isNumberArray, { timeoutMs: 5000 }); + const [, , optsArg] = calls[0]?.args ?? []; + assert.deepEqual(optsArg, { timeoutMs: 5000 }); + }); + + test("passes through a transport failure unchanged", async () => { + const failure = err({ kind: "timeout" as const, message: "timed out" }); + const { session } = recordingSession(failure); + const r = await evaluateJson(session, "[1]", isNumberArray); + assert.equal(r.success, false); + if (!r.success) { + assert.equal(r.error.kind, "timeout"); + assert.equal(r.error.message, "timed out"); + } + }); +}); -- cgit v1.2.3