diff --git a/docs/api.md b/docs/api.md index bb4446a21e1c02..8263c59b2f2481 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3688,6 +3688,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`. - `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`. + - `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. Launches browser instance that uses persistent storage located at `userDataDir`. diff --git a/src/browser.ts b/src/browser.ts index 4caabc079da8f7..02cb42c305c074 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -27,11 +27,6 @@ export interface Browser extends platform.EventEmitterType { _setDebugFunction(debugFunction: (message: string) => void): void; } -export type ConnectOptions = { - slowMo?: number, - wsEndpoint: string -}; - export async function createPageInNewContext(browser: Browser, options?: BrowserContextOptions): Promise { const context = await browser.newContext(options); const page = await context.newPage(); diff --git a/src/server/browserType.ts b/src/server/browserType.ts index 7b76cf4dcc546c..5dc7e7d0eb56d0 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { ConnectOptions } from '../browser'; import { BrowserContext } from '../browserContext'; import { BrowserServer } from './browserServer'; @@ -24,7 +23,7 @@ export type BrowserArgOptions = { devtools?: boolean, }; -export type LaunchOptions = BrowserArgOptions & { +export type ProcessOptions = BrowserArgOptions & { executablePath?: string, ignoreDefaultArgs?: boolean | string[], handleSIGINT?: boolean, @@ -39,11 +38,21 @@ export type LaunchOptions = BrowserArgOptions & { env?: {[key: string]: string} | undefined }; +export type ConnectOptions = { + wsEndpoint: string +} & BrowserOptions; + +type BrowserOptions = { + slowMo?: number, +} + +export type LaunchOptions = ProcessOptions & BrowserOptions; +export type ServerOptions = ProcessOptions & { port?: number }; export interface BrowserType { executablePath(): string; name(): string; - launch(options?: LaunchOptions & { slowMo?: number }): Promise; - launchServer(options?: LaunchOptions & { port?: number }): Promise; + launch(options?: LaunchOptions): Promise; + launchServer(options?: ServerOptions): Promise; launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise; connect(options: ConnectOptions): Promise; } diff --git a/src/server/chromium.ts b/src/server/chromium.ts index 0751ca698a2ee3..f5617e2c0c0a79 100644 --- a/src/server/chromium.ts +++ b/src/server/chromium.ts @@ -18,15 +18,15 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { debugError, helper } from '../helper'; +import { debugError, helper, assert } from '../helper'; import { CRBrowser } from '../chromium/crBrowser'; import * as platform from '../platform'; import * as ws from 'ws'; import { launchProcess } from '../server/processLauncher'; import { kBrowserCloseMessageId } from '../chromium/crConnection'; import { PipeTransport } from './pipeTransport'; -import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; -import { ConnectOptions, LaunchType } from '../browser'; +import { LaunchOptions, BrowserArgOptions, BrowserType, ProcessOptions, ConnectOptions, ServerOptions } from './browserType'; +import { LaunchType } from '../browser'; import { BrowserServer, WebSocketWrapper } from './browserServer'; import { Events } from '../events'; import { ConnectionTransport, ProtocolRequest } from '../transport'; @@ -45,28 +45,30 @@ export class Chromium implements BrowserType { return 'chromium'; } - async launch(options?: LaunchOptions & { slowMo?: number }): Promise { - if (options && (options as any).userDataDir) - throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); + async launch(options: LaunchOptions = {}): Promise { + assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); 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; return browser; } - async launchServer(options?: LaunchOptions & { port?: number }): Promise { - return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer; + async launchServer(options: ServerOptions = {}): Promise { + return (await this._launchServer(options, 'server', undefined, options.port)).browserServer; } - async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise { - const { timeout = 30000 } = options || {}; + async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise { + const { + timeout = 30000, + slowMo = undefined + } = options; 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); return browser._defaultContext; } - private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { + private async _launchServer(options: ProcessOptions, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { const { ignoreDefaultArgs = false, args = [], diff --git a/src/server/firefox.ts b/src/server/firefox.ts index ba0cb663590e6c..af8641017bc6df 100644 --- a/src/server/firefox.ts +++ b/src/server/firefox.ts @@ -19,16 +19,16 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as ws from 'ws'; -import { ConnectOptions, LaunchType } from '../browser'; +import { LaunchType } from '../browser'; import { BrowserContext } from '../browserContext'; import { TimeoutError } from '../errors'; import { Events } from '../events'; import { FFBrowser } from '../firefox/ffBrowser'; import { kBrowserCloseMessageId } from '../firefox/ffConnection'; -import { debugError, helper } from '../helper'; +import { debugError, helper, assert } from '../helper'; import * as platform from '../platform'; import { BrowserServer, WebSocketWrapper } from './browserServer'; -import { BrowserArgOptions, BrowserType, LaunchOptions } from './browserType'; +import { BrowserArgOptions, BrowserType, LaunchOptions, ServerOptions, ProcessOptions, ConnectOptions } from './browserType'; import { launchProcess, waitForLine } from './processLauncher'; import { ConnectionTransport, SequenceNumberMixer } from '../transport'; @@ -47,12 +47,11 @@ export class Firefox implements BrowserType { return 'firefox'; } - async launch(options?: LaunchOptions & { slowMo?: number }): Promise { - if (options && (options as any).userDataDir) - throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); + async launch(options: LaunchOptions = {}): Promise { + assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); const browserServer = await this._launchServer(options, 'local'); 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. browser.close = () => browserServer.close(); @@ -60,15 +59,18 @@ export class Firefox implements BrowserType { return browser; } - async launchServer(options?: LaunchOptions & { port?: number }): Promise { - return await this._launchServer(options, 'server', undefined, options && options.port); + async launchServer(options: ServerOptions = {}): Promise { + return await this._launchServer(options, 'server', undefined, options.port); } - async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise { - const { timeout = 30000 } = options || {}; + async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise { + const { + timeout = 30000, + slowMo = undefined, + } = options; const browserServer = await this._launchServer(options, 'persistent', userDataDir); 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); // Hack: for typical launch scenario, ensure that close waits for actual process termination. @@ -77,7 +79,7 @@ export class Firefox implements BrowserType { return browserContext; } - private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise { + private async _launchServer(options: ProcessOptions, launchType: LaunchType, userDataDir?: string, port?: number): Promise { const { ignoreDefaultArgs = false, args = [], diff --git a/src/server/webkit.ts b/src/server/webkit.ts index 1027a497992305..745c87c8f69873 100644 --- a/src/server/webkit.ts +++ b/src/server/webkit.ts @@ -22,12 +22,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as platform from '../platform'; import * as os from 'os'; -import { debugError, helper } from '../helper'; +import { debugError, helper, assert } from '../helper'; import { kBrowserCloseMessageId } from '../webkit/wkConnection'; -import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType'; +import { LaunchOptions, BrowserArgOptions, BrowserType, ProcessOptions, ServerOptions, ConnectOptions } from './browserType'; import { ConnectionTransport, SequenceNumberMixer } from '../transport'; import * as ws from 'ws'; -import { ConnectOptions, LaunchType } from '../browser'; +import { LaunchType } from '../browser'; import { BrowserServer, WebSocketWrapper } from './browserServer'; import { Events } from '../events'; import { BrowserContext } from '../browserContext'; @@ -45,28 +45,30 @@ export class WebKit implements BrowserType { return 'webkit'; } - async launch(options?: LaunchOptions & { slowMo?: number }): Promise { - if (options && (options as any).userDataDir) - throw new Error('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); + async launch(options: LaunchOptions = {}): Promise { + assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); 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; return browser; } - async launchServer(options?: LaunchOptions & { port?: number }): Promise { - return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer; + async launchServer(options: ServerOptions = {}): Promise { + return (await this._launchServer(options, 'server', undefined, options.port)).browserServer; } - async launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise { - const { timeout = 30000 } = options || {}; + async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise { + const { + timeout = 30000, + slowMo = undefined, + } = options || {}; 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); return browser._defaultContext; } - private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { + private async _launchServer(options: ProcessOptions, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> { const { ignoreDefaultArgs = false, args = [], diff --git a/test/launcher.spec.js b/test/launcher.spec.js index b954e67b27de91..eb6defcaff0b68 100644 --- a/test/launcher.spec.js +++ b/test/launcher.spec.js @@ -39,7 +39,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p await neverResolves; expect(error.message).toContain('Protocol error'); }); - it('should throw if userDataDir option is passed', async() => { + fit('should throw if userDataDir option is passed', async() => { let waitError = null; const options = Object.assign({}, defaultBrowserOptions, {userDataDir: 'random-path'}); await browserType.launch(options).catch(e => waitError = e);