feat(js): export buildContextOptions helper (#262)

Co-authored-by: 이민재 <19909783+honor2030@users.noreply.github.com>
This commit is contained in:
이민재
2026-05-25 00:15:27 +02:00
committed by GitHub
co-authored by 이민재
parent 0f3dc7201b
commit 243c1385a0
3 changed files with 61 additions and 16 deletions
+1 -1
View File
@@ -16,7 +16,7 @@
*/ */
// Launch functions (Playwright API) // Launch functions (Playwright API)
export { launch, launchContext, launchPersistentContext, buildLaunchOptions, humanizeBrowser } from "./playwright.js"; export { launch, launchContext, launchPersistentContext, buildLaunchOptions, buildContextOptions, humanizeBrowser } from "./playwright.js";
// Binary management // Binary management
export { ensureBinary, clearCache, binaryInfo, checkForUpdate } from "./download.js"; export { ensureBinary, clearCache, binaryInfo, checkForUpdate } from "./download.js";
+22 -14
View File
@@ -44,6 +44,26 @@ function filterStealthCtxOptions(ctx?: BrowserContextOptions): Partial<BrowserCo
return rest; return rest;
} }
/**
* Build Playwright BrowserContext options for CloakBrowser without launching a browser
* or creating a context.
*
* Useful when integrating CloakBrowser with an existing Playwright Browser while
* keeping the wrapper's stealth-safe defaults for `newContext()`.
*/
export function buildContextOptions(
options: LaunchContextOptions = {}
): BrowserContextOptions {
return {
// contextOptions first — explicit wrapper fields below override it.
// filterStealthCtxOptions strips locale/timezoneId to prevent CDP detection.
...filterStealthCtxOptions(options.contextOptions),
...(options.userAgent ? { userAgent: options.userAgent } : {}),
viewport: options.viewport === undefined ? DEFAULT_VIEWPORT : options.viewport,
...(options.colorScheme ? { colorScheme: options.colorScheme } : {}),
} as BrowserContextOptions;
}
/** /**
* Build Playwright launch options for CloakBrowser without starting Chromium. * Build Playwright launch options for CloakBrowser without starting Chromium.
* *
@@ -144,14 +164,7 @@ export async function launchContext(
let context: BrowserContext; let context: BrowserContext;
try { try {
context = await browser.newContext({ context = await browser.newContext(buildContextOptions(options));
// contextOptions first — explicit wrapper fields below override it.
// filterStealthCtxOptions strips locale/timezoneId to prevent CDP detection.
...filterStealthCtxOptions(options.contextOptions),
...(options.userAgent ? { userAgent: options.userAgent } : {}),
viewport: options.viewport === undefined ? DEFAULT_VIEWPORT : options.viewport,
...(options.colorScheme ? { colorScheme: options.colorScheme } : {}),
});
} catch (err) { } catch (err) {
await browser.close(); await browser.close();
throw err; throw err;
@@ -222,12 +235,7 @@ export async function launchPersistentContext(
args, args,
ignoreDefaultArgs: IGNORE_DEFAULT_ARGS, ignoreDefaultArgs: IGNORE_DEFAULT_ARGS,
...(proxyOption ? { proxy: proxyOption } : {}), ...(proxyOption ? { proxy: proxyOption } : {}),
// contextOptions before explicit wrapper fields so explicit wins. ...buildContextOptions(options),
// filterStealthCtxOptions strips locale/timezoneId to prevent CDP detection.
...filterStealthCtxOptions(options.contextOptions),
...(options.userAgent ? { userAgent: options.userAgent } : {}),
viewport: options.viewport === undefined ? DEFAULT_VIEWPORT : options.viewport,
...(options.colorScheme ? { colorScheme: options.colorScheme } : {}),
...options.launchOptions, ...options.launchOptions,
}); });
+38 -1
View File
@@ -40,13 +40,50 @@ describe("composable Playwright launch helpers", () => {
} }
}); });
it("exports buildLaunchOptions and humanizeBrowser from the package entrypoint", async () => { it("exports composable helpers from the package entrypoint", async () => {
const entry = await import("../src/index.js"); const entry = await import("../src/index.js");
expect(entry.buildLaunchOptions).toBeTypeOf("function"); expect(entry.buildLaunchOptions).toBeTypeOf("function");
expect(entry.buildContextOptions).toBeTypeOf("function");
expect(entry.humanizeBrowser).toBeTypeOf("function"); expect(entry.humanizeBrowser).toBeTypeOf("function");
}); });
it("buildContextOptions returns Playwright context options without launching a browser", async () => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const { buildContextOptions } = await import("../src/index.js");
const options = buildContextOptions({
userAgent: "Explicit/1.0",
viewport: { width: 1280, height: 720 },
colorScheme: "dark",
contextOptions: {
userAgent: "Context/9.9",
viewport: { width: 9999, height: 9999 },
colorScheme: "light",
storageState: "state.json",
locale: "de-DE",
timezoneId: "Europe/Berlin",
},
});
expect(options).toMatchObject({
userAgent: "Explicit/1.0",
viewport: { width: 1280, height: 720 },
colorScheme: "dark",
storageState: "state.json",
});
expect(options.locale).toBeUndefined();
expect(options.timezoneId).toBeUndefined();
expect(warnSpy).toHaveBeenCalledTimes(2);
});
it("buildContextOptions applies DEFAULT_VIEWPORT by default and allows null viewport", async () => {
const { buildContextOptions } = await import("../src/index.js");
expect(buildContextOptions().viewport).toEqual(DEFAULT_VIEWPORT);
expect(buildContextOptions({ viewport: null }).viewport).toBeNull();
});
it("buildLaunchOptions returns Playwright options without launching a browser", async () => { it("buildLaunchOptions returns Playwright options without launching a browser", async () => {
const freshConfig = await import("../src/config.js"); const freshConfig = await import("../src/config.js");
vi.spyOn(freshConfig, "getPlatformTag").mockReturnValue("darwin-arm64"); vi.spyOn(freshConfig, "getPlatformTag").mockReturnValue("darwin-arm64");