Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions js/src/playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ export function buildContextOptions(
export async function buildLaunchOptions(
options: LaunchOptions = {}
): Promise<PlaywrightLaunchOptions> {
// Standalone callers may pass `timezoneId` (Playwright's field name) instead
// of `timezone`. The launch* wrappers resolve this before delegating, but
// buildLaunchOptions is exported and used directly, and buildArgs only reads
// `options.timezone` to emit --fingerprint-timezone — so without this, a
// standalone `buildLaunchOptions({ timezoneId })` silently drops the flag.
options = resolveTimezone(options);
const binaryPath =
process.env.CLOAKBROWSER_BINARY_PATH ||
(await ensureBinary(options.licenseKey, options.browserVersion));
Expand Down
6 changes: 6 additions & 0 deletions js/src/puppeteer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "./config.js";
import { buildArgs } from "./args.js";
import { maybeWarnWindowsFonts } from "./fonts.js";
import { resolveTimezone } from "./playwright.js";
import { ensureBinary } from "./download.js";
import { isSocksProxy, normalizeHttpStringUrl, parseProxyUrl, reconstructHttpUrl, resolveProxyConfig, supportsHttpProxyInlineAuth } from "./proxy.js";
import { maybeResolveGeoip, resolveWebrtcArgs, appendWebrtcExitIp } from "./geoip.js";
Expand Down Expand Up @@ -45,6 +46,11 @@ function resolveDefaultViewport(options: LaunchOptions): { width: number; height

/** Resolve binary path, geoip, webrtc, and build final Chrome args. */
async function resolveArgs(options: LaunchOptions): Promise<{ binaryPath: string; args: string[] }> {
// Map Playwright's `timezoneId` onto `timezone` before buildArgs, which only
// reads `timezone` to emit --fingerprint-timezone. Every puppeteer entry
// point (launch/launchContext/launchPersistentContext) funnels through here,
// so resolving once covers all of them.
options = resolveTimezone(options);
const binaryPath =
process.env.CLOAKBROWSER_BINARY_PATH ||
(await ensureBinary(options.licenseKey, options.browserVersion));
Expand Down
34 changes: 33 additions & 1 deletion js/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
binarySupportsHeadlessNoViewport,
binarySupportsMaximizedWindow,
} from "../src/config.js";
import { _buildArgsForTest, resolveTimezone } from "../src/playwright.js";
import { _buildArgsForTest, resolveTimezone, buildLaunchOptions } from "../src/playwright.js";

describe("config", () => {
it("CHROMIUM_VERSION matches expected format", () => {
Expand Down Expand Up @@ -347,3 +347,35 @@ describe("buildArgs --start-maximized", () => {
expect(args.filter(a => a === "--start-maximized")).toHaveLength(1);
});
});

describe("buildLaunchOptions timezoneId resolution", () => {
// Standalone callers (not going through launch*/launchContext) may pass
// Playwright's `timezoneId`. buildArgs only reads `timezone`, so without
// resolveTimezone inside buildLaunchOptions the --fingerprint-timezone flag
// is silently dropped. Regression guard for that gap.
it("resolves timezoneId to --fingerprint-timezone for standalone callers", async () => {
const prev = process.env.CLOAKBROWSER_BINARY_PATH;
process.env.CLOAKBROWSER_BINARY_PATH = "/tmp/fake-cloak-binary";
try {
const opts = await buildLaunchOptions({
timezoneId: "Europe/Paris",
} as unknown as Parameters<typeof buildLaunchOptions>[0]);
expect(opts.args).toContain("--fingerprint-timezone=Europe/Paris");
} finally {
if (prev === undefined) delete process.env.CLOAKBROWSER_BINARY_PATH;
else process.env.CLOAKBROWSER_BINARY_PATH = prev;
}
});

it("still emits the flag when an explicit timezone is passed", async () => {
const prev = process.env.CLOAKBROWSER_BINARY_PATH;
process.env.CLOAKBROWSER_BINARY_PATH = "/tmp/fake-cloak-binary";
try {
const opts = await buildLaunchOptions({ timezone: "UTC" });
expect(opts.args).toContain("--fingerprint-timezone=UTC");
} finally {
if (prev === undefined) delete process.env.CLOAKBROWSER_BINARY_PATH;
else process.env.CLOAKBROWSER_BINARY_PATH = prev;
}
});
});
8 changes: 8 additions & 0 deletions js/tests/puppeteer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ describe("puppeteer launch", () => {
expect(callArgs.args).toContain("--lang=ja-JP");
});

it("resolves timezoneId to --fingerprint-timezone", async () => {
const { launch } = await import("../src/puppeteer.js");
await launch({ timezoneId: "Asia/Tokyo" } as unknown as Parameters<typeof launch>[0]);

const callArgs = vi.mocked(puppeteerMock.default.launch).mock.calls[0][0];
expect(callArgs.args).toContain("--fingerprint-timezone=Asia/Tokyo");
});

it("merges extra args", async () => {
const { launch } = await import("../src/puppeteer.js");
await launch({ args: ["--disable-gpu", "--no-first-run"] });
Expand Down