diff options
| author | amankumarsingh77 <amankumarsingh7702@gmail.com> | 2026-07-28 19:42:25 +0530 |
|---|---|---|
| committer | Aman Kumar <145339995+amankumarsingh77@users.noreply.github.com> | 2026-08-02 19:12:46 +0530 |
| commit | ce30547af39f692bef777591bdb329a7fa00e86e (patch) | |
| tree | 01b331fa9794d55d0ffa7d37f1ccfc8f75098cee | |
| parent | d3fb25a8e1297f00dbf7ce4a188b12290b594c73 (diff) | |
fix(cdp): re-enable domains when re-attaching to a known tab
Every Target.attachToTarget mints a new sessionId and CDP domain enablement is
per-session, but switchTo only enabled domains on a tab's first visit. Switching
away and back left the refreshed session with Page, Network, Runtime and Log
disabled, so that tab silently stopped collecting console and network records and
stopped reporting dialogs and load events for the rest of the session.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| -rw-r--r-- | src/cdp/session.ts | 5 | ||||
| -rw-r--r-- | test/cdp/session-switch-test.ts | 66 |
2 files changed, 68 insertions, 3 deletions
diff --git a/src/cdp/session.ts b/src/cdp/session.ts index bf423e1..39cd87e 100644 --- a/src/cdp/session.ts +++ b/src/cdp/session.ts @@ -369,12 +369,11 @@ export const createCdpSession = ( // Each Target.attachToTarget produces a new sessionId — update it. sessionIdToTargetId.delete(existing.sessionId); existing.sessionId = attachedSessionId; - sessionIdToTargetId.set(attachedSessionId, tid); } else { tabs.set(tid, tab); - await enableDomains(attachedSessionId); - sessionIdToTargetId.set(attachedSessionId, tid); } + sessionIdToTargetId.set(attachedSessionId, tid); + await enableDomains(attachedSessionId); // Update global pointers to point at the new active tab sessionId = tab.sessionId; targetId = tid; diff --git a/test/cdp/session-switch-test.ts b/test/cdp/session-switch-test.ts new file mode 100644 index 0000000..f09d26c --- /dev/null +++ b/test/cdp/session-switch-test.ts @@ -0,0 +1,66 @@ +import { test, describe } from "node:test"; +import assert from "node:assert/strict"; +import { createCdpSession } from "../../src/cdp/session"; +import { createOwnershipRegistry } from "../../src/cdp/ownership"; +import { createStubTransport } from "../domains/fake-client"; +import { ok } from "../../src/util/result"; + +const ENABLED_DOMAINS = ["Page", "DOM", "Runtime", "Network", "Accessibility", "Log"]; + +const twoTabSession = async () => { + const stub = createStubTransport({ + "Target.setDiscoverTargets": ok({}), + "Target.getTargets": ok({ + targetInfos: [ + { targetId: "t1", type: "page", title: "One", url: "https://one.test/" }, + { targetId: "t2", type: "page", title: "Two", url: "https://two.test/" }, + ], + }), + "Target.attachToTarget": [ok({ sessionId: "s1" }), ok({ sessionId: "s2" }), ok({ sessionId: "s3" })], + "Browser.getWindowForTarget": ok({ windowId: 7 }), + "Target.activateTarget": ok({}), + }); + const ownership = createOwnershipRegistry({ ownedTargetIds: ["t1", "t2"] }); + const session = createCdpSession(stub.transport, ownership); + const attached = await session.attachFirstPage(); + assert.equal(attached.success, true); + return { stub, session }; +}; + +const enablesFor = (stub: { calls: ReadonlyArray<{ method: string; sessionId: string | null | undefined }> }, sessionId: string): string[] => + stub.calls.filter((c) => c.sessionId === sessionId && c.method.endsWith(".enable")).map((c) => c.method); + +describe("CdpSession.switchTo domain enablement", () => { + test("a first visit enables every domain on the new session", async () => { + const { stub, session } = await twoTabSession(); + const switched = await session.switchTo("t2"); + assert.equal(switched.success, true); + assert.deepEqual( + enablesFor(stub, "s2").sort(), + ENABLED_DOMAINS.map((d) => `${d}.enable`).sort(), + ); + }); + + test("returning to a known tab re-enables domains on its new session", async () => { + const { stub, session } = await twoTabSession(); + await session.switchTo("t2"); + const back = await session.switchTo("t1"); + assert.equal(back.success, true); + assert.equal(session.current()?.sessionId, "s3"); + assert.deepEqual( + enablesFor(stub, "s3").sort(), + ENABLED_DOMAINS.map((d) => `${d}.enable`).sort(), + ); + }); + + test("calls on the returned-to tab are routed to its refreshed session id", async () => { + const { stub, session } = await twoTabSession(); + await session.switchTo("t2"); + await session.switchTo("t1"); + const r = await session.call("Page.bringToFront", {}); + assert.equal(r.success, true); + const last = stub.calls.at(-1); + assert.equal(last?.method, "Page.bringToFront"); + assert.equal(last?.sessionId, "s3"); + }); +}); |
