Skip to content

Commit c053b56

Browse files
Bind CDP session creation to context receivers
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent 7ea498a commit c053b56

5 files changed

Lines changed: 73 additions & 3 deletions

File tree

currentState.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ HyperAgent exposes a TypeScript SDK for browser automation with three primary pa
171171
- Hardened element-locator debug frame listing:
172172
- trap-prone frame arrays no longer collapse the entire debug "Available frames" payload,
173173
- readable frame entries are preserved even when individual frame getters/indexes trap.
174+
- Bound CDP session creation to context receivers in both discovery paths:
175+
- `PlaywrightCDPClient.createSession()` now calls `newCDPSession` with the browser context receiver explicitly.
176+
- `FrameContextManager.captureOOPIFs()` now calls `newCDPSession` with the discovered context receiver explicitly.
177+
- Added regressions proving receiver-sensitive `newCDPSession` implementations continue to work.
174178
- Hardened A11y DOM option ingestion (`useCache`, `onFrameChunk`, `filterAdTrackingFrames`) with trap-safe reads, so malformed option objects no longer break extraction setup.
175179
- Hardened A11y DOM debug-option lookup (`getDebugOptions`) with trap-safe fallback defaults and sanitized warning diagnostics.
176180
- Hardened OpenAI/Anthropic structured-schema debug-option reads so trap-prone debug-option access no longer interrupts structured invocation paths.

src/cdp/frame-context-manager.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,42 @@ describe("FrameContextManager listener bookkeeping", () => {
395395
}
396396
});
397397

398+
it("captureOOPIFs uses the browser-context receiver for session creation", async () => {
399+
const session = new FakeSession();
400+
const mainFrame = {
401+
url: () => "https://example.com",
402+
parentFrame: () => null,
403+
name: () => "main",
404+
isDetached: () => false,
405+
};
406+
const sameOriginFrame = {
407+
url: () => "https://example.com/child",
408+
parentFrame: () => mainFrame,
409+
name: () => "child",
410+
isDetached: () => false,
411+
};
412+
const context = {
413+
newCDPSession: jest.fn(function (this: unknown, frame: unknown) {
414+
if (this !== context) {
415+
throw new Error("invalid context receiver");
416+
}
417+
void frame;
418+
return Promise.reject(new Error("same origin frame"));
419+
}),
420+
};
421+
const page = {
422+
context: () => context,
423+
frames: () => [mainFrame, sameOriginFrame],
424+
mainFrame: () => mainFrame,
425+
};
426+
const manager = new FrameContextManager(
427+
createFakeClientWithPage(session, page)
428+
);
429+
430+
await expect(manager.captureOOPIFs(1)).resolves.toBeUndefined();
431+
expect(context.newCDPSession).toHaveBeenCalledWith(sameOriginFrame);
432+
});
433+
398434
it("captureOOPIFs skips ad/tracking frame session creation by default", async () => {
399435
const session = new FakeSession();
400436
const mainFrame = {

src/cdp/frame-context-manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,10 @@ export class FrameContextManager {
650650
try {
651651
oopifSession = await (
652652
newCDPSessionMethod as (
653+
this: object,
653654
frameArg: PlaywrightFrameHandle
654655
) => Promise<CDPSession>
655-
)(frame);
656+
).call(contextUnknown as object, frame);
656657
} catch {
657658
// Failed to create session = same-origin frame (already processed via DOM.getDocument)
658659
this.log(`[FrameContext] Frame ${frameUrl} is same-origin, skipping`);

src/cdp/playwright-adapter.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,30 @@ describe("playwright adapter error formatting", () => {
186186
warnSpy.mockRestore();
187187
}
188188
});
189+
190+
it("creates CDP sessions with the browser-context receiver", async () => {
191+
const session = {
192+
send: jest.fn().mockResolvedValue({}),
193+
on: jest.fn(),
194+
off: jest.fn(),
195+
detach: jest.fn().mockResolvedValue(undefined),
196+
} as unknown as PlaywrightSession;
197+
const context = {
198+
newCDPSession: jest.fn(function (this: unknown, target: unknown) {
199+
if (this !== context) {
200+
throw new Error("invalid context receiver");
201+
}
202+
void target;
203+
return Promise.resolve(session);
204+
}),
205+
};
206+
const page = {
207+
context: () => context,
208+
once: jest.fn(),
209+
} as unknown as Page;
210+
211+
const client = await getCDPClientForPage(page);
212+
await expect(client.acquireSession("lifecycle")).resolves.toBeDefined();
213+
expect(context.newCDPSession).toHaveBeenCalled();
214+
});
189215
});

src/cdp/playwright-adapter.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,11 @@ class PlaywrightCDPClient implements CDPClient {
198198
let session: PlaywrightSession;
199199
try {
200200
session = (await (
201-
newCDPSessionMethod as (targetArg: Page | Frame) => Promise<PlaywrightSession>
202-
)(target)) as PlaywrightSession;
201+
newCDPSessionMethod as (
202+
this: object,
203+
targetArg: Page | Frame
204+
) => Promise<PlaywrightSession>
205+
).call(pageContext as object, target)) as PlaywrightSession;
203206
} catch (error) {
204207
throw new Error(
205208
`[CDP][PlaywrightAdapter] Failed to create CDP session: ${formatPlaywrightAdapterDiagnostic(

0 commit comments

Comments
 (0)