chore: move some playwright-wide options to be per browser (#36342)

This commit is contained in:
Dmitry Gozman 2025-06-18 08:28:33 +01:00 committed by GitHub
parent a7ff65cb8b
commit 8fcf838c37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 43 additions and 35 deletions

View File

@ -14,7 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { SocksProxy } from './server/utils/socksProxy';
import { PlaywrightServer } from './remote/playwrightServer'; import { PlaywrightServer } from './remote/playwrightServer';
import { helper } from './server/helper'; import { helper } from './server/helper';
import { serverSideCallMetadata } from './server/instrumentation'; import { serverSideCallMetadata } from './server/instrumentation';
@ -41,10 +40,6 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
async launchServer(options: LaunchServerOptions & { _sharedBrowser?: boolean, _userDataDir?: string } = {}): Promise<BrowserServer> { async launchServer(options: LaunchServerOptions & { _sharedBrowser?: boolean, _userDataDir?: string } = {}): Promise<BrowserServer> {
const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true }); const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
// TODO: enable socks proxy once ipv6 is supported.
const socksProxy = false ? new SocksProxy() : undefined;
playwright.options.socksProxyPort = await socksProxy?.listen(0);
// 1. Pre-launch the browser // 1. Pre-launch the browser
const metadata = serverSideCallMetadata(); const metadata = serverSideCallMetadata();
const validatorContext = { const validatorContext = {
@ -83,7 +78,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`; const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`;
// 2. Start the server // 2. Start the server
const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy }); const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser });
const wsEndpoint = await server.listen(options.port, options.host); const wsEndpoint = await server.listen(options.port, options.host);
// 3. Return the BrowserServer interface // 3. Return the BrowserServer interface
@ -96,7 +91,6 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
(browserServer as any)._disconnectForTest = () => server.close(); (browserServer as any)._disconnectForTest = () => server.close();
(browserServer as any)._userDataDirForTest = (browser as any)._userDataDirForTest; (browserServer as any)._userDataDirForTest = (browser as any)._userDataDirForTest;
browser.options.browserProcess.onclose = (exitCode, signal) => { browser.options.browserProcess.onclose = (exitCode, signal) => {
socksProxy?.close().catch(() => {});
server.close(); server.close();
browserServer.emit('close', exitCode, signal); browserServer.emit('close', exitCode, signal);
}; };

View File

