feat(BrowserContext): introduce Browser Contexts. (#2523)
This patch introduces Browser Contexts and methods to manage them: - `browser.createIncognitoBrowserContext()` - to create new incognito context - `browser.browserContext()` - to get all existing contexts - `browserContext.dispose()` - to dispose incognito context. Fixes #85.
This commit is contained in:
parent
58c672b131
commit
3b03ff65c7
126
docs/api.md
126
docs/api.md
@ -31,7 +31,9 @@
|
||||
* [event: 'targetchanged'](#event-targetchanged)
|
||||
* [event: 'targetcreated'](#event-targetcreated)
|
||||
* [event: 'targetdestroyed'](#event-targetdestroyed)
|
||||
* [browser.browserContexts()](#browserbrowsercontexts)
|
||||
* [browser.close()](#browserclose)
|
||||
* [browser.createIncognitoBrowserContext()](#browsercreateincognitobrowsercontext)
|
||||
* [browser.disconnect()](#browserdisconnect)
|
||||
* [browser.newPage()](#browsernewpage)
|
||||
* [browser.pages()](#browserpages)
|
||||
@ -40,6 +42,15 @@
|
||||
* [browser.userAgent()](#browseruseragent)
|
||||
* [browser.version()](#browserversion)
|
||||
* [browser.wsEndpoint()](#browserwsendpoint)
|
||||
- [class: BrowserContext](#class-browsercontext)
|
||||
* [event: 'targetchanged'](#event-targetchanged-1)
|
||||
* [event: 'targetcreated'](#event-targetcreated-1)
|
||||
* [event: 'targetdestroyed'](#event-targetdestroyed-1)
|
||||
* [browserContext.browser()](#browsercontextbrowser)
|
||||
* [browserContext.close()](#browsercontextclose)
|
||||
* [browserContext.isIncognito()](#browsercontextisincognito)
|
||||
* [browserContext.newPage()](#browsercontextnewpage)
|
||||
* [browserContext.targets()](#browsercontexttargets)
|
||||
- [class: Page](#class-page)
|
||||
* [event: 'close'](#event-close)
|
||||
* [event: 'console'](#event-console)
|
||||
@ -240,6 +251,7 @@
|
||||
* [securityDetails.validTo()](#securitydetailsvalidto)
|
||||
- [class: Target](#class-target)
|
||||
* [target.browser()](#targetbrowser)
|
||||
* [target.browserContext()](#targetbrowsercontext)
|
||||
* [target.createCDPSession()](#targetcreatecdpsession)
|
||||
* [target.page()](#targetpage)
|
||||
* [target.type()](#targettype)
|
||||
@ -446,27 +458,57 @@ Emitted when Puppeteer gets disconnected from the Chromium instance. This might
|
||||
|
||||
Emitted when the url of a target changes.
|
||||
|
||||
> **NOTE** This includes target changes in incognito browser contexts.
|
||||
|
||||
|
||||
#### event: 'targetcreated'
|
||||
- <[Target]>
|
||||
|
||||
Emitted when a target is created, for example when a new page is opened by [`window.open`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) or [`browser.newPage`](#browsernewpage).
|
||||
|
||||
> **NOTE** This includes target creations in incognito browser contexts.
|
||||
|
||||
#### event: 'targetdestroyed'
|
||||
- <[Target]>
|
||||
|
||||
Emitted when a target is destroyed, for example when a page is closed.
|
||||
|
||||
> **NOTE** This includes target destructions in incognito browser contexts.
|
||||
|
||||
#### browser.browserContexts()
|
||||
- returns: <[Array]<[BrowserContext]>>
|
||||
|
||||
Returns an array of all open browser contexts. In a newly created browser, this will return
|
||||
a single instance of [BrowserContext].
|
||||
|
||||
#### browser.close()
|
||||
- returns: <[Promise]>
|
||||
|
||||
Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
||||
|
||||
#### browser.createIncognitoBrowserContext()
|
||||
- returns: <[Promise]<[BrowserContext]>>
|
||||
|
||||
Creates a new incognito browser context. This won't share cookies/cache with other browser contexts.
|
||||
|
||||
```js
|
||||
const browser = await puppeteer.launch();
|
||||
// Create a new incognito browser context.
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
// Create a new page in a pristine context.
|
||||
const page = await context.newPage();
|
||||
// Do stuff
|
||||
await page.goto('https://example.com');
|
||||
```
|
||||
|
||||
#### browser.disconnect()
|
||||
|
||||
Disconnects Puppeteer from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser] object is considered disposed and cannot be used anymore.
|
||||
|
||||
#### browser.newPage()
|
||||
- returns: <[Promise]<[Page]>> Promise which resolves to a new [Page] object.
|
||||
- returns: <[Promise]<[Page]>>
|
||||
|
||||
Promise which resolves to a new [Page] object. The [Page] is created in a default browser context.
|
||||
|
||||
#### browser.pages()
|
||||
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages.
|
||||
@ -475,7 +517,10 @@ Disconnects Puppeteer from the browser, but leaves the Chromium process running.
|
||||
- returns: <?[ChildProcess]> Spawned browser process. Returns `null` if the browser instance was created with [`puppeteer.connect`](#puppeteerconnectoptions) method.
|
||||
|
||||
#### browser.targets()
|
||||
- returns: <[Array]<[Target]>> An array of all active targets.
|
||||
- returns: <[Array]<[Target]>>
|
||||
|
||||
An array of all active targets inside the Browser. In case of multiple browser contexts,
|
||||
the method will return an array with all the targets in all browser contexts.
|
||||
|
||||
#### browser.userAgent()
|
||||
- returns: <[Promise]<[string]>> Promise which resolves to the browser's original user agent.
|
||||
@ -495,6 +540,76 @@ Browser websocket endpoint which can be used as an argument to
|
||||
|
||||
You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`. Learn more about the [devtools protocol](https://chromedevtools.github.io/devtools-protocol) and the [browser endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
||||
|
||||
### class: BrowserContext
|
||||
|
||||
* extends: [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
|
||||
BrowserContexts provide a way to operate multiple independent browser sessions. When a browser is launched, it has
|
||||
a single BrowserContext used by default. The method `browser.newPage()` creates a page in the default browser context.
|
||||
|
||||
If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser
|
||||
context.
|
||||
|
||||
Puppeteer allows creation of "incognito" browser contexts with `browser.createIncognitoBrowserContext()` method.
|
||||
"Incognito" browser contexts don't write any browsing data to disk.
|
||||
|
||||
```js
|
||||
// Create a new incognito browser context
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
// Create a new page inside context.
|
||||
const page = await context.newPage();
|
||||
// ... do stuff with page ...
|
||||
await page.goto('https://example.com');
|
||||
// Dispose context once it's no longer needed.
|
||||
await context.close();
|
||||
```
|
||||
|
||||
#### event: 'targetchanged'
|
||||
- <[Target]>
|
||||
|
||||
Emitted when the url of a target inside the browser context changes.
|
||||
|
||||
#### event: 'targetcreated'
|
||||
- <[Target]>
|
||||
|
||||
Emitted when a new target is created inside the browser context, for example when a new page is opened by [`window.open`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) or [`browserContext.newPage`](#browsercontextnewpage).
|
||||
|
||||
#### event: 'targetdestroyed'
|
||||
- <[Target]>
|
||||
|
||||
Emitted when a target inside the browser context is destroyed, for example when a page is closed.
|
||||
|
||||
#### browserContext.browser()
|
||||
- returns: <[Browser]>
|
||||
|
||||
The browser this browser context belongs to.
|
||||
|
||||
#### browserContext.close()
|
||||
- returns: <[Promise]>
|
||||
|
||||
Closes the browser context. All the targets that belong to the browser context
|
||||
will be closed.
|
||||
|
||||
> **NOTE** only incognito browser contexts can be closed.
|
||||
|
||||
#### browserContext.isIncognito()
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether BrowserContext is incognito.
|
||||
The default browser context is the only non-incognito browser context.
|
||||
|
||||
> **NOTE** the default browser context cannot be closed.
|
||||
|
||||
#### browserContext.newPage()
|
||||
- returns: <[Promise]<[Page]>>
|
||||
|
||||
Creates a new page in the browser context.
|
||||
|
||||
#### browserContext.targets()
|
||||
- returns: <[Array]<[Target]>>
|
||||
|
||||
An array of all active targets inside the browser context.
|
||||
|
||||
### class: Page
|
||||
|
||||
* extends: [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||
@ -2607,6 +2722,12 @@ Contains the URL of the response.
|
||||
|
||||
Get the browser the target belongs to.
|
||||
|
||||
#### target.browserContext()
|
||||
|
||||
- returns: <[BrowserContext]>
|
||||
|
||||
The browser context the target belongs to.
|
||||
|
||||
#### target.createCDPSession()
|
||||
- returns: <[Promise]<[CDPSession]>>
|
||||
|
||||
@ -2734,6 +2855,7 @@ reported.
|
||||
[stream.Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "stream.Readable"
|
||||
[CDPSession]: #class-cdpsession "CDPSession"
|
||||
[BrowserFetcher]: #class-browserfetcher "BrowserFetcher"
|
||||
[BrowserContext]: #class-browsercontext "BrowserContext"
|
||||
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
|
||||
[Frame]: #class-frame "Frame"
|
||||
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
|
||||
|
128
lib/Browser.js
128
lib/Browser.js
@ -22,11 +22,12 @@ const TaskQueue = require('./TaskQueue');
|
||||
class Browser extends EventEmitter {
|
||||
/**
|
||||
* @param {!Puppeteer.Connection} connection
|
||||
* @param {!Array<string>} contextIds
|
||||
* @param {!BrowserOptions=} options
|
||||
* @param {?Puppeteer.ChildProcess} process
|
||||
* @param {(function():Promise)=} closeCallback
|
||||
*/
|
||||
constructor(connection, options = {}, process, closeCallback) {
|
||||
constructor(connection, contextIds, options = {}, process, closeCallback) {
|
||||
super();
|
||||
this._ignoreHTTPSErrors = !!options.ignoreHTTPSErrors;
|
||||
this._appMode = !!options.appMode;
|
||||
@ -34,6 +35,13 @@ class Browser extends EventEmitter {
|
||||
this._screenshotTaskQueue = new TaskQueue();
|
||||
this._connection = connection;
|
||||
this._closeCallback = closeCallback || new Function();
|
||||
|
||||
this._defaultContext = new BrowserContext(this, null);
|
||||
/** @type {Map<string, BrowserContext>} */
|
||||
this._contexts = new Map();
|
||||
for (const contextId of contextIds)
|
||||
this._contexts.set(contextId, new BrowserContext(this, contextId));
|
||||
|
||||
/** @type {Map<string, Target>} */
|
||||
this._targets = new Map();
|
||||
this._connection.setClosedCallback(() => {
|
||||
@ -51,29 +59,60 @@ class Browser extends EventEmitter {
|
||||
return this._process;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!BrowserContext>}
|
||||
*/
|
||||
async createIncognitoBrowserContext() {
|
||||
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
||||
const context = new BrowserContext(this, browserContextId);
|
||||
this._contexts.set(browserContextId, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<!BrowserContext>}
|
||||
*/
|
||||
browserContexts() {
|
||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?string} contextId
|
||||
*/
|
||||
async _disposeContext(contextId) {
|
||||
await this._connection.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
|
||||
this._contexts.delete(contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Puppeteer.Connection} connection
|
||||
* @param {!Array<string>} contextIds
|
||||
* @param {!BrowserOptions=} options
|
||||
* @param {?Puppeteer.ChildProcess} process
|
||||
* @param {function()=} closeCallback
|
||||
*/
|
||||
static async create(connection, options, process, closeCallback) {
|
||||
const browser = new Browser(connection, options, process, closeCallback);
|
||||
static async create(connection, contextIds, options, process, closeCallback) {
|
||||
const browser = new Browser(connection, contextIds, options, process, closeCallback);
|
||||
await connection.send('Target.setDiscoverTargets', {discover: true});
|
||||
return browser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{targetInfo: !Puppeteer.TargetInfo}} event
|
||||
* @param {!Protocol.Target.targetCreatedPayload} event
|
||||
*/
|
||||
async _targetCreated(event) {
|
||||
const targetInfo = event.targetInfo;
|
||||
const target = new Target(targetInfo, this, () => this._connection.createSession(targetInfo.targetId), this._ignoreHTTPSErrors, !this._appMode, this._screenshotTaskQueue);
|
||||
const {browserContextId} = targetInfo;
|
||||
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;
|
||||
|
||||
const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo.targetId), this._ignoreHTTPSErrors, !this._appMode, this._screenshotTaskQueue);
|
||||
console.assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
||||
this._targets.set(event.targetInfo.targetId, target);
|
||||
|
||||
if (await target._initializedPromise)
|
||||
if (await target._initializedPromise) {
|
||||
this.emit(Browser.Events.TargetCreated, target);
|
||||
context.emit(BrowserContext.Events.TargetCreated, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,12 +123,14 @@ class Browser extends EventEmitter {
|
||||
target._initializedCallback(false);
|
||||
this._targets.delete(event.targetId);
|
||||
target._closedCallback();
|
||||
if (await target._initializedPromise)
|
||||
if (await target._initializedPromise) {
|
||||
this.emit(Browser.Events.TargetDestroyed, target);
|
||||
target.browserContext().emit(BrowserContext.Events.TargetDestroyed, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{targetInfo: !Puppeteer.TargetInfo}} event
|
||||
* @param {!Protocol.Target.targetInfoChangedPayload} event
|
||||
*/
|
||||
_targetInfoChanged(event) {
|
||||
const target = this._targets.get(event.targetInfo.targetId);
|
||||
@ -97,8 +138,10 @@ class Browser extends EventEmitter {
|
||||
const previousURL = target.url();
|
||||
const wasInitialized = target._isInitialized;
|
||||
target._targetInfoChanged(event.targetInfo);
|
||||
if (wasInitialized && previousURL !== target.url())
|
||||
if (wasInitialized && previousURL !== target.url()) {
|
||||
this.emit(Browser.Events.TargetChanged, target);
|
||||
target.browserContext().emit(BrowserContext.Events.TargetChanged, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +155,15 @@ class Browser extends EventEmitter {
|
||||
* @return {!Promise<!Puppeteer.Page>}
|
||||
*/
|
||||
async newPage() {
|
||||
const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank'});
|
||||
return this._defaultContext.newPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} contextId
|
||||
* @return {!Promise<!Puppeteer.Page>}
|
||||
*/
|
||||
async _createPageInContext(contextId) {
|
||||
const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined});
|
||||
const target = await this._targets.get(targetId);
|
||||
console.assert(await target._initializedPromise, 'Failed to create target for page');
|
||||
const page = await target.page();
|
||||
@ -175,12 +226,65 @@ Browser.Events = {
|
||||
Disconnected: 'disconnected'
|
||||
};
|
||||
|
||||
class BrowserContext extends EventEmitter {
|
||||
/**
|
||||
* @param {!Browser} browser
|
||||
* @param {?string} contextId
|
||||
*/
|
||||
constructor(browser, contextId) {
|
||||
super();
|
||||
this._browser = browser;
|
||||
this._id = contextId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<!Target>} target
|
||||
*/
|
||||
targets() {
|
||||
return this._browser.targets().filter(target => target.browserContext() === this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
isIncognito() {
|
||||
return !!this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Puppeteer.Page>}
|
||||
*/
|
||||
newPage() {
|
||||
return this._browser._createPageInContext(this._id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Browser}
|
||||
*/
|
||||
browser() {
|
||||
return this._browser;
|
||||
}
|
||||
|
||||
async close() {
|
||||
console.assert(this._id, 'Non-incognito profiles cannot be closed!');
|
||||
await this._browser._disposeContext(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
/** @enum {string} */
|
||||
BrowserContext.Events = {
|
||||
TargetCreated: 'targetcreated',
|
||||
TargetDestroyed: 'targetdestroyed',
|
||||
TargetChanged: 'targetchanged',
|
||||
};
|
||||
|
||||
helper.tracePublicAPI(BrowserContext);
|
||||
helper.tracePublicAPI(Browser);
|
||||
|
||||
module.exports = Browser;
|
||||
module.exports = {Browser, BrowserContext};
|
||||
|
||||
/**
|
||||
* @typedef {Object} BrowserOptions
|
||||
* @property {boolean=} appMode
|
||||
* @property {boolean=} ignoreHTTPSErrors
|
||||
*/
|
||||
*/
|
||||
|
@ -19,7 +19,7 @@ const removeFolder = require('rimraf');
|
||||
const childProcess = require('child_process');
|
||||
const BrowserFetcher = require('./BrowserFetcher');
|
||||
const {Connection} = require('./Connection');
|
||||
const Browser = require('./Browser');
|
||||
const {Browser} = require('./Browser');
|
||||
const readline = require('readline');
|
||||
const fs = require('fs');
|
||||
const {helper, debugError} = require('./helper');
|
||||
@ -159,7 +159,7 @@ class Launcher {
|
||||
} else {
|
||||
connection = Connection.createForPipe(/** @type {!NodeJS.WritableStream} */(chromeProcess.stdio[3]), /** @type {!NodeJS.ReadableStream} */ (chromeProcess.stdio[4]), connectionDelay);
|
||||
}
|
||||
return Browser.create(connection, options, chromeProcess, gracefullyCloseChrome);
|
||||
return Browser.create(connection, [], options, chromeProcess, gracefullyCloseChrome);
|
||||
} catch (e) {
|
||||
killChrome();
|
||||
throw e;
|
||||
@ -226,7 +226,8 @@ class Launcher {
|
||||
static async connect(options = {}) {
|
||||
const connectionDelay = options.slowMo || 0;
|
||||
const connection = await Connection.createForWebSocket(options.browserWSEndpoint, connectionDelay);
|
||||
return Browser.create(connection, options, null, () => connection.send('Browser.close').catch(debugError));
|
||||
const {browserContextIds} = await connection.send('Target.getBrowserContexts');
|
||||
return Browser.create(connection, browserContextIds, options, null, () => connection.send('Browser.close').catch(debugError));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,16 +3,16 @@ const {helper} = require('./helper');
|
||||
|
||||
class Target {
|
||||
/**
|
||||
* @param {!Puppeteer.TargetInfo} targetInfo
|
||||
* @param {!Puppeteer.Browser} browser
|
||||
* @param {!Protocol.Target.TargetInfo} targetInfo
|
||||
* @param {!Puppeteer.BrowserContext} browserContext
|
||||
* @param {!function():!Promise<!Puppeteer.CDPSession>} sessionFactory
|
||||
* @param {boolean} ignoreHTTPSErrors
|
||||
* @param {boolean} setDefaultViewport
|
||||
* @param {!Puppeteer.TaskQueue} screenshotTaskQueue
|
||||
*/
|
||||
constructor(targetInfo, browser, sessionFactory, ignoreHTTPSErrors, setDefaultViewport, screenshotTaskQueue) {
|
||||
constructor(targetInfo, browserContext, sessionFactory, ignoreHTTPSErrors, setDefaultViewport, screenshotTaskQueue) {
|
||||
this._targetInfo = targetInfo;
|
||||
this._browser = browser;
|
||||
this._browserContext = browserContext;
|
||||
this._targetId = targetInfo.targetId;
|
||||
this._sessionFactory = sessionFactory;
|
||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
@ -66,11 +66,18 @@ class Target {
|
||||
* @return {!Puppeteer.Browser}
|
||||
*/
|
||||
browser() {
|
||||
return this._browser;
|
||||
return this._browserContext.browser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Puppeteer.TargetInfo} targetInfo
|
||||
* @return {!Puppeteer.BrowserContext}
|
||||
*/
|
||||
browserContext() {
|
||||
return this._browserContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Protocol.Target.TargetInfo} targetInfo
|
||||
*/
|
||||
_targetInfoChanged(targetInfo) {
|
||||
this._targetInfo = targetInfo;
|
||||
|
13
lib/externs.d.ts
vendored
13
lib/externs.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import { Connection as RealConnection, CDPSession as RealCDPSession } from './Connection.js';
|
||||
import * as RealBrowser from './Browser.js';
|
||||
import { Browser as RealBrowser, BrowserContext as RealBrowserContext} from './Browser.js';
|
||||
import * as RealTarget from './Target.js';
|
||||
import * as RealPage from './Page.js';
|
||||
import * as RealTaskQueue from './TaskQueue.js';
|
||||
@ -21,6 +21,7 @@ declare global {
|
||||
export class Touchscreen extends RealTouchscreen {}
|
||||
export class TaskQueue extends RealTaskQueue {}
|
||||
export class Browser extends RealBrowser {}
|
||||
export class BrowserContext extends RealBrowserContext {}
|
||||
export class Target extends RealTarget {}
|
||||
export class Frame extends RealFrame {}
|
||||
export class FrameManager extends RealFrameManager {}
|
||||
@ -37,15 +38,7 @@ declare global {
|
||||
close();
|
||||
}
|
||||
|
||||
export interface TargetInfo {
|
||||
type: string;
|
||||
targetId: string;
|
||||
title: string;
|
||||
url: string;
|
||||
attached: boolean;
|
||||
}
|
||||
|
||||
export interface ChildProcess extends child_process.ChildProcess {}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
134
test/browsercontext.spec.js
Normal file
134
test/browsercontext.spec.js
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
|
||||
module.exports.addTests = function({testRunner, expect, puppeteer}) {
|
||||
const {describe, xdescribe, fdescribe} = testRunner;
|
||||
const {it, fit, xit} = testRunner;
|
||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||
|
||||
describe('BrowserContext', function() {
|
||||
it('should have default context', async function({browser, server}) {
|
||||
expect(browser.browserContexts().length).toBe(1);
|
||||
const defaultContext = browser.browserContexts()[0];
|
||||
expect(defaultContext.isIncognito()).toBe(false);
|
||||
let error = null;
|
||||
await defaultContext.close().catch(e => error = e);
|
||||
expect(error.message).toContain('cannot be closed');
|
||||
});
|
||||
it('should create new incognito context', async function({browser, server}) {
|
||||
expect(browser.browserContexts().length).toBe(1);
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
expect(context.isIncognito()).toBe(true);
|
||||
expect(browser.browserContexts().length).toBe(2);
|
||||
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
|
||||
await context.close();
|
||||
expect(browser.browserContexts().length).toBe(1);
|
||||
});
|
||||
it('should close all belonging targets once closing context', async function({browser, server}) {
|
||||
expect((await browser.pages()).length).toBe(2);
|
||||
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
await context.newPage();
|
||||
expect((await browser.pages()).length).toBe(3);
|
||||
|
||||
await context.close();
|
||||
expect((await browser.pages()).length).toBe(2);
|
||||
});
|
||||
it('window.open should use parent tab context', async function({browser, server}) {
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE);
|
||||
const popupTarget = await utils.waitEvent(browser, 'targetcreated');
|
||||
expect(popupTarget.browserContext()).toBe(context);
|
||||
await context.close();
|
||||
});
|
||||
it('should fire target events', async function({browser, server}) {
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
const events = [];
|
||||
context.on('targetcreated', target => events.push('CREATED: ' + target.url()));
|
||||
context.on('targetchanged', target => events.push('CHANGED: ' + target.url()));
|
||||
context.on('targetdestroyed', target => events.push('DESTRYOED: ' + target.url()));
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.close();
|
||||
expect(events).toEqual([
|
||||
'CREATED: about:blank',
|
||||
'CHANGED: http://localhost:8907/empty.html',
|
||||
'DESTRYOED: http://localhost:8907/empty.html'
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
it('should isolate localStorage and cookies', async function({browser, server}) {
|
||||
// Create two incognito contexts.
|
||||
const context1 = await browser.createIncognitoBrowserContext();
|
||||
const context2 = await browser.createIncognitoBrowserContext();
|
||||
expect(context1.targets().length).toBe(0);
|
||||
expect(context2.targets().length).toBe(0);
|
||||
|
||||
// Create a page in first incognito context.
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.EMPTY_PAGE);
|
||||
await page1.evaluate(() => {
|
||||
localStorage.setItem('name', 'page1');
|
||||
document.cookie = 'name=page1';
|
||||
});
|
||||
|
||||
expect(context1.targets().length).toBe(1);
|
||||
expect(context2.targets().length).toBe(0);
|
||||
|
||||
// Create a page in second incognito context.
|
||||
const page2 = await context2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
await page2.evaluate(() => {
|
||||
localStorage.setItem('name', 'page2');
|
||||
document.cookie = 'name=page2';
|
||||
});
|
||||
|
||||
expect(context1.targets().length).toBe(1);
|
||||
expect(context1.targets()[0]).toBe(page1.target());
|
||||
expect(context2.targets().length).toBe(1);
|
||||
expect(context2.targets()[0]).toBe(page2.target());
|
||||
|
||||
// Make sure pages don't share localstorage or cookies.
|
||||
expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1');
|
||||
expect(await page1.evaluate(() => document.cookie)).toBe('name=page1');
|
||||
expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2');
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('name=page2');
|
||||
|
||||
// Cleanup contexts.
|
||||
await Promise.all([
|
||||
context1.close(),
|
||||
context2.close()
|
||||
]);
|
||||
expect(browser.browserContexts().length).toBe(1);
|
||||
});
|
||||
it('should work across sessions', async function({browser, server}) {
|
||||
expect(browser.browserContexts().length).toBe(1);
|
||||
const context = await browser.createIncognitoBrowserContext();
|
||||
expect(browser.browserContexts().length).toBe(2);
|
||||
const remoteBrowser = await puppeteer.connect({
|
||||
browserWSEndpoint: browser.wsEndpoint()
|
||||
});
|
||||
const contexts = remoteBrowser.browserContexts();
|
||||
expect(contexts.length).toBe(2);
|
||||
await remoteBrowser.disconnect();
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
};
|
@ -122,6 +122,7 @@ describe('Page', function() {
|
||||
// Page-level tests that are given a browser and a page.
|
||||
require('./CDPSession.spec.js').addTests({testRunner, expect});
|
||||
require('./browser.spec.js').addTests({testRunner, expect, puppeteer, headless});
|
||||
require('./browsercontext.spec.js').addTests({testRunner, expect, puppeteer});
|
||||
require('./cookies.spec.js').addTests({testRunner, expect});
|
||||
require('./coverage.spec.js').addTests({testRunner, expect});
|
||||
require('./elementhandle.spec.js').addTests({testRunner, expect});
|
||||
|
Loading…
Reference in New Issue
Block a user