feat(api): move targets from CRBrowser to CRBrowserContext (#1089)

This makes them work for default context.
This commit is contained in:
Dmitry Gozman 2020-02-24 14:35:51 -08:00 committed by GitHub
parent de03f37a99
commit 6acc439450
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 146 deletions

View file

@ -25,6 +25,7 @@
- [class: BrowserServer](#class-browserserver) - [class: BrowserServer](#class-browserserver)
- [class: BrowserType](#class-browsertype) - [class: BrowserType](#class-browsertype)
- [class: ChromiumBrowser](#class-chromiumbrowser) - [class: ChromiumBrowser](#class-chromiumbrowser)
- [class: ChromiumBrowserContext](#class-chromiumbrowsercontext)
- [class: ChromiumCoverage](#class-chromiumcoverage) - [class: ChromiumCoverage](#class-chromiumcoverage)
- [class: ChromiumSession](#class-chromiumsession) - [class: ChromiumSession](#class-chromiumsession)
- [class: ChromiumTarget](#class-chromiumtarget) - [class: ChromiumTarget](#class-chromiumtarget)
@ -3532,15 +3533,9 @@ await browser.stopTracing();
``` ```
<!-- GEN:toc --> <!-- GEN:toc -->
- [event: 'targetchanged'](#event-targetchanged)
- [event: 'targetcreated'](#event-targetcreated)
- [event: 'targetdestroyed'](#event-targetdestroyed)
- [chromiumBrowser.browserTarget()](#chromiumbrowserbrowsertarget) - [chromiumBrowser.browserTarget()](#chromiumbrowserbrowsertarget)
- [chromiumBrowser.pageTarget(page)](#chromiumbrowserpagetargetpage)
- [chromiumBrowser.startTracing(page, [options])](#chromiumbrowserstarttracingpage-options) - [chromiumBrowser.startTracing(page, [options])](#chromiumbrowserstarttracingpage-options)
- [chromiumBrowser.stopTracing()](#chromiumbrowserstoptracing) - [chromiumBrowser.stopTracing()](#chromiumbrowserstoptracing)
- [chromiumBrowser.targets(context)](#chromiumbrowsertargetscontext)
- [chromiumBrowser.waitForTarget(predicate[, options])](#chromiumbrowserwaitfortargetpredicate-options)
<!-- GEN:stop --> <!-- GEN:stop -->
<!-- GEN:toc-extends-Browser --> <!-- GEN:toc-extends-Browser -->
- [event: 'disconnected'](#event-disconnected) - [event: 'disconnected'](#event-disconnected)
@ -3551,37 +3546,11 @@ await browser.stopTracing();
- [browser.newPage([options])](#browsernewpageoptions) - [browser.newPage([options])](#browsernewpageoptions)
<!-- GEN:stop --> <!-- GEN:stop -->
#### event: 'targetchanged'
- <[ChromiumTarget]>
Emitted when the url of a target changes.
> **NOTE** This includes target changes in incognito browser contexts.
#### event: 'targetcreated'
- <[ChromiumTarget]>
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 [`browserContext.newPage`](#browsercontextnewpage).
> **NOTE** This includes target creations in incognito browser contexts.
#### event: 'targetdestroyed'
- <[ChromiumTarget]>
Emitted when a target is destroyed, for example when a page is closed.
> **NOTE** This includes target destructions in incognito browser contexts.
#### chromiumBrowser.browserTarget() #### chromiumBrowser.browserTarget()
- returns: <[ChromiumTarget]> - returns: <[ChromiumTarget]>
Returns browser target. Returns browser target.
#### chromiumBrowser.pageTarget(page)
- `page` <[Page]> Page to return target for.
- returns: <[ChromiumTarget]> a target given page was created from.
#### chromiumBrowser.startTracing(page, [options]) #### chromiumBrowser.startTracing(page, [options])
- `page` <[Page]> Optional, if specified, tracing includes screenshots of the given page. - `page` <[Page]> Optional, if specified, tracing includes screenshots of the given page.
- `options` <[Object]> - `options` <[Object]>
@ -3595,25 +3564,83 @@ Only one trace can be active at a time per browser.
#### chromiumBrowser.stopTracing() #### chromiumBrowser.stopTracing()
- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with trace data. - returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with trace data.
#### chromiumBrowser.targets(context) ### class: ChromiumBrowserContext
- `context` <[BrowserContext]> Optional, if specified, only targets from this context are returned.
* extends: [BrowserContext]
Chromium-specific features including targets, service worker support, etc.
```js
const backroundPageTarget = await context.waitForTarget(target => target.type() === 'background_page');
const backgroundPage = await backroundPageTarget.page();
```
<!-- GEN:toc -->
- [event: 'targetchanged'](#event-targetchanged)
- [event: 'targetcreated'](#event-targetcreated)
- [event: 'targetdestroyed'](#event-targetdestroyed)
- [chromiumBrowserContext.pageTarget(page)](#chromiumbrowsercontextpagetargetpage)
- [chromiumBrowserContext.targets()](#chromiumbrowsercontexttargets)
- [chromiumBrowserContext.waitForTarget(predicate[, options])](#chromiumbrowsercontextwaitfortargetpredicate-options)
<!-- GEN:stop -->
<!-- GEN:toc-extends-BrowserContext -->
- [event: 'close'](#event-close)
- [browserContext.clearCookies()](#browsercontextclearcookies)
- [browserContext.clearPermissions()](#browsercontextclearpermissions)
- [browserContext.close()](#browsercontextclose)
- [browserContext.cookies([...urls])](#browsercontextcookiesurls)
- [browserContext.newPage()](#browsercontextnewpage)
- [browserContext.pages()](#browsercontextpages)
- [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
- [browserContext.setDefaultNavigationTimeout(timeout)](#browsercontextsetdefaultnavigationtimeouttimeout)
- [browserContext.setDefaultTimeout(timeout)](#browsercontextsetdefaulttimeouttimeout)
- [browserContext.setGeolocation(geolocation)](#browsercontextsetgeolocationgeolocation)
- [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
<!-- GEN:stop -->
#### event: 'targetchanged'
- <[ChromiumTarget]>
Emitted when the url of a target changes.
> **NOTE** Only includes targets from this browser context.
#### event: 'targetcreated'
- <[ChromiumTarget]>
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 [`browserContext.newPage`](#browsercontextnewpage).
> **NOTE** Only includes targets from this browser context.
#### event: 'targetdestroyed'
- <[ChromiumTarget]>
Emitted when a target is destroyed, for example when a page is closed.
> **NOTE** Only includes targets from this browser context.
#### chromiumBrowserContext.pageTarget(page)
- `page` <[Page]> Page to return target for.
- returns: <[ChromiumTarget]> a target given page was created from.
#### chromiumBrowserContext.targets()
- returns: <[Array]<[ChromiumTarget]>> - returns: <[Array]<[ChromiumTarget]>>
An array of all active targets inside the Browser. In case of multiple browser contexts, An array of all active targets inside the browser context.
the method will return an array with all the targets in all browser contexts.
#### chromiumBrowser.waitForTarget(predicate[, options]) #### chromiumBrowserContext.waitForTarget(predicate[, options])
- `predicate` <[function]\([ChromiumTarget]\):[boolean]> A function to be run for every target - `predicate` <[function]\([ChromiumTarget]\):[boolean]> A function to be run for every target
- `options` <[Object]> - `options` <[Object]>
- `timeout` <[number]> Maximum wait time in milliseconds. Pass `0` to disable the timeout. Defaults to 30 seconds. - `timeout` <[number]> Maximum wait time in milliseconds. Pass `0` to disable the timeout. Defaults to 30 seconds.
- returns: <[Promise]<[ChromiumTarget]>> Promise which resolves to the first target found that matches the `predicate` function. - returns: <[Promise]<[ChromiumTarget]>> Promise which resolves to the first target found that matches the `predicate` function.
This searches for a target in all browser contexts. This searches for a target in the browser context.
An example of finding a target for a page opened via `window.open`: An example of finding a target for a page opened via `window.open`:
```js ```js
await page.evaluate(() => window.open('https://www.example.com/')); await page.evaluate(() => window.open('https://www.example.com/'));
const newWindowTarget = await browser.chromium.waitForTarget(target => target.url() === 'https://www.example.com/'); const newWindowTarget = await page.context().waitForTarget(target => target.url() === 'https://www.example.com/');
``` ```
### class: ChromiumCoverage ### class: ChromiumCoverage
@ -3875,6 +3902,7 @@ const { chromium } = require('playwright');
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer" [Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess" [ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser" [ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: #class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumSession]: #class-chromiumsession "ChromiumSession" [ChromiumSession]: #class-chromiumsession "ChromiumSession"
[ChromiumTarget]: #class-chromiumtarget "ChromiumTarget" [ChromiumTarget]: #class-chromiumtarget "ChromiumTarget"
[ConsoleMessage]: #class-consolemessage "ConsoleMessage" [ConsoleMessage]: #class-consolemessage "ConsoleMessage"

View file

@ -29,6 +29,7 @@ export { FileChooser, Page, Worker } from './page';
export { Selectors } from './selectors'; export { Selectors } from './selectors';
export { CRBrowser as ChromiumBrowser } from './chromium/crBrowser'; export { CRBrowser as ChromiumBrowser } from './chromium/crBrowser';
export { CRBrowserContext as ChromiumBrowserContext } from './chromium/crBrowser';
export { CRCoverage as ChromiumCoverage } from './chromium/crCoverage'; export { CRCoverage as ChromiumCoverage } from './chromium/crCoverage';
export { CRSession as ChromiumSession } from './chromium/crConnection'; export { CRSession as ChromiumSession } from './chromium/crConnection';
export { CRTarget as ChromiumTarget } from './chromium/crTarget'; export { CRTarget as ChromiumTarget } from './chromium/crTarget';

View file

@ -35,7 +35,7 @@ import { TimeoutSettings } from '../timeoutSettings';
export class CRBrowser extends platform.EventEmitter implements Browser { export class CRBrowser extends platform.EventEmitter implements Browser {
_connection: CRConnection; _connection: CRConnection;
_client: CRSession; _client: CRSession;
readonly _defaultContext: BrowserContext; readonly _defaultContext: CRBrowserContext;
readonly _contexts = new Map<string, CRBrowserContext>(); readonly _contexts = new Map<string, CRBrowserContext>();
_targets = new Map<string, CRTarget>(); _targets = new Map<string, CRTarget>();
@ -93,7 +93,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
this._targets.set(event.targetInfo.targetId, target); this._targets.set(event.targetInfo.targetId, target);
if (target._isInitialized || await target._initializedPromise) if (target._isInitialized || await target._initializedPromise)
this.emit(Events.CRBrowser.TargetCreated, target); context.emit(Events.CRBrowserContext.TargetCreated, target);
} }
async _targetDestroyed(event: { targetId: string; }) { async _targetDestroyed(event: { targetId: string; }) {
@ -102,7 +102,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
this._targets.delete(event.targetId); this._targets.delete(event.targetId);
target._didClose(); target._didClose();
if (await target._initializedPromise) if (await target._initializedPromise)
this.emit(Events.CRBrowser.TargetDestroyed, target); target.context().emit(Events.CRBrowserContext.TargetDestroyed, target);
} }
_targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) { _targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) {
@ -112,7 +112,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
const wasInitialized = target._isInitialized; const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo); target._targetInfoChanged(event.targetInfo);
if (wasInitialized && previousURL !== target.url()) if (wasInitialized && previousURL !== target.url())
this.emit(Events.CRBrowser.TargetChanged, target); target.context().emit(Events.CRBrowserContext.TargetChanged, target);
} }
async _closePage(page: Page) { async _closePage(page: Page) {
@ -123,32 +123,6 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return Array.from(this._targets.values()).filter(target => target._isInitialized); return Array.from(this._targets.values()).filter(target => target._isInitialized);
} }
async waitForTarget(predicate: (arg0: CRTarget) => boolean, options: { timeout?: number; } | undefined = {}): Promise<CRTarget> {
const {
timeout = 30000
} = options;
const existingTarget = this._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve: (target: CRTarget) => void;
const targetPromise = new Promise<CRTarget>(x => resolve = x);
this.on(Events.CRBrowser.TargetCreated, check);
this.on(Events.CRBrowser.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally {
this.removeListener(Events.CRBrowser.TargetCreated, check);
this.removeListener(Events.CRBrowser.TargetChanged, check);
}
function check(target: CRTarget) {
if (predicate(target))
resolve(target);
}
}
async close() { async close() {
const disconnected = new Promise(f => this._connection.once(ConnectionEvents.Disconnected, f)); const disconnected = new Promise(f => this._connection.once(ConnectionEvents.Disconnected, f));
await Promise.all(this.contexts().map(context => context.close())); await Promise.all(this.contexts().map(context => context.close()));
@ -199,15 +173,6 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return contentPromise; return contentPromise;
} }
targets(context?: BrowserContext): CRTarget[] {
const targets = this._allTargets();
return context ? targets.filter(t => t.context() === context) : targets;
}
pageTarget(page: Page): CRTarget {
return CRTarget.fromPage(page);
}
isConnected(): boolean { isConnected(): boolean {
return !this._connection._closed; return !this._connection._closed;
} }
@ -339,6 +304,38 @@ export class CRBrowserContext extends platform.EventEmitter implements BrowserCo
this.emit(CommonEvents.BrowserContext.Close); this.emit(CommonEvents.BrowserContext.Close);
} }
pageTarget(page: Page): CRTarget {
return CRTarget.fromPage(page);
}
targets(): CRTarget[] {
return this._browser._allTargets().filter(t => t.context() === this);
}
async waitForTarget(predicate: (arg0: CRTarget) => boolean, options: { timeout?: number; } = {}): Promise<CRTarget> {
const { timeout = 30000 } = options;
const existingTarget = this._browser._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve: (target: CRTarget) => void;
const targetPromise = new Promise<CRTarget>(x => resolve = x);
this.on(Events.CRBrowserContext.TargetCreated, check);
this.on(Events.CRBrowserContext.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
} finally {
this.removeListener(Events.CRBrowserContext.TargetCreated, check);
this.removeListener(Events.CRBrowserContext.TargetChanged, check);
}
function check(target: CRTarget) {
if (predicate(target))
resolve(target);
}
}
_browserClosed() { _browserClosed() {
this._closed = true; this._closed = true;
for (const page of this._existingPages()) for (const page of this._existingPages())

View file

@ -15,8 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { CRBrowser } from './crBrowser'; import { CRBrowser, CRBrowserContext } from './crBrowser';
import { BrowserContext } from '../browserContext';
import { CRSession, CRSessionEvents } from './crConnection'; import { CRSession, CRSessionEvents } from './crConnection';
import { Events } from '../events'; import { Events } from '../events';
import { Page, Worker } from '../page'; import { Page, Worker } from '../page';
@ -30,7 +29,7 @@ const targetSymbol = Symbol('target');
export class CRTarget { export class CRTarget {
private _targetInfo: Protocol.Target.TargetInfo; private _targetInfo: Protocol.Target.TargetInfo;
private readonly _browser: CRBrowser; private readonly _browser: CRBrowser;
private readonly _browserContext: BrowserContext; private readonly _browserContext: CRBrowserContext;
readonly _targetId: string; readonly _targetId: string;
private _sessionFactory: () => Promise<CRSession>; private _sessionFactory: () => Promise<CRSession>;
private _pagePromise: Promise<Page> | null = null; private _pagePromise: Promise<Page> | null = null;
@ -47,7 +46,7 @@ export class CRTarget {
constructor( constructor(
browser: CRBrowser, browser: CRBrowser,
targetInfo: Protocol.Target.TargetInfo, targetInfo: Protocol.Target.TargetInfo,
browserContext: BrowserContext, browserContext: CRBrowserContext,
sessionFactory: () => Promise<CRSession>) { sessionFactory: () => Promise<CRSession>) {
this._targetInfo = targetInfo; this._targetInfo = targetInfo;
this._browser = browser; this._browser = browser;
@ -120,7 +119,7 @@ export class CRTarget {
return 'other'; return 'other';
} }
context(): BrowserContext { context(): CRBrowserContext {
return this._browserContext; return this._browserContext;
} }

View file

@ -16,7 +16,7 @@
*/ */
export const Events = { export const Events = {
CRBrowser: { CRBrowserContext: {
TargetCreated: 'targetcreated', TargetCreated: 'targetcreated',
TargetDestroyed: 'targetdestroyed', TargetDestroyed: 'targetdestroyed',
TargetChanged: 'targetchanged', TargetChanged: 'targetchanged',

View file

@ -68,7 +68,7 @@ export class Chromium implements BrowserType {
const { timeout = 30000 } = options || {}; const { timeout = 30000 } = options || {};
const { browserServer, transport } = await this._launchServer(options, 'persistent', userDataDir); const { browserServer, transport } = await this._launchServer(options, 'persistent', userDataDir);
const browser = await CRBrowser.connect(transport!); const browser = await CRBrowser.connect(transport!);
await helper.waitWithTimeout(browser.waitForTarget(t => t.type() === 'page'), 'first page', timeout); await helper.waitWithTimeout(browser._defaultContext.waitForTarget(t => t.type() === 'page'), 'first page', timeout);
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
const browserContext = browser._defaultContext; const browserContext = browser._defaultContext;
browserContext.close = () => browserServer.close(); browserContext.close = () => browserServer.close();

View file

@ -38,11 +38,11 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const [popupTarget] = await Promise.all([ const [popup] = await Promise.all([
utils.waitEvent(page, 'popup'), utils.waitEvent(page, 'popup'),
page.evaluate(url => window.open(url), server.EMPTY_PAGE) page.evaluate(url => window.open(url), server.EMPTY_PAGE)
]); ]);
expect(popupTarget.context()).toBe(context); expect(popup.context()).toBe(context);
await context.close(); await context.close();
}); });
it('should isolate localStorage and cookies', async function({browser, server}) { it('should isolate localStorage and cookies', async function({browser, server}) {

View file

@ -25,28 +25,31 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Target', function() { describe('Target', function() {
it('Chromium.targets should return all of the targets', async({page, server, browser}) => { it('ChromiumBrowserContext.targets should return all of the targets', async({page, server, browser}) => {
// The pages will be the testing page and the original newtab page const second = await page.context().newPage();
const targets = browser.targets(); await second.goto(server.EMPTY_PAGE);
expect(targets.some(target => target.type() === 'page' && const targets = page.context().targets();
target.url() === 'about:blank')).toBeTruthy('Missing blank page'); // The pages will be the testing page from the harness and the one created here.
expect(targets.some(target => target.type() === 'browser')).toBeTruthy('Missing browser target'); expect(targets.length).toBe(2);
expect(targets.some(target => target.type() !== 'page')).toBe(false);
expect(targets.some(target => target.url() === 'about:blank')).toBeTruthy('Missing blank page');
expect(targets.some(target => target.url() === server.EMPTY_PAGE)).toBeTruthy('Missing new page');
await second.close();
}); });
it('Browser.pages should return all of the pages', async({page, server, context}) => { it('BrowserContext.pages should return all of the pages', async({page, server, context}) => {
// The pages will be the testing page const second = await page.context().newPage();
const allPages = await context.pages(); const allPages = await context.pages();
expect(allPages.length).toBe(1); expect(allPages.length).toBe(2);
expect(allPages).toContain(page); expect(allPages).toContain(page);
expect(allPages[0]).not.toBe(allPages[1]); expect(allPages).toContain(second);
await second.close();
}); });
it('should contain browser target', async({browser}) => { it('should report browser target', async({browser}) => {
const targets = browser.targets(); expect(browser.browserTarget()).toBeTruthy();
const browserTarget = targets.find(target => target.type() === 'browser');
expect(browserTarget).toBe(browser.browserTarget());
}); });
it('should report when a new page is created and closed', async({browser, page, server, context}) => { it('should report when a new page is created and closed', async({browser, page, server, context}) => {
const [otherPage] = await Promise.all([ const [otherPage] = await Promise.all([
browser.waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()), page.context().waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()),
page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'), page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'),
]); ]);
expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX); expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX);
@ -57,31 +60,31 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
expect(allPages).toContain(page); expect(allPages).toContain(page);
expect(allPages).toContain(otherPage); expect(allPages).toContain(otherPage);
const closePagePromise = new Promise(fulfill => browser.once('targetdestroyed', target => fulfill(target.page()))); const closePagePromise = new Promise(fulfill => page.context().once('targetdestroyed', target => fulfill(target.page())));
await otherPage.close(); await otherPage.close();
expect(await closePagePromise).toBe(otherPage); expect(await closePagePromise).toBe(otherPage);
allPages = await Promise.all(browser.targets().map(target => target.page())); allPages = await Promise.all(page.context().targets().map(target => target.page()));
expect(allPages).toContain(page); expect(allPages).toContain(page);
expect(allPages).not.toContain(otherPage); expect(allPages).not.toContain(otherPage);
}); });
it('should report when a service worker is created and destroyed', async({browser, page, server, context}) => { it('should report when a service worker is created and destroyed', async({browser, page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const createdTarget = new Promise(fulfill => browser.once('targetcreated', target => fulfill(target))); const createdTarget = new Promise(fulfill => page.context().once('targetcreated', target => fulfill(target)));
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
expect((await createdTarget).type()).toBe('service_worker'); expect((await createdTarget).type()).toBe('service_worker');
expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js'); expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js');
const destroyedTarget = new Promise(fulfill => browser.once('targetdestroyed', target => fulfill(target))); const destroyedTarget = new Promise(fulfill => page.context().once('targetdestroyed', target => fulfill(target)));
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister())); await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(await destroyedTarget).toBe(await createdTarget); expect(await destroyedTarget).toBe(await createdTarget);
}); });
it('should create a worker from a service worker', async({browser, page, server, context}) => { it('should create a worker from a service worker', async({browser, page, server, context}) => {
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const target = await browser.waitForTarget(target => target.type() === 'service_worker'); const target = await page.context().waitForTarget(target => target.type() === 'service_worker');
const worker = await target.serviceWorker(); const worker = await target.serviceWorker();
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
}); });
@ -90,17 +93,17 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
await page.evaluate(() => { await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")'); new SharedWorker('data:text/javascript,console.log("hi")');
}); });
const target = await browser.waitForTarget(target => target.type() === 'shared_worker'); const target = await page.context().waitForTarget(target => target.type() === 'shared_worker');
const worker = await target.serviceWorker(); const worker = await target.serviceWorker();
expect(worker).toBe(null); expect(worker).toBe(null);
}); });
it('should report when a target url changes', async({browser, page, server, context}) => { it('should report when a target url changes', async({browser, page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
let changedTarget = new Promise(fulfill => browser.once('targetchanged', target => fulfill(target))); let changedTarget = new Promise(fulfill => page.context().once('targetchanged', target => fulfill(target)));
await page.goto(server.CROSS_PROCESS_PREFIX + '/'); await page.goto(server.CROSS_PROCESS_PREFIX + '/');
expect((await changedTarget).url()).toBe(server.CROSS_PROCESS_PREFIX + '/'); expect((await changedTarget).url()).toBe(server.CROSS_PROCESS_PREFIX + '/');
changedTarget = new Promise(fulfill => browser.once('targetchanged', target => fulfill(target))); changedTarget = new Promise(fulfill => page.context().once('targetchanged', target => fulfill(target)));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect((await changedTarget).url()).toBe(server.EMPTY_PAGE); expect((await changedTarget).url()).toBe(server.EMPTY_PAGE);
}); });
@ -108,13 +111,13 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
let targetChanged = false; let targetChanged = false;
const listener = () => targetChanged = true; const listener = () => targetChanged = true;
browser.on('targetchanged', listener); browser.on('targetchanged', listener);
const targetPromise = new Promise(fulfill => browser.once('targetcreated', target => fulfill(target))); const targetPromise = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const newPagePromise = context.newPage(); const newPagePromise = context.newPage();
const target = await targetPromise; const target = await targetPromise;
expect(target.url()).toBe('about:blank'); expect(target.url()).toBe('about:blank');
const newPage = await newPagePromise; const newPage = await newPagePromise;
const targetPromise2 = new Promise(fulfill => browser.once('targetcreated', target => fulfill(target))); const targetPromise2 = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const evaluatePromise = newPage.evaluate(() => window.open('about:blank')); const evaluatePromise = newPage.evaluate(() => window.open('about:blank'));
const target2 = await targetPromise2; const target2 = await targetPromise2;
expect(target2.url()).toBe('about:blank'); expect(target2.url()).toBe('about:blank');
@ -132,7 +135,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
server.waitForRequest('/one-style.css') server.waitForRequest('/one-style.css')
]); ]);
// Connect to the opened page. // Connect to the opened page.
const target = await browser.waitForTarget(target => target.url().includes('one-style.html')); const target = await page.context().waitForTarget(target => target.url().includes('one-style.html'));
const newPage = await target.page(); const newPage = await target.page();
// Issue a redirect. // Issue a redirect.
serverResponse.writeHead(302, { location: '/injectedstyle.css' }); serverResponse.writeHead(302, { location: '/injectedstyle.css' });
@ -145,23 +148,21 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
it('should have an opener', async({browser, page, server, context}) => { it('should have an opener', async({browser, page, server, context}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const [createdTarget] = await Promise.all([ const [createdTarget] = await Promise.all([
new Promise(fulfill => browser.once('targetcreated', target => fulfill(target))), new Promise(fulfill => page.context().once('targetcreated', target => fulfill(target))),
page.goto(server.PREFIX + '/popup/window-open.html') page.goto(server.PREFIX + '/popup/window-open.html')
]); ]);
expect((await createdTarget.page()).url()).toBe(server.PREFIX + '/popup/popup.html'); expect((await createdTarget.page()).url()).toBe(server.PREFIX + '/popup/popup.html');
expect(createdTarget.opener()).toBe(browser.pageTarget(page)); expect(createdTarget.opener()).toBe(page.context().pageTarget(page));
expect(browser.pageTarget(page).opener()).toBe(null); expect(page.context().pageTarget(page).opener()).toBe(null);
}); });
it('should close all belonging targets once closing context', async function({browser}) { it('should close all belonging targets once closing context', async function({browser}) {
const targets = async (context) => (await browser.targets()).filter(t => t.type() === 'page' && t.context() === context);
const context = await browser.newContext(); const context = await browser.newContext();
await context.newPage(); await context.newPage();
expect((await targets(context)).length).toBe(1); expect((await context.targets()).length).toBe(1);
expect((await context.pages()).length).toBe(1); expect((await context.pages()).length).toBe(1);
await context.close(); await context.close();
expect((await targets(context)).length).toBe(0); expect((await context.targets()).length).toBe(0);
}); });
}); });
@ -169,7 +170,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
it('should wait for a target', async function({server, browser}) { it('should wait for a target', async function({server, browser}) {
const context = await browser.newContext(); const context = await browser.newContext();
let resolved = false; let resolved = false;
const targetPromise = browser.waitForTarget(target => target.context() === context && target.url() === server.EMPTY_PAGE); const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true); targetPromise.then(() => resolved = true);
const page = await context.newPage(); const page = await context.newPage();
expect(resolved).toBe(false); expect(resolved).toBe(false);
@ -179,26 +180,27 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
await context.close(); await context.close();
}); });
it('should timeout waiting for a non-existent target', async function({browser, context, server}) { it('should timeout waiting for a non-existent target', async function({browser, context, server}) {
const error = await browser.waitForTarget(target => target.context() === context && target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e); const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e);
expect(error).toBeInstanceOf(playwright.errors.TimeoutError); expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
}); });
it('should wait for a target', async function({browser, server}) { it('should wait for a target', async function({browser, server}) {
const context = await browser.newContext();
let resolved = false; let resolved = false;
const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE); const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true); targetPromise.then(() => resolved = true);
const page = await browser.newPage(); const page = await context.newPage();
expect(resolved).toBe(false); expect(resolved).toBe(false);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const target = await targetPromise; const target = await targetPromise;
expect(await target.page()).toBe(page); expect(await target.page()).toBe(page);
await page.context().close(); await context.close();
}); });
it('should fire target events', async function({browser, server}) { it('should fire target events', async function({browser, server}) {
const context = await browser.newContext(); const context = await browser.newContext();
const events = []; const events = [];
browser.on('targetcreated', target => events.push('CREATED: ' + target.url())); context.on('targetcreated', target => events.push('CREATED: ' + target.url()));
browser.on('targetchanged', target => events.push('CHANGED: ' + target.url())); context.on('targetchanged', target => events.push('CHANGED: ' + target.url()));
browser.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url())); context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url()));
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.close(); await page.close();

View file

@ -51,17 +51,18 @@ module.exports.describe = function({testRunner, expect, playwright, defaultBrows
it('background_page target type should be available', async() => { it('background_page target type should be available', async() => {
const browserWithExtension = await playwright.launch(extensionOptions); const browserWithExtension = await playwright.launch(extensionOptions);
const page = await browserWithExtension.newPage(); const page = await browserWithExtension.newPage();
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); const backgroundPageTarget = await page.context().waitForTarget(target => target.type() === 'background_page');
await page.close(); await page.close();
await browserWithExtension.close(); await browserWithExtension.close();
expect(backgroundPageTarget).toBeTruthy(); expect(backgroundPageTarget).toBeTruthy();
}); });
it('target.page() should return a background_page', async({}) => { it('target.page() should return a background_page', async({}) => {
const browserWithExtension = await playwright.launch(extensionOptions); const browserWithExtension = await playwright.launch(extensionOptions);
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); const page = await browserWithExtension.newPage();
const page = await backgroundPageTarget.page(); const backgroundPageTarget = await page.context().waitForTarget(target => target.type() === 'background_page');
expect(await page.evaluate(() => 2 * 3)).toBe(6); const backgroundPage = await backgroundPageTarget.page();
expect(await page.evaluate(() => window.MAGIC)).toBe(42); expect(await backgroundPage.evaluate(() => 2 * 3)).toBe(6);
expect(await backgroundPage.evaluate(() => window.MAGIC)).toBe(42);
await browserWithExtension.close(); await browserWithExtension.close();
}); });
// TODO: Support OOOPIF. @see https://github.com/GoogleChrome/puppeteer/issues/2548 // TODO: Support OOOPIF. @see https://github.com/GoogleChrome/puppeteer/issues/2548
@ -91,7 +92,7 @@ module.exports.describe = function({testRunner, expect, playwright, defaultBrows
const context = await browser.newContext(); const context = await browser.newContext();
await Promise.all([ await Promise.all([
context.newPage(), context.newPage(),
browser.waitForTarget(target => target.context() === context && target.url().includes('devtools://')), context.waitForTarget(target => target.url().includes('devtools://')),
]); ]);
await browser.close(); await browser.close();
}); });

View file

@ -59,9 +59,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const browser = await playwright.launch(defaultBrowserOptions); const browser = await playwright.launch(defaultBrowserOptions);
const context = await browser.newContext(); const context = await browser.newContext();
const events = []; const events = [];
browser.on('targetcreated', target => target.context() === context && events.push('CREATED')); context.on('targetcreated', target => events.push('CREATED'));
browser.on('targetchanged', target => target.context() === context && events.push('CHANGED')); context.on('targetchanged', target => events.push('CHANGED'));
browser.on('targetdestroyed', target => target.context() === context && events.push('DESTROYED')); context.on('targetdestroyed', target => events.push('DESTROYED'));
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.close(); await page.close();

View file

@ -43,18 +43,18 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
}); });
xit('should report oopif frames', async function({browser, page, server, context}) { xit('should report oopif frames', async function({browser, page, server, context}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(browser).length).toBe(1); expect(oopifs(page.context()).length).toBe(1);
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
}); });
it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) { it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
await page.route('*', request => request.continue()); await page.route('*', request => request.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(browser).length).toBe(1); expect(oopifs(page.context()).length).toBe(1);
}); });
}); });
}; };
function oopifs(browser) { function oopifs(context) {
return browser.targets().filter(target => target._targetInfo.type === 'iframe'); return context.targets().filter(target => target._targetInfo.type === 'iframe');
} }

View file

@ -26,7 +26,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Chromium.createCDPSession', function() { describe('Chromium.createCDPSession', function() {
it('should work', async function({page, browser, server}) { it('should work', async function({page, browser, server}) {
const client = await browser.pageTarget(page).createCDPSession(); const client = await page.context().pageTarget(page).createCDPSession();
await Promise.all([ await Promise.all([
client.send('Runtime.enable'), client.send('Runtime.enable'),
@ -36,7 +36,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(foo).toBe('bar'); expect(foo).toBe('bar');
}); });
it('should send events', async function({page, browser, server}) { it('should send events', async function({page, browser, server}) {
const client = await browser.pageTarget(page).createCDPSession(); const client = await page.context().pageTarget(page).createCDPSession();
await client.send('Network.enable'); await client.send('Network.enable');
const events = []; const events = [];
client.on('Network.requestWillBeSent', event => events.push(event)); client.on('Network.requestWillBeSent', event => events.push(event));
@ -44,7 +44,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(events.length).toBe(1); expect(events.length).toBe(1);
}); });
it('should enable and disable domains independently', async function({page, browser, server}) { it('should enable and disable domains independently', async function({page, browser, server}) {
const client = await browser.pageTarget(page).createCDPSession(); const client = await page.context().pageTarget(page).createCDPSession();
await client.send('Runtime.enable'); await client.send('Runtime.enable');
await client.send('Debugger.enable'); await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain. // JS coverage enables and then disables Debugger domain.
@ -59,7 +59,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(event.url).toBe('foo.js'); expect(event.url).toBe('foo.js');
}); });
it('should be able to detach session', async function({page, browser, server}) { it('should be able to detach session', async function({page, browser, server}) {
const client = await browser.pageTarget(page).createCDPSession(); const client = await page.context().pageTarget(page).createCDPSession();
await client.send('Runtime.enable'); await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true}); const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3); expect(evalResponse.result.value).toBe(3);
@ -73,7 +73,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(error.message).toContain('Session closed.'); expect(error.message).toContain('Session closed.');
}); });
it('should throw nice errors', async function({page, browser}) { it('should throw nice errors', async function({page, browser}) {
const client = await browser.pageTarget(page).createCDPSession(); const client = await page.context().pageTarget(page).createCDPSession();
const error = await theSourceOfTheProblems().catch(error => error); const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems'); expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist'); expect(error.message).toContain('ThisCommand.DoesNotExist');