From 9fd30e58e23ebd53fc64f474b653a33d669f9cef Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 13 Jul 2020 15:26:09 -0700 Subject: [PATCH] feat(rpc): ensure feature-detection works as before (#2898) For this, some tests are migrated from skip() to feature detection, like our users would do. --- src/rpc/channels.ts | 7 +- src/rpc/client/browser.ts | 17 +---- src/rpc/client/browserContext.ts | 58 +++------------ src/rpc/client/browserType.ts | 2 +- src/rpc/client/chromiumBrowser.ts | 33 +++++++++ src/rpc/client/chromiumBrowserContext.ts | 56 +++++++++++++++ src/rpc/client/connection.ts | 14 +++- src/rpc/client/page.ts | 33 +++++---- src/rpc/client/worker.ts | 3 +- src/rpc/server/browserContextDispatcher.ts | 27 ++++--- src/rpc/server/browserTypeDispatcher.ts | 2 +- src/rpc/server/pageDispatcher.ts | 2 + test/channels.spec.js | 82 ++++++++++++---------- test/chromium/chromium.spec.js | 2 +- test/{chromium => }/coverage.spec.js | 14 ++-- test/defaultbrowsercontext.spec.js | 15 ++++ test/{chromium => }/pdf.spec.js | 12 +++- test/test.config.js | 4 +- 18 files changed, 235 insertions(+), 148 deletions(-) create mode 100644 src/rpc/client/chromiumBrowser.ts create mode 100644 src/rpc/client/chromiumBrowserContext.ts rename test/{chromium => }/coverage.spec.js (95%) rename test/{chromium => }/pdf.spec.js (70%) diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index 7ba3511a0c..6df884c811 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -98,11 +98,7 @@ export interface BrowserContextChannel extends Channel { on(event: 'crServiceWorker', callback: (params: WorkerChannel) => void): this; crNewCDPSession(params: { page: PageChannel }): Promise; } -export type BrowserContextInitializer = { - pages: PageChannel[], - crBackgroundPages: PageChannel[], - crServiceWorkers: WorkerChannel[], -}; +export type BrowserContextInitializer = {}; export interface PageChannel extends Channel { @@ -117,7 +113,6 @@ export interface PageChannel extends Channel { on(event: 'frameAttached', callback: (params: FrameChannel) => void): this; on(event: 'frameDetached', callback: (params: FrameChannel) => void): this; on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this; - on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this; on(event: 'load', callback: () => void): this; on(event: 'pageError', callback: (params: { error: types.Error }) => void): this; on(event: 'popup', callback: (params: PageChannel) => void): this; diff --git a/src/rpc/client/browser.ts b/src/rpc/client/browser.ts index 2a2e3e5afe..683a0c969c 100644 --- a/src/rpc/client/browser.ts +++ b/src/rpc/client/browser.ts @@ -20,14 +20,15 @@ import { BrowserContext } from './browserContext'; import { Page } from './page'; import { ChannelOwner } from './channelOwner'; import { Events } from '../../events'; -import { CDPSession } from './cdpSession'; import { LoggerSink } from '../../loggerSink'; +import { BrowserType } from './browserType'; export class Browser extends ChannelOwner { readonly _contexts = new Set(); private _isConnected = true; private _isClosedOrClosing = false; private _closedPromise: Promise; + readonly _browserType: BrowserType; static from(browser: BrowserChannel): Browser { return (browser as any)._object; @@ -39,6 +40,7 @@ export class Browser extends ChannelOwner { constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserInitializer) { super(parent, type, guid, initializer, true); + this._browserType = parent as BrowserType; this._channel.on('close', () => { this._isConnected = false; this.emit(Events.Browser.Disconnected); @@ -54,7 +56,6 @@ export class Browser extends ChannelOwner { const context = BrowserContext.from(await this._channel.newContext(options)); this._contexts.add(context); context._logger = logger || this._logger; - context._browser = this; return context; } @@ -81,16 +82,4 @@ export class Browser extends ChannelOwner { } await this._closedPromise; } - - async newBrowserCDPSession(): Promise { - return CDPSession.from(await this._channel.crNewBrowserCDPSession()); - } - - async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { - await this._channel.crStartTracing({ ...options, page: page ? page._channel : undefined }); - } - - async stopTracing(): Promise { - return Buffer.from(await this._channel.crStopTracing(), 'base64'); - } } diff --git a/src/rpc/client/browserContext.ts b/src/rpc/client/browserContext.ts index 26ed48932f..872357209e 100644 --- a/src/rpc/client/browserContext.ts +++ b/src/rpc/client/browserContext.ts @@ -25,16 +25,13 @@ import { helper } from '../../helper'; import { Browser } from './browser'; import { Events } from '../../events'; import { TimeoutSettings } from '../../timeoutSettings'; -import { CDPSession } from './cdpSession'; -import { Events as ChromiumEvents } from '../../chromium/events'; -import { Worker } from './worker'; +import { BrowserType } from './browserType'; export class BrowserContext extends ChannelOwner { _pages = new Set(); - _crBackgroundPages = new Set(); - _crServiceWorkers = new Set(); private _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; - _browser: Browser | undefined; + readonly _browser: Browser | undefined; + readonly _browserType: BrowserType; readonly _bindings = new Map(); private _pendingWaitForEvents = new Map<(error: Error) => void, string>(); _timeoutSettings = new TimeoutSettings(); @@ -52,43 +49,22 @@ export class BrowserContext extends ChannelOwner { - const page = Page.from(p); - this._pages.add(page); - page._setBrowserContext(this); - }); + if (parent instanceof Browser) { + this._browser = parent; + this._browserType = this._browser._browserType; + } else { + // Launching persistent context does not produce a browser at all. + this._browserType = parent as BrowserType; + } + this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall))); this._channel.on('close', () => this._onClose()); this._channel.on('page', page => this._onPage(Page.from(page))); this._channel.on('route', ({ route, request }) => this._onRoute(network.Route.from(route), network.Request.from(request))); this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f)); - - initializer.crBackgroundPages.forEach(p => { - const page = Page.from(p); - this._crBackgroundPages.add(page); - page._setBrowserContext(this); - }); - this._channel.on('crBackgroundPage', pageChannel => { - const page = Page.from(pageChannel); - page._setBrowserContext(this); - this._crBackgroundPages.add(page); - this.emit(ChromiumEvents.CRBrowserContext.BackgroundPage, page); - }); - initializer.crServiceWorkers.forEach(w => { - const worker = Worker.from(w); - worker._context = this; - this._crServiceWorkers.add(worker); - }); - this._channel.on('crServiceWorker', serviceWorkerChannel => { - const worker = Worker.from(serviceWorkerChannel); - worker._context = this; - this._crServiceWorkers.add(worker); - this.emit(ChromiumEvents.CRBrowserContext.ServiceWorker, worker); - }); } private _onPage(page: Page): void { - page._setBrowserContext(this); this._pages.add(page); this.emit(Events.BrowserContext.Page, page); } @@ -234,16 +210,4 @@ export class BrowserContext extends ChannelOwner { - return CDPSession.from(await this._channel.crNewCDPSession({ page: page._channel })); - } - - backgroundPages(): Page[] { - return [...this._crBackgroundPages]; - } - - serviceWorkers(): Worker[] { - return [...this._crServiceWorkers]; - } } diff --git a/src/rpc/client/browserType.ts b/src/rpc/client/browserType.ts index d33ee24b95..cf0a83bb71 100644 --- a/src/rpc/client/browserType.ts +++ b/src/rpc/client/browserType.ts @@ -29,7 +29,7 @@ export class BrowserType extends ChannelOwner { + return CDPSession.from(await this._channel.crNewBrowserCDPSession()); + } + + async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { + await this._channel.crStartTracing({ ...options, page: page ? page._channel : undefined }); + } + + async stopTracing(): Promise { + return Buffer.from(await this._channel.crStopTracing(), 'base64'); + } +} diff --git a/src/rpc/client/chromiumBrowserContext.ts b/src/rpc/client/chromiumBrowserContext.ts new file mode 100644 index 0000000000..0819505278 --- /dev/null +++ b/src/rpc/client/chromiumBrowserContext.ts @@ -0,0 +1,56 @@ +/** + * Copyright 2017 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * 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. + */ + +import { Page } from './page'; +import { BrowserContextInitializer } from '../channels'; +import { ChannelOwner } from './channelOwner'; +import { CDPSession } from './cdpSession'; +import { Events as ChromiumEvents } from '../../chromium/events'; +import { Worker } from './worker'; +import { BrowserContext } from './browserContext'; + +export class ChromiumBrowserContext extends BrowserContext { + _backgroundPages = new Set(); + _serviceWorkers = new Set(); + + constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) { + super(parent, type, guid, initializer); + this._channel.on('crBackgroundPage', pageChannel => { + const page = Page.from(pageChannel); + this._backgroundPages.add(page); + this.emit(ChromiumEvents.CRBrowserContext.BackgroundPage, page); + }); + this._channel.on('crServiceWorker', serviceWorkerChannel => { + const worker = Worker.from(serviceWorkerChannel); + worker._context = this; + this._serviceWorkers.add(worker); + this.emit(ChromiumEvents.CRBrowserContext.ServiceWorker, worker); + }); + } + + backgroundPages(): Page[] { + return [...this._backgroundPages]; + } + + serviceWorkers(): Worker[] { + return [...this._serviceWorkers]; + } + + async newCDPSession(page: Page): Promise { + return CDPSession.from(await this._channel.crNewCDPSession({ page: page._channel })); + } +} diff --git a/src/rpc/client/connection.ts b/src/rpc/client/connection.ts index 9cc43e66fc..cdd51e29ba 100644 --- a/src/rpc/client/connection.ts +++ b/src/rpc/client/connection.ts @@ -33,6 +33,8 @@ import { BrowserServer } from './browserServer'; import { CDPSession } from './cdpSession'; import { Playwright } from './playwright'; import { Channel } from '../channels'; +import { ChromiumBrowser } from './chromiumBrowser'; +import { ChromiumBrowserContext } from './chromiumBrowserContext'; class Root extends ChannelOwner { constructor(connection: Connection) { @@ -133,7 +135,10 @@ export class Connection { result = new BindingCall(parent, type, guid, initializer); break; case 'browser': - result = new Browser(parent, type, guid, initializer); + if ((parent as BrowserType).name() === 'chromium') + result = new ChromiumBrowser(parent, type, guid, initializer); + else + result = new Browser(parent, type, guid, initializer); break; case 'browserServer': result = new BrowserServer(parent, type, guid, initializer); @@ -145,7 +150,12 @@ export class Connection { result = new CDPSession(parent, type, guid, initializer); break; case 'context': - result = new BrowserContext(parent, type, guid, initializer); + // Launching persistent context produces BrowserType parent directly for BrowserContext. + const browserType = parent instanceof Browser ? parent._browserType : parent as BrowserType; + if (browserType.name() === 'chromium') + result = new ChromiumBrowserContext(parent, type, guid, initializer); + else + result = new BrowserContext(parent, type, guid, initializer); break; case 'consoleMessage': result = new ConsoleMessage(parent, type, guid, initializer); diff --git a/src/rpc/client/page.ts b/src/rpc/client/page.ts index 0e3c8beb88..d63a44db31 100644 --- a/src/rpc/client/page.ts +++ b/src/rpc/client/page.ts @@ -40,8 +40,7 @@ import { Buffer } from 'buffer'; import { Coverage } from './coverage'; export class Page extends ChannelOwner { - - private _browserContext: BrowserContext | undefined; + private _browserContext: BrowserContext; _ownedContext: BrowserContext | undefined; private _mainFrame: Frame; @@ -54,10 +53,12 @@ export class Page extends ChannelOwner { readonly accessibility: Accessibility; readonly keyboard: Keyboard; readonly mouse: Mouse; - readonly coverage: Coverage; + coverage: Coverage | null = null; + pdf?: (options?: types.PDFOptions) => Promise; + readonly _bindings = new Map(); private _pendingWaitForEvents = new Map<(error: Error) => void, string>(); - private _timeoutSettings = new TimeoutSettings(); + private _timeoutSettings: TimeoutSettings; _isPageCall = false; static from(page: PageChannel): Page { @@ -70,10 +71,12 @@ export class Page extends ChannelOwner { constructor(parent: ChannelOwner, type: string, guid: string, initializer: PageInitializer) { super(parent, type, guid, initializer); + this._browserContext = parent as BrowserContext; + this._timeoutSettings = new TimeoutSettings(this._browserContext._timeoutSettings); + this.accessibility = new Accessibility(this._channel); this.keyboard = new Keyboard(this._channel); this.mouse = new Mouse(this._channel); - this.coverage = new Coverage(this._channel); this._mainFrame = Frame.from(initializer.mainFrame); this._mainFrame._page = this; @@ -101,11 +104,11 @@ export class Page extends ChannelOwner { this._channel.on('response', response => this.emit(Events.Page.Response, Response.from(response))); this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request))); this._channel.on('worker', worker => this._onWorker(Worker.from(worker))); - } - _setBrowserContext(context: BrowserContext) { - this._browserContext = context; - this._timeoutSettings = new TimeoutSettings(context._timeoutSettings); + if (this._browserContext._browserType.name() === 'chromium') { + this.coverage = new Coverage(this._channel); + this.pdf = options => this._pdf(options); + } } private _onRequestFailed(request: Request, failureText: string | null) { @@ -142,7 +145,7 @@ export class Page extends ChannelOwner { return; } } - this._browserContext!._onRoute(route, request); + this._browserContext._onRoute(route, request); } async _onBinding(bindingCall: BindingCall) { @@ -151,7 +154,7 @@ export class Page extends ChannelOwner { bindingCall.call(func); return; } - this._browserContext!._onBinding(bindingCall); + this._browserContext._onBinding(bindingCall); } _onWorker(worker: Worker): void { @@ -162,7 +165,7 @@ export class Page extends ChannelOwner { private _onClose() { this._closed = true; - this._browserContext!._pages.delete(this); + this._browserContext._pages.delete(this); this._rejectPendingOperations(false); this.emit(Events.Page.Close); } @@ -184,7 +187,7 @@ export class Page extends ChannelOwner { } context(): BrowserContext { - return this._browserContext!; + return this._browserContext; } async opener(): Promise { @@ -280,7 +283,7 @@ export class Page extends ChannelOwner { async exposeBinding(name: string, binding: FunctionWithSource) { if (this._bindings.has(name)) throw new Error(`Function "${name}" has been already registered`); - if (this._browserContext!._bindings.has(name)) + if (this._browserContext._bindings.has(name)) throw new Error(`Function "${name}" has been already registered in the browser context`); this._bindings.set(name, binding); await this._channel.exposeBinding({ name }); @@ -499,7 +502,7 @@ export class Page extends ChannelOwner { return this; } - async pdf(options: types.PDFOptions = {}): Promise { + async _pdf(options: types.PDFOptions = {}): Promise { const transportOptions: PDFOptions = { ...options } as PDFOptions; if (transportOptions.margin) transportOptions.margin = { ...transportOptions.margin }; diff --git a/src/rpc/client/worker.ts b/src/rpc/client/worker.ts index bcf901a8ea..be479889c6 100644 --- a/src/rpc/client/worker.ts +++ b/src/rpc/client/worker.ts @@ -21,6 +21,7 @@ import { ChannelOwner } from './channelOwner'; import { Func1, JSHandle, parseResult, serializeArgument, SmartHandle } from './jsHandle'; import { Page } from './page'; import { BrowserContext } from './browserContext'; +import { ChromiumBrowserContext } from './chromiumBrowserContext'; export class Worker extends ChannelOwner { _page: Page | undefined; // Set for web workers. @@ -36,7 +37,7 @@ export class Worker extends ChannelOwner { if (this._page) this._page._workers.delete(this); if (this._context) - this._context._crServiceWorkers.delete(this); + (this._context as ChromiumBrowserContext)._serviceWorkers.delete(this); this.emit(Events.Worker.Close, this); }); } diff --git a/src/rpc/server/browserContextDispatcher.ts b/src/rpc/server/browserContextDispatcher.ts index 666ef89f07..c3d7e3fe6e 100644 --- a/src/rpc/server/browserContextDispatcher.ts +++ b/src/rpc/server/browserContextDispatcher.ts @@ -30,26 +30,25 @@ export class BrowserContextDispatcher extends Dispatcher new PageDispatcher(scope, p)); - context.on(ChromiumEvents.CRBrowserContext.BackgroundPage, page => this._dispatchEvent('crBackgroundPage', new PageDispatcher(this._scope, page))); - crServiceWorkers = (context as CRBrowserContext).serviceWorkers().map(w => new WorkerDispatcher(scope, w)); - context.on(ChromiumEvents.CRBrowserContext.ServiceWorker, serviceWorker => this._dispatchEvent('crServiceWorker', new WorkerDispatcher(this._scope, serviceWorker))); - } - - super(scope, context, 'context', { - pages: context.pages().map(p => new PageDispatcher(scope, p)), - crBackgroundPages, - crServiceWorkers, - }, true); + super(scope, context, 'context', {}, true); this._context = context; + + for (const page of context.pages()) + this._dispatchEvent('page', new PageDispatcher(this._scope, page)); context.on(Events.BrowserContext.Page, page => this._dispatchEvent('page', new PageDispatcher(this._scope, page))); context.on(Events.BrowserContext.Close, () => { this._dispatchEvent('close'); this._dispose(); }); + + if (context._browserBase._options.name === 'chromium') { + for (const page of (context as CRBrowserContext).backgroundPages()) + this._dispatchEvent('crBackgroundPage', new PageDispatcher(this._scope, page)); + context.on(ChromiumEvents.CRBrowserContext.BackgroundPage, page => this._dispatchEvent('crBackgroundPage', new PageDispatcher(this._scope, page))); + for (const serviceWorker of (context as CRBrowserContext).serviceWorkers()) + this._dispatchEvent('crServiceWorker', new WorkerDispatcher(this._scope, serviceWorker)); + context.on(ChromiumEvents.CRBrowserContext.ServiceWorker, serviceWorker => this._dispatchEvent('crServiceWorker', new WorkerDispatcher(this._scope, serviceWorker))); + } } async setDefaultNavigationTimeoutNoReply(params: { timeout: number }) { diff --git a/src/rpc/server/browserTypeDispatcher.ts b/src/rpc/server/browserTypeDispatcher.ts index 91bb05f48f..51d25230ca 100644 --- a/src/rpc/server/browserTypeDispatcher.ts +++ b/src/rpc/server/browserTypeDispatcher.ts @@ -29,7 +29,7 @@ export class BrowserTypeDispatcher extends Dispatcher { diff --git a/src/rpc/server/pageDispatcher.ts b/src/rpc/server/pageDispatcher.ts index a769631980..5daeec5915 100644 --- a/src/rpc/server/pageDispatcher.ts +++ b/src/rpc/server/pageDispatcher.ts @@ -37,6 +37,8 @@ export class PageDispatcher extends Dispatcher implements private _page: Page; constructor(scope: DispatcherScope, page: Page) { + // TODO: theoretically, there could be more than one frame already. + // If we split pageCreated and pageReady, there should be no main frame during pageCreated. super(scope, page, 'page', { mainFrame: FrameDispatcher.from(scope, page.mainFrame()), viewportSize: page.viewportSize(), diff --git a/test/channels.spec.js b/test/channels.spec.js index 713e38bedd..9057e8fbc3 100644 --- a/test/channels.spec.js +++ b/test/channels.spec.js @@ -22,15 +22,16 @@ describe.skip(!CHANNEL)('Channels', function() { expect(!!browser._connection).toBeTruthy(); }); - it('should scope context handles', async({browser, server}) => { + it('should scope context handles', async({browserType, browser, server}) => { const GOLDEN_PRECONDITION = { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [] } + ] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, { _guid: 'playwright' }, - { _guid: 'browser', objects: [] }, ] }; await expectScopeState(browser, GOLDEN_PRECONDITION); @@ -41,18 +42,19 @@ describe.skip(!CHANNEL)('Channels', function() { await expectScopeState(browser, { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, - { _guid: 'playwright' }, - { _guid: 'browser', objects: [ - { _guid: 'context', objects: [ - { _guid: 'frame' }, - { _guid: 'page' }, - { _guid: 'request' }, - { _guid: 'response' }, - ]}, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [ + { _guid: 'context', objects: [ + { _guid: 'frame' }, + { _guid: 'page' }, + { _guid: 'request' }, + { _guid: 'response' }, + ]}, + ] }, ] }, + { _guid: 'playwright' }, ] }); @@ -64,11 +66,12 @@ describe.skip(!CHANNEL)('Channels', function() { const GOLDEN_PRECONDITION = { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [] } + ] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, { _guid: 'playwright' }, - { _guid: 'browser', objects: [] }, ] }; await expectScopeState(browserType, GOLDEN_PRECONDITION); @@ -77,13 +80,14 @@ describe.skip(!CHANNEL)('Channels', function() { await expectScopeState(browserType, { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, - { _guid: 'playwright' }, - { _guid: 'browser', objects: [ - { _guid: 'cdpSession', objects: [] }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [ + { _guid: 'cdpSession', objects: [] }, + ] }, ] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'playwright' }, ] }); @@ -95,11 +99,12 @@ describe.skip(!CHANNEL)('Channels', function() { const GOLDEN_PRECONDITION = { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [] } + ] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, { _guid: 'playwright' }, - { _guid: 'browser', objects: [] }, ] }; await expectScopeState(browserType, GOLDEN_PRECONDITION); @@ -109,14 +114,15 @@ describe.skip(!CHANNEL)('Channels', function() { await expectScopeState(browserType, { _guid: '', objects: [ - { _guid: 'chromium' }, - { _guid: 'firefox' }, - { _guid: 'webkit' }, - { _guid: 'playwright' }, - { _guid: 'browser', objects: [ - { _guid: 'context', objects: [] }, + { _guid: 'browserType', objects: [ + { _guid: 'browser', objects: [ + { _guid: 'context', objects: [] } + ] }, + { _guid: 'browser', objects: [] }, ] }, - { _guid: 'browser', objects: [] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'browserType', objects: [] }, + { _guid: 'playwright' }, ] }); @@ -152,6 +158,8 @@ function trimGuids(object) { const result = {}; for (const key in object) result[key] = trimGuids(object[key]); + if (result._guid === 'chromium' || result._guid === 'firefox' || result._guid === 'webkit') + result._guid = 'browserType'; return result; } if (typeof object === 'string') diff --git a/test/chromium/chromium.spec.js b/test/chromium/chromium.spec.js index f36d4d530a..7a024ffdb4 100644 --- a/test/chromium/chromium.spec.js +++ b/test/chromium/chromium.spec.js @@ -16,7 +16,7 @@ const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType); -describe('ChromiumBrowserContext', function() { +describe('Service Worker', function() { it('should create a worker from a service worker', async({browser, page, server, context}) => { const [worker] = await Promise.all([ context.waitForEvent('serviceworker'), diff --git a/test/chromium/coverage.spec.js b/test/coverage.spec.js similarity index 95% rename from test/chromium/coverage.spec.js rename to test/coverage.spec.js index 52a21facfc..bc1b1335d7 100644 --- a/test/chromium/coverage.spec.js +++ b/test/coverage.spec.js @@ -14,10 +14,16 @@ * limitations under the License. */ -const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = require('../utils').testOptions(browserType); +const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType); -describe('JSCoverage', function() { +describe.skip(CHROMIUM)('Page.coverage missing', function() { it('should work', async function({page, server}) { + expect(page.coverage).toBe(null); + }); +}); + +describe.skip(!CHROMIUM)('JSCoverage', function() { + it('should work', async function({browserType, page, server}) { await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); const coverage = await page.coverage.stopJSCoverage(); @@ -88,8 +94,8 @@ describe('JSCoverage', function() { }); }); -describe('CSSCoverage', function() { - it('should work', async function({page, server}) { +describe.skip(!CHROMIUM)('CSSCoverage', function() { + it('should work', async function({browserType, page, server}) { await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/simple.html'); const coverage = await page.coverage.stopCSSCoverage(); diff --git a/test/defaultbrowsercontext.spec.js b/test/defaultbrowsercontext.spec.js index e120ce8195..cfd109f472 100644 --- a/test/defaultbrowsercontext.spec.js +++ b/test/defaultbrowsercontext.spec.js @@ -374,4 +374,19 @@ describe('launchPersistentContext()', function() { await close(state); expect(closed).toBe(true); }); + it.skip(!CHROMIUM)('coverage should work', async state => { + const { page, server } = await launch(state); + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/jscoverage/simple.html'); + expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); + await close(state); + }); + it.skip(CHROMIUM)('coverage should be missing', async state => { + const { page } = await launch(state); + expect(page.coverage).toBe(null); + await close(state); + }); }); diff --git a/test/chromium/pdf.spec.js b/test/pdf.spec.js similarity index 70% rename from test/chromium/pdf.spec.js rename to test/pdf.spec.js index d812aeeb14..74f2e763c6 100644 --- a/test/chromium/pdf.spec.js +++ b/test/pdf.spec.js @@ -16,10 +16,10 @@ const fs = require('fs'); const path = require('path'); -const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR} = require('../utils').testOptions(browserType); +const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR} = require('./utils').testOptions(browserType); -// Printing to pdf is currently only supported in headless -describe.skip(!HEADLESS)('Page.pdf', function() { +// Printing to pdf is currently only supported in headless chromium. +describe.skip(!(HEADLESS && CHROMIUM))('Page.pdf', function() { it('should be able to save file', async({page, server}) => { const outputFile = path.join(OUTPUT_DIR, 'output.pdf'); await page.pdf({path: outputFile}); @@ -27,3 +27,9 @@ describe.skip(!HEADLESS)('Page.pdf', function() { fs.unlinkSync(outputFile); }); }); + +describe.skip(CHROMIUM)('Page.pdf missing', function() { + it('should be able to save file', async({page, server}) => { + expect(page.pdf).toBe(undefined); + }); +}); diff --git a/test/test.config.js b/test/test.config.js index 1f5e98488e..2e7f723028 100644 --- a/test/test.config.js +++ b/test/test.config.js @@ -75,6 +75,7 @@ module.exports = { './autowaiting.spec.js', './click.spec.js', './cookies.spec.js', + './coverage.spec.js', './dialog.spec.js', './dispatchevent.spec.js', './download.spec.js', @@ -90,6 +91,7 @@ module.exports = { './navigation.spec.js', './network.spec.js', './page.spec.js', + './pdf.spec.js', './queryselector.spec.js', './screenshot.spec.js', './waittask.spec.js', @@ -105,8 +107,6 @@ module.exports = { { files: [ './chromium/chromium.spec.js', - './chromium/coverage.spec.js', - './chromium/pdf.spec.js', './chromium/session.spec.js', ], browsers: ['chromium'],