@ -107,9 +107,9 @@ export class PlaywrightConnection {
this._root = new RootDispatcher(this._dispatcherConnection, async (scope, options) => { this._root = new RootDispatcher(this._dispatcherConnection, async (scope, options) => {
await startProfiling(); await startProfiling();
if (clientType === 'reuse-browser') if (clientType === 'reuse-browser')
return await this._initReuseBrowsersMode(scope); return await this._initReuseBrowsersMode(scope, options);
if (clientType === 'pre-launched-browser-or-android') if (clientType === 'pre-launched-browser-or-android')
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope); return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope, options) : await this._initPreLaunchedAndroidMode(scope);
if (clientType === 'launch-browser') if (clientType === 'launch-browser')
return await this._initLaunchBrowserMode(scope, options); return await this._initLaunchBrowserMode(scope, options);
throw new Error('Unsupported client type: ' + clientType); throw new Error('Unsupported client type: ' + clientType);
@ -120,7 +120,7 @@ export class PlaywrightConnection {
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`); debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const playwright = createPlaywright({ sdkLanguage: options.sdkLanguage, isServer: true }); const playwright = createPlaywright({ sdkLanguage: options.sdkLanguage, isServer: true });
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright); const ownedSocksProxy = await this._createOwnedSocksProxy();
let browserName = this._options.browserName; let browserName = this._options.browserName;
if ('bidi' === browserName) { if ('bidi' === browserName) {
if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes('firefox')) if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes('firefox'))
@ -129,6 +129,7 @@ export class PlaywrightConnection {
browserName = 'bidiChromium'; browserName = 'bidiChromium';
} }
const browser = await playwright[browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions); const browser = await playwright[browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);
browser.options.sdkLanguage = options.sdkLanguage;
this._cleanups.push(async () => { this._cleanups.push(async () => {
for (const browser of playwright.allBrowsers()) for (const browser of playwright.allBrowsers())
@ -142,7 +143,7 @@ export class PlaywrightConnection {
return new PlaywrightDispatcher(scope, playwright, { socksProxy: ownedSocksProxy, preLaunchedBrowser: browser }); return new PlaywrightDispatcher(scope, playwright, { socksProxy: ownedSocksProxy, preLaunchedBrowser: browser });
} }
private async _initPreLaunchedBrowserMode(scope: RootDispatcher) { private async _initPreLaunchedBrowserMode(scope: RootDispatcher, options: channels.RootInitializeParams) {
debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`); debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`);
const playwright = this._preLaunched.playwright!; const playwright = this._preLaunched.playwright!;
@ -150,6 +151,7 @@ export class PlaywrightConnection {
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern); this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
const browser = this._preLaunched.browser!; const browser = this._preLaunched.browser!;
browser.options.sdkLanguage = options.sdkLanguage;
browser.on(Browser.Events.Disconnected, () => { browser.on(Browser.Events.Disconnected, () => {
// Underlying browser did close for some reason - force disconnect the client. // Underlying browser did close for some reason - force disconnect the client.
this.close({ code: 1001, reason: 'Browser closed' }); this.close({ code: 1001, reason: 'Browser closed' });
@ -189,7 +191,7 @@ export class PlaywrightConnection {
return new DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController); return new DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
} }
private async _initReuseBrowsersMode(scope: RootDispatcher) { private async _initReuseBrowsersMode(scope: RootDispatcher, options: channels.RootInitializeParams) {
// Note: reuse browser mode does not support socks proxy, because // Note: reuse browser mode does not support socks proxy, because
// clients come and go, while the browser stays the same. // clients come and go, while the browser stays the same.
@ -222,6 +224,7 @@ export class PlaywrightConnection {
this.close({ code: 1001, reason: 'Browser closed' }); this.close({ code: 1001, reason: 'Browser closed' });
}); });
} }
browser.options.sdkLanguage = options.sdkLanguage;
this._cleanups.push(async () => { this._cleanups.push(async () => {
// Don't close the pages so that user could debug them, // Don't close the pages so that user could debug them,
@ -242,13 +245,15 @@ export class PlaywrightConnection {
return playwrightDispatcher; return playwrightDispatcher;
} }
private async _createOwnedSocksProxy(playwright: Playwright): Promise<SocksProxy | undefined> { private async _createOwnedSocksProxy(): Promise<SocksProxy | undefined> {
if (!this._options.socksProxyPattern) if (!this._options.socksProxyPattern) {
this._options.launchOptions.socksProxyPort = undefined;
return; return;
}
const socksProxy = new SocksProxy(); const socksProxy = new SocksProxy();
socksProxy.setPattern(this._options.socksProxyPattern); socksProxy.setPattern(this._options.socksProxyPattern);
playwright.options.socksProxyPort = await socksProxy.listen(0); this._options.launchOptions.socksProxyPort = await socksProxy.listen(0);
debugLogger.log('server', `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`); debugLogger.log('server', `[${this._id}] started socks proxy on port ${this._options.launchOptions.socksProxyPort}`);
this._cleanups.push(() => socksProxy.close()); this._cleanups.push(() => socksProxy.close());
return socksProxy; return socksProxy;
} }

View File

@ -144,14 +144,14 @@ export class BidiChromium extends BrowserType {
const proxyURL = new URL(proxy.server); const proxyURL = new URL(proxy.server);
const isSocks = proxyURL.protocol === 'socks5:'; const isSocks = proxyURL.protocol === 'socks5:';
// https://www.chromium.org/developers/design-documents/network-settings // https://www.chromium.org/developers/design-documents/network-settings
if (isSocks && !this.attribution.playwright.options.socksProxyPort) { if (isSocks && !options.socksProxyPort) {
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy // https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`); chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
} }
chromeArguments.push(`--proxy-server=${proxy.server}`); chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = []; const proxyBypassRules = [];
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578 // https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
if (this.attribution.playwright.options.socksProxyPort) if (options.socksProxyPort)
proxyBypassRules.push('<-loopback>'); proxyBypassRules.push('<-loopback>');
if (proxy.bypass) if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t)); proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));

