feat: add missing slowMo to launchPersistentContext (#1597)

`slowMo` was missing in `launchPersistentContext`, and I refactored the types a bit.
This commit is contained in:
Joel Einbinder 2020-03-31 16:34:59 -07:00 committed by GitHub
parent a853690c16
commit 6053784188
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 52 deletions

View file

@ -3690,6 +3690,7 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'.
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`. - `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0.
- returns: <[Promise]<[BrowserContext]>> Promise which resolves to the browser app instance. - returns: <[Promise]<[BrowserContext]>> Promise which resolves to the browser app instance.
Launches browser instance that uses persistent storage located at `userDataDir`. Launches browser instance that uses persistent storage located at `userDataDir`.

View file

@ -27,11 +27,6 @@ export interface Browser extends platform.EventEmitterType {
_setDebugFunction(debugFunction: (message: string) => void): void; _setDebugFunction(debugFunction: (message: string) => void): void;
} }
export type ConnectOptions = {
slowMo?: number,
wsEndpoint: string
};
export async function createPageInNewContext(browser: Browser, options?: BrowserContextOptions): Promise<Page> { export async function createPageInNewContext(browser: Browser, options?: BrowserContextOptions): Promise<Page> {
const context = await browser.newContext(options); const context = await browser.newContext(options);
const page = await context.newPage(); const page = await context.newPage();

View file

@ -14,7 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { ConnectOptions } from '../browser';
import { BrowserContext } from '../browserContext'; import { BrowserContext } from '../browserContext';
import { BrowserServer } from './browserServer'; import { BrowserServer } from './browserServer';
@ -24,7 +23,7 @@ export type BrowserArgOptions = {
devtools?: boolean, devtools?: boolean,
}; };
export type LaunchOptions = BrowserArgOptions & { type LaunchOptionsBase = BrowserArgOptions & {
executablePath?: string, executablePath?: string,
ignoreDefaultArgs?: boolean | string[], ignoreDefaultArgs?: boolean | string[],
handleSIGINT?: boolean, handleSIGINT?: boolean,
@ -39,11 +38,17 @@ export type LaunchOptions = BrowserArgOptions & {
env?: {[key: string]: string} | undefined env?: {[key: string]: string} | undefined
}; };
export type ConnectOptions = {
wsEndpoint: string,
slowMo?: number
};
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
export type LaunchServerOptions = LaunchOptionsBase & { port?: number };
export interface BrowserType<Browser> { export interface BrowserType<Browser> {
executablePath(): string; executablePath(): string;
name(): string; name(): string;
launch(options?: LaunchOptions & { slowMo?: number }): Promise<Browser>; launch(options?: LaunchOptions): Promise<Browser>;
launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer>; launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext>; launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext>;
connect(options: ConnectOptions): Promise<Browser>; connect(options: ConnectOptions): Promise<Browser>;
} }

View file

@ -18,15 +18,15 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import { debugError, helper } from '../helper'; import { debugError, helper, assert } from '../helper';
import { CRBrowser } from '../chromium/crBrowser'; import { CRBrowser } from '../chromium/crBrowser';
import * as platform from '../platform'; import * as platform from '../platform';
import * as ws from 'ws'; import * as ws from 'ws';
import { launchProcess } from '../server/processLauncher'; import { launchProcess } from '../server/processLauncher';
import { kBrowserCloseMessageId } from '../chromium/crConnection'; import { kBrowserCloseMessageId } from '../chromium/crConnection';
import { PipeTransport } from './pipeTransport'; import { PipeTransport } from './pipeTransport';
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; import { LaunchOptions, BrowserArgOptions, BrowserType, ConnectOptions, LaunchServerOptions } from './browserType';
import { ConnectOptions, LaunchType } from '../browser'; import { LaunchType } from '../browser';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { Events } from '../events'; import { Events } from '../events';
import { ConnectionTransport, ProtocolRequest } from '../transport'; import { ConnectionTransport, ProtocolRequest } from '../transport';
@ -45,28 +45,30 @@ export class Chromium implements BrowserType<CRBrowser> {
return 'chromium'; return 'chromium';
} }
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<CRBrowser> { async launch(options: LaunchOptions = {}): Promise<CRBrowser> {
if (options && (options as any).userDataDir) assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
const { browserServer, transport } = await this._launchServer(options, 'local'); const { browserServer, transport } = await this._launchServer(options, 'local');
const browser = await CRBrowser.connect(transport!, false, options && options.slowMo); const browser = await CRBrowser.connect(transport!, false, options.slowMo);
(browser as any)['__server__'] = browserServer; (browser as any)['__server__'] = browserServer;
return browser; return browser;
} }
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> { async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer; return (await this._launchServer(options, 'server')).browserServer;
} }
async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext> { async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
const { timeout = 30000 } = options || {}; const {
timeout = 30000,
slowMo = 0
} = options;
const { transport } = await this._launchServer(options, 'persistent', userDataDir); const { transport } = await this._launchServer(options, 'persistent', userDataDir);
const browser = await CRBrowser.connect(transport!, true); const browser = await CRBrowser.connect(transport!, true, slowMo);
await helper.waitWithTimeout(browser._firstPagePromise, 'first page', timeout); await helper.waitWithTimeout(browser._firstPagePromise, 'first page', timeout);
return browser._defaultContext; return browser._defaultContext;
} }
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -76,7 +78,9 @@ export class Chromium implements BrowserType<CRBrowser> {
handleSIGINT = true, handleSIGINT = true,
handleSIGTERM = true, handleSIGTERM = true,
handleSIGHUP = true, handleSIGHUP = true,
port = 0,
} = options; } = options;
assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.');
let temporaryUserDataDir: string | null = null; let temporaryUserDataDir: string | null = null;
if (!userDataDir) { if (!userDataDir) {
@ -124,7 +128,7 @@ export class Chromium implements BrowserType<CRBrowser> {
let transport: PipeTransport | undefined = undefined; let transport: PipeTransport | undefined = undefined;
let browserServer: BrowserServer | undefined = undefined; let browserServer: BrowserServer | undefined = undefined;
transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream, () => browserServer!.close()); transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream, () => browserServer!.close());
browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? wrapTransportWithWebSocket(transport, port || 0) : null); browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? wrapTransportWithWebSocket(transport, port) : null);
return { browserServer, transport }; return { browserServer, transport };
} }

