mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-11 12:33:45 +03:00
chore: add Playwright to attribution (#23447)
This makes it easier to plumb all kinds of options around.
This commit is contained in:
parent
2719f408b2
commit
14a1eaa474
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */);
|
||||
|
@ -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);
|
||||
|
@ -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';
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 };
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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', () => {
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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,',
|
||||
|
@ -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 {
|
||||
|
@ -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 = [
|
||||
|
@ -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 }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user