mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 21:53:35 +03:00
chore: centralize playwright creation, bind context listeners to instance (#5217)
This commit is contained in:
parent
7fe7d0ef32
commit
975519150e
@ -70,7 +70,7 @@ export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
||||
|
||||
this._browser = browser;
|
||||
this._wsEndpoint = '';
|
||||
this._process = browser._options.browserProcess.process!;
|
||||
this._process = browser.options.browserProcess.process!;
|
||||
|
||||
let readyCallback = () => {};
|
||||
this._ready = new Promise<void>(f => readyCallback = f);
|
||||
@ -86,7 +86,7 @@ export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
||||
this._clientAttached(socket);
|
||||
});
|
||||
|
||||
browser._options.browserProcess.onclose = (exitCode, signal) => {
|
||||
browser.options.browserProcess.onclose = (exitCode, signal) => {
|
||||
this._server.close();
|
||||
this.emit('close', exitCode, signal);
|
||||
};
|
||||
@ -101,11 +101,11 @@ export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._browser._options.browserProcess.close();
|
||||
await this._browser.options.browserProcess.close();
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
await this._browser._options.browserProcess.kill();
|
||||
await this._browser.options.browserProcess.kill();
|
||||
}
|
||||
|
||||
private _clientAttached(socket: ws) {
|
||||
@ -158,7 +158,7 @@ class ConnectedBrowser extends BrowserDispatcher {
|
||||
async newContext(params: channels.BrowserNewContextParams): Promise<{ context: channels.BrowserContextChannel }> {
|
||||
if (params.recordVideo) {
|
||||
// TODO: we should create a separate temp directory or accept a launchServer parameter.
|
||||
params.recordVideo.dir = this._object._options.downloadsPath!;
|
||||
params.recordVideo.dir = this._object.options.downloadsPath!;
|
||||
}
|
||||
const result = await super.newContext(params);
|
||||
const dispatcher = result.context as BrowserContextDispatcher;
|
||||
|
@ -18,15 +18,12 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { installInspectorController } from '../server/supplements/inspectorController';
|
||||
import { DispatcherConnection } from '../dispatchers/dispatcher';
|
||||
import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher';
|
||||
import { installBrowsersWithProgressBar } from '../install/installer';
|
||||
import { Transport } from '../protocol/transport';
|
||||
import { Playwright } from '../server/playwright';
|
||||
import { createPlaywright } from '../server/playwright';
|
||||
import { gracefullyCloseAll } from '../server/processLauncher';
|
||||
import { installHarTracer } from '../trace/harTracer';
|
||||
import { installTracer } from '../trace/tracer';
|
||||
import { BrowserName } from '../utils/browserPaths';
|
||||
|
||||
export function printApiJson() {
|
||||
@ -38,10 +35,6 @@ export function printProtocol() {
|
||||
}
|
||||
|
||||
export function runServer() {
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
|
||||
const dispatcherConnection = new DispatcherConnection();
|
||||
const transport = new Transport(process.stdout, process.stdin);
|
||||
transport.onmessage = message => dispatcherConnection.dispatch(JSON.parse(message));
|
||||
@ -56,7 +49,7 @@ export function runServer() {
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
||||
const playwright = createPlaywright();
|
||||
new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
private _context: BrowserContext;
|
||||
|
||||
constructor(scope: DispatcherScope, context: BrowserContext) {
|
||||
super(scope, context, 'BrowserContext', { isChromium: context._browser._options.isChromium }, true);
|
||||
super(scope, context, 'BrowserContext', { isChromium: context._browser.options.isChromium }, true);
|
||||
this._context = context;
|
||||
|
||||
for (const page of context.pages())
|
||||
@ -41,7 +41,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
context.on(BrowserContext.Events.StdOut, data => this._dispatchEvent('stdout', { data: Buffer.from(data, 'utf8').toString('base64') }));
|
||||
context.on(BrowserContext.Events.StdErr, data => this._dispatchEvent('stderr', { data: Buffer.from(data, 'utf8').toString('base64') }));
|
||||
|
||||
if (context._browser._options.name === 'chromium') {
|
||||
if (context._browser.options.name === 'chromium') {
|
||||
for (const page of (context as CRBrowserContext).backgroundPages())
|
||||
this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) });
|
||||
context.on(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) }));
|
||||
@ -139,7 +139,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
}
|
||||
|
||||
async pause() {
|
||||
if (!this._context._browser._options.headful)
|
||||
if (!this._context._browser.options.headful)
|
||||
return;
|
||||
const recorder = await RecorderSupplement.getOrCreate(this._context, 'pause', {
|
||||
language: 'javascript',
|
||||
@ -149,7 +149,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||
}
|
||||
|
||||
async crNewCDPSession(params: channels.BrowserContextCrNewCDPSessionParams): Promise<channels.BrowserContextCrNewCDPSessionResult> {
|
||||
if (!this._object._browser._options.isChromium)
|
||||
if (!this._object._browser.options.isChromium)
|
||||
throw new Error(`CDP session is only available in Chromium`);
|
||||
const crBrowserContext = this._object as CRBrowserContext;
|
||||
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page as PageDispatcher)._object)) };
|
||||
|
@ -24,7 +24,7 @@ import { PageDispatcher } from './pageDispatcher';
|
||||
|
||||
export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserInitializer> implements channels.BrowserChannel {
|
||||
constructor(scope: DispatcherScope, browser: Browser) {
|
||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser._options.name }, true);
|
||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true);
|
||||
browser.on(Browser.Events.Disconnected, () => this._didClose());
|
||||
}
|
||||
|
||||
@ -45,21 +45,21 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserIniti
|
||||
}
|
||||
|
||||
async crNewBrowserCDPSession(): Promise<channels.BrowserCrNewBrowserCDPSessionResult> {
|
||||
if (!this._object._options.isChromium)
|
||||
if (!this._object.options.isChromium)
|
||||
throw new Error(`CDP session is only available in Chromium`);
|
||||
const crBrowser = this._object as CRBrowser;
|
||||
return { session: new CDPSessionDispatcher(this._scope, await crBrowser.newBrowserCDPSession()) };
|
||||
}
|
||||
|
||||
async crStartTracing(params: channels.BrowserCrStartTracingParams): Promise<void> {
|
||||
if (!this._object._options.isChromium)
|
||||
if (!this._object.options.isChromium)
|
||||
throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object as CRBrowser;
|
||||
await crBrowser.startTracing(params.page ? (params.page as PageDispatcher)._object : undefined, params);
|
||||
}
|
||||
|
||||
async crStopTracing(): Promise<channels.BrowserCrStopTracingResult> {
|
||||
if (!this._object._options.isChromium)
|
||||
if (!this._object.options.isChromium)
|
||||
throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object as CRBrowser;
|
||||
const buffer = await crBrowser.stopTracing();
|
||||
|
@ -15,22 +15,14 @@
|
||||
*/
|
||||
|
||||
import { DispatcherConnection } from './dispatchers/dispatcher';
|
||||
import { Playwright as PlaywrightImpl } from './server/playwright';
|
||||
import { createPlaywright } from './server/playwright';
|
||||
import type { Playwright as PlaywrightAPI } from './client/playwright';
|
||||
import { PlaywrightDispatcher } from './dispatchers/playwrightDispatcher';
|
||||
import { Connection } from './client/connection';
|
||||
import { BrowserServerLauncherImpl } from './browserServerImpl';
|
||||
import { installInspectorController } from './server/supplements/inspectorController';
|
||||
import { installTracer } from './trace/tracer';
|
||||
import { installHarTracer } from './trace/harTracer';
|
||||
import * as path from 'path';
|
||||
|
||||
function setupInProcess(): PlaywrightAPI {
|
||||
const playwright = new PlaywrightImpl(path.join(__dirname, '..'), require('../browsers.json')['browsers']);
|
||||
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
const playwright = createPlaywright();
|
||||
|
||||
const clientConnection = new Connection();
|
||||
const dispatcherConnection = new DispatcherConnection();
|
||||
|
@ -17,20 +17,13 @@
|
||||
import * as debug from 'debug';
|
||||
import * as http from 'http';
|
||||
import * as WebSocket from 'ws';
|
||||
import { installInspectorController } from '../server/supplements/inspectorController';
|
||||
import { DispatcherConnection } from '../dispatchers/dispatcher';
|
||||
import { PlaywrightDispatcher } from '../dispatchers/playwrightDispatcher';
|
||||
import { Playwright } from '../server/playwright';
|
||||
import { createPlaywright } from '../server/playwright';
|
||||
import { gracefullyCloseAll } from '../server/processLauncher';
|
||||
import { installTracer } from '../trace/tracer';
|
||||
import { installHarTracer } from '../trace/harTracer';
|
||||
|
||||
const debugLog = debug('pw:server');
|
||||
|
||||
installInspectorController();
|
||||
installTracer();
|
||||
installHarTracer();
|
||||
|
||||
export class PlaywrightServer {
|
||||
private _server: http.Server | undefined;
|
||||
private _client: WebSocket | undefined;
|
||||
@ -62,8 +55,7 @@ export class PlaywrightServer {
|
||||
this._onDisconnect();
|
||||
});
|
||||
dispatcherConnection.onmessage = message => ws.send(JSON.stringify(message));
|
||||
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
||||
new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright);
|
||||
new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), createPlaywright());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import * as stream from 'stream';
|
||||
import * as util from 'util';
|
||||
import * as ws from 'ws';
|
||||
import { createGuid, makeWaitForNextTask } from '../../utils/utils';
|
||||
import { BrowserOptions, BrowserProcess } from '../browser';
|
||||
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
|
||||
import { BrowserContext, validateBrowserContextOptions } from '../browserContext';
|
||||
import { ProgressController } from '../progress';
|
||||
import { CRBrowser } from '../chromium/crBrowser';
|
||||
@ -57,9 +57,11 @@ export class Android {
|
||||
private _backend: Backend;
|
||||
private _devices = new Map<string, AndroidDevice>();
|
||||
readonly _timeoutSettings: TimeoutSettings;
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(backend: Backend) {
|
||||
constructor(backend: Backend, playwrightOptions: PlaywrightOptions) {
|
||||
this._backend = backend;
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
}
|
||||
|
||||
@ -255,6 +257,7 @@ export class AndroidDevice extends EventEmitter {
|
||||
this._browserConnections.add(androidBrowser);
|
||||
|
||||
const browserOptions: BrowserOptions = {
|
||||
...this._android._playwrightOptions,
|
||||
name: 'clank',
|
||||
isChromium: true,
|
||||
slowMo: 0,
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import * as types from './types';
|
||||
import { BrowserContext, Video } from './browserContext';
|
||||
import { BrowserContext, ContextListener, Video } from './browserContext';
|
||||
import { Page } from './page';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Download } from './download';
|
||||
@ -30,7 +30,11 @@ export interface BrowserProcess {
|
||||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
export type BrowserOptions = types.UIOptions & {
|
||||
export type PlaywrightOptions = {
|
||||
contextListeners: ContextListener[]
|
||||
};
|
||||
|
||||
export type BrowserOptions = PlaywrightOptions & {
|
||||
name: string,
|
||||
isChromium: boolean,
|
||||
downloadsPath?: string,
|
||||
@ -40,6 +44,7 @@ export type BrowserOptions = types.UIOptions & {
|
||||
proxy?: ProxySettings,
|
||||
protocolLogger: types.ProtocolLogger,
|
||||
browserLogsCollector: RecentLogsCollector,
|
||||
slowMo?: number,
|
||||
};
|
||||
|
||||
export abstract class Browser extends EventEmitter {
|
||||
@ -47,7 +52,7 @@ export abstract class Browser extends EventEmitter {
|
||||
Disconnected: 'disconnected',
|
||||
};
|
||||
|
||||
readonly _options: BrowserOptions;
|
||||
readonly options: BrowserOptions;
|
||||
private _downloads = new Map<string, Download>();
|
||||
_defaultContext: BrowserContext | null = null;
|
||||
private _startedClosing = false;
|
||||
@ -55,7 +60,7 @@ export abstract class Browser extends EventEmitter {
|
||||
|
||||
constructor(options: BrowserOptions) {
|
||||
super();
|
||||
this._options = options;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
abstract newContext(options?: types.BrowserContextOptions): Promise<BrowserContext>;
|
||||
@ -71,7 +76,7 @@ export abstract class Browser extends EventEmitter {
|
||||
}
|
||||
|
||||
_downloadCreated(page: Page, uuid: string, url: string, suggestedFilename?: string) {
|
||||
const download = new Download(page, this._options.downloadsPath || '', uuid, url, suggestedFilename);
|
||||
const download = new Download(page, this.options.downloadsPath || '', uuid, url, suggestedFilename);
|
||||
this._downloads.set(uuid, download);
|
||||
}
|
||||
|
||||
@ -117,7 +122,7 @@ export abstract class Browser extends EventEmitter {
|
||||
async close() {
|
||||
if (!this._startedClosing) {
|
||||
this._startedClosing = true;
|
||||
await this._options.browserProcess.close();
|
||||
await this.options.browserProcess.close();
|
||||
}
|
||||
if (this.isConnected())
|
||||
await new Promise(x => this.once(Browser.Events.Disconnected, x));
|
||||
|
@ -94,8 +94,6 @@ export interface ContextListener {
|
||||
onContextDidDestroy(context: BrowserContext): Promise<void>;
|
||||
}
|
||||
|
||||
export const contextListeners = new Set<ContextListener>();
|
||||
|
||||
export abstract class BrowserContext extends EventEmitter {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
@ -140,7 +138,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
for (const listener of contextListeners)
|
||||
for (const listener of this._browser.options.contextListeners)
|
||||
await listener.onContextCreated(this);
|
||||
}
|
||||
|
||||
@ -259,7 +257,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||
}
|
||||
|
||||
protected _authenticateProxyViaHeader() {
|
||||
const proxy = this._options.proxy || this._browser._options.proxy || { username: undefined, password: undefined };
|
||||
const proxy = this._options.proxy || this._browser.options.proxy || { username: undefined, password: undefined };
|
||||
const { username, password } = proxy;
|
||||
if (username) {
|
||||
this._options.httpCredentials = { username, password: password! };
|
||||
@ -272,7 +270,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||
}
|
||||
|
||||
protected _authenticateProxyViaCredentials() {
|
||||
const proxy = this._options.proxy || this._browser._options.proxy;
|
||||
const proxy = this._options.proxy || this._browser.options.proxy;
|
||||
if (!proxy)
|
||||
return;
|
||||
const { username, password } = proxy;
|
||||
@ -294,7 +292,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||
this.emit(BrowserContext.Events.BeforeClose);
|
||||
this._closedStatus = 'closing';
|
||||
|
||||
for (const listener of contextListeners)
|
||||
for (const listener of this._browser.options.contextListeners)
|
||||
await listener.onContextWillDestroy(this);
|
||||
|
||||
// Collect videos/downloads that we will await.
|
||||
@ -323,7 +321,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||
await this._browser.close();
|
||||
|
||||
// Bookkeeping.
|
||||
for (const listener of contextListeners)
|
||||
for (const listener of this._browser.options.contextListeners)
|
||||
await listener.onContextDidDestroy(this);
|
||||
this._didCloseInternal();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import * as util from 'util';
|
||||
import { BrowserContext, normalizeProxySettings, validateBrowserContextOptions } from './browserContext';
|
||||
import * as browserPaths from '../utils/browserPaths';
|
||||
import { ConnectionTransport } from './transport';
|
||||
import { BrowserOptions, Browser, BrowserProcess } from './browser';
|
||||
import { BrowserOptions, Browser, BrowserProcess, PlaywrightOptions } from './browser';
|
||||
import { launchProcess, Env, envArrayToObject } from './processLauncher';
|
||||
import { PipeTransport } from './pipeTransport';
|
||||
import { Progress, ProgressController } from './progress';
|
||||
@ -42,8 +42,10 @@ export abstract class BrowserType {
|
||||
private _executablePath: string;
|
||||
private _browserDescriptor: browserPaths.BrowserDescriptor;
|
||||
readonly _browserPath: string;
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(packagePath: string, browser: browserPaths.BrowserDescriptor) {
|
||||
constructor(packagePath: string, browser: browserPaths.BrowserDescriptor, playwrightOptions: PlaywrightOptions) {
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._name = browser.name;
|
||||
const browsersPath = browserPaths.browsersPath(packagePath);
|
||||
this._browserDescriptor = browser;
|
||||
@ -87,6 +89,7 @@ export abstract class BrowserType {
|
||||
if ((options as any).__testHookBeforeCreateBrowser)
|
||||
await (options as any).__testHookBeforeCreateBrowser();
|
||||
const browserOptions: BrowserOptions = {
|
||||
...this._playwrightOptions,
|
||||
name: this._name,
|
||||
isChromium: this._name === 'chromium',
|
||||
slowMo: options.slowMo,
|
||||
|
@ -24,15 +24,15 @@ import { BrowserType } from '../browserType';
|
||||
import { ConnectionTransport, ProtocolRequest } from '../transport';
|
||||
import type { BrowserDescriptor } from '../../utils/browserPaths';
|
||||
import { CRDevTools } from './crDevTools';
|
||||
import { BrowserOptions } from '../browser';
|
||||
import { BrowserOptions, PlaywrightOptions } from '../browser';
|
||||
import * as types from '../types';
|
||||
import { isDebugMode } from '../../utils/utils';
|
||||
|
||||
export class Chromium extends BrowserType {
|
||||
private _devtools: CRDevTools | undefined;
|
||||
|
||||
constructor(packagePath: string, browser: BrowserDescriptor) {
|
||||
super(packagePath, browser);
|
||||
constructor(packagePath: string, browser: BrowserDescriptor, playwrightOptions: PlaywrightOptions) {
|
||||
super(packagePath, browser, playwrightOptions);
|
||||
if (isDebugMode())
|
||||
this._devtools = this._createDevTools();
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ export class CRBrowser extends Browser {
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this._options);
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
const { browserContextId } = await this._session.send('Target.createBrowserContext', {
|
||||
disposeOnDetach: true,
|
||||
proxyServer: options.proxy ? options.proxy.server : undefined,
|
||||
@ -119,7 +119,7 @@ export class CRBrowser extends Browser {
|
||||
}
|
||||
|
||||
isClank(): boolean {
|
||||
return this._options.name === 'clank';
|
||||
return this.options.name === 'clank';
|
||||
}
|
||||
|
||||
_onAttachedToTarget({targetInfo, sessionId, waitingForDebugger}: Protocol.Target.attachedToTargetPayload) {
|
||||
@ -293,11 +293,11 @@ export class CRBrowserContext extends BrowserContext {
|
||||
async _initialize() {
|
||||
assert(!Array.from(this._browser._crPages.values()).some(page => page._browserContext === this));
|
||||
const promises: Promise<any>[] = [ super._initialize() ];
|
||||
if (this._browser._options.downloadsPath) {
|
||||
if (this._browser.options.downloadsPath) {
|
||||
promises.push(this._browser._session.send('Browser.setDownloadBehavior', {
|
||||
behavior: this._options.acceptDownloads ? 'allowAndName' : 'deny',
|
||||
browserContextId: this._browserContextId,
|
||||
downloadPath: this._browser._options.downloadsPath
|
||||
downloadPath: this._browser.options.downloadsPath
|
||||
}));
|
||||
}
|
||||
if (this._options.permissions)
|
||||
|
@ -845,7 +845,7 @@ class FrameSession {
|
||||
];
|
||||
if (this._windowId) {
|
||||
let insets = { width: 0, height: 0 };
|
||||
if (this._crPage._browserContext._browser._options.headful) {
|
||||
if (this._crPage._browserContext._browser.options.headful) {
|
||||
// TODO: popup windows have their own insets.
|
||||
insets = { width: 24, height: 88 };
|
||||
if (process.platform === 'win32')
|
||||
|
@ -29,7 +29,7 @@ import type {BrowserWindow} from 'electron';
|
||||
import { Progress, ProgressController, runAbortableTask } from '../progress';
|
||||
import { EventEmitter } from 'events';
|
||||
import { helper } from '../helper';
|
||||
import { BrowserOptions, BrowserProcess } from '../browser';
|
||||
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as readline from 'readline';
|
||||
import { RecentLogsCollector } from '../../utils/debugLogger';
|
||||
@ -139,6 +139,12 @@ export class ElectronApplication extends EventEmitter {
|
||||
}
|
||||
|
||||
export class Electron {
|
||||
private _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(playwrightOptions: PlaywrightOptions) {
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
}
|
||||
|
||||
async launch(executablePath: string, options: ElectronLaunchOptionsBase = {}): Promise<ElectronApplication> {
|
||||
const {
|
||||
args = [],
|
||||
@ -190,6 +196,7 @@ export class Electron {
|
||||
kill
|
||||
};
|
||||
const browserOptions: BrowserOptions = {
|
||||
...this._playwrightOptions,
|
||||
name: 'electron',
|
||||
isChromium: true,
|
||||
headful: true,
|
||||
|
@ -72,7 +72,7 @@ export class FFBrowser extends Browser {
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this._options);
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
if (options.isMobile)
|
||||
throw new Error('options.isMobile is not supported in Firefox');
|
||||
const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
|
||||
@ -149,12 +149,12 @@ export class FFBrowserContext extends BrowserContext {
|
||||
assert(!this._ffPages().length);
|
||||
const browserContextId = this._browserContextId;
|
||||
const promises: Promise<any>[] = [ super._initialize() ];
|
||||
if (this._browser._options.downloadsPath) {
|
||||
if (this._browser.options.downloadsPath) {
|
||||
promises.push(this._browser._connection.send('Browser.setDownloadOptions', {
|
||||
browserContextId,
|
||||
downloadOptions: {
|
||||
behavior: this._options.acceptDownloads ? 'saveToDisk' : 'cancel',
|
||||
downloadsDir: this._browser._options.downloadsPath,
|
||||
downloadsDir: this._browser.options.downloadsPath,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
async _doSlowMo() {
|
||||
const slowMo = this._browserContext._browser._options.slowMo;
|
||||
const slowMo = this._browserContext._browser.options.slowMo;
|
||||
if (!slowMo)
|
||||
return;
|
||||
await new Promise(x => setTimeout(x, slowMo));
|
||||
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import { Tracer } from '../trace/tracer';
|
||||
import * as browserPaths from '../utils/browserPaths';
|
||||
import { Android } from './android/android';
|
||||
import { AdbBackend } from './android/backendAdb';
|
||||
@ -21,6 +23,8 @@ import { Chromium } from './chromium/chromium';
|
||||
import { Electron } from './electron/electron';
|
||||
import { Firefox } from './firefox/firefox';
|
||||
import { serverSelectors } from './selectors';
|
||||
import { HarTracer } from './supplements/har/harTracer';
|
||||
import { InspectorController } from './supplements/inspectorController';
|
||||
import { WebKit } from './webkit/webkit';
|
||||
|
||||
export class Playwright {
|
||||
@ -30,18 +34,29 @@ export class Playwright {
|
||||
readonly electron: Electron;
|
||||
readonly firefox: Firefox;
|
||||
readonly webkit: WebKit;
|
||||
readonly options = {
|
||||
contextListeners: [
|
||||
new InspectorController(),
|
||||
new Tracer(),
|
||||
new HarTracer()
|
||||
]
|
||||
};
|
||||
|
||||
constructor(packagePath: string, browsers: browserPaths.BrowserDescriptor[]) {
|
||||
const chromium = browsers.find(browser => browser.name === 'chromium');
|
||||
this.chromium = new Chromium(packagePath, chromium!);
|
||||
this.chromium = new Chromium(packagePath, chromium!, this.options);
|
||||
|
||||
const firefox = browsers.find(browser => browser.name === 'firefox');
|
||||
this.firefox = new Firefox(packagePath, firefox!);
|
||||
this.firefox = new Firefox(packagePath, firefox!, this.options);
|
||||
|
||||
const webkit = browsers.find(browser => browser.name === 'webkit');
|
||||
this.webkit = new WebKit(packagePath, webkit!);
|
||||
this.webkit = new WebKit(packagePath, webkit!, this.options);
|
||||
|
||||
this.electron = new Electron();
|
||||
this.android = new Android(new AdbBackend());
|
||||
this.electron = new Electron(this.options);
|
||||
this.android = new Android(new AdbBackend(), this.options);
|
||||
}
|
||||
}
|
||||
|
||||
export function createPlaywright() {
|
||||
return new Playwright(path.join(__dirname, '..', '..'), require('../../browsers.json')['browsers']);
|
||||
}
|
||||
|
@ -16,19 +16,15 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { BrowserContext, ContextListener, contextListeners } from '../server/browserContext';
|
||||
import { helper } from '../server/helper';
|
||||
import * as network from '../server/network';
|
||||
import { Page } from '../server/page';
|
||||
import { BrowserContext, ContextListener } from '../../browserContext';
|
||||
import { helper } from '../../helper';
|
||||
import * as network from '../../network';
|
||||
import { Page } from '../../page';
|
||||
import * as har from './har';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
export function installHarTracer() {
|
||||
contextListeners.add(new HarTracer());
|
||||
}
|
||||
|
||||
class HarTracer implements ContextListener {
|
||||
export class HarTracer implements ContextListener {
|
||||
private _contextTracers = new Map<BrowserContext, HarContextTracer>();
|
||||
|
||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||
@ -68,10 +64,10 @@ class HarContextTracer {
|
||||
version: '1.2',
|
||||
creator: {
|
||||
name: 'Playwright',
|
||||
version: require('../../package.json')['version'],
|
||||
version: require('../../../../package.json')['version'],
|
||||
},
|
||||
browser: {
|
||||
name: context._browser._options.name,
|
||||
name: context._browser.options.name,
|
||||
version: context._browser.version()
|
||||
},
|
||||
pages: [],
|
@ -14,18 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext, ContextListener, contextListeners } from '../browserContext';
|
||||
import { BrowserContext, ContextListener } from '../browserContext';
|
||||
import { isDebugMode } from '../../utils/utils';
|
||||
import { ConsoleApiSupplement } from './consoleApiSupplement';
|
||||
import { RecorderSupplement } from './recorderSupplement';
|
||||
import { Page } from '../page';
|
||||
import { ConsoleMessage } from '../console';
|
||||
|
||||
export function installInspectorController() {
|
||||
contextListeners.add(new InspectorController());
|
||||
}
|
||||
|
||||
class InspectorController implements ContextListener {
|
||||
export class InspectorController implements ContextListener {
|
||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||
if (isDebugMode()) {
|
||||
const consoleApi = new ConsoleApiSupplement(context);
|
||||
|
@ -92,7 +92,7 @@ export class RecorderSupplement {
|
||||
this._output.setEnabled(app === 'codegen');
|
||||
context.on(BrowserContext.Events.BeforeClose, () => this._output.flush());
|
||||
|
||||
const generator = new CodeGenerator(context._browser._options.name, app === 'codegen', params.launchOptions || {}, params.contextOptions || {}, this._output, languageGenerator, params.device, params.saveStorage);
|
||||
const generator = new CodeGenerator(context._browser.options.name, app === 'codegen', params.launchOptions || {}, params.contextOptions || {}, this._output, languageGenerator, params.device, params.saveStorage);
|
||||
this._generator = generator;
|
||||
}
|
||||
|
||||
|
@ -271,7 +271,7 @@ type LaunchOptionsBase = {
|
||||
chromiumSandbox?: boolean,
|
||||
slowMo?: number,
|
||||
};
|
||||
export type LaunchOptions = LaunchOptionsBase & UIOptions & {
|
||||
export type LaunchOptions = LaunchOptionsBase & {
|
||||
firefoxUserPrefs?: { [key: string]: string | number | boolean },
|
||||
};
|
||||
export type LaunchPersistentOptions = LaunchOptionsBase & BrowserContextOptions;
|
||||
@ -326,10 +326,6 @@ export type Error = {
|
||||
stack?: string,
|
||||
};
|
||||
|
||||
export type UIOptions = {
|
||||
slowMo?: number;
|
||||
};
|
||||
|
||||
export type NameValueList = {
|
||||
name: string;
|
||||
value: string;
|
||||
|
@ -74,7 +74,7 @@ export class WKBrowser extends Browser {
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this._options);
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
const createOptions = options.proxy ? {
|
||||
proxyServer: options.proxy.server,
|
||||
proxyBypassList: options.proxy.bypass
|
||||
@ -208,10 +208,10 @@ export class WKBrowserContext extends BrowserContext {
|
||||
assert(!this._wkPages().length);
|
||||
const browserContextId = this._browserContextId;
|
||||
const promises: Promise<any>[] = [ super._initialize() ];
|
||||
if (this._browser._options.downloadsPath) {
|
||||
if (this._browser.options.downloadsPath) {
|
||||
promises.push(this._browser._browserSession.send('Playwright.setDownloadBehavior', {
|
||||
behavior: this._options.acceptDownloads ? 'allow' : 'deny',
|
||||
downloadPath: this._browser._options.downloadsPath,
|
||||
downloadPath: this._browser.options.downloadsPath,
|
||||
browserContextId
|
||||
}));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ActionListener, ActionMetadata, BrowserContext, ContextListener, contextListeners, Video } from '../server/browserContext';
|
||||
import { ActionListener, ActionMetadata, BrowserContext, ContextListener, Video } from '../server/browserContext';
|
||||
import type { SnapshotterResource as SnapshotterResource, SnapshotterBlob, SnapshotterDelegate } from './snapshotter';
|
||||
import * as trace from './traceTypes';
|
||||
import * as path from 'path';
|
||||
@ -34,11 +34,7 @@ const fsAppendFileAsync = util.promisify(fs.appendFile.bind(fs));
|
||||
const fsAccessAsync = util.promisify(fs.access.bind(fs));
|
||||
const envTrace = getFromENV('PW_TRACE_DIR');
|
||||
|
||||
export function installTracer() {
|
||||
contextListeners.add(new Tracer());
|
||||
}
|
||||
|
||||
class Tracer implements ContextListener {
|
||||
export class Tracer implements ContextListener {
|
||||
private _contextTracers = new Map<BrowserContext, ContextTracer>();
|
||||
|
||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||
@ -93,7 +89,7 @@ class ContextTracer implements SnapshotterDelegate, ActionListener {
|
||||
const event: trace.ContextCreatedTraceEvent = {
|
||||
timestamp: monotonicTime(),
|
||||
type: 'context-created',
|
||||
browserName: context._browser._options.name,
|
||||
browserName: context._browser.options.name,
|
||||
contextId: this._contextId,
|
||||
isMobile: !!context._options.isMobile,
|
||||
deviceScaleFactor: context._options.deviceScaleFactor || 1,
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
import { folio as baseFolio } from './fixtures';
|
||||
import * as fs from 'fs';
|
||||
import type * as har from '../src/trace/har';
|
||||
import type * as har from '../src/server/supplements/har/har';
|
||||
import type { BrowserContext, Page } from '../index';
|
||||
|
||||
const builder = baseFolio.extend<{
|
||||
|
@ -138,7 +138,7 @@ DEPS['src/server/injected/'] = ['src/server/common/'];
|
||||
DEPS['src/server/android/'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/protocol/'];
|
||||
DEPS['src/server/electron/'] = [...DEPS['src/server/'], 'src/server/chromium/'];
|
||||
|
||||
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/', 'src/server/android/', 'src/server/electron/'];
|
||||
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/trace/', 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/', 'src/server/android/', 'src/server/electron/'];
|
||||
DEPS['src/cli/driver.ts'] = DEPS['src/inprocess.ts'] = DEPS['src/browserServerImpl.ts'] = ['src/**'];
|
||||
|
||||
// Tracing is a client/server plugin, nothing should depend on it.
|
||||
|
Loading…
Reference in New Issue
Block a user