mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
fix(wrapper): track real window geometry on headed launches
Headed launches applied a fixed emulated viewport on top of the real browser window, yielding outerWidth < innerWidth (an impossible window). Default headed new_page()/new_context() to no_viewport so the page tracks the real window; headless keeps a deterministic viewport. Covers Python launch/launch_context/launch_persistent_context (+async) and the JS Playwright/Puppeteer wrappers. Explicit viewport still honored.
This commit is contained in:
@@ -84,6 +84,32 @@ describe("composable Playwright launch helpers", () => {
|
||||
expect(buildContextOptions({ viewport: null }).viewport).toBeNull();
|
||||
});
|
||||
|
||||
it("buildContextOptions uses no viewport (null) when headed, so the page tracks the real window", async () => {
|
||||
const { buildContextOptions } = await import("../src/index.js");
|
||||
|
||||
// Headed: no emulated viewport (CDP emulation would force outerWidth < innerWidth).
|
||||
expect(buildContextOptions({ headless: false }).viewport).toBeNull();
|
||||
// Headless keeps the deterministic default.
|
||||
expect(buildContextOptions({ headless: true }).viewport).toEqual(DEFAULT_VIEWPORT);
|
||||
// Explicit viewport always honored, even headed.
|
||||
const custom = { width: 800, height: 600 };
|
||||
expect(buildContextOptions({ headless: false, viewport: custom }).viewport).toEqual(custom);
|
||||
});
|
||||
|
||||
it("buildContextOptions reads effective headless from launchOptions.headless", async () => {
|
||||
const { buildContextOptions } = await import("../src/index.js");
|
||||
|
||||
// buildLaunchOptions spreads launchOptions LAST, so launchOptions.headless wins
|
||||
// at the actual launch. Viewport must follow it — a raw headless:false (browser
|
||||
// actually headed) must NOT get a fixed viewport (would reintroduce outer<inner).
|
||||
expect(buildContextOptions({ launchOptions: { headless: false } }).viewport).toBeNull();
|
||||
// And launchOptions.headless:true forces the deterministic viewport even if the
|
||||
// top-level field said headed.
|
||||
expect(
|
||||
buildContextOptions({ headless: false, launchOptions: { headless: true } }).viewport,
|
||||
).toEqual(DEFAULT_VIEWPORT);
|
||||
});
|
||||
|
||||
it("buildLaunchOptions returns Playwright options without launching a browser", async () => {
|
||||
const freshConfig = await import("../src/config.js");
|
||||
vi.spyOn(freshConfig, "getPlatformTag").mockReturnValue("darwin-arm64");
|
||||
|
||||
@@ -65,6 +65,53 @@ describe("puppeteer launch", () => {
|
||||
expect(callArgs.args.some((a: string) => a.startsWith("--fingerprint="))).toBe(false);
|
||||
});
|
||||
|
||||
it("headless (default) uses a fixed defaultViewport; headed uses null", async () => {
|
||||
const { DEFAULT_VIEWPORT } = await import("../src/config.js");
|
||||
const { launch } = await import("../src/puppeteer.js");
|
||||
|
||||
// Headless (default): deterministic viewport.
|
||||
await launch();
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toEqual(DEFAULT_VIEWPORT);
|
||||
|
||||
// Headed: null so the page tracks the real window (else Puppeteer forces 800x600).
|
||||
vi.mocked(puppeteerMock.default.launch).mockClear();
|
||||
await launch({ headless: false });
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("honors an explicit launchOptions.defaultViewport (incl. null)", async () => {
|
||||
const { launch } = await import("../src/puppeteer.js");
|
||||
|
||||
const custom = { width: 640, height: 480 };
|
||||
await launch({ headless: true, launchOptions: { defaultViewport: custom } });
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toEqual(custom);
|
||||
|
||||
// Explicit null honored even in headless (would otherwise default to DEFAULT_VIEWPORT).
|
||||
vi.mocked(puppeteerMock.default.launch).mockClear();
|
||||
await launch({ headless: true, launchOptions: { defaultViewport: null } });
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("Puppeteer headless precedence: top-level headless wins over launchOptions.headless", async () => {
|
||||
const { DEFAULT_VIEWPORT } = await import("../src/config.js");
|
||||
const { launch } = await import("../src/puppeteer.js");
|
||||
|
||||
// Puppeteer sets headless AFTER the launchOptions spread, so top-level wins at
|
||||
// launch — the viewport decision must follow the same (top-level) value.
|
||||
await launch({ headless: true, launchOptions: { headless: false } });
|
||||
const opts = vi.mocked(puppeteerMock.default.launch).mock.calls[0][0];
|
||||
expect(opts.headless).toBe(true);
|
||||
expect(opts.defaultViewport).toEqual(DEFAULT_VIEWPORT);
|
||||
});
|
||||
|
||||
it("adds --proxy-server for string proxy", async () => {
|
||||
const { launch } = await import("../src/puppeteer.js");
|
||||
await launch({ proxy: "http://proxy:8080" });
|
||||
@@ -212,6 +259,22 @@ describe("puppeteer launchPersistentContext", () => {
|
||||
expect(callArgs.args.some((a: string) => a.startsWith("--fingerprint="))).toBe(true);
|
||||
});
|
||||
|
||||
it("headed persistent context uses null defaultViewport (tracks real window)", async () => {
|
||||
const { DEFAULT_VIEWPORT } = await import("../src/config.js");
|
||||
const { launchPersistentContext } = await import("../src/puppeteer.js");
|
||||
|
||||
await launchPersistentContext({ userDataDir: "./my-profile", headless: false });
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toBeNull();
|
||||
|
||||
vi.mocked(puppeteerMock.default.launch).mockClear();
|
||||
await launchPersistentContext({ userDataDir: "./my-profile", headless: true });
|
||||
expect(
|
||||
vi.mocked(puppeteerMock.default.launch).mock.calls[0][0].defaultViewport
|
||||
).toEqual(DEFAULT_VIEWPORT);
|
||||
});
|
||||
|
||||
it("uses page.authenticate fallback for http proxy in persistent context on unsupported platform", async () => {
|
||||
const config = await import("../src/config.js");
|
||||
vi.spyOn(config, "getPlatformTag").mockReturnValue("darwin-arm64");
|
||||
|
||||
Reference in New Issue
Block a user