View file

@ -19,16 +19,16 @@ import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as ws from 'ws'; import * as ws from 'ws';
import { ConnectOptions, LaunchType } from '../browser'; import { LaunchType } from '../browser';
import { BrowserContext } from '../browserContext'; import { BrowserContext } from '../browserContext';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import { Events } from '../events'; import { Events } from '../events';
import { FFBrowser } from '../firefox/ffBrowser'; import { FFBrowser } from '../firefox/ffBrowser';
import { kBrowserCloseMessageId } from '../firefox/ffConnection'; import { kBrowserCloseMessageId } from '../firefox/ffConnection';
import { debugError, helper } from '../helper'; import { debugError, helper, assert } from '../helper';
import * as platform from '../platform'; import * as platform from '../platform';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { BrowserArgOptions, BrowserType, LaunchOptions } from './browserType'; import { BrowserArgOptions, BrowserType, LaunchOptions, LaunchServerOptions, ConnectOptions } from './browserType';
import { launchProcess, waitForLine } from './processLauncher'; import { launchProcess, waitForLine } from './processLauncher';
import { ConnectionTransport, SequenceNumberMixer } from '../transport'; import { ConnectionTransport, SequenceNumberMixer } from '../transport';
@ -47,12 +47,11 @@ export class Firefox implements BrowserType<FFBrowser> {
return 'firefox'; return 'firefox';
} }
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<FFBrowser> { async launch(options: LaunchOptions = {}): Promise<FFBrowser> {
if (options && (options as any).userDataDir) assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
const browserServer = await this._launchServer(options, 'local'); const browserServer = await this._launchServer(options, 'local');
const browser = await platform.connectToWebsocket(browserServer.wsEndpoint()!, transport => { const browser = await platform.connectToWebsocket(browserServer.wsEndpoint()!, transport => {
return FFBrowser.connect(transport, false, options && options.slowMo); return FFBrowser.connect(transport, false, options.slowMo);
}); });
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
browser.close = () => browserServer.close(); browser.close = () => browserServer.close();
@ -60,15 +59,18 @@ export class Firefox implements BrowserType<FFBrowser> {
return browser; return browser;
} }
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> { async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
return await this._launchServer(options, 'server', undefined, options && options.port); return await this._launchServer(options, 'server');
} }
async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext> { async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
const { timeout = 30000 } = options || {}; const {
timeout = 30000,
slowMo = 0,
} = options;
const browserServer = await this._launchServer(options, 'persistent', userDataDir); const browserServer = await this._launchServer(options, 'persistent', userDataDir);
const browser = await platform.connectToWebsocket(browserServer.wsEndpoint()!, transport => { const browser = await platform.connectToWebsocket(browserServer.wsEndpoint()!, transport => {
return FFBrowser.connect(transport, true); return FFBrowser.connect(transport, true, slowMo);
}); });
await helper.waitWithTimeout(browser._firstPagePromise, 'first page', timeout); await helper.waitWithTimeout(browser._firstPagePromise, 'first page', timeout);
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
@ -77,7 +79,7 @@ export class Firefox implements BrowserType<FFBrowser> {
return browserContext; return browserContext;
} }
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<BrowserServer> { private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<BrowserServer> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -88,7 +90,9 @@ export class Firefox implements BrowserType<FFBrowser> {
handleSIGINT = true, handleSIGINT = true,
handleSIGTERM = true, handleSIGTERM = true,
timeout = 30000, timeout = 30000,
port = 0,
} = options; } = options;
assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.');
const firefoxArguments = []; const firefoxArguments = [];
@ -145,7 +149,7 @@ export class Firefox implements BrowserType<FFBrowser> {
let browserServer: BrowserServer | undefined = undefined; let browserServer: BrowserServer | undefined = undefined;
let browserWSEndpoint: string | undefined = undefined; let browserWSEndpoint: string | undefined = undefined;
const webSocketWrapper = launchType === 'server' ? (await platform.connectToWebsocket(innerEndpoint, t => wrapTransportWithWebSocket(t, port || 0))) : new WebSocketWrapper(innerEndpoint, []); const webSocketWrapper = launchType === 'server' ? (await platform.connectToWebsocket(innerEndpoint, t => wrapTransportWithWebSocket(t, port))) : new WebSocketWrapper(innerEndpoint, []);
browserWSEndpoint = webSocketWrapper.wsEndpoint; browserWSEndpoint = webSocketWrapper.wsEndpoint;
browserServer = new BrowserServer(launchedProcess, gracefullyClose, webSocketWrapper); browserServer = new BrowserServer(launchedProcess, gracefullyClose, webSocketWrapper);
return browserServer; return browserServer;

View file

@ -22,12 +22,12 @@ import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as platform from '../platform'; import * as platform from '../platform';
import * as os from 'os'; import * as os from 'os';
import { debugError, helper } from '../helper'; import { debugError, helper, assert } from '../helper';
import { kBrowserCloseMessageId } from '../webkit/wkConnection'; import { kBrowserCloseMessageId } from '../webkit/wkConnection';
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; import { LaunchOptions, BrowserArgOptions, BrowserType, LaunchServerOptions, ConnectOptions } from './browserType';
import { ConnectionTransport, SequenceNumberMixer } from '../transport'; import { ConnectionTransport, SequenceNumberMixer } from '../transport';
import * as ws from 'ws'; import * as ws from 'ws';
import { ConnectOptions, LaunchType } from '../browser'; import { LaunchType } from '../browser';
import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserServer, WebSocketWrapper } from './browserServer';
import { Events } from '../events'; import { Events } from '../events';
import { BrowserContext } from '../browserContext'; import { BrowserContext } from '../browserContext';
@ -45,28 +45,30 @@ export class WebKit implements BrowserType<WKBrowser> {
return 'webkit'; return 'webkit';
} }
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<WKBrowser> { async launch(options: LaunchOptions = {}): Promise<WKBrowser> {
if (options && (options as any).userDataDir) assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
const { browserServer, transport } = await this._launchServer(options, 'local'); const { browserServer, transport } = await this._launchServer(options, 'local');
const browser = await WKBrowser.connect(transport!, options && options.slowMo); const browser = await WKBrowser.connect(transport!, options.slowMo);
(browser as any)['__server__'] = browserServer; (browser as any)['__server__'] = browserServer;
return browser; return browser;
} }
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> { async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer; return (await this._launchServer(options, 'server')).browserServer;
} }
async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext> { async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
const { timeout = 30000 } = options || {}; const {
timeout = 30000,
slowMo = 0,
} = options;
const { transport } = await this._launchServer(options, 'persistent', userDataDir); const { transport } = await this._launchServer(options, 'persistent', userDataDir);
const browser = await WKBrowser.connect(transport!, undefined, true); const browser = await WKBrowser.connect(transport!, slowMo, true);
await helper.waitWithTimeout(browser._waitForFirstPageTarget(), 'first page', timeout); await helper.waitWithTimeout(browser._waitForFirstPageTarget(), 'first page', timeout);
return browser._defaultContext; return browser._defaultContext;
} }
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -76,7 +78,9 @@ export class WebKit implements BrowserType<WKBrowser> {
handleSIGINT = true, handleSIGINT = true,
handleSIGTERM = true, handleSIGTERM = true,
handleSIGHUP = true, handleSIGHUP = true,
port = 0,
} = options; } = options;
assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.');
let temporaryUserDataDir: string | null = null; let temporaryUserDataDir: string | null = null;
if (!userDataDir) { if (!userDataDir) {
@ -86,9 +90,9 @@ export class WebKit implements BrowserType<WKBrowser> {
const webkitArguments = []; const webkitArguments = [];
if (!ignoreDefaultArgs) if (!ignoreDefaultArgs)
webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir!, port || 0)); webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir!, port));
else if (Array.isArray(ignoreDefaultArgs)) else if (Array.isArray(ignoreDefaultArgs))
webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir!, port || 0).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1)); webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir!, port).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));
else else
webkitArguments.push(...args); webkitArguments.push(...args);