Files
oh-my-pi/packages/coding-agent/test/tools/browser-stealth-targets.test.ts
T
roboomp 1a279bba88 fix(browser): handled null head in stealth bootstrap
Appended the stealth iframe to documentElement when document.head is not available during new-document evaluation.

Added regression coverage for the null-head bootstrap path.

Fixes #1267
2026-05-21 15:43:02 +00:00

210 lines
5.6 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import type { Browser, CDPSession, Target } from "puppeteer-core";
import {
buildStealthInjectionScriptForTest,
configureUserAgentTargetsForTest,
targetSupportsUserAgentOverrideForTest,
} from "../../src/tools/browser/launch";
type SentCommand = {
method: string;
params?: Record<string, unknown>;
};
class FakeSession {
readonly commands: SentCommand[] = [];
detached = false;
readonly #delayMs: number;
constructor(delayMs = 0) {
this.#delayMs = delayMs;
}
async send(method: string, params?: Record<string, unknown>): Promise<unknown> {
this.commands.push({ method, params });
if (this.#delayMs > 0) await Bun.sleep(this.#delayMs);
return {};
}
async detach(): Promise<void> {
this.detached = true;
}
on(): void {}
connection(): { session: () => null } {
return { session: () => null };
}
}
class FakeTarget {
readonly session: FakeSession;
createCalls = 0;
readonly #type: string;
constructor(type: string, delayMs = 0) {
this.#type = type;
this.session = new FakeSession(delayMs);
}
type(): string {
return this.#type;
}
async createCDPSession(): Promise<CDPSession> {
this.createCalls++;
return this.session as unknown as CDPSession;
}
}
class FakeBrowser {
readonly browserTarget = new FakeTarget("browser");
readonly #targets: FakeTarget[];
constructor(targets: FakeTarget[]) {
this.#targets = targets;
}
target(): Target {
return this.browserTarget as unknown as Target;
}
targets(): Target[] {
return this.#targets as unknown as Target[];
}
}
const override = {
userAgent: "Mozilla/5.0 Chrome/142.0.0.0 Safari/537.36",
acceptLanguage: "en-US,en",
platform: "Win32",
userAgentMetadata: {
brands: [{ brand: "Chromium", version: "142" }],
fullVersion: "142.0.0.0",
platform: "Windows",
platformVersion: "10.0.0",
architecture: "x86",
model: "",
mobile: false,
},
};
describe("browser stealth bootstrap", () => {
it("uses documentElement as the iframe container when document.head is unavailable", () => {
type NativeWindow = Pick<
typeof globalThis,
"Function" | "Object" | "setTimeout" | "Math" | "Event" | "Promise" | "Blob" | "Proxy" | "Intl" | "Date"
>;
type FakeContainer = {
appendChild(node: FakeIframe): void;
removeChild(node: FakeIframe): void;
};
type FakeIframe = {
contentWindow: NativeWindow;
parentNode: FakeContainer | null;
style: { display?: string };
};
const appended: FakeIframe[] = [];
const removed: FakeIframe[] = [];
const documentElement: FakeContainer = {
appendChild(node) {
appended.push(node);
node.parentNode = documentElement;
},
removeChild(node) {
removed.push(node);
node.parentNode = null;
},
};
const nativeWindow: NativeWindow = {
Function,
Object,
setTimeout,
Math,
Event,
Promise,
Blob,
Proxy,
Intl,
Date,
};
const iframe: FakeIframe = { contentWindow: nativeWindow, parentNode: null, style: {} };
const createdTags: string[] = [];
const document = {
head: null,
documentElement,
createElement(tagName: string) {
createdTags.push(tagName);
return iframe;
},
};
const run = new Function("document", buildStealthInjectionScriptForTest([]));
run(document);
expect(createdTags).toEqual(["iframe"]);
expect(iframe.style.display).toBe("none");
expect(appended).toEqual([iframe]);
expect(removed).toEqual([iframe]);
expect(iframe.parentNode).toBeNull();
});
});
describe("browser stealth target setup", () => {
it("attempts user-agent override for page-like existing targets and skips non-page worker/browser targets", async () => {
const page = new FakeTarget("page");
const webview = new FakeTarget("webview");
const backgroundPage = new FakeTarget("background_page");
const serviceWorker = new FakeTarget("service_worker");
const sharedWorker = new FakeTarget("shared_worker");
const browserTarget = new FakeTarget("browser");
const other = new FakeTarget("other");
const browser = new FakeBrowser([
page,
webview,
backgroundPage,
serviceWorker,
sharedWorker,
browserTarget,
other,
]);
await configureUserAgentTargetsForTest(browser as unknown as Browser, { browserSession: null, override });
expect(page.createCalls).toBe(1);
expect(webview.createCalls).toBe(1);
expect(backgroundPage.createCalls).toBe(1);
expect(serviceWorker.createCalls).toBe(0);
expect(sharedWorker.createCalls).toBe(0);
expect(browserTarget.createCalls).toBe(0);
expect(other.createCalls).toBe(0);
expect(page.session.detached).toBe(true);
expect(webview.session.detached).toBe(true);
expect(backgroundPage.session.detached).toBe(true);
});
it("classifies only targets with page surfaces as user-agent override targets", () => {
const supportedTypes = ["page", "webview", "background_page"];
const skippedTypes = ["browser", "service_worker", "shared_worker", "other"];
for (const type of supportedTypes) {
expect(targetSupportsUserAgentOverrideForTest({ type: () => type } as unknown as Target)).toBe(true);
}
for (const type of skippedTypes) {
expect(targetSupportsUserAgentOverrideForTest({ type: () => type } as unknown as Target)).toBe(false);
}
});
it("gives a slow page-like target a bounded but non-trivial window to receive user-agent override", async () => {
const slowPage = new FakeTarget("page", 200);
const browser = new FakeBrowser([slowPage]);
const started = performance.now();
await configureUserAgentTargetsForTest(browser as unknown as Browser, { browserSession: null, override }, 50);
const elapsed = performance.now() - started;
expect(elapsed).toBeGreaterThanOrEqual(45);
expect(elapsed).toBeLessThan(150);
});
});