feat(default context): support selected options for default context (#2177)
This commit is contained in:
parent
2f993018ea
commit
aae3f1e75d
|
|
@ -6,7 +6,7 @@
|
|||
},
|
||||
{
|
||||
"name": "firefox",
|
||||
"revision": "1094"
|
||||
"revision": "1097"
|
||||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
|
|
|
|||
27
docs/api.md
27
docs/api.md
|
|
@ -4031,9 +4031,32 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'.
|
|||
- `env` <[Object]<[string], [string]|[number]|[boolean]>> 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.
|
||||
- `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
|
||||
- `bypassCSP` <[boolean]> Toggles bypassing page's Content-Security-Policy.
|
||||
- `viewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `null` disables the default viewport.
|
||||
- `width` <[number]> page width in pixels.
|
||||
- `height` <[number]> page height in pixels.
|
||||
- `userAgent` <[string]> Specific user agent to use in this context.
|
||||
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
|
||||
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account and touch events are enabled. Defaults to `false`. Not supported in Firefox.
|
||||
- `hasTouch` <[boolean]> Specifies if viewport supports touch events. Defaults to false.
|
||||
- `javaScriptEnabled` <[boolean]> Whether or not to enable JavaScript in the context. Defaults to true.
|
||||
- `timezoneId` <[string]> Changes the timezone of the context. See [ICU’s `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
|
||||
- `geolocation` <[Object]>
|
||||
- `latitude` <[number]> Latitude between -90 and 90.
|
||||
- `longitude` <[number]> Longitude between -180 and 180.
|
||||
- `accuracy` <[number]> Non-negative accuracy value. Defaults to `0`.
|
||||
- `locale` <[string]> Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language` request header value as well as number and date formatting rules.
|
||||
- `permissions` <[Array]<[string]>> A list of permissions to grant to all pages in this context. See [browserContext.grantPermissions](#browsercontextgrantpermissionspermissions-options) for more details.
|
||||
- `extraHTTPHeaders` <[Object]<[string], [string]>> An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
- `offline` <[boolean]> Whether to emulate network being offline. Defaults to `false`.
|
||||
- `httpCredentials` <[Object]> Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
- `username` <[string]>
|
||||
- `password` <[string]>
|
||||
- `colorScheme` <"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'.
|
||||
- returns: <[Promise]<[BrowserContext]>> Promise that resolves to the persistent browser context instance.
|
||||
|
||||
Launches browser instance that uses persistent storage located at `userDataDir`.
|
||||
Launches browser that uses persistent storage located at `userDataDir` and returns the only context. Closing this context will automatically close the browser.
|
||||
|
||||
#### browserType.launchServer([options])
|
||||
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
|
||||
|
|
|
|||
|
|
@ -14,22 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext, BrowserContextOptions, BrowserContextBase } from './browserContext';
|
||||
import { BrowserContext, BrowserContextOptions, BrowserContextBase, PersistentContextOptions } from './browserContext';
|
||||
import { Page } from './page';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Download } from './download';
|
||||
import type { BrowserServer } from './server/browserServer';
|
||||
import { Events } from './events';
|
||||
import { InnerLogger, Log } from './logger';
|
||||
import * as types from './types';
|
||||
|
||||
export type BrowserOptions = {
|
||||
logger: InnerLogger,
|
||||
downloadsPath: string,
|
||||
downloadsPath?: string,
|
||||
headful?: boolean,
|
||||
persistent?: boolean,
|
||||
persistent?: PersistentContextOptions, // Undefined means no persistent context.
|
||||
slowMo?: number,
|
||||
viewport?: types.Size | null,
|
||||
ownedServer?: BrowserServer,
|
||||
};
|
||||
|
||||
|
|
@ -64,7 +62,7 @@ export abstract class BrowserBase extends EventEmitter implements Browser, Inner
|
|||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { BrowserBase } from './browser';
|
|||
import { Log, InnerLogger, Logger, RootLogger } from './logger';
|
||||
import { FunctionWithSource } from './frames';
|
||||
|
||||
export type BrowserContextOptions = {
|
||||
export type PersistentContextOptions = {
|
||||
viewport?: types.Size | null,
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
javaScriptEnabled?: boolean,
|
||||
|
|
@ -44,6 +44,9 @@ export type BrowserContextOptions = {
|
|||
isMobile?: boolean,
|
||||
hasTouch?: boolean,
|
||||
colorScheme?: types.ColorScheme,
|
||||
};
|
||||
|
||||
export type BrowserContextOptions = PersistentContextOptions & {
|
||||
acceptDownloads?: boolean,
|
||||
logger?: Logger,
|
||||
};
|
||||
|
|
@ -188,9 +191,15 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements
|
|||
await this.waitForEvent('page');
|
||||
const pages = this.pages();
|
||||
await pages[0].waitForLoadState();
|
||||
if (pages.length !== 1 || pages[0].url() !== 'about:blank') {
|
||||
await this.close().catch(e => null);
|
||||
if (pages.length !== 1 || pages[0].url() !== 'about:blank')
|
||||
throw new Error(`Arguments can not specify page to be opened (first url is ${pages[0].url()})`);
|
||||
if (this._options.isMobile || this._options.locale) {
|
||||
// Workaround for:
|
||||
// - chromium fails to change isMobile for existing page;
|
||||
// - webkit fails to change locale for existing page.
|
||||
const oldPage = pages[0];
|
||||
await this.newPage();
|
||||
await oldPage.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +212,28 @@ export function assertBrowserContextIsNotOwned(context: BrowserContextBase) {
|
|||
}
|
||||
|
||||
export function validateBrowserContextOptions(options: BrowserContextOptions): BrowserContextOptions {
|
||||
const result = { ...options };
|
||||
// Copy all fields manually to strip any extra junk.
|
||||
// Especially useful when we share context and launch options for launchPersistent.
|
||||
const result: BrowserContextOptions = {
|
||||
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
||||
bypassCSP: options.bypassCSP,
|
||||
locale: options.locale,
|
||||
timezoneId: options.timezoneId,
|
||||
offline: options.offline,
|
||||
colorScheme: options.colorScheme,
|
||||
acceptDownloads: options.acceptDownloads,
|
||||
viewport: options.viewport,
|
||||
javaScriptEnabled: options.javaScriptEnabled,
|
||||
userAgent: options.userAgent,
|
||||
geolocation: options.geolocation,
|
||||
permissions: options.permissions,
|
||||
extraHTTPHeaders: options.extraHTTPHeaders,
|
||||
httpCredentials: options.httpCredentials,
|
||||
deviceScaleFactor: options.deviceScaleFactor,
|
||||
isMobile: options.isMobile,
|
||||
hasTouch: options.hasTouch,
|
||||
logger: options.logger,
|
||||
};
|
||||
if (result.viewport === null && result.deviceScaleFactor !== undefined)
|
||||
throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
|
||||
if (result.viewport === null && result.isMobile !== undefined)
|
||||
|
|
@ -219,6 +249,12 @@ export function validateBrowserContextOptions(options: BrowserContextOptions): B
|
|||
return result;
|
||||
}
|
||||
|
||||
export function validatePersistentContextOptions(options: PersistentContextOptions): PersistentContextOptions {
|
||||
if ((options as any).acceptDownloads !== undefined)
|
||||
throw new Error(`Option "acceptDownloads" is not supported for persistent context`);
|
||||
return validateBrowserContextOptions(options);
|
||||
}
|
||||
|
||||
export function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation {
|
||||
const result = { ...geolocation };
|
||||
result.accuracy = result.accuracy || 0;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ export class CRBrowser extends BrowserBase {
|
|||
return browser;
|
||||
}
|
||||
|
||||
browser._defaultContext = new CRBrowserContext(browser, null, options.persistent);
|
||||
|
||||
const existingTargetAttachPromises: Promise<any>[] = [];
|
||||
// First page, background pages and their service workers in the persistent context
|
||||
// are created automatically and may be initialized before we enable auto-attach.
|
||||
|
|
@ -77,6 +79,7 @@ export class CRBrowser extends BrowserBase {
|
|||
await Promise.all([
|
||||
startDiscover,
|
||||
autoAttachAndStopDiscover,
|
||||
(browser._defaultContext as CRBrowserContext)._initialize(),
|
||||
]);
|
||||
|
||||
// Wait for initial targets to arrive.
|
||||
|
|
@ -88,9 +91,6 @@ export class CRBrowser extends BrowserBase {
|
|||
super(options);
|
||||
this._connection = connection;
|
||||
this._session = this._connection.rootSession;
|
||||
|
||||
if (options.persistent)
|
||||
this._defaultContext = new CRBrowserContext(this, null, validateBrowserContextOptions({ viewport: options.viewport }));
|
||||
this._connection.on(ConnectionEvents.Disconnected, () => {
|
||||
for (const context of this._contexts.values())
|
||||
context._browserClosed();
|
||||
|
|
@ -290,19 +290,17 @@ export class CRBrowserContext extends BrowserContextBase {
|
|||
}
|
||||
|
||||
async _initialize() {
|
||||
const promises: Promise<any>[] = [
|
||||
this._browser._session.send('Browser.setDownloadBehavior', {
|
||||
assert(!Array.from(this._browser._crPages.values()).some(page => page._browserContext === this));
|
||||
const promises: Promise<any>[] = [];
|
||||
if (this._browser._options.downloadsPath) {
|
||||
promises.push(this._browser._session.send('Browser.setDownloadBehavior', {
|
||||
behavior: this._options.acceptDownloads ? 'allowAndName' : 'deny',
|
||||
browserContextId: this._browserContextId || undefined,
|
||||
downloadPath: this._browser._options.downloadsPath
|
||||
})
|
||||
];
|
||||
}));
|
||||
}
|
||||
if (this._options.permissions)
|
||||
promises.push(this.grantPermissions(this._options.permissions));
|
||||
if (this._options.offline)
|
||||
promises.push(this.setOffline(this._options.offline));
|
||||
if (this._options.httpCredentials)
|
||||
promises.push(this.setHTTPCredentials(this._options.httpCredentials));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,14 @@ export class FFBrowser extends BrowserBase {
|
|||
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
|
||||
const connection = new FFConnection(SlowMoTransport.wrap(transport, options.slowMo), options.logger);
|
||||
const browser = new FFBrowser(connection, options);
|
||||
await connection.send('Browser.enable', { attachToDefaultContext: !!options.persistent });
|
||||
const promises: Promise<any>[] = [
|
||||
connection.send('Browser.enable', { attachToDefaultContext: !!options.persistent }),
|
||||
];
|
||||
if (options.persistent) {
|
||||
browser._defaultContext = new FFBrowserContext(browser, null, options.persistent);
|
||||
promises.push((browser._defaultContext as FFBrowserContext)._initialize());
|
||||
}
|
||||
await Promise.all(promises);
|
||||
return browser;
|
||||
}
|
||||
|
||||
|
|
@ -45,9 +52,6 @@ export class FFBrowser extends BrowserBase {
|
|||
super(options);
|
||||
this._connection = connection;
|
||||
this._ffPages = new Map();
|
||||
|
||||
if (options.persistent)
|
||||
this._defaultContext = new FFBrowserContext(this, null, validateBrowserContextOptions({}));
|
||||
this._contexts = new Map();
|
||||
this._connection.on(ConnectionEvents.Disconnected, () => {
|
||||
for (const context of this._contexts.values())
|
||||
|
|
@ -151,16 +155,18 @@ export class FFBrowserContext extends BrowserContextBase {
|
|||
}
|
||||
|
||||
async _initialize() {
|
||||
assert(!this._ffPages().length);
|
||||
const browserContextId = this._browserContextId || undefined;
|
||||
const promises: Promise<any>[] = [
|
||||
this._browser._connection.send('Browser.setDownloadOptions', {
|
||||
const promises: Promise<any>[] = [];
|
||||
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,
|
||||
},
|
||||
}),
|
||||
];
|
||||
}));
|
||||
}
|
||||
if (this._options.viewport) {
|
||||
const viewport = {
|
||||
viewportSize: { width: this._options.viewport.width, height: this._options.viewport.height },
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@ export class BrowserServer extends EventEmitter {
|
|||
private _webSocketWrapper: WebSocketWrapper | null = null;
|
||||
readonly _launchOptions: LaunchOptions;
|
||||
readonly _logger: RootLogger;
|
||||
readonly _downloadsPath: string;
|
||||
readonly _downloadsPath: string | undefined;
|
||||
readonly _transport: ConnectionTransport;
|
||||
readonly _headful: boolean;
|
||||
|
||||
constructor(options: LaunchOptions, process: ChildProcess, gracefullyClose: () => Promise<void>, transport: ConnectionTransport, downloadsPath: string, webSocketWrapper: WebSocketWrapper | null) {
|
||||
constructor(options: LaunchOptions, process: ChildProcess, gracefullyClose: () => Promise<void>, transport: ConnectionTransport, downloadsPath: string | undefined, webSocketWrapper: WebSocketWrapper | null) {
|
||||
super();
|
||||
this._launchOptions = options;
|
||||
this._headful = !processBrowserArgOptions(options).headless;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { BrowserContext, PersistentContextOptions, validatePersistentContextOptions } from '../browserContext';
|
||||
import { BrowserServer } from './browserServer';
|
||||
import * as browserPaths from '../install/browserPaths';
|
||||
import { Logger, RootLogger } from '../logger';
|
||||
|
|
@ -60,7 +60,7 @@ export interface BrowserType {
|
|||
name(): string;
|
||||
launch(options?: LaunchOptions): Promise<Browser>;
|
||||
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
|
||||
launchPersistentContext(userDataDir: string, options?: LaunchOptions): Promise<BrowserContext>;
|
||||
launchPersistentContext(userDataDir: string, options?: LaunchOptions & PersistentContextOptions): Promise<BrowserContext>;
|
||||
connect(options: ConnectOptions): Promise<Browser>;
|
||||
}
|
||||
|
||||
|
|
@ -88,15 +88,16 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
|
||||
async launch(options: LaunchOptions = {}): Promise<Browser> {
|
||||
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
|
||||
return this._innerLaunch('local', options);
|
||||
return this._innerLaunch('local', options, undefined);
|
||||
}
|
||||
|
||||
async launchPersistentContext(userDataDir: string, options: LaunchOptions = {}): Promise<BrowserContext> {
|
||||
const browser = await this._innerLaunch('persistent', options, userDataDir);
|
||||
async launchPersistentContext(userDataDir: string, options: LaunchOptions & PersistentContextOptions = {}): Promise<BrowserContext> {
|
||||
const persistent = validatePersistentContextOptions(options);
|
||||
const browser = await this._innerLaunch('persistent', options, persistent, userDataDir);
|
||||
return browser._defaultContext!;
|
||||
}
|
||||
|
||||
async _innerLaunch(launchType: LaunchType, options: LaunchOptions, userDataDir?: string): Promise<BrowserBase> {
|
||||
async _innerLaunch(launchType: LaunchType, options: LaunchOptions, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
||||
const deadline = TimeoutSettings.computeDeadline(options.timeout, 30000);
|
||||
const logger = new RootLogger(options.logger);
|
||||
logger.startLaunchRecording();
|
||||
|
|
@ -104,7 +105,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
let browserServer: BrowserServer | undefined;
|
||||
try {
|
||||
browserServer = await this._launchServer(options, launchType, logger, deadline, userDataDir);
|
||||
const promise = this._innerLaunchPromise(browserServer, launchType, options);
|
||||
const promise = this._innerLaunchPromise(browserServer, options, persistent);
|
||||
const browser = await helper.waitWithDeadline(promise, 'the browser to launch', deadline, 'pw:browser*');
|
||||
return browser;
|
||||
} catch (e) {
|
||||
|
|
@ -119,12 +120,12 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
}
|
||||
}
|
||||
|
||||
async _innerLaunchPromise(browserServer: BrowserServer, launchType: LaunchType, options: LaunchOptions): Promise<BrowserBase> {
|
||||
async _innerLaunchPromise(browserServer: BrowserServer, options: LaunchOptions, persistent: PersistentContextOptions | undefined): Promise<BrowserBase> {
|
||||
if ((options as any).__testHookBeforeCreateBrowser)
|
||||
await (options as any).__testHookBeforeCreateBrowser();
|
||||
|
||||
const browser = await this._connectToServer(browserServer, launchType === 'persistent');
|
||||
if (launchType === 'persistent' && (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))) {
|
||||
const browser = await this._connectToServer(browserServer, persistent);
|
||||
if (persistent && (!options.ignoreDefaultArgs || Array.isArray(options.ignoreDefaultArgs))) {
|
||||
const context = browser._defaultContext!;
|
||||
await context._loadDefaultContext();
|
||||
}
|
||||
|
|
@ -166,10 +167,10 @@ export abstract class BrowserTypeBase implements BrowserType {
|
|||
async _innerConnectPromise(transport: ConnectionTransport, options: ConnectOptions, logger: RootLogger): Promise<Browser> {
|
||||
if ((options as any).__testHookBeforeCreateBrowser)
|
||||
await (options as any).__testHookBeforeCreateBrowser();
|
||||
return this._connectToTransport(transport, { slowMo: options.slowMo, logger, downloadsPath: '' });
|
||||
return this._connectToTransport(transport, { slowMo: options.slowMo, logger });
|
||||
}
|
||||
|
||||
abstract _launchServer(options: LaunchServerOptions, launchType: LaunchType, logger: RootLogger, deadline: number, userDataDir?: string): Promise<BrowserServer>;
|
||||
abstract _connectToServer(browserServer: BrowserServer, persistent: boolean): Promise<BrowserBase>;
|
||||
abstract _connectToServer(browserServer: BrowserServer, persistent: PersistentContextOptions | undefined): Promise<BrowserBase>;
|
||||
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import { InnerLogger, logError, RootLogger } from '../logger';
|
|||
import { BrowserDescriptor } from '../install/browserPaths';
|
||||
import { CRDevTools } from '../chromium/crDevTools';
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { PersistentContextOptions } from '../browserContext';
|
||||
|
||||
export class Chromium extends BrowserTypeBase {
|
||||
private _devtools: CRDevTools | undefined;
|
||||
|
|
@ -47,7 +48,7 @@ export class Chromium extends BrowserTypeBase {
|
|||
return new CRDevTools(path.join(this._browserPath, 'devtools-preferences.json'));
|
||||
}
|
||||
|
||||
async _connectToServer(browserServer: BrowserServer, persistent: boolean): Promise<BrowserBase> {
|
||||
async _connectToServer(browserServer: BrowserServer, persistent: PersistentContextOptions | undefined): Promise<BrowserBase> {
|
||||
const options = browserServer._launchOptions;
|
||||
let devtools = this._devtools;
|
||||
if ((options as any).__testHookForDevTools) {
|
||||
|
|
|
|||
|
|
@ -198,8 +198,8 @@ export class Electron {
|
|||
|
||||
const chromeMatch = await waitForLine(launchedProcess, launchedProcess.stderr, /^DevTools listening on (ws:\/\/.*)$/, helper.timeUntilDeadline(deadline), timeoutError);
|
||||
const chromeTransport = await WebSocketTransport.connect(chromeMatch[1], logger, deadline);
|
||||
const browserServer = new BrowserServer(options, launchedProcess, gracefullyClose, chromeTransport, '', null);
|
||||
const browser = await CRBrowser.connect(chromeTransport, { headful: true, logger, persistent: true, viewport: null, ownedServer: browserServer, downloadsPath: '' });
|
||||
const browserServer = new BrowserServer(options, launchedProcess, gracefullyClose, chromeTransport, undefined, null);
|
||||
const browser = await CRBrowser.connect(chromeTransport, { headful: true, logger, persistent: { viewport: null }, ownedServer: browserServer });
|
||||
app = new ElectronApplication(logger, browser, nodeConnection);
|
||||
await app._init();
|
||||
return app;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { ConnectionTransport, SequenceNumberMixer, WebSocketTransport } from '..
|
|||
import { InnerLogger, logError, RootLogger } from '../logger';
|
||||
import { BrowserDescriptor } from '../install/browserPaths';
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { PersistentContextOptions } from '../browserContext';
|
||||
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ export class Firefox extends BrowserTypeBase {
|
|||
super(packagePath, browser);
|
||||
}
|
||||
|
||||
_connectToServer(browserServer: BrowserServer, persistent: boolean): Promise<BrowserBase> {
|
||||
_connectToServer(browserServer: BrowserServer, persistent: PersistentContextOptions | undefined): Promise<BrowserBase> {
|
||||
return FFBrowser.connect(browserServer._transport, {
|
||||
slowMo: browserServer._launchOptions.slowMo,
|
||||
logger: browserServer._logger,
|
||||
|
|
|
|||
|
|
@ -32,13 +32,14 @@ import { Events } from '../events';
|
|||
import { InnerLogger, logError, RootLogger } from '../logger';
|
||||
import { BrowserDescriptor } from '../install/browserPaths';
|
||||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { PersistentContextOptions } from '../browserContext';
|
||||
|
||||
export class WebKit extends BrowserTypeBase {
|
||||
constructor(packagePath: string, browser: BrowserDescriptor) {
|
||||
super(packagePath, browser);
|
||||
}
|
||||
|
||||
_connectToServer(browserServer: BrowserServer, persistent: boolean): Promise<BrowserBase> {
|
||||
_connectToServer(browserServer: BrowserServer, persistent: PersistentContextOptions | undefined): Promise<BrowserBase> {
|
||||
return WKBrowser.connect(browserServer._transport, {
|
||||
slowMo: browserServer._launchOptions.slowMo,
|
||||
headful: browserServer._headful,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
import { BrowserBase, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, BrowserContextOptions, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { helper, RegisteredListener } from '../helper';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||
|
|
@ -38,7 +38,14 @@ export class WKBrowser extends BrowserBase {
|
|||
|
||||
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
|
||||
const browser = new WKBrowser(SlowMoTransport.wrap(transport, options.slowMo), options);
|
||||
await browser._browserSession.send('Playwright.enable');
|
||||
const promises: Promise<any>[] = [
|
||||
browser._browserSession.send('Playwright.enable'),
|
||||
];
|
||||
if (options.persistent) {
|
||||
browser._defaultContext = new WKBrowserContext(browser, undefined, options.persistent);
|
||||
promises.push((browser._defaultContext as WKBrowserContext)._initialize());
|
||||
}
|
||||
await Promise.all(promises);
|
||||
return browser;
|
||||
}
|
||||
|
||||
|
|
@ -46,10 +53,6 @@ export class WKBrowser extends BrowserBase {
|
|||
super(options);
|
||||
this._connection = new WKConnection(transport, options.logger, this._onDisconnect.bind(this));
|
||||
this._browserSession = this._connection.browserSession;
|
||||
|
||||
if (options.persistent)
|
||||
this._defaultContext = new WKBrowserContext(this, undefined, validateBrowserContextOptions({}));
|
||||
|
||||
this._eventListeners = [
|
||||
helper.addEventListener(this._browserSession, 'Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)),
|
||||
helper.addEventListener(this._browserSession, 'Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
||||
|
|
@ -200,14 +203,16 @@ export class WKBrowserContext extends BrowserContextBase {
|
|||
}
|
||||
|
||||
async _initialize() {
|
||||
assert(!this._wkPages().length);
|
||||
const browserContextId = this._browserContextId;
|
||||
const promises: Promise<any>[] = [
|
||||
this._browser._browserSession.send('Playwright.setDownloadBehavior', {
|
||||
const promises: Promise<any>[] = [];
|
||||
if (this._browser._options.downloadsPath) {
|
||||
promises.push(this._browser._browserSession.send('Playwright.setDownloadBehavior', {
|
||||
behavior: this._options.acceptDownloads ? 'allow' : 'deny',
|
||||
downloadPath: this._browser._options.downloadsPath,
|
||||
browserContextId
|
||||
})
|
||||
];
|
||||
}));
|
||||
}
|
||||
if (this._options.ignoreHTTPSErrors)
|
||||
promises.push(this._browser._browserSession.send('Playwright.setIgnoreCertificateErrors', { browserContextId, ignore: true }));
|
||||
if (this._options.locale)
|
||||
|
|
|
|||
|
|
@ -102,6 +102,12 @@ describe('BrowserContext', function() {
|
|||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
await context.close();
|
||||
});
|
||||
it('should respect deviceScaleFactor', async({ browser }) => {
|
||||
const context = await browser.newContext({ deviceScaleFactor: 3 });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
await context.close();
|
||||
});
|
||||
it('should not allow deviceScaleFactor with null viewport', async({ browser }) => {
|
||||
const error = await browser.newContext({ viewport: null, deviceScaleFactor: 1 }).catch(e => e);
|
||||
expect(error.message).toBe('"deviceScaleFactor" option is not supported with null "viewport"');
|
||||
|
|
|
|||
|
|
@ -15,23 +15,28 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const utils = require('./utils');
|
||||
const {makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, MAC, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
|
||||
const {FFOX, MAC, CHROMIUM, WEBKIT, WIN} = utils.testOptions(browserType);
|
||||
|
||||
describe('launchPersistentContext()', function() {
|
||||
beforeEach(async state => {
|
||||
async function launch(state, options = {}) {
|
||||
state.userDataDir = await makeUserDataDir();
|
||||
state.browserContext = await state.browserType.launchPersistentContext(state.userDataDir, state.defaultBrowserOptions);
|
||||
state.page = await state.browserContext.newPage();
|
||||
});
|
||||
afterEach(async state => {
|
||||
await state.browserContext.close();
|
||||
delete state.browserContext;
|
||||
delete state.page;
|
||||
state.context = await state.browserType.launchPersistentContext(state.userDataDir, {...state.defaultBrowserOptions, ...options});
|
||||
state.page = state.context.pages()[0];
|
||||
return state;
|
||||
}
|
||||
async function close(state) {
|
||||
await state.context.close();
|
||||
await removeUserDataDir(state.userDataDir);
|
||||
});
|
||||
it('context.cookies() should work', async({page, server}) => {
|
||||
delete state.page;
|
||||
delete state.context;
|
||||
delete state.userDataDir;
|
||||
}
|
||||
|
||||
it('context.cookies() should work', async state => {
|
||||
const { page, server } = await launch(state);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
|
|
@ -46,8 +51,10 @@ describe('launchPersistentContext()', function() {
|
|||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
await close(state);
|
||||
});
|
||||
it('context.addCookies() should work', async({page, server}) => {
|
||||
it('context.addCookies() should work', async state => {
|
||||
const { page, server } = await launch(state);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
|
|
@ -65,8 +72,10 @@ describe('launchPersistentContext()', function() {
|
|||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
await close(state);
|
||||
});
|
||||
it('context.clearCookies() should work', async({page, server}) => {
|
||||
it('context.clearCookies() should work', async state => {
|
||||
const { page, server } = await launch(state);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
|
|
@ -82,8 +91,10 @@ describe('launchPersistentContext()', function() {
|
|||
await page.reload();
|
||||
expect(await page.context().cookies([])).toEqual([]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
await close(state);
|
||||
});
|
||||
it('should(not) block third party cookies', async({browserContext, page, server}) => {
|
||||
it('should(not) block third party cookies', async state => {
|
||||
const { page, server, context } = await launch(state);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
|
|
@ -97,7 +108,7 @@ describe('launchPersistentContext()', function() {
|
|||
await page.frames()[1].evaluate(`document.cookie = 'username=John Doe'`);
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = CHROMIUM || FFOX;
|
||||
const cookies = await browserContext.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
|
|
@ -114,5 +125,255 @@ describe('launchPersistentContext()', function() {
|
|||
} else {
|
||||
expect(cookies).toEqual([]);
|
||||
}
|
||||
await close(state);
|
||||
});
|
||||
it('should support viewport option', async state => {
|
||||
let { page, context } = await launch(state, {viewport: { width: 456, height: 789 }});
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
page = await context.newPage();
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
await close(state);
|
||||
});
|
||||
it('should support deviceScaleFactor option', async state => {
|
||||
const { page } = await launch(state, {deviceScaleFactor: 3});
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
await close(state);
|
||||
});
|
||||
it('should support userAgent option', async state => {
|
||||
const { page, server } = await launch(state, {userAgent: 'foobar'});
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toBe('foobar');
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await close(state);
|
||||
});
|
||||
it('should support bypassCSP option', async state => {
|
||||
const { page, server } = await launch(state, {bypassCSP: true});
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await close(state);
|
||||
});
|
||||
it('should support javascriptEnabled option', async state => {
|
||||
const { page } = await launch(state, {javaScriptEnabled: false});
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
await close(state);
|
||||
});
|
||||
it('should support httpCredentials option', async state => {
|
||||
const { page, server } = await launch(state, {httpCredentials: { username: 'user', password: 'pass' }});
|
||||
server.setAuth('/playground.html', 'user', 'pass');
|
||||
const response = await page.goto(server.PREFIX + '/playground.html');
|
||||
expect(response.status()).toBe(200);
|
||||
await close(state);
|
||||
});
|
||||
it('should support offline option', async state => {
|
||||
const { page, server } = await launch(state, {offline: true});
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error).toBeTruthy();
|
||||
await close(state);
|
||||
});
|
||||
it.skip(true)('should support acceptDownloads option', async state => {
|
||||
// TODO: unskip once we support downloads in persistent context.
|
||||
const { page, server } = await launch(state, {acceptDownloads: true});
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await close(state);
|
||||
});
|
||||
it('should support hasTouch option', async state => {
|
||||
const { page, server } = await launch(state, {hasTouch: true});
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
await close(state);
|
||||
});
|
||||
it.skip(FFOX)('should work in persistent context', async state => {
|
||||
// Firefox does not support mobile.
|
||||
const { page, server } = await launch(state, {viewport: {width: 320, height: 480}, isMobile: true});
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
await close(state);
|
||||
});
|
||||
it('should support colorScheme option', async state => {
|
||||
const { page } = await launch(state, {colorScheme: 'dark'});
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
await close(state);
|
||||
});
|
||||
it('should support timezoneId option', async state => {
|
||||
const { page } = await launch(state, {timezoneId: 'America/Jamaica'});
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
await close(state);
|
||||
});
|
||||
it('should support locale option', async state => {
|
||||
const { page } = await launch(state, {locale: 'fr-CH'});
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
await close(state);
|
||||
});
|
||||
it('should support geolocation and permissions options', async state => {
|
||||
const { page, server } = await launch(state, {geolocation: {longitude: 10, latitude: 10}, permissions: ['geolocation']});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({latitude: 10, longitude: 10});
|
||||
await close(state);
|
||||
});
|
||||
it('should support ignoreHTTPSErrors option', async state => {
|
||||
const { page, httpsServer } = await launch(state, {ignoreHTTPSErrors: true});
|
||||
let error = null;
|
||||
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBe(null);
|
||||
expect(response.ok()).toBe(true);
|
||||
await close(state);
|
||||
});
|
||||
it('should support extraHTTPHeaders option', async state => {
|
||||
const { page, server } = await launch(state, {extraHTTPHeaders: { foo: 'bar' }});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
await close(state);
|
||||
});
|
||||
it('should accept userDataDir', async state => {
|
||||
const { userDataDir, context } = await launch(state);
|
||||
// Note: we need an open page to make sure its functional.
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await context.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('should restore state from userDataDir', async({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
it.slow().fail(WIN && CHROMIUM)('should restore cookies from userDataDir', async({browserType, defaultBrowserOptions, server}) => {
|
||||
// TODO: Flaky! See https://github.com/microsoft/playwright/pull/1795/checks?check_run_id=587685496
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
it('should have default URL when launching browser', async state => {
|
||||
const { context } = await launch(state);
|
||||
const urls = context.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await close(state);
|
||||
});
|
||||
it.skip(FFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const args = browserType._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
...defaultBrowserOptions,
|
||||
args: [...args, server.EMPTY_PAGE],
|
||||
ignoreDefaultArgs: true,
|
||||
};
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
if (!browserContext.pages().length)
|
||||
await browserContext.waitForEvent('page');
|
||||
await browserContext.pages()[0].waitForLoadState();
|
||||
const gotUrls = browserContext.pages().map(page => page.url());
|
||||
expect(gotUrls).toEqual([server.EMPTY_PAGE]);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should handle timeout', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('Waiting for the browser to launch failed: timeout exceeded. Re-run with the DEBUG=pw:browser* env variable to see the debug log.');
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should handle exception', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const e = new Error('Dummy');
|
||||
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error).toBe(e);
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should throw on unsupported options', async ({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const optionNames = [ 'acceptDownloads' ];
|
||||
for (const option of optionNames) {
|
||||
const options = { ...defaultBrowserOptions };
|
||||
options[option] = 'hello';
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toBe(`Option "${option}" is not supported for persistent context`);
|
||||
}
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const utils = require('./utils');
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN} = utils.testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
|
||||
|
||||
describe('Playwright', function() {
|
||||
describe('browserType.launch', function() {
|
||||
|
|
@ -70,57 +69,6 @@ describe('Playwright', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistentContext', function() {
|
||||
it('should have default URL when launching browser', async ({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.skip(FFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = [server.EMPTY_PAGE].concat(options.args || []);
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const args = browserType._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
...defaultBrowserOptions,
|
||||
args: [...args, server.EMPTY_PAGE],
|
||||
ignoreDefaultArgs: true,
|
||||
};
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
if (!browserContext.pages().length)
|
||||
await browserContext.waitForEvent('page');
|
||||
await browserContext.pages()[0].waitForLoadState();
|
||||
const gotUrls = browserContext.pages().map(page => page.url());
|
||||
expect(gotUrls).toEqual([server.EMPTY_PAGE]);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should handle timeout', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('Waiting for the browser to launch failed: timeout exceeded. Re-run with the DEBUG=pw:browser* env variable to see the debug log.');
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should handle exception', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const e = new Error('Dummy');
|
||||
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error).toBe(e);
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchServer', function() {
|
||||
it('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
|
|
@ -332,68 +280,3 @@ describe('browserType.connect', function() {
|
|||
expect(error).toBe(e);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistentContext', function() {
|
||||
it('userDataDir option', async({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
// Open a page to make sure its functional.
|
||||
await browserContext.newPage();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await browserContext.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('userDataDir option should restore state', async({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
// TODO: Flaky! See https://github.com/microsoft/playwright/pull/1795/checks?check_run_id=587685496
|
||||
it.slow().fail(WIN && CHROMIUM)('userDataDir option should restore cookies', async({browserType, defaultBrowserOptions, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -202,12 +202,12 @@ const utils = module.exports = {
|
|||
|
||||
const logger = {
|
||||
isEnabled: (name, severity) => {
|
||||
return name === 'browser' || (name === 'protocol' && dumpProtocolOnFailure);
|
||||
return name.startsWith('browser') || (name === 'protocol' && dumpProtocolOnFailure);
|
||||
},
|
||||
log: (name, severity, message, args) => {
|
||||
if (!testRun)
|
||||
return;
|
||||
if (name === 'browser') {
|
||||
if (name.startsWith('browser')) {
|
||||
if (severity === 'warning')
|
||||
testRun.log(`${prefix}\x1b[31m[browser]\x1b[0m ${message}`)
|
||||
else
|
||||
|
|
|
|||
Loading…
Reference in a new issue