mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(rpc): use rpc protocol for browserType.connect (#3380)
This commit is contained in:
parent
a4eb86c3df
commit
18b2cf5ec7
147
src/rpc/browserServerImpl.ts
Normal file
147
src/rpc/browserServerImpl.ts
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LaunchServerOptions } from './client/types';
|
||||
import { BrowserTypeBase } from '../server/browserType';
|
||||
import * as ws from 'ws';
|
||||
import { helper } from '../helper';
|
||||
import { BrowserBase } from '../browser';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { Events } from '../events';
|
||||
import { EventEmitter } from 'ws';
|
||||
import { DispatcherScope, DispatcherConnection } from './server/dispatcher';
|
||||
import { BrowserTypeDispatcher } from './server/browserTypeDispatcher';
|
||||
import { BrowserDispatcher } from './server/browserDispatcher';
|
||||
import { BrowserContextDispatcher } from './server/browserContextDispatcher';
|
||||
import { BrowserNewContextParams, BrowserContextChannel } from './channels';
|
||||
import { BrowserServerLauncher, BrowserServer } from './client/browserType';
|
||||
|
||||
export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
private _browserType: BrowserTypeBase;
|
||||
|
||||
constructor(browserType: BrowserTypeBase) {
|
||||
this._browserType = browserType;
|
||||
}
|
||||
|
||||
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServerImpl> {
|
||||
const browser = await this._browserType.launch(options);
|
||||
return new BrowserServerImpl(this._browserType, browser as BrowserBase, options.port);
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
||||
private _server: ws.Server;
|
||||
private _browserType: BrowserTypeBase;
|
||||
private _browser: BrowserBase;
|
||||
private _wsEndpoint: string;
|
||||
private _process: ChildProcess;
|
||||
|
||||
constructor(browserType: BrowserTypeBase, browser: BrowserBase, port: number = 0) {
|
||||
super();
|
||||
|
||||
this._browserType = browserType;
|
||||
this._browser = browser;
|
||||
|
||||
const token = helper.guid();
|
||||
this._server = new ws.Server({ port });
|
||||
const address = this._server.address();
|
||||
this._wsEndpoint = typeof address === 'string' ? `${address}/${token}` : `ws://127.0.0.1:${address.port}/${token}`;
|
||||
const browserServer = browser._options.ownedServer!;
|
||||
this._process = browserServer.process();
|
||||
|
||||
this._server.on('connection', (socket: ws, req) => {
|
||||
if (req.url !== '/' + token) {
|
||||
socket.close();
|
||||
return;
|
||||
}
|
||||
this._clientAttached(socket);
|
||||
});
|
||||
|
||||
browserServer.on(Events.BrowserServer.Close, (exitCode, signal) => {
|
||||
this._server.close();
|
||||
this.emit('close', exitCode, signal);
|
||||
});
|
||||
}
|
||||
|
||||
process(): ChildProcess {
|
||||
return this._process;
|
||||
}
|
||||
|
||||
wsEndpoint(): string {
|
||||
return this._wsEndpoint;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
const browserServer = this._browser._options.ownedServer!;
|
||||
await browserServer.close();
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
const browserServer = this._browser._options.ownedServer!;
|
||||
await browserServer.kill();
|
||||
}
|
||||
|
||||
private _clientAttached(socket: ws) {
|
||||
const connection = new DispatcherConnection();
|
||||
connection.onmessage = message => {
|
||||
if (socket.readyState !== ws.CLOSING)
|
||||
socket.send(JSON.stringify(message));
|
||||
};
|
||||
socket.on('message', (message: string) => {
|
||||
connection.dispatch(JSON.parse(Buffer.from(message).toString()));
|
||||
});
|
||||
socket.on('error', () => {});
|
||||
const browserType = new BrowserTypeDispatcher(connection.rootDispatcher(), this._browserType);
|
||||
const browser = new ConnectedBrowser(browserType._scope, this._browser);
|
||||
socket.on('close', () => {
|
||||
// Avoid sending any more messages over closed socket.
|
||||
connection.onmessage = () => {};
|
||||
// Cleanup contexts upon disconnect.
|
||||
browser.close().catch(e => {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectedBrowser extends BrowserDispatcher {
|
||||
private _contexts: BrowserContextDispatcher[] = [];
|
||||
_closed = false;
|
||||
|
||||
constructor(scope: DispatcherScope, browser: BrowserBase) {
|
||||
super(scope, browser, 'connectedBrowser');
|
||||
}
|
||||
|
||||
async newContext(params: BrowserNewContextParams): Promise<{ context: BrowserContextChannel }> {
|
||||
const result = await super.newContext(params);
|
||||
this._contexts.push(result.context as BrowserContextDispatcher);
|
||||
return result;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
// Only close our own contexts.
|
||||
await Promise.all(this._contexts.map(context => context.close()));
|
||||
this._didClose();
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
if (!this._closed) {
|
||||
// We come here multiple times:
|
||||
// - from ConnectedBrowser.close();
|
||||
// - from underlying Browser.on('close').
|
||||
this._closed = true;
|
||||
super._didClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,23 +141,9 @@ export type BrowserTypeInitializer = {
|
||||
name: string,
|
||||
};
|
||||
export interface BrowserTypeChannel extends Channel {
|
||||
connect(params: BrowserTypeConnectParams): Promise<BrowserTypeConnectResult>;
|
||||
launch(params: BrowserTypeLaunchParams): Promise<BrowserTypeLaunchResult>;
|
||||
launchServer(params: BrowserTypeLaunchServerParams): Promise<BrowserTypeLaunchServerResult>;
|
||||
launchPersistentContext(params: BrowserTypeLaunchPersistentContextParams): Promise<BrowserTypeLaunchPersistentContextResult>;
|
||||
}
|
||||
export type BrowserTypeConnectParams = {
|
||||
wsEndpoint: string,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
};
|
||||
export type BrowserTypeConnectOptions = {
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
};
|
||||
export type BrowserTypeConnectResult = {
|
||||
browser: BrowserChannel,
|
||||
};
|
||||
export type BrowserTypeLaunchParams = {
|
||||
executablePath?: string,
|
||||
args?: string[],
|
||||
@ -213,61 +199,6 @@ export type BrowserTypeLaunchOptions = {
|
||||
export type BrowserTypeLaunchResult = {
|
||||
browser: BrowserChannel,
|
||||
};
|
||||
export type BrowserTypeLaunchServerParams = {
|
||||
executablePath?: string,
|
||||
args?: string[],
|
||||
ignoreAllDefaultArgs?: boolean,
|
||||
ignoreDefaultArgs?: string[],
|
||||
handleSIGINT?: boolean,
|
||||
handleSIGTERM?: boolean,
|
||||
handleSIGHUP?: boolean,
|
||||
timeout?: number,
|
||||
env?: {
|
||||
name: string,
|
||||
value: string,
|
||||
}[],
|
||||
headless?: boolean,
|
||||
devtools?: boolean,
|
||||
proxy?: {
|
||||
server: string,
|
||||
bypass?: string,
|
||||
username?: string,
|
||||
password?: string,
|
||||
},
|
||||
downloadsPath?: string,
|
||||
firefoxUserPrefs?: any,
|
||||
chromiumSandbox?: boolean,
|
||||
port?: number,
|
||||
};
|
||||
export type BrowserTypeLaunchServerOptions = {
|
||||
executablePath?: string,
|
||||
args?: string[],
|
||||
ignoreAllDefaultArgs?: boolean,
|
||||
ignoreDefaultArgs?: string[],
|
||||
handleSIGINT?: boolean,
|
||||
handleSIGTERM?: boolean,
|
||||
handleSIGHUP?: boolean,
|
||||
timeout?: number,
|
||||
env?: {
|
||||
name: string,
|
||||
value: string,
|
||||
}[],
|
||||
headless?: boolean,
|
||||
devtools?: boolean,
|
||||
proxy?: {
|
||||
server: string,
|
||||
bypass?: string,
|
||||
username?: string,
|
||||
password?: string,
|
||||
},
|
||||
downloadsPath?: string,
|
||||
firefoxUserPrefs?: any,
|
||||
chromiumSandbox?: boolean,
|
||||
port?: number,
|
||||
};
|
||||
export type BrowserTypeLaunchServerResult = {
|
||||
server: BrowserServerChannel,
|
||||
};
|
||||
export type BrowserTypeLaunchPersistentContextParams = {
|
||||
userDataDir: string,
|
||||
executablePath?: string,
|
||||
@ -385,27 +316,6 @@ export type BrowserTypeLaunchPersistentContextResult = {
|
||||
context: BrowserContextChannel,
|
||||
};
|
||||
|
||||
// ----------- BrowserServer -----------
|
||||
export type BrowserServerInitializer = {
|
||||
wsEndpoint: string,
|
||||
pid: number,
|
||||
};
|
||||
export interface BrowserServerChannel extends Channel {
|
||||
on(event: 'close', callback: (params: BrowserServerCloseEvent) => void): this;
|
||||
close(params?: BrowserServerCloseParams): Promise<BrowserServerCloseResult>;
|
||||
kill(params?: BrowserServerKillParams): Promise<BrowserServerKillResult>;
|
||||
}
|
||||
export type BrowserServerCloseEvent = {
|
||||
exitCode?: number,
|
||||
signal?: string,
|
||||
};
|
||||
export type BrowserServerCloseParams = {};
|
||||
export type BrowserServerCloseOptions = {};
|
||||
export type BrowserServerCloseResult = void;
|
||||
export type BrowserServerKillParams = {};
|
||||
export type BrowserServerKillOptions = {};
|
||||
export type BrowserServerKillResult = void;
|
||||
|
||||
// ----------- Browser -----------
|
||||
export type BrowserInitializer = {
|
||||
version: string,
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
export { Accessibility } from './accessibility';
|
||||
export { Browser } from './browser';
|
||||
export { BrowserContext } from './browserContext';
|
||||
export { BrowserServer } from './browserServer';
|
||||
export { BrowserServer } from './browserType';
|
||||
export { BrowserType } from './browserType';
|
||||
export { ConsoleMessage } from './consoleMessage';
|
||||
export { Dialog } from './dialog';
|
||||
|
||||
@ -41,11 +41,7 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserInitializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this._browserType = parent as BrowserType;
|
||||
this._channel.on('close', () => {
|
||||
this._isConnected = false;
|
||||
this.emit(Events.Browser.Disconnected);
|
||||
this._isClosedOrClosing = true;
|
||||
});
|
||||
this._channel.on('close', () => this._didClose());
|
||||
this._closedPromise = new Promise(f => this.once(Events.Browser.Disconnected, f));
|
||||
}
|
||||
|
||||
@ -95,4 +91,10 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
|
||||
await this._closedPromise;
|
||||
});
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
this._isConnected = false;
|
||||
this.emit(Events.Browser.Disconnected);
|
||||
this._isClosedOrClosing = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _onClose() {
|
||||
async _onClose() {
|
||||
this._isClosedOrClosing = true;
|
||||
if (this._browser)
|
||||
this._browser._contexts.delete(this);
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { BrowserServerChannel, BrowserServerInitializer } from '../channels';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { Events } from './events';
|
||||
|
||||
export class BrowserServer extends ChannelOwner<BrowserServerChannel, BrowserServerInitializer> {
|
||||
static from(server: BrowserServerChannel): BrowserServer {
|
||||
return (server as any)._object;
|
||||
}
|
||||
|
||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserServerInitializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this._channel.on('close', ({ exitCode, signal }) => {
|
||||
this.emit(Events.BrowserServer.Close, exitCode === undefined ? null : exitCode, signal === undefined ? null : signal);
|
||||
});
|
||||
}
|
||||
|
||||
process(): ChildProcess {
|
||||
return { pid: this._initializer.pid } as any;
|
||||
}
|
||||
|
||||
wsEndpoint(): string {
|
||||
return this._initializer.wsEndpoint;
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
return this._wrapApiCall('browserServer.kill', async () => {
|
||||
await this._channel.kill();
|
||||
});
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
return this._wrapApiCall('browserServer.close', async () => {
|
||||
await this._channel.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -14,16 +14,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserTypeChannel, BrowserTypeInitializer, BrowserTypeLaunchParams, BrowserTypeLaunchServerParams, BrowserTypeLaunchPersistentContextParams } from '../channels';
|
||||
import { BrowserTypeChannel, BrowserTypeInitializer, BrowserTypeLaunchParams, BrowserTypeLaunchPersistentContextParams } from '../channels';
|
||||
import { Browser } from './browser';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { BrowserServer } from './browserServer';
|
||||
import { headersObjectToArray, envObjectToArray } from '../../converters';
|
||||
import { assert } from '../../helper';
|
||||
import { assert, helper } from '../../helper';
|
||||
import { LaunchOptions, LaunchServerOptions, ConnectOptions, LaunchPersistentContextOptions } from './types';
|
||||
import * as WebSocket from 'ws';
|
||||
import { Connection } from './connection';
|
||||
import { serializeError } from '../serializers';
|
||||
import { Events } from './events';
|
||||
import { TimeoutSettings } from '../../timeoutSettings';
|
||||
import { ChildProcess } from 'child_process';
|
||||
|
||||
export interface BrowserServerLauncher {
|
||||
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
|
||||
}
|
||||
|
||||
export interface BrowserServer {
|
||||
process(): ChildProcess;
|
||||
wsEndpoint(): string;
|
||||
close(): Promise<void>;
|
||||
kill(): Promise<void>;
|
||||
}
|
||||
|
||||
export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeInitializer> {
|
||||
private _timeoutSettings = new TimeoutSettings();
|
||||
_serverLauncher?: BrowserServerLauncher;
|
||||
|
||||
static from(browserType: BrowserTypeChannel): BrowserType {
|
||||
return (browserType as any)._object;
|
||||
@ -60,17 +78,9 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
|
||||
}
|
||||
|
||||
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
|
||||
const logger = options.logger;
|
||||
options = { ...options, logger: undefined };
|
||||
return this._wrapApiCall('browserType.launchServer', async () => {
|
||||
const launchServerOptions: BrowserTypeLaunchServerParams = {
|
||||
...options,
|
||||
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
|
||||
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
|
||||
env: options.env ? envObjectToArray(options.env) : undefined,
|
||||
};
|
||||
return BrowserServer.from((await this._channel.launchServer(launchServerOptions)).server);
|
||||
}, logger);
|
||||
if (!this._serverLauncher)
|
||||
throw new Error('Launching server is not supported');
|
||||
return this._serverLauncher.launchServer(options);
|
||||
}
|
||||
|
||||
async launchPersistentContext(userDataDir: string, options: LaunchPersistentContextOptions = {}): Promise<BrowserContext> {
|
||||
@ -96,11 +106,64 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
|
||||
|
||||
async connect(options: ConnectOptions): Promise<Browser> {
|
||||
const logger = options.logger;
|
||||
options = { ...options, logger: undefined };
|
||||
return this._wrapApiCall('browserType.connect', async () => {
|
||||
const browser = Browser.from((await this._channel.connect(options)).browser);
|
||||
browser._logger = logger;
|
||||
return browser;
|
||||
const connection = new Connection();
|
||||
|
||||
const ws = new WebSocket(options.wsEndpoint, [], {
|
||||
perMessageDeflate: false,
|
||||
maxPayload: 256 * 1024 * 1024, // 256Mb,
|
||||
handshakeTimeout: this._timeoutSettings.timeout(options),
|
||||
});
|
||||
|
||||
// The 'ws' module in node sometimes sends us multiple messages in a single task.
|
||||
const waitForNextTask = options.slowMo
|
||||
? (cb: () => any) => setTimeout(cb, options.slowMo)
|
||||
: helper.makeWaitForNextTask();
|
||||
connection.onmessage = message => {
|
||||
if (ws.readyState !== WebSocket.OPEN) {
|
||||
setTimeout(() => {
|
||||
connection.dispatch({ id: (message as any).id, error: serializeError(new Error('Browser has been closed')) });
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
ws.send(JSON.stringify(message));
|
||||
};
|
||||
ws.addEventListener('message', event => {
|
||||
waitForNextTask(() => connection.dispatch(JSON.parse(event.data)));
|
||||
});
|
||||
|
||||
return await new Promise<Browser>(async (fulfill, reject) => {
|
||||
if ((options as any).__testHookBeforeCreateBrowser) {
|
||||
try {
|
||||
await (options as any).__testHookBeforeCreateBrowser();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
ws.addEventListener('open', async () => {
|
||||
const browser = (await connection.waitForObjectWithKnownName('connectedBrowser')) as Browser;
|
||||
browser._logger = logger;
|
||||
const closeListener = () => {
|
||||
// Emulate all pages, contexts and the browser closing upon disconnect.
|
||||
for (const context of browser.contexts()) {
|
||||
for (const page of context.pages())
|
||||
page._onClose();
|
||||
context._onClose();
|
||||
}
|
||||
browser._didClose();
|
||||
};
|
||||
ws.addEventListener('close', closeListener);
|
||||
browser.on(Events.Browser.Disconnected, () => {
|
||||
ws.removeEventListener('close', closeListener);
|
||||
ws.close();
|
||||
});
|
||||
fulfill(browser);
|
||||
});
|
||||
ws.addEventListener('error', event => {
|
||||
ws.close();
|
||||
reject(new Error('WebSocket error: ' + event.message));
|
||||
});
|
||||
});
|
||||
}, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ import { ConsoleMessage } from './consoleMessage';
|
||||
import { Dialog } from './dialog';
|
||||
import { Download } from './download';
|
||||
import { parseError } from '../serializers';
|
||||
import { BrowserServer } from './browserServer';
|
||||
import { CDPSession } from './cdpSession';
|
||||
import { Playwright } from './playwright';
|
||||
import { Electron, ElectronApplication } from './electron';
|
||||
@ -160,9 +159,6 @@ export class Connection {
|
||||
else
|
||||
result = new BrowserContext(parent, type, guid, initializer, browserName);
|
||||
break;
|
||||
case 'BrowserServer':
|
||||
result = new BrowserServer(parent, type, guid, initializer);
|
||||
break;
|
||||
case 'BrowserType':
|
||||
result = new BrowserType(parent, type, guid, initializer);
|
||||
break;
|
||||
|
||||
@ -173,7 +173,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
|
||||
this.emit(Events.Page.Worker, worker);
|
||||
}
|
||||
|
||||
private _onClose() {
|
||||
_onClose() {
|
||||
this._closed = true;
|
||||
this._browserContext._pages.delete(this);
|
||||
this.emit(Events.Page.Close);
|
||||
|
||||
@ -15,13 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserNewContextOptions, BrowserTypeLaunchOptions, BrowserTypeLaunchServerOptions, BrowserTypeConnectParams } from '../channels';
|
||||
import { BrowserNewContextOptions, BrowserTypeLaunchOptions } from '../channels';
|
||||
|
||||
type LoggerSeverity = 'verbose' | 'info' | 'warning' | 'error';
|
||||
export interface LoggerSink {
|
||||
isEnabled(name: string, severity: LoggerSeverity): boolean;
|
||||
log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }): void;
|
||||
}
|
||||
// This is a workaround for the documentation generation.
|
||||
export interface Logger extends LoggerSink {}
|
||||
|
||||
export type Size = { width: number, height: number };
|
||||
export type Point = { x: number, y: number };
|
||||
@ -56,6 +58,33 @@ type FirefoxUserPrefs = {
|
||||
};
|
||||
type LaunchOptionsBase = Omit<BrowserTypeLaunchOptions, 'ignoreAllDefaultArgs' | 'ignoreDefaultArgs' | 'env' | 'firefoxUserPrefs'> & LaunchOverrides;
|
||||
export type LaunchOptions = LaunchOptionsBase & FirefoxUserPrefs;
|
||||
export type LaunchServerOptions = Omit<BrowserTypeLaunchServerOptions, 'ignoreAllDefaultArgs' | 'ignoreDefaultArgs' | 'env' | 'firefoxUserPrefs'> & LaunchOverrides & FirefoxUserPrefs;
|
||||
export type LaunchPersistentContextOptions = LaunchOptionsBase & BrowserContextOptions;
|
||||
export type ConnectOptions = BrowserTypeConnectParams & { logger?: LoggerSink };
|
||||
|
||||
export type ConnectOptions = {
|
||||
wsEndpoint: string,
|
||||
slowMo?: number,
|
||||
timeout?: number,
|
||||
logger?: LoggerSink,
|
||||
};
|
||||
export type LaunchServerOptions = {
|
||||
executablePath?: string,
|
||||
args?: string[],
|
||||
ignoreDefaultArgs?: boolean | string[],
|
||||
handleSIGINT?: boolean,
|
||||
handleSIGTERM?: boolean,
|
||||
handleSIGHUP?: boolean,
|
||||
timeout?: number,
|
||||
env?: Env,
|
||||
headless?: boolean,
|
||||
devtools?: boolean,
|
||||
proxy?: {
|
||||
server: string,
|
||||
bypass?: string,
|
||||
username?: string,
|
||||
password?: string
|
||||
},
|
||||
downloadsPath?: string,
|
||||
chromiumSandbox?: boolean,
|
||||
port?: number,
|
||||
logger?: LoggerSink,
|
||||
} & FirefoxUserPrefs;
|
||||
|
||||
@ -21,6 +21,7 @@ import { PlaywrightDispatcher } from './server/playwrightDispatcher';
|
||||
import { setUseApiName } from '../progress';
|
||||
import { Connection } from './client/connection';
|
||||
import { isUnderTest } from '../helper';
|
||||
import { BrowserServerLauncherImpl } from './browserServerImpl';
|
||||
|
||||
export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
|
||||
setUseApiName(false);
|
||||
@ -34,13 +35,16 @@ export function setupInProcess(playwright: PlaywrightImpl): PlaywrightAPI {
|
||||
|
||||
// Initialize Playwright channel.
|
||||
new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright);
|
||||
const playwrightAPI = clientConnection.getObjectWithKnownName('Playwright');
|
||||
const playwrightAPI = clientConnection.getObjectWithKnownName('Playwright') as PlaywrightAPI;
|
||||
playwrightAPI.chromium._serverLauncher = new BrowserServerLauncherImpl(playwright.chromium);
|
||||
playwrightAPI.firefox._serverLauncher = new BrowserServerLauncherImpl(playwright.firefox);
|
||||
playwrightAPI.webkit._serverLauncher = new BrowserServerLauncherImpl(playwright.webkit);
|
||||
|
||||
// Switch to async dispatch after we got Playwright object.
|
||||
dispatcherConnection.onmessage = message => setImmediate(() => clientConnection.dispatch(message));
|
||||
clientConnection.onmessage = message => setImmediate(() => dispatcherConnection.dispatch(message));
|
||||
|
||||
if (isUnderTest())
|
||||
playwrightAPI._toImpl = (x: any) => dispatcherConnection._dispatchers.get(x._guid)!._object;
|
||||
(playwrightAPI as any)._toImpl = (x: any) => dispatcherConnection._dispatchers.get(x._guid)!._object;
|
||||
return playwrightAPI;
|
||||
}
|
||||
|
||||
@ -176,14 +176,6 @@ BrowserType:
|
||||
|
||||
commands:
|
||||
|
||||
connect:
|
||||
parameters:
|
||||
wsEndpoint: string
|
||||
slowMo: number?
|
||||
timeout: number?
|
||||
returns:
|
||||
browser: Browser
|
||||
|
||||
launch:
|
||||
parameters:
|
||||
executablePath: string?
|
||||
@ -221,43 +213,6 @@ BrowserType:
|
||||
returns:
|
||||
browser: Browser
|
||||
|
||||
launchServer:
|
||||
parameters:
|
||||
executablePath: string?
|
||||
args:
|
||||
type: array?
|
||||
items: string
|
||||
ignoreAllDefaultArgs: boolean?
|
||||
ignoreDefaultArgs:
|
||||
type: array?
|
||||
items: string
|
||||
handleSIGINT: boolean?
|
||||
handleSIGTERM: boolean?
|
||||
handleSIGHUP: boolean?
|
||||
timeout: number?
|
||||
env:
|
||||
type: array?
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name: string
|
||||
value: string
|
||||
headless: boolean?
|
||||
devtools: boolean?
|
||||
proxy:
|
||||
type: object?
|
||||
properties:
|
||||
server: string
|
||||
bypass: string?
|
||||
username: string?
|
||||
password: string?
|
||||
downloadsPath: string?
|
||||
firefoxUserPrefs: json?
|
||||
chromiumSandbox: boolean?
|
||||
port: number?
|
||||
returns:
|
||||
server: BrowserServer
|
||||
|
||||
launchPersistentContext:
|
||||
parameters:
|
||||
userDataDir: string
|
||||
@ -340,28 +295,6 @@ BrowserType:
|
||||
context: BrowserContext
|
||||
|
||||
|
||||
|
||||
BrowserServer:
|
||||
type: interface
|
||||
|
||||
initializer:
|
||||
wsEndpoint: string
|
||||
pid: number
|
||||
|
||||
commands:
|
||||
|
||||
close:
|
||||
|
||||
kill:
|
||||
|
||||
events:
|
||||
|
||||
close:
|
||||
parameters:
|
||||
exitCode: number?
|
||||
signal: string?
|
||||
|
||||
|
||||
Browser:
|
||||
type: interface
|
||||
|
||||
|
||||
@ -26,12 +26,14 @@ import { PageDispatcher } from './pageDispatcher';
|
||||
import { headersArrayToObject } from '../../converters';
|
||||
|
||||
export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> implements BrowserChannel {
|
||||
constructor(scope: DispatcherScope, browser: BrowserBase) {
|
||||
super(scope, browser, 'Browser', { version: browser.version() }, true);
|
||||
browser.on(Events.Browser.Disconnected, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
constructor(scope: DispatcherScope, browser: BrowserBase, guid?: string) {
|
||||
super(scope, browser, 'Browser', { version: browser.version() }, true, guid);
|
||||
browser.on(Events.Browser.Disconnected, () => this._didClose());
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
}
|
||||
|
||||
async newContext(params: BrowserNewContextParams): Promise<{ context: BrowserContextChannel }> {
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserServer } from '../../server/browserServer';
|
||||
import { BrowserServerChannel, BrowserServerInitializer } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { Events } from '../../events';
|
||||
|
||||
export class BrowserServerDispatcher extends Dispatcher<BrowserServer, BrowserServerInitializer> implements BrowserServerChannel {
|
||||
constructor(scope: DispatcherScope, browserServer: BrowserServer) {
|
||||
super(scope, browserServer, 'BrowserServer', {
|
||||
wsEndpoint: browserServer.wsEndpoint(),
|
||||
pid: browserServer.process().pid
|
||||
}, true);
|
||||
browserServer.on(Events.BrowserServer.Close, (exitCode, signal) => {
|
||||
this._dispatchEvent('close', {
|
||||
exitCode: exitCode === null ? undefined : exitCode,
|
||||
signal: signal === null ? undefined : signal,
|
||||
});
|
||||
this._dispose();
|
||||
});
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this._object.close();
|
||||
}
|
||||
|
||||
async kill(): Promise<void> {
|
||||
await this._object.close();
|
||||
}
|
||||
}
|
||||
@ -16,13 +16,11 @@
|
||||
|
||||
import { BrowserBase } from '../../browser';
|
||||
import { BrowserTypeBase, BrowserType } from '../../server/browserType';
|
||||
import * as types from '../../types';
|
||||
import { BrowserDispatcher } from './browserDispatcher';
|
||||
import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer, BrowserServerChannel, BrowserTypeLaunchParams, BrowserTypeLaunchPersistentContextParams, BrowserTypeLaunchServerParams } from '../channels';
|
||||
import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeInitializer, BrowserTypeLaunchParams, BrowserTypeLaunchPersistentContextParams } from '../channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { BrowserContextBase } from '../../browserContext';
|
||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||
import { BrowserServerDispatcher } from './browserServerDispatcher';
|
||||
import { headersArrayToObject, envArrayToObject } from '../../converters';
|
||||
|
||||
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeInitializer> implements BrowserTypeChannel {
|
||||
@ -54,18 +52,4 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeIn
|
||||
const browserContext = await this._object.launchPersistentContext(params.userDataDir, options);
|
||||
return { context: new BrowserContextDispatcher(this._scope, browserContext as BrowserContextBase) };
|
||||
}
|
||||
|
||||
async launchServer(params: BrowserTypeLaunchServerParams): Promise<{ server: BrowserServerChannel }> {
|
||||
const options = {
|
||||
...params,
|
||||
ignoreDefaultArgs: params.ignoreAllDefaultArgs ? true : params.ignoreDefaultArgs,
|
||||
env: params.env ? envArrayToObject(params.env) : undefined,
|
||||
};
|
||||
return { server: new BrowserServerDispatcher(this._scope, await this._object.launchServer(options)) };
|
||||
}
|
||||
|
||||
async connect(params: types.ConnectOptions): Promise<{ browser: BrowserChannel }> {
|
||||
const browser = await this._object.connect(params);
|
||||
return { browser: new BrowserDispatcher(this._scope, browser as BrowserBase) };
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,11 +100,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
name: tString,
|
||||
handle: tChannel('ElementHandle'),
|
||||
});
|
||||
scheme.BrowserTypeConnectParams = tObject({
|
||||
wsEndpoint: tString,
|
||||
slowMo: tOptional(tNumber),
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.BrowserTypeLaunchParams = tObject({
|
||||
executablePath: tOptional(tString),
|
||||
args: tOptional(tArray(tString)),
|
||||
@ -131,32 +126,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
chromiumSandbox: tOptional(tBoolean),
|
||||
slowMo: tOptional(tNumber),
|
||||
});
|
||||
scheme.BrowserTypeLaunchServerParams = tObject({
|
||||
executablePath: tOptional(tString),
|
||||
args: tOptional(tArray(tString)),
|
||||
ignoreAllDefaultArgs: tOptional(tBoolean),
|
||||
ignoreDefaultArgs: tOptional(tArray(tString)),
|
||||
handleSIGINT: tOptional(tBoolean),
|
||||
handleSIGTERM: tOptional(tBoolean),
|
||||
handleSIGHUP: tOptional(tBoolean),
|
||||
timeout: tOptional(tNumber),
|
||||
env: tOptional(tArray(tObject({
|
||||
name: tString,
|
||||
value: tString,
|
||||
}))),
|
||||
headless: tOptional(tBoolean),
|
||||
devtools: tOptional(tBoolean),
|
||||
proxy: tOptional(tObject({
|
||||
server: tString,
|
||||
bypass: tOptional(tString),
|
||||
username: tOptional(tString),
|
||||
password: tOptional(tString),
|
||||
})),
|
||||
downloadsPath: tOptional(tString),
|
||||
firefoxUserPrefs: tOptional(tAny),
|
||||
chromiumSandbox: tOptional(tBoolean),
|
||||
port: tOptional(tNumber),
|
||||
});
|
||||
scheme.BrowserTypeLaunchPersistentContextParams = tObject({
|
||||
userDataDir: tString,
|
||||
executablePath: tOptional(tString),
|
||||
@ -214,8 +183,6 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
colorScheme: tOptional(tEnum(['light', 'dark', 'no-preference'])),
|
||||
acceptDownloads: tOptional(tBoolean),
|
||||
});
|
||||
scheme.BrowserServerCloseParams = tOptional(tObject({}));
|
||||
scheme.BrowserServerKillParams = tOptional(tObject({}));
|
||||
scheme.BrowserCloseParams = tOptional(tObject({}));
|
||||
scheme.BrowserNewContextParams = tObject({
|
||||
noDefaultViewport: tOptional(tBoolean),
|
||||
|
||||
@ -19,7 +19,7 @@ import './base.fixture';
|
||||
import utils from './utils';
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
|
||||
|
||||
it.slow()('should be able to reconnect to a browser', async({browserType, defaultBrowserOptions, server, toImpl}) => {
|
||||
it.skip(USES_HOOKS).slow()('should be able to reconnect to a browser', async({browserType, defaultBrowserOptions, server}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
@ -35,33 +35,27 @@ it.slow()('should be able to reconnect to a browser', async({browserType, defaul
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await browser.close();
|
||||
}
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it.fail(USES_HOOKS || (CHROMIUM && WIN)).slow()('should handle exceptions during connect', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS).fail(CHROMIUM && WIN).slow()('should handle exceptions during connect', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const __testHookBeforeCreateBrowser = () => { throw new Error('Dummy') };
|
||||
const error = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint(), __testHookBeforeCreateBrowser } as any).catch(e => e);
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
expect(error.message).toContain('Dummy');
|
||||
});
|
||||
|
||||
it('should set the browser connected state', async ({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should set the browser connected state', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(remote.isConnected()).toBe(true);
|
||||
await remote.close();
|
||||
expect(remote.isConnected()).toBe(false);
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should throw when used after isConnected returns false', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should throw when used after isConnected returns false', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
|
||||
@ -21,7 +21,7 @@ import fs from 'fs';
|
||||
import utils from './utils';
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
|
||||
|
||||
it('should work', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should work', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
@ -31,12 +31,10 @@ it('should work', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||
await page.close();
|
||||
await browser.close();
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should fire "disconnected" when closing the server', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should fire "disconnected" when closing the server', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||
@ -48,7 +46,7 @@ it('should fire "disconnected" when closing the server', async({browserType, def
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fire "close" event during kill', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should fire "close" event during kill', async({browserType, defaultBrowserOptions}) => {
|
||||
const order = [];
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const closedPromise = new Promise(f => browserServer.on('close', () => {
|
||||
@ -62,13 +60,13 @@ it('should fire "close" event during kill', async({browserType, defaultBrowserOp
|
||||
expect(order).toEqual(['closed', 'killed']);
|
||||
});
|
||||
|
||||
it('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
expect(browserServer.process().pid).toBeGreaterThan(0);
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should fire close event', async ({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should fire close event', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const [result] = await Promise.all([
|
||||
new Promise(f => (browserServer as any).on('close', (exitCode, signal) => f({ exitCode, signal }))),
|
||||
@ -78,7 +76,7 @@ it('should fire close event', async ({browserType, defaultBrowserOptions}) => {
|
||||
expect(result['signal']).toBe(null);
|
||||
});
|
||||
|
||||
it('should reject navigation when browser closes', async({browserType, defaultBrowserOptions, server, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should reject navigation when browser closes', async({browserType, defaultBrowserOptions, server}) => {
|
||||
server.setRoute('/one-style.css', () => {});
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
@ -88,12 +86,10 @@ it('should reject navigation when browser closes', async({browserType, defaultBr
|
||||
await remote.close();
|
||||
const error = await navigationPromise;
|
||||
expect(error.message).toContain('Navigation failed because page was closed!');
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should reject waitForSelector when browser closes', async({browserType, defaultBrowserOptions, server, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should reject waitForSelector when browser closes', async({browserType, defaultBrowserOptions, server}) => {
|
||||
server.setRoute('/empty.html', () => {});
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
@ -106,24 +102,20 @@ it('should reject waitForSelector when browser closes', async({browserType, defa
|
||||
await remote.close();
|
||||
const error = await watchdog;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should throw if used after disconnect', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should throw if used after disconnect', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
await remote.close();
|
||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||
expect((error as Error).message).toContain('has been closed');
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should emit close events on pages and contexts', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS)('should emit close events on pages and contexts', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const context = await remote.newContext();
|
||||
@ -137,7 +129,7 @@ it('should emit close events on pages and contexts', async({browserType, default
|
||||
expect(pageClosed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should terminate network waiters', async({browserType, defaultBrowserOptions, server}) => {
|
||||
it.skip(USES_HOOKS)('should terminate network waiters', async({browserType, defaultBrowserOptions, server}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const newPage = await remote.newPage();
|
||||
|
||||
@ -21,14 +21,14 @@ import { ChromiumBrowser, ChromiumBrowserContext } from '../..';
|
||||
const {makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
|
||||
|
||||
it.skip(!CHROMIUM)('should throw with remote-debugging-pipe argument', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS || !CHROMIUM)('should throw with remote-debugging-pipe argument', async({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should not throw with remote-debugging-port argument', async({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS || !CHROMIUM)('should not throw with remote-debugging-port argument', async({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
|
||||
const browser = await browserType.launchServer(options);
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
*/
|
||||
import './base.fixture';
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
|
||||
|
||||
it('should work across sessions', async ({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should work across sessions', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(browser1.contexts().length).toBe(0);
|
||||
@ -35,12 +35,10 @@ it('should work across sessions', async ({browserType, defaultBrowserOptions, to
|
||||
await browser1.close();
|
||||
await browser2.close();
|
||||
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it.slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async ({browserType, defaultBrowserOptions}) => {
|
||||
it.skip(USES_HOOKS).slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const originalBrowser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const wsEndpoint = browserServer.wsEndpoint();
|
||||
@ -74,7 +72,7 @@ it.slow()('should be emitted when: browser gets closed, disconnected or underlyi
|
||||
expect(disconnectedRemote2).toBe(1);
|
||||
});
|
||||
|
||||
it('should be able to connect multiple times to the same browser', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should be able to connect multiple times to the same browser', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
@ -85,12 +83,10 @@ it('should be able to connect multiple times to the same browser', async({browse
|
||||
const page2 = await browser2.newPage();
|
||||
expect(await page2.evaluate(() => 7 * 6)).toBe(42); // original browser should still work
|
||||
await browser2.close();
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
it('should not be able to close remote browser', async({browserType, defaultBrowserOptions, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should not be able to close remote browser', async({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
@ -102,7 +98,5 @@ it('should not be able to close remote browser', async({browserType, defaultBrow
|
||||
await remote.newContext();
|
||||
await remote.close();
|
||||
}
|
||||
if (toImpl)
|
||||
await toImpl(browserServer)._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user