chore: add Playwright to attribution (#23447)

This makes it easier to plumb all kinds of options around.
This commit is contained in:
Dmitry Gozman 2023-06-01 17:54:43 -07:00 committed by GitHub
parent 2719f408b2
commit 14a1eaa474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 96 additions and 104 deletions

View File

@ -24,7 +24,7 @@ import { PlaywrightServer } from './remote/playwrightServer';
export class AndroidServerLauncherImpl {
async launchServer(options: LaunchAndroidServerOptions = {}): Promise<BrowserServer> {
const playwright = createPlaywright('javascript');
const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
// 1. Pre-connect to the device
let devices = await playwright.android.devices({
host: options.adbHost,

View File

@ -36,7 +36,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
}
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
const playwright = createPlaywright('javascript');
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);

View File

@ -33,7 +33,7 @@ export function printApiJson() {
export function runDriver() {
const dispatcherConnection = new DispatcherConnection();
new RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => {
const playwright = createPlaywright(sdkLanguage);
const playwright = createPlaywright({ sdkLanguage });
return new PlaywrightDispatcher(rootScope, playwright);
});
const transport = new PipeTransport(process.stdout, process.stdin);

View File

@ -22,7 +22,7 @@ import { AndroidServerLauncherImpl } from './androidServerImpl';
import type { Language } from './utils/isomorphic/locatorGenerators';
export function createInProcessPlaywright(): PlaywrightAPI {
const playwright = createPlaywright((process.env.PW_LANG_NAME as Language | undefined) || 'javascript');
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });
const clientConnection = new Connection();
const dispatcherConnection = new DispatcherConnection(true /* local */);

View File

@ -108,7 +108,7 @@ export class PlaywrightConnection {
private async _initLaunchBrowserMode(scope: RootDispatcher) {
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const playwright = createPlaywright('javascript');
const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
const browser = await playwright[this._options.browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);

View File

@ -49,9 +49,9 @@ export class PlaywrightServer {
constructor(options: ServerOptions) {
this._options = options;
if (options.preLaunchedBrowser)
this._preLaunchedPlaywright = options.preLaunchedBrowser.options.rootSdkObject as Playwright;
this._preLaunchedPlaywright = options.preLaunchedBrowser.attribution.playwright;
if (options.preLaunchedAndroidDevice)
this._preLaunchedPlaywright = options.preLaunchedAndroidDevice._android._playwrightOptions.rootSdkObject as Playwright;
this._preLaunchedPlaywright = options.preLaunchedAndroidDevice._android.attribution.playwright;
}
async listen(port: number = 0): Promise<string> {
@ -114,7 +114,7 @@ export class PlaywrightServer {
const isExtension = this._options.mode === 'extension';
if (isExtension) {
if (!this._preLaunchedPlaywright)
this._preLaunchedPlaywright = createPlaywright('javascript');
this._preLaunchedPlaywright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
}
let clientType: ClientType = 'launch-browser';

View File

@ -23,7 +23,7 @@ import type * as stream from 'stream';
import { wsReceiver, wsSender } from '../../utilsBundle';
import { createGuid, makeWaitForNextTask, isUnderTest } from '../../utils';
import { removeFolders } from '../../utils/fileUtils';
import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import type { BrowserOptions, BrowserProcess } from '../browser';
import type { BrowserContext } from '../browserContext';
import { validateBrowserContextOptions } from '../browserContext';
import { ProgressController } from '../progress';
@ -63,12 +63,10 @@ export class Android extends SdkObject {
private _backend: Backend;
private _devices = new Map<string, AndroidDevice>();
readonly _timeoutSettings: TimeoutSettings;
readonly _playwrightOptions: PlaywrightOptions;
constructor(backend: Backend, playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject, 'android');
constructor(parent: SdkObject, backend: Backend) {
super(parent, 'android');
this._backend = backend;
this._playwrightOptions = playwrightOptions;
this._timeoutSettings = new TimeoutSettings();
}
@ -326,7 +324,6 @@ export class AndroidDevice extends SdkObject {
cleanupArtifactsDir().catch(e => debug('pw:android')(`could not cleanup artifacts dir: ${e}`));
});
const browserOptions: BrowserOptions = {
...this._android._playwrightOptions,
name: 'clank',
isChromium: true,
slowMo: 0,
@ -342,7 +339,7 @@ export class AndroidDevice extends SdkObject {
};
validateBrowserContextOptions(options, browserOptions);
const browser = await CRBrowser.connect(androidBrowser, browserOptions);
const browser = await CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions);
const controller = new ProgressController(serverSideCallMetadata(), this);
const defaultContext = browser._defaultContext!;
await controller.run(async progress => {

View File

@ -25,8 +25,6 @@ import type { RecentLogsCollector } from '../common/debugLogger';
import type { CallMetadata } from './instrumentation';
import { SdkObject } from './instrumentation';
import { Artifact } from './artifact';
import type { Selectors } from './selectors';
import type { Language } from '../utils/isomorphic/locatorGenerators';
export interface BrowserProcess {
onclose?: ((exitCode: number | null, signal: string | null) => void);
@ -35,14 +33,7 @@ export interface BrowserProcess {
close(): Promise<void>;
}
export type PlaywrightOptions = {
rootSdkObject: SdkObject;
selectors: Selectors;
socksProxyPort?: number;
sdkLanguage: Language,
};
export type BrowserOptions = PlaywrightOptions & {
export type BrowserOptions = {
name: string,
isChromium: boolean,
channel?: string,
@ -74,8 +65,8 @@ export abstract class Browser extends SdkObject {
readonly _idToVideo = new Map<string, { context: BrowserContext, artifact: Artifact }>();
private _contextForReuse: { context: BrowserContext, hash: string } | undefined;
constructor(options: BrowserOptions) {
super(options.rootSdkObject, 'browser');
constructor(parent: SdkObject, options: BrowserOptions) {
super(parent, 'browser');
this.attribution.browser = this;
this.options = options;
this.instrumentation.onBrowserOpen(this);

View File

@ -107,11 +107,11 @@ export abstract class BrowserContext extends SdkObject {
}
selectors(): Selectors {
return this._selectors || this._browser.options.selectors;
return this._selectors || this.attribution.playwright.selectors;
}
async _initialize() {
if (this.attribution.isInternalPlaywright)
if (this.attribution.playwright.options.isInternalPlaywright)
return;
// Debugger will pause execution upon page.pause in headed mode.
this._debugger = new Debugger(this);

View File

@ -23,7 +23,7 @@ import type { BrowserName } from './registry';
import { registry } from './registry';
import type { ConnectionTransport } from './transport';
import { WebSocketTransport } from './transport';
import type { BrowserOptions, Browser, BrowserProcess, PlaywrightOptions } from './browser';
import type { BrowserOptions, Browser, BrowserProcess } from './browser';
import type { Env } from '../utils/processLauncher';
import { launchProcess, envArrayToObject } from '../utils/processLauncher';
import { PipeTransport } from './pipeTransport';
@ -45,17 +45,15 @@ export const kNoXServerRunningError = 'Looks like you launched a headed browser
export abstract class BrowserType extends SdkObject {
private _name: BrowserName;
readonly _playwrightOptions: PlaywrightOptions;
constructor(browserName: BrowserName, playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject, 'browser-type');
constructor(parent: SdkObject, browserName: BrowserName) {
super(parent, 'browser-type');
this.attribution.browserType = this;
this._playwrightOptions = playwrightOptions;
this._name = browserName;
}
executablePath(): string {
return registry.findExecutable(this._name).executablePath(this._playwrightOptions.sdkLanguage) || '';
return registry.findExecutable(this._name).executablePath(this.attribution.playwright.options.sdkLanguage) || '';
}
name(): string {
@ -107,7 +105,6 @@ export abstract class BrowserType extends SdkObject {
if ((options as any).__testHookBeforeCreateBrowser)
await (options as any).__testHookBeforeCreateBrowser();
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
name: this._name,
isChromium: this._name === 'chromium',
channel: options.channel,
@ -184,8 +181,8 @@ export abstract class BrowserType extends SdkObject {
const registryExecutable = registry.findExecutable(options.channel || this._name);
if (!registryExecutable || registryExecutable.browserName !== this._name)
throw new Error(`Unsupported ${this._name} channel "${options.channel}"`);
executable = registryExecutable.executablePathOrDie(this._playwrightOptions.sdkLanguage);
await registryExecutable.validateHostRequirements(this._playwrightOptions.sdkLanguage);
executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
await registryExecutable.validateHostRequirements(this.attribution.playwright.options.sdkLanguage);
}
const waitForWSEndpoint = (options.useWebSocket || options.args?.some(a => a.startsWith('--remote-debugging-port'))) ? new ManualPromise<string>() : undefined;
@ -275,8 +272,8 @@ export abstract class BrowserType extends SdkObject {
headless = false;
if (downloadsPath && !path.isAbsolute(downloadsPath))
downloadsPath = path.join(process.cwd(), downloadsPath);
if (this._playwrightOptions.socksProxyPort)
proxy = { server: `socks5://127.0.0.1:${this._playwrightOptions.socksProxyPort}` };
if (this.attribution.playwright.options.socksProxyPort)
proxy = { server: `socks5://127.0.0.1:${this.attribution.playwright.options.socksProxyPort}` };
return { ...options, devtools, headless, downloadsPath, proxy };
}

View File

@ -28,7 +28,7 @@ import { BrowserType, kNoXServerRunningError } from '../browserType';
import type { ConnectionTransport, ProtocolRequest } from '../transport';
import { WebSocketTransport } from '../transport';
import { CRDevTools } from './crDevTools';
import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import type { BrowserOptions, BrowserProcess } from '../browser';
import { Browser } from '../browser';
import type * as types from '../types';
import type * as channels from '@protocol/channels';
@ -43,7 +43,7 @@ import type { Progress } from '../progress';
import { ProgressController } from '../progress';
import { TimeoutSettings } from '../../common/timeoutSettings';
import { helper } from '../helper';
import type { CallMetadata } from '../instrumentation';
import type { CallMetadata, SdkObject } from '../instrumentation';
import type http from 'http';
import { registry } from '../registry';
import { ManualPromise } from '../../utils/manualPromise';
@ -55,8 +55,8 @@ const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
export class Chromium extends BrowserType {
private _devtools: CRDevTools | undefined;
constructor(playwrightOptions: PlaywrightOptions) {
super('chromium', playwrightOptions);
constructor(parent: SdkObject) {
super(parent, 'chromium');
if (debugMode())
this._devtools = this._createDevTools();
@ -99,7 +99,6 @@ export class Chromium extends BrowserType {
const browserProcess: BrowserProcess = { close: doClose, kill: doClose };
const persistent: channels.BrowserNewContextParams = { noDefaultViewport: true };
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
slowMo: options.slowMo,
name: 'chromium',
isChromium: true,
@ -120,7 +119,7 @@ export class Chromium extends BrowserType {
};
validateBrowserContextOptions(persistent, browserOptions);
progress.throwIfAborted();
const browser = await CRBrowser.connect(chromeTransport, browserOptions);
const browser = await CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
browser.on(Browser.Events.Disconnected, doCleanup);
return browser;
}
@ -137,7 +136,7 @@ export class Chromium extends BrowserType {
devtools = this._createDevTools();
await (options as any).__testHookForDevTools(devtools);
}
return CRBrowser.connect(transport, options, devtools);
return CRBrowser.connect(this.attribution.playwright, transport, options, devtools);
}
_rewriteStartupError(error: Error): Error {
@ -309,14 +308,14 @@ export class Chromium extends BrowserType {
const proxyURL = new URL(proxy.server);
const isSocks = proxyURL.protocol === 'socks5:';
// https://www.chromium.org/developers/design-documents/network-settings
if (isSocks && !this._playwrightOptions.socksProxyPort) {
if (isSocks && !this.attribution.playwright.options.socksProxyPort) {
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
}
chromeArguments.push(`--proxy-server=${proxy.server}`);
const proxyBypassRules = [];
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
if (this._playwrightOptions.socksProxyPort)
if (this.attribution.playwright.options.socksProxyPort)
proxyBypassRules.push('<-loopback>');
if (proxy.bypass)
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));

View File

@ -34,6 +34,7 @@ import { readProtocolStream } from './crProtocolHelper';
import type { Protocol } from './protocol';
import type { CRDevTools } from './crDevTools';
import { CRServiceWorker } from './crServiceWorker';
import type { SdkObject } from '../instrumentation';
export class CRBrowser extends Browser {
readonly _connection: CRConnection;
@ -51,11 +52,11 @@ export class CRBrowser extends Browser {
private _tracingClient: CRSession | undefined;
private _userAgent: string = '';
static async connect(transport: ConnectionTransport, options: BrowserOptions, devtools?: CRDevTools): Promise<CRBrowser> {
static async connect(parent: SdkObject, transport: ConnectionTransport, options: BrowserOptions, devtools?: CRDevTools): Promise<CRBrowser> {
// Make a copy in case we need to update `headful` property below.
options = { ...options };
const connection = new CRConnection(transport, options.protocolLogger, options.browserLogsCollector);
const browser = new CRBrowser(connection, options);
const browser = new CRBrowser(parent, connection, options);
browser._devtools = devtools;
const session = connection.rootSession;
if ((options as any).__testHookOnConnectToBrowser)
@ -85,8 +86,8 @@ export class CRBrowser extends Browser {
return browser;
}
constructor(connection: CRConnection, options: BrowserOptions) {
super(options);
constructor(parent: SdkObject, connection: CRConnection, options: BrowserOptions) {
super(parent, options);
this._connection = connection;
this._session = this._connection.rootSession;
this._connection.on(ConnectionEvents.Disconnected, () => this._didClose());

View File

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

View File

@ -86,7 +86,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
const selectorsRegistry = this.frame._page.context().selectors();
for (const [name, { source }] of selectorsRegistry._engines)
custom.push(`{ name: '${name}', engine: (${source}) }`);
const sdkLanguage = this.frame._page.context()._browser.options.sdkLanguage;
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
const source = `
(() => {
const module = {};

View File

@ -35,7 +35,8 @@ import type { Progress } from '../progress';
import { ProgressController } from '../progress';
import { helper } from '../helper';
import { eventsHelper } from '../../utils/eventsHelper';
import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import type { BrowserOptions, BrowserProcess } from '../browser';
import type { Playwright } from '../playwright';
import type * as childProcess from 'child_process';
import * as readline from 'readline';
import { RecentLogsCollector } from '../../common/debugLogger';
@ -116,11 +117,8 @@ export class ElectronApplication extends SdkObject {
}
export class Electron extends SdkObject {
private _playwrightOptions: PlaywrightOptions;
constructor(playwrightOptions: PlaywrightOptions) {
super(playwrightOptions.rootSdkObject, 'electron');
this._playwrightOptions = playwrightOptions;
constructor(playwright: Playwright) {
super(playwright, 'electron');
}
async launch(options: channels.ElectronLaunchParams): Promise<ElectronApplication> {
@ -224,7 +222,6 @@ export class Electron extends SdkObject {
noDefaultViewport: true,
};
const browserOptions: BrowserOptions = {
...this._playwrightOptions,
name: 'electron',
isChromium: true,
headful: true,
@ -238,7 +235,7 @@ export class Electron extends SdkObject {
originalLaunchOptions: {},
};
validateBrowserContextOptions(contextOptions, browserOptions);
const browser = await CRBrowser.connect(chromeTransport, browserOptions);
const browser = await CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
await app.initialize();
return app;

View File

@ -28,6 +28,7 @@ import type * as channels from '@protocol/channels';
import { ConnectionEvents, FFConnection } from './ffConnection';
import { FFPage } from './ffPage';
import type { Protocol } from './protocol';
import type { SdkObject } from '../instrumentation';
export class FFBrowser extends Browser {
_connection: FFConnection;
@ -36,9 +37,9 @@ export class FFBrowser extends Browser {
private _version = '';
private _userAgent: string = '';
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
static async connect(parent: SdkObject, transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
const connection = new FFConnection(transport, options.protocolLogger, options.browserLogsCollector);
const browser = new FFBrowser(connection, options);
const browser = new FFBrowser(parent, connection, options);
if ((options as any).__testHookOnConnectToBrowser)
await (options as any).__testHookOnConnectToBrowser();
let firefoxUserPrefs = options.persistent ? {} : options.originalLaunchOptions.firefoxUserPrefs ?? {};
@ -61,8 +62,8 @@ export class FFBrowser extends Browser {
return browser;
}
constructor(connection: FFConnection, options: BrowserOptions) {
super(options);
constructor(parent: SdkObject, connection: FFConnection, options: BrowserOptions) {
super(parent, options);
this._connection = connection;
this._ffPages = new Map();
this._contexts = new Map();

View File

@ -22,18 +22,19 @@ import { kBrowserCloseMessageId } from './ffConnection';
import { BrowserType, kNoXServerRunningError } from '../browserType';
import type { Env } from '../../utils/processLauncher';
import type { ConnectionTransport } from '../transport';
import type { BrowserOptions, PlaywrightOptions } from '../browser';
import type { BrowserOptions } from '../browser';
import type * as types from '../types';
import { rewriteErrorMessage } from '../../utils/stackTrace';
import { wrapInASCIIBox } from '../../utils';
import type { SdkObject } from '../instrumentation';
export class Firefox extends BrowserType {
constructor(playwrightOptions: PlaywrightOptions) {
super('firefox', playwrightOptions);
constructor(parent: SdkObject) {
super(parent, 'firefox');
}
_connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
return FFBrowser.connect(transport, options);
return FFBrowser.connect(this.attribution.playwright, transport, options);
}
_rewriteStartupError(error: Error): Error {

View File

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

View File

@ -23,9 +23,10 @@ import type { BrowserType } from './browserType';
import type { ElementHandle } from './dom';
import type { Frame } from './frames';
import type { Page } from './page';
import type { Playwright } from './playwright';
export type Attribution = {
isInternalPlaywright: boolean,
playwright: Playwright;
browserType?: BrowserType;
browser?: Browser;
context?: BrowserContext | APIRequestContext;

View File

@ -16,7 +16,7 @@
import { Android } from './android/android';
import { AdbBackend } from './android/backendAdb';
import type { Browser, PlaywrightOptions } from './browser';
import type { Browser } from './browser';
import { Chromium } from './chromium/chromium';
import { Electron } from './electron/electron';
import { Firefox } from './firefox/firefox';
@ -29,6 +29,13 @@ import type { Page } from './page';
import { DebugController } from './debugController';
import type { Language } from '../utils/isomorphic/locatorGenerators';
type PlaywrightOptions = {
socksProxyPort?: number;
sdkLanguage: Language;
isInternalPlaywright?: boolean;
isServer?: boolean;
};
export class Playwright extends SdkObject {
readonly selectors: Selectors;
readonly chromium: Chromium;
@ -41,8 +48,10 @@ export class Playwright extends SdkObject {
private _allPages = new Set<Page>();
private _allBrowsers = new Set<Browser>();
constructor(sdkLanguage: Language, isInternalPlaywright: boolean) {
super({ attribution: { isInternalPlaywright }, instrumentation: createInstrumentation() } as any, undefined, 'Playwright');
constructor(options: PlaywrightOptions) {
super({ attribution: {}, instrumentation: createInstrumentation() } as any, undefined, 'Playwright');
this.options = options;
this.attribution.playwright = this;
this.instrumentation.addListener({
onBrowserOpen: browser => this._allBrowsers.add(browser),
onBrowserClose: browser => this._allBrowsers.delete(browser),
@ -52,17 +61,12 @@ export class Playwright extends SdkObject {
debugLogger.log(logName as any, message);
}
}, null);
this.options = {
rootSdkObject: this,
selectors: new Selectors(),
sdkLanguage: sdkLanguage,
};
this.chromium = new Chromium(this.options);
this.firefox = new Firefox(this.options);
this.webkit = new WebKit(this.options);
this.electron = new Electron(this.options);
this.android = new Android(new AdbBackend(), this.options);
this.selectors = this.options.selectors;
this.chromium = new Chromium(this);
this.firefox = new Firefox(this);
this.webkit = new WebKit(this);
this.electron = new Electron(this);
this.android = new Android(this, new AdbBackend());
this.selectors = new Selectors();
this.debugController = new DebugController(this);
}
@ -79,6 +83,6 @@ export class Playwright extends SdkObject {
}
}
export function createPlaywright(sdkLanguage: Language, isInternalPlaywright: boolean = false) {
return new Playwright(sdkLanguage, isInternalPlaywright);
export function createPlaywright(options: PlaywrightOptions) {
return new Playwright(options);
}

View File

@ -358,7 +358,7 @@ class ContextRecorder extends EventEmitter {
this._context = context;
this._params = params;
this._recorderSources = [];
const language = params.language || context._browser.options.sdkLanguage;
const language = params.language || context.attribution.playwright.options.sdkLanguage;
this.setOutput(language, params.outputFile);
const generator = new CodeGenerator(context._browser.options.name, params.mode === 'recording', params.launchOptions || {}, params.contextOptions || {}, params.device, params.saveStorage);
generator.on('change', () => {

View File

@ -114,9 +114,9 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
}
static async open(recorder: Recorder, inspectedContext: BrowserContext, handleSIGINT: boolean | undefined): Promise<IRecorderApp> {
const sdkLanguage = inspectedContext._browser.options.sdkLanguage;
const sdkLanguage = inspectedContext.attribution.playwright.options.sdkLanguage;
const headed = !!inspectedContext._browser.options.headful;
const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)('javascript', true);
const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)({ sdkLanguage: 'javascript', isInternalPlaywright: true });
const args = [
'--app=data:text/html,',
'--window-size=600,600',

View File

@ -99,7 +99,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
options: {},
platform: process.platform,
wallTime: 0,
sdkLanguage: (context as BrowserContext)?._browser?.options?.sdkLanguage,
sdkLanguage: context.attribution.playwright.options.sdkLanguage,
testIdAttributeName
};
if (context instanceof BrowserContext) {
@ -119,7 +119,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
throw new Error('Cannot start tracing while stopping');
// Re-write for testing.
this._contextCreatedEvent.sdkLanguage = (this._context as BrowserContext)?._browser?.options?.sdkLanguage;
this._contextCreatedEvent.sdkLanguage = this._context.attribution.playwright.options.sdkLanguage;
if (this._state) {
const o = this._state.options;

View File

@ -74,7 +74,7 @@ export async function showTraceViewer(traceUrls: string[], browserName: string,
const urlPrefix = await server.start({ preferredPort: port, host });
const traceViewerPlaywright = createPlaywright('javascript', true);
const traceViewerPlaywright = createPlaywright({ sdkLanguage: 'javascript', isInternalPlaywright: true });
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
const args = traceViewerBrowser === 'chromium' ? [
'--app=data:text/html,',

View File

@ -21,18 +21,19 @@ import path from 'path';
import { kBrowserCloseMessageId } from './wkConnection';
import { BrowserType, kNoXServerRunningError } from '../browserType';
import type { ConnectionTransport } from '../transport';
import type { BrowserOptions, PlaywrightOptions } from '../browser';
import type { BrowserOptions } from '../browser';
import type * as types from '../types';
import { rewriteErrorMessage } from '../../utils/stackTrace';
import { wrapInASCIIBox } from '../../utils';
import type { SdkObject } from '../instrumentation';
export class WebKit extends BrowserType {
constructor(playwrightOptions: PlaywrightOptions) {
super('webkit', playwrightOptions);
constructor(parent: SdkObject) {
super(parent, 'webkit');
}
_connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
return WKBrowser.connect(transport, options);
return WKBrowser.connect(this.attribution.playwright, transport, options);
}
_amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env {

View File

@ -31,6 +31,7 @@ import type { PageProxyMessageReceivedPayload } from './wkConnection';
import { kPageProxyMessageReceived, WKConnection, WKSession } from './wkConnection';
import { WKPage } from './wkPage';
import { kBrowserClosedError } from '../../common/errors';
import type { SdkObject } from '../instrumentation';
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15';
const BROWSER_VERSION = '16.4';
@ -42,8 +43,8 @@ export class WKBrowser extends Browser {
readonly _wkPages = new Map<string, WKPage>();
private readonly _eventListeners: RegisteredListener[];
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
const browser = new WKBrowser(transport, options);
static async connect(parent: SdkObject, transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
const browser = new WKBrowser(parent, transport, options);
if ((options as any).__testHookOnConnectToBrowser)
await (options as any).__testHookOnConnectToBrowser();
const promises: Promise<any>[] = [
@ -58,8 +59,8 @@ export class WKBrowser extends Browser {
return browser;
}
constructor(transport: ConnectionTransport, options: BrowserOptions) {
super(options);
constructor(parent: SdkObject, transport: ConnectionTransport, options: BrowserOptions) {
super(parent, options);
this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector);
this._browserSession = this._connection.browserSession;
this._eventListeners = [

View File

@ -774,7 +774,7 @@ test('should display waitForLoadState even if did not wait for it', async ({ run
});
test('should display language-specific locators', async ({ runAndTrace, server, page, toImpl }) => {
toImpl(page.context())._browser.options.sdkLanguage = 'python';
toImpl(page).attribution.playwright.options.sdkLanguage = 'python';
const traceViewer = await runAndTrace(async () => {
await page.setContent('<button>Submit</button>');
await page.getByRole('button', { name: 'Submit' }).click();
@ -783,6 +783,7 @@ test('should display language-specific locators', async ({ runAndTrace, server,
/page.setContent/,
/locator.clickget_by_role\("button", name="Submit"\)/,
]);
toImpl(page).attribution.playwright.options.sdkLanguage = 'javascript';
});
test('should pick locator', async ({ page, runAndTrace, server }) => {