View File

@ -27,6 +27,7 @@ import type { ProxySettings } from './types';
import type { RecentLogsCollector } from './utils/debugLogger'; import type { RecentLogsCollector } from './utils/debugLogger';
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
import type { ChildProcess } from 'child_process'; import type { ChildProcess } from 'child_process';
import type { Language } from '../utils';
export interface BrowserProcess { export interface BrowserProcess {
@ -52,6 +53,7 @@ export type BrowserOptions = {
browserLogsCollector: RecentLogsCollector, browserLogsCollector: RecentLogsCollector,
slowMo?: number; slowMo?: number;
wsEndpoint?: string; // Only there when connected over web socket. wsEndpoint?: string; // Only there when connected over web socket.
sdkLanguage?: Language;
originalLaunchOptions: types.LaunchOptions; originalLaunchOptions: types.LaunchOptions;
}; };
@ -84,6 +86,10 @@ export abstract class Browser extends SdkObject {
abstract version(): string; abstract version(): string;
abstract userAgent(): string; abstract userAgent(): string;
sdkLanguage() {
return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage;
}
async newContext(metadata: CallMetadata, options: types.BrowserContextOptions): Promise<BrowserContext> { async newContext(metadata: CallMetadata, options: types.BrowserContextOptions): Promise<BrowserContext> {
validateBrowserContextOptions(options, this.options); validateBrowserContextOptions(options, this.options);
let clientCertificatesProxy: ClientCertificatesProxy | undefined; let clientCertificatesProxy: ClientCertificatesProxy | undefined;

View File

@ -79,7 +79,7 @@ export abstract class BrowserType extends SdkObject {
return browser; return browser;
} }
async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { timeout: number, cdpPort?: number, internalIgnoreHTTPSErrors?: boolean }): Promise<BrowserContext> { async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { timeout: number, cdpPort?: number, internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
const launchOptions = this._validateLaunchOptions(options); const launchOptions = this._validateLaunchOptions(options);
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
const browser = await controller.run(async progress => { const browser = await controller.run(async progress => {
@ -288,8 +288,8 @@ export abstract class BrowserType extends SdkObject {
headless = false; headless = false;
if (downloadsPath && !path.isAbsolute(downloadsPath)) if (downloadsPath && !path.isAbsolute(downloadsPath))
downloadsPath = path.join(process.cwd(), downloadsPath); downloadsPath = path.join(process.cwd(), downloadsPath);
if (this.attribution.playwright.options.socksProxyPort) if (options.socksProxyPort)
proxy = { server: `socks5://127.0.0.1:${this.attribution.playwright.options.socksProxyPort}` }; proxy = { server: `socks5://127.0.0.1:${options.socksProxyPort}` };
return { ...options, devtools, headless, downloadsPath, proxy }; return { ...options, devtools, headless, downloadsPath, proxy };
} }

View File

@ -331,14 +331,14 @@ export class Chromium extends BrowserType {
const proxyURL = new URL(proxy.server); const proxyURL = new URL(proxy.server);
const isSocks = proxyURL.protocol === 'socks5:'; const isSocks = proxyURL.protocol === 'socks5:';
// https://www.chromium.org/developers/design-documents/network-settings // https://www.chromium.org/developers/design-documents/network-settings
if (isSocks && !this.attribution.playwright.options.socksProxyPort) { if (isSocks && !options.socksProxyPort) {
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy // https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`); chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
} }
chromeArguments.push(`--proxy-server=${proxy.server}`); chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = []; const proxyBypassRules = [];
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578 // https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
if (this.attribution.playwright.options.socksProxyPort) if (options.socksProxyPort)
proxyBypassRules.push('<-loopback>'); proxyBypassRules.push('<-loopback>');
if (proxy.bypass) if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t)); proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));

View File

@ -894,7 +894,7 @@ class FrameSession {
async _createVideoRecorder(screencastId: string, options: types.PageScreencastOptions): Promise<void> { async _createVideoRecorder(screencastId: string, options: types.PageScreencastOptions): Promise<void> {
assert(!this._screencastId); assert(!this._screencastId);
const ffmpegPath = registry.findExecutable('ffmpeg')!.executablePathOrDie(this._page.attribution.playwright.options.sdkLanguage); const ffmpegPath = registry.findExecutable('ffmpeg')!.executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
this._videoRecorder = await VideoRecorder.launch(this._crPage._page, ffmpegPath, options); this._videoRecorder = await VideoRecorder.launch(this._crPage._page, ffmpegPath, options);
this._screencastId = screencastId; this._screencastId = screencastId;
} }

View File

@ -87,7 +87,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
const selectorsRegistry = this.frame._page.browserContext.selectors(); const selectorsRegistry = this.frame._page.browserContext.selectors();
for (const [name, { source }] of selectorsRegistry._engines) for (const [name, { source }] of selectorsRegistry._engines)
customEngines.push({ name, source: `(${source})` }); customEngines.push({ name, source: `(${source})` });
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage; const sdkLanguage = this.frame._page.browserContext._browser.sdkLanguage();
const options: InjectedScriptOptions = { const options: InjectedScriptOptions = {
isUnderTest: isUnderTest(), isUnderTest: isUnderTest(),
sdkLanguage, sdkLanguage,

View File

@ -134,7 +134,7 @@ export class FrameSelectors {
for (const chunk of frameChunks) { for (const chunk of frameChunks) {
visitAllSelectorParts(chunk, (part, nested) => { visitAllSelectorParts(chunk, (part, nested) => {
if (nested && part.name === 'internal:control' && part.body === 'enter-frame') { if (nested && part.name === 'internal:control' && part.body === 'enter-frame') {
const locator = asLocator(this.frame._page.attribution.playwright.options.sdkLanguage, selector); const locator = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`); throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`);
} }
}); });

View File

@ -1711,7 +1711,7 @@ export class Frame extends SdkObject {
} }
private _asLocator(selector: string) { private _asLocator(selector: string) {
return asLocator(this._page.attribution.playwright.options.sdkLanguage, selector); return asLocator(this._page.browserContext._browser.sdkLanguage(), selector);
} }
} }

View File

@ -484,11 +484,11 @@ export class Page extends SdkObject {
} }
if (handler.resolved) { if (handler.resolved) {
++this._locatorHandlerRunningCounter; ++this._locatorHandlerRunningCounter;
progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`); progress.log(` found ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)}, intercepting action to run the handler`);
const promise = handler.resolved.then(async () => { const promise = handler.resolved.then(async () => {
progress.throwIfAborted(); progress.throwIfAborted();
if (!handler.noWaitAfter) { if (!handler.noWaitAfter) {
progress.log(` locator handler has finished, waiting for ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)} to be hidden`); progress.log(` locator handler has finished, waiting for ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)} to be hidden`);
await this.mainFrame().waitForSelectorInternal(progress, handler.selector, false, { state: 'hidden' }); await this.mainFrame().waitForSelectorInternal(progress, handler.selector, false, { state: 'hidden' });
} else { } else {
progress.log(` locator handler has finished`); progress.log(` locator handler has finished`);

View File

@ -33,7 +33,6 @@ import type { CallMetadata } from './instrumentation';
import type { Page } from './page'; import type { Page } from './page';
type PlaywrightOptions = { type PlaywrightOptions = {
socksProxyPort?: number;
sdkLanguage: Language; sdkLanguage: Language;
isInternalPlaywright?: boolean; isInternalPlaywright?: boolean;
isServer?: boolean; isServer?: boolean;

View File

@ -63,7 +63,7 @@ export class ContextRecorder extends EventEmitter {
this._params = params; this._params = params;
this._delegate = delegate; this._delegate = delegate;
this._recorderSources = []; this._recorderSources = [];
const language = params.language || context.attribution.playwright.options.sdkLanguage; const language = params.language || context._browser.sdkLanguage();
this.setOutput(language, params.outputFile); this.setOutput(language, params.outputFile);
// Make a copy of options to modify them later. // Make a copy of options to modify them later.

View File

@ -103,7 +103,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
} }
private static async _open(recorder: IRecorder, inspectedContext: BrowserContext): Promise<IRecorderApp> { private static async _open(recorder: IRecorder, inspectedContext: BrowserContext): Promise<IRecorderApp> {
const sdkLanguage = inspectedContext.attribution.playwright.options.sdkLanguage; const sdkLanguage = inspectedContext._browser.sdkLanguage();
const headed = !!inspectedContext._browser.options.headful; const headed = !!inspectedContext._browser.options.headful;
const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)({ sdkLanguage: 'javascript', isInternalPlaywright: true }); const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)({ sdkLanguage: 'javascript', isInternalPlaywright: true });
const { context, page } = await launchApp(recorderPlaywright.chromium, { const { context, page } = await launchApp(recorderPlaywright.chromium, {

View File

@ -109,7 +109,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
platform: process.platform, platform: process.platform,
wallTime: 0, wallTime: 0,
monotonicTime: 0, monotonicTime: 0,
sdkLanguage: context.attribution.playwright.options.sdkLanguage, sdkLanguage: this._sdkLanguage(),
testIdAttributeName, testIdAttributeName,
contextId: context.guid, contextId: context.guid,
}; };
@ -122,6 +122,10 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
} }
} }
private _sdkLanguage() {
return this._context instanceof BrowserContext ? this._context._browser.sdkLanguage() : this._context.attribution.playwright.options.sdkLanguage;
}
async resetForReuse() { async resetForReuse() {
// Discard previous chunk if any and ignore any errors there. // Discard previous chunk if any and ignore any errors there.
await this.stopChunk({ mode: 'discard' }).catch(() => {}); await this.stopChunk({ mode: 'discard' }).catch(() => {});
@ -136,7 +140,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
throw new Error('Tracing has been already started'); throw new Error('Tracing has been already started');
// Re-write for testing. // Re-write for testing.
this._contextCreatedEvent.sdkLanguage = this._context.attribution.playwright.options.sdkLanguage; this._contextCreatedEvent.sdkLanguage = this._sdkLanguage();
// TODO: passing the same name for two contexts makes them write into a single file // TODO: passing the same name for two contexts makes them write into a single file
// and conflict. // and conflict.

View File

@ -169,7 +169,6 @@ export async function openTraceViewerApp(url: string, browserName: string, optio
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName; const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
const { context, page } = await launchApp(traceViewerPlaywright[traceViewerBrowser as 'chromium'], { const { context, page } = await launchApp(traceViewerPlaywright[traceViewerBrowser as 'chromium'], {
// TODO: store language in the trace.
sdkLanguage: traceViewerPlaywright.options.sdkLanguage, sdkLanguage: traceViewerPlaywright.options.sdkLanguage,
windowSize: { width: 1280, height: 800 }, windowSize: { width: 1280, height: 800 },
persistentContextOptions: { persistentContextOptions: {

View File

@ -158,6 +158,7 @@ export type LaunchOptions = channels.BrowserTypeLaunchParams & {
cdpPort?: number, cdpPort?: number,
proxyOverride?: ProxySettings, proxyOverride?: ProxySettings,
assistantMode?: boolean, assistantMode?: boolean,
socksProxyPort?: number,
}; };
export type BrowserContextOptions = channels.BrowserNewContextOptions & { export type BrowserContextOptions = channels.BrowserNewContextOptions & {