diff --git a/docs/src/api/class-androiddevice.md b/docs/src/api/class-androiddevice.md index 3f4b2896a7..5ad7c37ef0 100644 --- a/docs/src/api/class-androiddevice.md +++ b/docs/src/api/class-androiddevice.md @@ -100,7 +100,7 @@ Either a path to the apk file, or apk file content. Optional arguments to pass to the `shell:cmd package install` call. Defaults to `-r -t -S`. ## async method: AndroidDevice.launchBrowser -- returns: <[ChromiumBrowserContext]> +- returns: <[BrowserContext]> Launches Chrome browser on the device, and returns its persistent context. diff --git a/docs/src/api/class-browser.md b/docs/src/api/class-browser.md index 5a36590f3e..2ea1e3537f 100644 --- a/docs/src/api/class-browser.md +++ b/docs/src/api/class-browser.md @@ -117,6 +117,16 @@ print(len(browser.contexts())) # prints `1` Indicates that the browser is connected. +## async method: Browser.newBrowserCDPSession +* langs: js, python +- returns: <[CDPSession]> + +:::note +CDP Sessions are only supported on Chromium-based browsers. +::: + +Returns the newly created browser session. + ## async method: Browser.newContext - returns: <[BrowserContext]> @@ -189,6 +199,64 @@ testing frameworks should explicitly create [`method: Browser.newContext`] follo ### option: Browser.newPage.storageStatePath = %%-csharp-java-context-option-storage-state-path-%% +## async method: Browser.startTracing +* langs: js, python + +:::note +Tracing is only supported on Chromium-based browsers. +::: + +You can use [`method: Browser.startTracing`] and [`method: Browser.stopTracing`] to create a trace file that can +be opened in Chrome DevTools performance panel. + +```js +await browser.startTracing(page, {path: 'trace.json'}); +await page.goto('https://www.google.com'); +await browser.stopTracing(); +``` + +```python async +await browser.start_tracing(page, path="trace.json") +await page.goto("https://www.google.com") +await browser.stop_tracing() +``` + +```python sync +browser.start_tracing(page, path="trace.json") +page.goto("https://www.google.com") +browser.stop_tracing() +``` + +### param: Browser.startTracing.page +- `page` <[Page]> + +Optional, if specified, tracing includes screenshots of the given page. + +### option: Browser.startTracing.path +- `path` <[path]> + +A path to write the trace file to. + +### option: Browser.startTracing.screenshots +- `screenshots` <[boolean]> + +captures screenshots in the trace. + +### option: Browser.startTracing.categories +- `categories` <[Array]<[string]>> + +specify custom categories to use instead of default. + +## async method: Browser.stopTracing +* langs: js, python +- returns: <[Buffer]> + +:::note +Tracing is only supported on Chromium-based browsers. +::: + +Returns the buffer with trace data. + ## method: Browser.version - returns: <[string]> diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 6136cfbb82..91351e96eb 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -49,6 +49,29 @@ page.goto("https://example.com") context.close() ``` +## event: BrowserContext.backgroundPage +* langs: js, python +- argument: <[Page]> + +:::note +Only works with Chromium browser's persistent context. +::: + +Emitted when new background page is created in the context. + + +```js +const backgroundPage = await context.waitForEvent('backgroundpage'); +``` + +```python async +background_page = await context.wait_for_event("backgroundpage") +``` + +```python sync +background_page = context.wait_for_event("backgroundpage") +``` + ## event: BrowserContext.close - argument: <[BrowserContext]> @@ -101,6 +124,16 @@ Use [`method: Page.waitForLoadState`] to wait until the page gets to a particula cases). ::: +## event: BrowserContext.serviceWorker +* langs: js, python +- argument: <[Worker]> + +:::note +Service workers are only supported on Chromium-based browsers. +::: + +Emitted when new service worker is created in the context. + ## async method: BrowserContext.addCookies Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies can be @@ -199,6 +232,16 @@ Script to be evaluated in all pages in the browser context. Optional argument to pass to [`param: script`] (only supported when passing a function). +## method: BrowserContext.backgroundPages +* langs: js, python +- returns: <[Array]<[Page]>> + +:::note +Background pages are only supported on Chromium-based browsers. +::: + +All existing background pages in the context. + ## method: BrowserContext.browser - returns: <[null]|[Browser]> @@ -630,6 +673,21 @@ A permission or an array of permissions to grant. Permissions can be one of the The [origin] to grant permissions to, e.g. "https://example.com". +## async method: BrowserContext.newCDPSession +* langs: js, python +- returns: <[CDPSession]> + +:::note +CDP sessions are only supported on Chromium-based browsers. +::: + +Returns the newly created session. + +### param: BrowserContext.newCDPSession.page +- `page` <[Page]> + +Page to create new session for. + ## async method: BrowserContext.newPage - returns: <[Page]> @@ -742,6 +800,16 @@ handler function to route the request. handler function to route the request. +## method: BrowserContext.serviceWorkers +* langs: js, python +- returns: <[Array]<[Worker]>> + +:::note +Service workers are only supported on Chromium-based browsers. +::: + +All existing service workers in the context. + ## method: BrowserContext.setDefaultNavigationTimeout This setting will change the default maximum navigation time for the following methods and related shortcuts: diff --git a/docs/src/api/class-browsertype.md b/docs/src/api/class-browsertype.md index 559bbcd4bf..565b65cebb 100644 --- a/docs/src/api/class-browsertype.md +++ b/docs/src/api/class-browsertype.md @@ -181,7 +181,7 @@ Whether to run browser in headless mode. More details for ### option: BrowserType.launch.channel - `channel` <[BrowserChannel]<"chrome"|"chrome-beta"|"chrome-dev"|"chrome-canary"|"msedge"|"msedge-beta"|"msedge-dev"|"msedge-canary">> -Browser distribution channel. Read more about using [Google Chrome and Microsoft Edge](./browsers#google-chrome--microsoft-edge). +Browser distribution channel. Read more about using [Google Chrome and Microsoft Edge](./browsers.md#google-chrome--microsoft-edge). ### option: BrowserType.launch.executablePath - `executablePath` <[path]> diff --git a/docs/src/api/class-chromiumbrowser.md b/docs/src/api/class-chromiumbrowser.md deleted file mode 100644 index e9dc1cf655..0000000000 --- a/docs/src/api/class-chromiumbrowser.md +++ /dev/null @@ -1,73 +0,0 @@ -# class: ChromiumBrowser -* langs: js -* extends: [Browser] - -Chromium-specific features including Tracing, service worker support, etc. You can use [`method: -ChromiumBrowser.startTracing`] and [`method: ChromiumBrowser.stopTracing`] to create a trace file which can be -opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/). - -```js -await browser.startTracing(page, {path: 'trace.json'}); -await page.goto('https://www.google.com'); -await browser.stopTracing(); -``` - -[ChromiumBrowser] can also be used for testing Chrome Extensions. - -:::note -Extensions in Chrome / Chromium currently only work in non-headless mode. -::: - -The following is code for getting a handle to the [background page](https://developer.chrome.com/extensions/background_pages) of an extension whose source is located in `./my-extension`: -```js -const { chromium } = require('playwright'); - -(async () => { - const pathToExtension = require('path').join(__dirname, 'my-extension'); - const userDataDir = '/tmp/test-user-data-dir'; - const browserContext = await chromium.launchPersistentContext(userDataDir,{ - headless: false, - args: [ - `--disable-extensions-except=${pathToExtension}`, - `--load-extension=${pathToExtension}` - ] - }); - const backgroundPage = browserContext.backgroundPages()[0]; - // Test the background page as you would any other page. - await browserContext.close(); -})(); -``` - -## async method: ChromiumBrowser.newBrowserCDPSession -- returns: <[CDPSession]> - -Returns the newly created browser session. - -## async method: ChromiumBrowser.startTracing - -Only one trace can be active at a time per browser. - -### param: ChromiumBrowser.startTracing.page -- `page` <[Page]> - -Optional, if specified, tracing includes screenshots of the given page. - -### option: ChromiumBrowser.startTracing.path -- `path` <[path]> - -A path to write the trace file to. - -### option: ChromiumBrowser.startTracing.screenshots -- `screenshots` <[boolean]> - -captures screenshots in the trace. - -### option: ChromiumBrowser.startTracing.categories -- `categories` <[Array]<[string]>> - -specify custom categories to use instead of default. - -## async method: ChromiumBrowser.stopTracing -- returns: <[Buffer]> - -Returns the buffer with trace data. diff --git a/docs/src/api/class-chromiumbrowsercontext.md b/docs/src/api/class-chromiumbrowsercontext.md deleted file mode 100644 index 0ee89ed8c0..0000000000 --- a/docs/src/api/class-chromiumbrowsercontext.md +++ /dev/null @@ -1,51 +0,0 @@ -# class: ChromiumBrowserContext -* langs: js, python -* extends: [BrowserContext] - -Chromium-specific features including background pages, service worker support, etc. - -```js -const backgroundPage = await context.waitForEvent('backgroundpage'); -``` - -```python async -background_page = await context.wait_for_event("backgroundpage") -``` - -```python sync -background_page = context.wait_for_event("backgroundpage") -``` - -## event: ChromiumBrowserContext.backgroundPage -- argument: <[Page]> - -Emitted when new background page is created in the context. - -:::note -Only works with persistent context. -::: - -## event: ChromiumBrowserContext.serviceWorker -- argument: <[Worker]> - -Emitted when new service worker is created in the context. - -## method: ChromiumBrowserContext.backgroundPages -- returns: <[Array]<[Page]>> - -All existing background pages in the context. - -## async method: ChromiumBrowserContext.newCDPSession -- returns: <[CDPSession]> - -Returns the newly created session. - -### param: ChromiumBrowserContext.newCDPSession.page -- `page` <[Page]> - -Page to create new session for. - -## method: ChromiumBrowserContext.serviceWorkers -- returns: <[Array]<[Worker]>> - -All existing service workers in the context. diff --git a/docs/src/api/class-chromiumcoverage.md b/docs/src/api/class-coverage.md similarity index 85% rename from docs/src/api/class-chromiumcoverage.md rename to docs/src/api/class-coverage.md index c1929fe691..4aeaf2ec6a 100644 --- a/docs/src/api/class-chromiumcoverage.md +++ b/docs/src/api/class-coverage.md @@ -1,10 +1,14 @@ -# class: ChromiumCoverage +# class: Coverage * langs: js Coverage gathers information about parts of JavaScript and CSS that were used by the page. An example of using JavaScript coverage to produce Istanbul report for page load: +:::note +Coverage APIs are only supported on Chromium-based browsers. +::: + ```js const { chromium } = require('playwright'); const v8toIstanbul = require('v8-to-istanbul'); @@ -25,16 +29,16 @@ const v8toIstanbul = require('v8-to-istanbul'); })(); ``` -## async method: ChromiumCoverage.startCSSCoverage +## async method: Coverage.startCSSCoverage Returns coverage is started -### option: ChromiumCoverage.startCSSCoverage.resetOnNavigation +### option: Coverage.startCSSCoverage.resetOnNavigation - `resetOnNavigation` <[boolean]> Whether to reset coverage on every navigation. Defaults to `true`. -## async method: ChromiumCoverage.startJSCoverage +## async method: Coverage.startJSCoverage Returns coverage is started @@ -44,17 +48,17 @@ on the page using `eval` or `new Function`. If [`option: reportAnonymousScripts` will have `__playwright_evaluation_script__` as their URL. ::: -### option: ChromiumCoverage.startJSCoverage.resetOnNavigation +### option: Coverage.startJSCoverage.resetOnNavigation - `resetOnNavigation` <[boolean]> Whether to reset coverage on every navigation. Defaults to `true`. -### option: ChromiumCoverage.startJSCoverage.reportAnonymousScripts +### option: Coverage.startJSCoverage.reportAnonymousScripts - `reportAnonymousScripts` <[boolean]> Whether anonymous scripts generated by the page should be reported. Defaults to `false`. -## async method: ChromiumCoverage.stopCSSCoverage +## async method: Coverage.stopCSSCoverage - returns: <[Array]<[Object]>> - `url` <[string]> StyleSheet URL - `text` <[string]> StyleSheet content, if available. @@ -68,7 +72,7 @@ Returns the array of coverage reports for all stylesheets CSS Coverage doesn't include dynamically injected style tags without sourceURLs. ::: -## async method: ChromiumCoverage.stopJSCoverage +## async method: Coverage.stopJSCoverage - returns: <[Array]<[Object]>> - `url` <[string]> Script URL - `scriptId` <[string]> Script ID diff --git a/docs/src/api/class-download.md b/docs/src/api/class-download.md index 055dbaefa7..1a1c7afa96 100644 --- a/docs/src/api/class-download.md +++ b/docs/src/api/class-download.md @@ -73,7 +73,7 @@ Returns download error if any. Will wait for the download to finish if necessary - returns: <[null]|[path]> Returns path to the downloaded file in case of successful download. The method will -wait for the download to finish if necessary. The method throws when connected remotely via [`method: BrowserType.connect`]. +wait for the download to finish if necessary. The method throws when connected remotely. ## async method: Download.saveAs diff --git a/docs/src/api/class-firefoxbrowser.md b/docs/src/api/class-firefoxbrowser.md deleted file mode 100644 index d9d90f1695..0000000000 --- a/docs/src/api/class-firefoxbrowser.md +++ /dev/null @@ -1,5 +0,0 @@ -# class: FirefoxBrowser -* langs: js -* extends: [Browser] - -Firefox browser instance does not expose Firefox-specific features. diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 6750fe738b..ee31cfd857 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -597,10 +597,13 @@ Get the browser context that the page belongs to. ## property: Page.coverage * langs: js -- type: <[null]|[ChromiumCoverage]> +- type: <[Coverage]> -Browser-specific Coverage implementation, only available for Chromium atm. See -[ChromiumCoverage](#class-chromiumcoverage) for more details. +:::note +Only available for Chromium atm. +::: + +Browser-specific Coverage implementation. See [Coverage](#class-coverage) for more details. ## async method: Page.dblclick diff --git a/docs/src/api/class-playwright.md b/docs/src/api/class-playwright.md index 8ccf739686..e0c6984606 100644 --- a/docs/src/api/class-playwright.md +++ b/docs/src/api/class-playwright.md @@ -68,7 +68,7 @@ with sync_playwright() as playwright: ## property: Playwright.chromium - type: <[BrowserType]> -This object can be used to launch or connect to Chromium, returning instances of [ChromiumBrowser]. +This object can be used to launch or connect to Chromium, returning instances of [Browser]. ## property: Playwright.devices * langs: js, python @@ -169,7 +169,7 @@ except TimeoutError as e: ## property: Playwright.firefox - type: <[BrowserType]> -This object can be used to launch or connect to Firefox, returning instances of [FirefoxBrowser]. +This object can be used to launch or connect to Firefox, returning instances of [Browser]. ## property: Playwright.selectors - type: <[Selectors]> @@ -180,4 +180,4 @@ Selectors can be used to install custom selector engines. See ## property: Playwright.webkit - type: <[BrowserType]> -This object can be used to launch or connect to WebKit, returning instances of [WebKitBrowser]. +This object can be used to launch or connect to WebKit, returning instances of [Browser]. diff --git a/docs/src/api/class-video.md b/docs/src/api/class-video.md index 626a9183cd..7b4de9ac82 100644 --- a/docs/src/api/class-video.md +++ b/docs/src/api/class-video.md @@ -26,7 +26,7 @@ Deletes the video file. Will wait for the video to finish if necessary. - returns: <[path]> Returns the file system path this video will be recorded to. The video is guaranteed to be written to the filesystem -upon closing the browser context. This method throws when connected remotely via [`method: BrowserType.connect`]. +upon closing the browser context. This method throws when connected remotely. ## async method: Video.saveAs diff --git a/docs/src/api/class-webkitbrowser.md b/docs/src/api/class-webkitbrowser.md deleted file mode 100644 index f4c7187a2d..0000000000 --- a/docs/src/api/class-webkitbrowser.md +++ /dev/null @@ -1,5 +0,0 @@ -# class: WebKitBrowser -* langs: js -* extends: [Browser] - -WebKit browser instance does not expose WebKit-specific features. diff --git a/index.d.ts b/index.d.ts index 4c0c836d4d..9277d34f15 100644 --- a/index.d.ts +++ b/index.d.ts @@ -17,8 +17,8 @@ import * as types from './types/types'; export * from './types/types'; -export const webkit: types.BrowserType; -export const chromium: types.BrowserType; -export const firefox: types.BrowserType; +export const webkit: types.BrowserType; +export const chromium: types.BrowserType; +export const firefox: types.BrowserType; export const _electron: types.Electron; export const _android: types.Android; diff --git a/packages/common/index.d.ts b/packages/common/index.d.ts index edce00e135..227bc8b780 100644 --- a/packages/common/index.d.ts +++ b/packages/common/index.d.ts @@ -17,8 +17,8 @@ import * as types from './types/types'; export * from './types/types'; -export const chromium: types.BrowserType; -export const firefox: types.BrowserType; -export const webkit: types.BrowserType; +export const chromium: types.BrowserType; +export const firefox: types.BrowserType; +export const webkit: types.BrowserType; export const _electron: types.Electron; export const _android: types.Android; diff --git a/src/client/android.ts b/src/client/android.ts index 3afc651a8d..cda58c9a7a 100644 --- a/src/client/android.ts +++ b/src/client/android.ts @@ -27,7 +27,6 @@ import { Page } from './page'; import { TimeoutSettings } from '../utils/timeoutSettings'; import { Waiter } from './waiter'; import { EventEmitter } from 'events'; -import { ChromiumBrowserContext } from './chromiumBrowserContext'; type Direction = 'down' | 'up' | 'left' | 'right'; type SpeedOptions = { speed?: number }; @@ -228,11 +227,11 @@ export class AndroidDevice extends ChannelOwner { + async launchBrowser(options: types.BrowserContextOptions & { pkg?: string } = {}): Promise { return this._wrapApiCall('androidDevice.launchBrowser', async (channel: channels.AndroidDeviceChannel) => { const contextOptions = await prepareBrowserContextParams(options); const { context } = await channel.launchBrowser(contextOptions); - return BrowserContext.from(context) as ChromiumBrowserContext; + return BrowserContext.from(context) as BrowserContext; }); } diff --git a/src/client/api.ts b/src/client/api.ts index 968ef5a7ab..fa0a7a165a 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -21,6 +21,7 @@ export { BrowserContext } from './browserContext'; export { BrowserServer } from './browserType'; export { BrowserType } from './browserType'; export { ConsoleMessage } from './consoleMessage'; +export { Coverage } from './coverage'; export { Dialog } from './dialog'; export { Download } from './download'; export { Electron, ElectronApplication } from './electron'; @@ -36,13 +37,5 @@ export { Page } from './page'; export { Selectors } from './selectors'; export { Video } from './video'; export { Worker } from './worker'; - -export { ChromiumBrowser } from './chromiumBrowser'; -export { ChromiumBrowserContext } from './chromiumBrowserContext'; -export { ChromiumCoverage } from './chromiumCoverage'; export { CDPSession } from './cdpSession'; - -export { WebKitBrowser } from './webkitBrowser'; - -export { FirefoxBrowser } from './firefoxBrowser'; export { Playwright } from './playwright'; diff --git a/src/client/browser.ts b/src/client/browser.ts index 44751f15f2..b76d17c814 100644 --- a/src/client/browser.ts +++ b/src/client/browser.ts @@ -22,12 +22,14 @@ import { Events } from './events'; import { BrowserContextOptions } from './types'; import { isSafeCloseError } from '../utils/errors'; import * as api from '../../types/types'; +import { CDPSession } from './cdpSession'; export class Browser extends ChannelOwner implements api.Browser { readonly _contexts = new Set(); private _isConnected = true; private _closedPromise: Promise; _isRemote = false; + readonly _name: string; static from(browser: channels.BrowserChannel): Browser { return (browser as any)._object; @@ -39,6 +41,7 @@ export class Browser extends ChannelOwner this._didClose()); this._closedPromise = new Promise(f => this.once(Events.Browser.Disconnected, f)); } @@ -76,6 +79,24 @@ export class Browser extends ChannelOwner { + return this._wrapApiCall('browser.newBrowserCDPSession', async (channel: channels.BrowserChannel) => { + return CDPSession.from((await channel.newBrowserCDPSession()).session); + }); + } + + async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { + return this._wrapApiCall('browser.startTracing', async (channel: channels.BrowserChannel) => { + await channel.startTracing({ ...options, page: page ? page._channel : undefined }); + }); + } + + async stopTracing(): Promise { + return this._wrapApiCall('browser.stopTracing', async (channel: channels.BrowserChannel) => { + return Buffer.from((await channel.stopTracing()).binary, 'base64'); + }); + } + async close(): Promise { try { await this._wrapApiCall('browser.close', async (channel: channels.BrowserChannel) => { diff --git a/src/client/browserContext.ts b/src/client/browserContext.ts index e849f39182..23ba426f27 100644 --- a/src/client/browserContext.ts +++ b/src/client/browserContext.ts @@ -23,6 +23,7 @@ import fs from 'fs'; import { ChannelOwner } from './channelOwner'; import { deprecate, evaluationScript, urlMatches } from './clientHelper'; import { Browser } from './browser'; +import { Worker } from './worker'; import { Events } from './events'; import { TimeoutSettings } from '../utils/timeoutSettings'; import { Waiter } from './waiter'; @@ -31,6 +32,7 @@ import { isUnderTest, headersObjectToArray, mkdirIfNeeded } from '../utils/utils import { isSafeCloseError } from '../utils/errors'; import * as api from '../../types/types'; import * as structs from '../../types/structs'; +import { CDPSession } from './cdpSession'; const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs)); const fsReadFileAsync = util.promisify(fs.readFile.bind(fs)); @@ -47,6 +49,10 @@ export class BrowserContext extends ChannelOwner(); + readonly _serviceWorkers = new Set(); + readonly _isChromium: boolean; + static from(context: channels.BrowserContextChannel): BrowserContext { return (context as any)._object; } @@ -59,11 +65,23 @@ export class BrowserContext extends ChannelOwner this._onBinding(BindingCall.from(binding))); 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._channel.on('backgroundPage', ({ page }) => { + const backgroundPage = Page.from(page); + this._backgroundPages.add(backgroundPage); + this.emit(Events.BrowserContext.BackgroundPage, backgroundPage); + }); + this._channel.on('serviceWorker', ({worker}) => { + const serviceWorker = Worker.from(worker); + serviceWorker._context = this; + this._serviceWorkers.add(serviceWorker); + this.emit(Events.BrowserContext.ServiceWorker, serviceWorker); + }); this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f)); } @@ -238,6 +256,21 @@ export class BrowserContext extends ChannelOwner { + return this._wrapApiCall('browserContext.newCDPSession', async (channel: channels.BrowserContextChannel) => { + const result = await channel.newCDPSession({ page: page._channel }); + return CDPSession.from(result.session); + }); + } + _onClose() { if (this._browser) this._browser._contexts.delete(this); diff --git a/src/client/chromiumBrowser.ts b/src/client/chromiumBrowser.ts deleted file mode 100644 index cc9052a891..0000000000 --- a/src/client/chromiumBrowser.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 * as channels from '../protocol/channels'; -import { Page } from './page'; -import { CDPSession } from './cdpSession'; -import { Browser } from './browser'; -import * as api from '../../types/types'; -import { ChromiumBrowserContext } from './chromiumBrowserContext'; -import { BrowserContextOptions } from './types'; - -export class ChromiumBrowser extends Browser implements api.ChromiumBrowser { - contexts(): ChromiumBrowserContext[] { - return super.contexts() as ChromiumBrowserContext[]; - } - - newContext(options?: BrowserContextOptions): Promise { - return super.newContext(options) as Promise; - } - - async newBrowserCDPSession(): Promise { - return this._wrapApiCall('chromiumBrowser.newBrowserCDPSession', async (channel: channels.BrowserChannel) => { - return CDPSession.from((await channel.crNewBrowserCDPSession()).session); - }); - } - - async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { - return this._wrapApiCall('chromiumBrowser.startTracing', async (channel: channels.BrowserChannel) => { - await channel.crStartTracing({ ...options, page: page ? page._channel : undefined }); - }); - } - - async stopTracing(): Promise { - return this._wrapApiCall('chromiumBrowser.stopTracing', async (channel: channels.BrowserChannel) => { - return Buffer.from((await channel.crStopTracing()).binary, 'base64'); - }); - } -} diff --git a/src/client/chromiumBrowserContext.ts b/src/client/chromiumBrowserContext.ts deleted file mode 100644 index 575e2bae5c..0000000000 --- a/src/client/chromiumBrowserContext.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * 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 * as channels from '../protocol/channels'; -import { ChannelOwner } from './channelOwner'; -import { CDPSession } from './cdpSession'; -import { Events } from './events'; -import { Worker } from './worker'; -import { BrowserContext } from './browserContext'; -import * as api from '../../types/types'; - -export class ChromiumBrowserContext extends BrowserContext implements api.ChromiumBrowserContext { - _backgroundPages = new Set(); - _serviceWorkers = new Set(); - _isChromium = true; - - constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.BrowserContextInitializer) { - super(parent, type, guid, initializer); - this._channel.on('crBackgroundPage', ({ page }) => { - const backgroundPage = Page.from(page); - this._backgroundPages.add(backgroundPage); - this.emit(Events.ChromiumBrowserContext.BackgroundPage, backgroundPage); - }); - this._channel.on('crServiceWorker', ({worker}) => { - const serviceWorker = Worker.from(worker); - serviceWorker._context = this; - this._serviceWorkers.add(serviceWorker); - this.emit(Events.ChromiumBrowserContext.ServiceWorker, serviceWorker); - }); - } - - backgroundPages(): Page[] { - return [...this._backgroundPages]; - } - - serviceWorkers(): Worker[] { - return [...this._serviceWorkers]; - } - - async newCDPSession(page: Page): Promise { - return this._wrapApiCall('chromiumBrowserContext.newCDPSession', async (channel: channels.BrowserContextChannel) => { - const result = await channel.crNewCDPSession({ page: page._channel }); - return CDPSession.from(result.session); - }); - } -} diff --git a/src/client/connection.ts b/src/client/connection.ts index bebab7e206..d6c5712e8b 100644 --- a/src/client/connection.ts +++ b/src/client/connection.ts @@ -31,11 +31,7 @@ import { CDPSession } from './cdpSession'; import { Playwright } from './playwright'; import { Electron, ElectronApplication } from './electron'; import * as channels from '../protocol/channels'; -import { ChromiumBrowser } from './chromiumBrowser'; -import { ChromiumBrowserContext } from './chromiumBrowserContext'; import { Stream } from './stream'; -import { WebKitBrowser } from './webkitBrowser'; -import { FirefoxBrowser } from './firefoxBrowser'; import { debugLogger } from '../utils/debugLogger'; import { SelectorsOwner } from './selectors'; import { isUnderTest } from '../utils/utils'; @@ -162,26 +158,12 @@ export class Connection { case 'BindingCall': result = new BindingCall(parent, type, guid, initializer); break; - case 'Browser': { - const browserName = (initializer as channels.BrowserInitializer).name; - if (browserName === 'chromium') - result = new ChromiumBrowser(parent, type, guid, initializer); - else if (browserName === 'webkit') - result = new WebKitBrowser(parent, type, guid, initializer); - else if (browserName === 'firefox') - result = new FirefoxBrowser(parent, type, guid, initializer); - else - result = new Browser(parent, type, guid, initializer); + case 'Browser': + result = new Browser(parent, type, guid, initializer); break; - } - case 'BrowserContext': { - const {isChromium} = (initializer as channels.BrowserContextInitializer); - if (isChromium) - result = new ChromiumBrowserContext(parent, type, guid, initializer); - else - result = new BrowserContext(parent, type, guid, initializer); + case 'BrowserContext': + result = new BrowserContext(parent, type, guid, initializer); break; - } case 'BrowserType': result = new BrowserType(parent, type, guid, initializer); break; diff --git a/src/client/chromiumCoverage.ts b/src/client/coverage.ts similarity index 59% rename from src/client/chromiumCoverage.ts rename to src/client/coverage.ts index 1420eaf052..358609843f 100644 --- a/src/client/chromiumCoverage.ts +++ b/src/client/coverage.ts @@ -17,26 +17,26 @@ import * as channels from '../protocol/channels'; import * as api from '../../types/types'; -export class ChromiumCoverage implements api.ChromiumCoverage { +export class Coverage implements api.Coverage { private _channel: channels.PageChannel; constructor(channel: channels.PageChannel) { this._channel = channel; } - async startJSCoverage(options: channels.PageCrStartJSCoverageOptions = {}) { - await this._channel.crStartJSCoverage(options); + async startJSCoverage(options: channels.PageStartJSCoverageOptions = {}) { + await this._channel.startJSCoverage(options); } - async stopJSCoverage(): Promise { - return (await this._channel.crStopJSCoverage()).entries; + async stopJSCoverage(): Promise { + return (await this._channel.stopJSCoverage()).entries; } - async startCSSCoverage(options: channels.PageCrStartCSSCoverageOptions = {}) { - await this._channel.crStartCSSCoverage(options); + async startCSSCoverage(options: channels.PageStartCSSCoverageOptions = {}) { + await this._channel.startCSSCoverage(options); } - async stopCSSCoverage(): Promise { - return (await this._channel.crStopCSSCoverage()).entries; + async stopCSSCoverage(): Promise { + return (await this._channel.stopCSSCoverage()).entries; } } diff --git a/src/client/electron.ts b/src/client/electron.ts index 2be69fcd5c..157b54f593 100644 --- a/src/client/electron.ts +++ b/src/client/electron.ts @@ -20,7 +20,6 @@ import * as channels from '../protocol/channels'; import { TimeoutSettings } from '../utils/timeoutSettings'; import { BrowserContext } from './browserContext'; import { ChannelOwner } from './channelOwner'; -import type { ChromiumBrowserContext } from './chromiumBrowserContext'; import { envObjectToArray } from './clientHelper'; import { Events } from './events'; import { JSHandle, parseResult, serializeArgument } from './jsHandle'; @@ -90,8 +89,8 @@ export class ElectronApplication extends ChannelOwner Promise; readonly _bindings = new Map any>(); readonly _timeoutSettings: TimeoutSettings; @@ -145,12 +143,7 @@ export class Page extends ChannelOwner this.emit(Events.Page.WebSocket, WebSocket.from(webSocket))); this._channel.on('worker', ({ worker }) => this._onWorker(Worker.from(worker))); - if ((this._browserContext as ChromiumBrowserContext)._isChromium) { - this.coverage = new ChromiumCoverage(this._channel); - this.pdf = options => this._pdf(options); - } else { - this.pdf = undefined as any; - } + this.coverage = new Coverage(this._channel); this._closedOrCrashedPromise = Promise.race([ new Promise(f => this.once(Events.Page.Close, f)), @@ -670,7 +663,7 @@ export class Page extends ChannelOwner { + async pdf(options: PDFOptions = {}): Promise { return this._wrapApiCall('page.pdf', async (channel: channels.PageChannel) => { const transportOptions: channels.PagePdfParams = { ...options } as channels.PagePdfParams; if (transportOptions.margin) diff --git a/src/client/webkitBrowser.ts b/src/client/webkitBrowser.ts deleted file mode 100644 index 98a0d16f97..0000000000 --- a/src/client/webkitBrowser.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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 { Browser } from './browser'; -import * as api from '../../types/types'; - -export class WebKitBrowser extends Browser implements api.WebKitBrowser { -} diff --git a/src/client/worker.ts b/src/client/worker.ts index 8022032792..1c4076cfb2 100644 --- a/src/client/worker.ts +++ b/src/client/worker.ts @@ -20,7 +20,6 @@ import { ChannelOwner } from './channelOwner'; import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle'; import { Page } from './page'; import { BrowserContext } from './browserContext'; -import { ChromiumBrowserContext } from './chromiumBrowserContext'; import * as api from '../../types/types'; import * as structs from '../../types/structs'; @@ -38,7 +37,7 @@ export class Worker extends ChannelOwner this._dispatchEvent('crBackgroundPage', { page: new PageDispatcher(this._scope, page) })); + this._dispatchEvent('backgroundPage', { page: new PageDispatcher(this._scope, page) }); + context.on(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: new PageDispatcher(this._scope, page) })); for (const serviceWorker of (context as CRBrowserContext).serviceWorkers()) - this._dispatchEvent('crServiceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker)}); - context.on(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('crServiceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) })); + this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker)}); + context.on(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) })); } } @@ -151,7 +151,7 @@ export class BrowserContextDispatcher extends Dispatcher { + async newCDPSession(params: channels.BrowserContextNewCDPSessionParams): Promise { if (!this._object._browser.options.isChromium) throw new Error(`CDP session is only available in Chromium`); const crBrowserContext = this._object as CRBrowserContext; diff --git a/src/dispatchers/browserDispatcher.ts b/src/dispatchers/browserDispatcher.ts index 5d711d6f50..f7cf8bc6be 100644 --- a/src/dispatchers/browserDispatcher.ts +++ b/src/dispatchers/browserDispatcher.ts @@ -45,21 +45,21 @@ export class BrowserDispatcher extends Dispatcher { + async newBrowserCDPSession(): Promise { if (!this._object.options.isChromium) throw new Error(`CDP session is only available in Chromium`); const crBrowser = this._object as CRBrowser; return { session: new CDPSessionDispatcher(this._scope, await crBrowser.newBrowserCDPSession()) }; } - async crStartTracing(params: channels.BrowserCrStartTracingParams): Promise { + async startTracing(params: channels.BrowserStartTracingParams): Promise { if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`); const crBrowser = this._object as CRBrowser; await crBrowser.startTracing(params.page ? (params.page as PageDispatcher)._object : undefined, params); } - async crStopTracing(): Promise { + async stopTracing(): Promise { if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`); const crBrowser = this._object as CRBrowser; diff --git a/src/dispatchers/pageDispatcher.ts b/src/dispatchers/pageDispatcher.ts index 580c7599ba..f8893f2886 100644 --- a/src/dispatchers/pageDispatcher.ts +++ b/src/dispatchers/pageDispatcher.ts @@ -217,22 +217,22 @@ export class PageDispatcher extends Dispatcher i await this._page.bringToFront(); } - async crStartJSCoverage(params: channels.PageCrStartJSCoverageParams, metadata: CallMetadata): Promise { + async startJSCoverage(params: channels.PageStartJSCoverageParams, metadata: CallMetadata): Promise { const coverage = this._page.coverage as CRCoverage; await coverage.startJSCoverage(params); } - async crStopJSCoverage(params: channels.PageCrStopJSCoverageParams, metadata: CallMetadata): Promise { + async stopJSCoverage(params: channels.PageStopJSCoverageParams, metadata: CallMetadata): Promise { const coverage = this._page.coverage as CRCoverage; return { entries: await coverage.stopJSCoverage() }; } - async crStartCSSCoverage(params: channels.PageCrStartCSSCoverageParams, metadata: CallMetadata): Promise { + async startCSSCoverage(params: channels.PageStartCSSCoverageParams, metadata: CallMetadata): Promise { const coverage = this._page.coverage as CRCoverage; await coverage.startCSSCoverage(params); } - async crStopCSSCoverage(params: channels.PageCrStopCSSCoverageParams, metadata: CallMetadata): Promise { + async stopCSSCoverage(params: channels.PageStopCSSCoverageParams, metadata: CallMetadata): Promise { const coverage = this._page.coverage as CRCoverage; return { entries: await coverage.stopCSSCoverage() }; } diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index 37a8cb643e..58592fa0e3 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -430,9 +430,9 @@ export interface BrowserChannel extends Channel { on(event: 'close', callback: (params: BrowserCloseEvent) => void): this; close(params?: BrowserCloseParams, metadata?: Metadata): Promise; newContext(params: BrowserNewContextParams, metadata?: Metadata): Promise; - crNewBrowserCDPSession(params?: BrowserCrNewBrowserCDPSessionParams, metadata?: Metadata): Promise; - crStartTracing(params: BrowserCrStartTracingParams, metadata?: Metadata): Promise; - crStopTracing(params?: BrowserCrStopTracingParams, metadata?: Metadata): Promise; + newBrowserCDPSession(params?: BrowserNewBrowserCDPSessionParams, metadata?: Metadata): Promise; + startTracing(params: BrowserStartTracingParams, metadata?: Metadata): Promise; + stopTracing(params?: BrowserStopTracingParams, metadata?: Metadata): Promise; } export type BrowserCloseEvent = {}; export type BrowserCloseParams = {}; @@ -556,27 +556,27 @@ export type BrowserNewContextOptions = { export type BrowserNewContextResult = { context: BrowserContextChannel, }; -export type BrowserCrNewBrowserCDPSessionParams = {}; -export type BrowserCrNewBrowserCDPSessionOptions = {}; -export type BrowserCrNewBrowserCDPSessionResult = { +export type BrowserNewBrowserCDPSessionParams = {}; +export type BrowserNewBrowserCDPSessionOptions = {}; +export type BrowserNewBrowserCDPSessionResult = { session: CDPSessionChannel, }; -export type BrowserCrStartTracingParams = { +export type BrowserStartTracingParams = { page?: PageChannel, path?: string, screenshots?: boolean, categories?: string[], }; -export type BrowserCrStartTracingOptions = { +export type BrowserStartTracingOptions = { page?: PageChannel, path?: string, screenshots?: boolean, categories?: string[], }; -export type BrowserCrStartTracingResult = void; -export type BrowserCrStopTracingParams = {}; -export type BrowserCrStopTracingOptions = {}; -export type BrowserCrStopTracingResult = { +export type BrowserStartTracingResult = void; +export type BrowserStopTracingParams = {}; +export type BrowserStopTracingOptions = {}; +export type BrowserStopTracingResult = { binary: Binary, }; @@ -590,8 +590,8 @@ export interface BrowserContextChannel extends Channel { on(event: 'page', callback: (params: BrowserContextPageEvent) => void): this; on(event: 'route', callback: (params: BrowserContextRouteEvent) => void): this; on(event: 'video', callback: (params: BrowserContextVideoEvent) => void): this; - on(event: 'crBackgroundPage', callback: (params: BrowserContextCrBackgroundPageEvent) => void): this; - on(event: 'crServiceWorker', callback: (params: BrowserContextCrServiceWorkerEvent) => void): this; + on(event: 'backgroundPage', callback: (params: BrowserContextBackgroundPageEvent) => void): this; + on(event: 'serviceWorker', callback: (params: BrowserContextServiceWorkerEvent) => void): this; addCookies(params: BrowserContextAddCookiesParams, metadata?: Metadata): Promise; addInitScript(params: BrowserContextAddInitScriptParams, metadata?: Metadata): Promise; clearCookies(params?: BrowserContextClearCookiesParams, metadata?: Metadata): Promise; @@ -611,7 +611,7 @@ export interface BrowserContextChannel extends Channel { storageState(params?: BrowserContextStorageStateParams, metadata?: Metadata): Promise; pause(params?: BrowserContextPauseParams, metadata?: Metadata): Promise; recorderSupplementEnable(params: BrowserContextRecorderSupplementEnableParams, metadata?: Metadata): Promise; - crNewCDPSession(params: BrowserContextCrNewCDPSessionParams, metadata?: Metadata): Promise; + newCDPSession(params: BrowserContextNewCDPSessionParams, metadata?: Metadata): Promise; } export type BrowserContextBindingCallEvent = { binding: BindingCallChannel, @@ -627,10 +627,10 @@ export type BrowserContextRouteEvent = { export type BrowserContextVideoEvent = { artifact: ArtifactChannel, }; -export type BrowserContextCrBackgroundPageEvent = { +export type BrowserContextBackgroundPageEvent = { page: PageChannel, }; -export type BrowserContextCrServiceWorkerEvent = { +export type BrowserContextServiceWorkerEvent = { worker: WorkerChannel, }; export type BrowserContextAddCookiesParams = { @@ -779,13 +779,13 @@ export type BrowserContextRecorderSupplementEnableOptions = { outputFile?: string, }; export type BrowserContextRecorderSupplementEnableResult = void; -export type BrowserContextCrNewCDPSessionParams = { +export type BrowserContextNewCDPSessionParams = { page: PageChannel, }; -export type BrowserContextCrNewCDPSessionOptions = { +export type BrowserContextNewCDPSessionOptions = { }; -export type BrowserContextCrNewCDPSessionResult = { +export type BrowserContextNewCDPSessionResult = { session: CDPSessionChannel, }; @@ -847,10 +847,10 @@ export interface PageChannel extends Channel { touchscreenTap(params: PageTouchscreenTapParams, metadata?: Metadata): Promise; accessibilitySnapshot(params: PageAccessibilitySnapshotParams, metadata?: Metadata): Promise; pdf(params: PagePdfParams, metadata?: Metadata): Promise; - crStartJSCoverage(params: PageCrStartJSCoverageParams, metadata?: Metadata): Promise; - crStopJSCoverage(params?: PageCrStopJSCoverageParams, metadata?: Metadata): Promise; - crStartCSSCoverage(params: PageCrStartCSSCoverageParams, metadata?: Metadata): Promise; - crStopCSSCoverage(params?: PageCrStopCSSCoverageParams, metadata?: Metadata): Promise; + startJSCoverage(params: PageStartJSCoverageParams, metadata?: Metadata): Promise; + stopJSCoverage(params?: PageStopJSCoverageParams, metadata?: Metadata): Promise; + startCSSCoverage(params: PageStartCSSCoverageParams, metadata?: Metadata): Promise; + stopCSSCoverage(params?: PageStopCSSCoverageParams, metadata?: Metadata): Promise; bringToFront(params?: PageBringToFrontParams, metadata?: Metadata): Promise; } export type PageBindingCallEvent = { @@ -1185,18 +1185,18 @@ export type PagePdfOptions = { export type PagePdfResult = { pdf: Binary, }; -export type PageCrStartJSCoverageParams = { +export type PageStartJSCoverageParams = { resetOnNavigation?: boolean, reportAnonymousScripts?: boolean, }; -export type PageCrStartJSCoverageOptions = { +export type PageStartJSCoverageOptions = { resetOnNavigation?: boolean, reportAnonymousScripts?: boolean, }; -export type PageCrStartJSCoverageResult = void; -export type PageCrStopJSCoverageParams = {}; -export type PageCrStopJSCoverageOptions = {}; -export type PageCrStopJSCoverageResult = { +export type PageStartJSCoverageResult = void; +export type PageStopJSCoverageParams = {}; +export type PageStopJSCoverageOptions = {}; +export type PageStopJSCoverageResult = { entries: { url: string, scriptId: string, @@ -1212,16 +1212,16 @@ export type PageCrStopJSCoverageResult = { }[], }[], }; -export type PageCrStartCSSCoverageParams = { +export type PageStartCSSCoverageParams = { resetOnNavigation?: boolean, }; -export type PageCrStartCSSCoverageOptions = { +export type PageStartCSSCoverageOptions = { resetOnNavigation?: boolean, }; -export type PageCrStartCSSCoverageResult = void; -export type PageCrStopCSSCoverageParams = {}; -export type PageCrStopCSSCoverageOptions = {}; -export type PageCrStopCSSCoverageResult = { +export type PageStartCSSCoverageResult = void; +export type PageStopCSSCoverageParams = {}; +export type PageStopCSSCoverageOptions = {}; +export type PageStopCSSCoverageResult = { entries: { url: string, text?: string, diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 368eb0ac78..bfef85c51f 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -462,11 +462,11 @@ Browser: returns: context: BrowserContext - crNewBrowserCDPSession: + newBrowserCDPSession: returns: session: CDPSession - crStartTracing: + startTracing: parameters: page: Page? path: string? @@ -475,7 +475,7 @@ Browser: type: array? items: string - crStopTracing: + stopTracing: returns: binary: binary @@ -598,7 +598,7 @@ BrowserContext: saveStorage: string? outputFile: string? - crNewCDPSession: + newCDPSession: parameters: page: Page returns: @@ -625,11 +625,11 @@ BrowserContext: parameters: artifact: Artifact - crBackgroundPage: + backgroundPage: parameters: page: Page - crServiceWorker: + serviceWorker: parameters: worker: Worker @@ -859,12 +859,12 @@ Page: returns: pdf: binary - crStartJSCoverage: + startJSCoverage: parameters: resetOnNavigation: boolean? reportAnonymousScripts: boolean? - crStopJSCoverage: + stopJSCoverage: returns: entries: type: array @@ -890,11 +890,11 @@ Page: endOffset: number count: number - crStartCSSCoverage: + startCSSCoverage: parameters: resetOnNavigation: boolean? - crStopCSSCoverage: + stopCSSCoverage: returns: entries: type: array diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index 761173e66a..76171b9496 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -312,14 +312,14 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { origins: tOptional(tArray(tType('OriginStorage'))), })), }); - scheme.BrowserCrNewBrowserCDPSessionParams = tOptional(tObject({})); - scheme.BrowserCrStartTracingParams = tObject({ + scheme.BrowserNewBrowserCDPSessionParams = tOptional(tObject({})); + scheme.BrowserStartTracingParams = tObject({ page: tOptional(tChannel('Page')), path: tOptional(tString), screenshots: tOptional(tBoolean), categories: tOptional(tArray(tString)), }); - scheme.BrowserCrStopTracingParams = tOptional(tObject({})); + scheme.BrowserStopTracingParams = tOptional(tObject({})); scheme.BrowserContextAddCookiesParams = tObject({ cookies: tArray(tType('SetNetworkCookie')), }); @@ -381,7 +381,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { saveStorage: tOptional(tString), outputFile: tOptional(tString), }); - scheme.BrowserContextCrNewCDPSessionParams = tObject({ + scheme.BrowserContextNewCDPSessionParams = tObject({ page: tChannel('Page'), }); scheme.PageSetDefaultNavigationTimeoutNoReplyParams = tObject({ @@ -504,15 +504,15 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { right: tOptional(tString), })), }); - scheme.PageCrStartJSCoverageParams = tObject({ + scheme.PageStartJSCoverageParams = tObject({ resetOnNavigation: tOptional(tBoolean), reportAnonymousScripts: tOptional(tBoolean), }); - scheme.PageCrStopJSCoverageParams = tOptional(tObject({})); - scheme.PageCrStartCSSCoverageParams = tObject({ + scheme.PageStopJSCoverageParams = tOptional(tObject({})); + scheme.PageStartCSSCoverageParams = tObject({ resetOnNavigation: tOptional(tBoolean), }); - scheme.PageCrStopCSSCoverageParams = tOptional(tObject({})); + scheme.PageStopCSSCoverageParams = tOptional(tObject({})); scheme.PageBringToFrontParams = tOptional(tObject({})); scheme.FrameEvalOnSelectorParams = tObject({ selector: tString, diff --git a/test/channels.spec.ts b/test/channels.spec.ts index db75ac28bc..d6441405ce 100644 --- a/test/channels.spec.ts +++ b/test/channels.spec.ts @@ -17,7 +17,6 @@ import domain from 'domain'; import { folio } from './fixtures'; -import type { ChromiumBrowser } from '..'; const { it, expect } = folio; it('should work', async ({browser}) => { @@ -90,7 +89,7 @@ it('should scope CDPSession handles', (test, { browserName }) => { }; await expectScopeState(browserType, GOLDEN_PRECONDITION); - const session = await (browser as ChromiumBrowser).newBrowserCDPSession(); + const session = await browser.newBrowserCDPSession(); await expectScopeState(browserType, { _guid: '', objects: [ diff --git a/test/checkCoverage.js b/test/checkCoverage.js index dddee8869d..1cdaa73e36 100644 --- a/test/checkCoverage.js +++ b/test/checkCoverage.js @@ -25,7 +25,7 @@ let api = new Set(installCoverageHooks(browserName).coverage.keys()); if (browserName === 'chromium') { // Sometimes we already have a background page while launching, before adding a listener. - api.delete('chromiumBrowserContext.emit("backgroundpage")'); + api.delete('browserContext.emit("backgroundpage")'); } if (browserName !== 'chromium') { diff --git a/test/chromium-js-coverage.spec.ts b/test/chromium-js-coverage.spec.ts index f2a8912d61..b48aa5d06b 100644 --- a/test/chromium-js-coverage.spec.ts +++ b/test/chromium-js-coverage.spec.ts @@ -16,13 +16,6 @@ import { it, expect, describe } from './fixtures'; -it('should be missing', (test, { browserName }) => { - test.skip(browserName === 'chromium'); -}, -async function({page}) { - expect(page.coverage).toBe(null); -}); - describe('JS Coverage', (suite, { browserName }) => { suite.skip(browserName !== 'chromium'); }, () => { diff --git a/test/chromium/chromium.spec.ts b/test/chromium/chromium.spec.ts index f131e30d1e..ab861e3c4e 100644 --- a/test/chromium/chromium.spec.ts +++ b/test/chromium/chromium.spec.ts @@ -15,7 +15,6 @@ * limitations under the License. */ import { it, expect, describe } from '../fixtures'; -import type { ChromiumBrowserContext } from '../..'; import http from 'http'; describe('chromium', (suite, { browserName }) => { @@ -23,7 +22,7 @@ describe('chromium', (suite, { browserName }) => { }, () => { it('should create a worker from a service worker', async ({page, server, context}) => { const [worker] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + context.waitForEvent('serviceworker'), page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') ]); expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); @@ -31,17 +30,17 @@ describe('chromium', (suite, { browserName }) => { it('serviceWorkers() should return current workers', async ({page, server, context}) => { const [worker1] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + context.waitForEvent('serviceworker'), page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') ]); - let workers = (context as ChromiumBrowserContext).serviceWorkers(); + let workers = context.serviceWorkers(); expect(workers.length).toBe(1); const [worker2] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + context.waitForEvent('serviceworker'), page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html') ]); - workers = (context as ChromiumBrowserContext).serviceWorkers(); + workers = context.serviceWorkers(); expect(workers.length).toBe(2); expect(workers).toContain(worker1); expect(workers).toContain(worker2); @@ -50,7 +49,7 @@ describe('chromium', (suite, { browserName }) => { it('should not create a worker from a shared worker', async ({page, server, context}) => { await page.goto(server.EMPTY_PAGE); let serviceWorkerCreated; - (context as ChromiumBrowserContext).once('serviceworker', () => serviceWorkerCreated = true); + context.once('serviceworker', () => serviceWorkerCreated = true); await page.evaluate(() => { new SharedWorker('data:text/javascript,console.log("hi")'); }); @@ -58,7 +57,7 @@ describe('chromium', (suite, { browserName }) => { }); it('should close service worker together with the context', async ({browser, server}) => { - const context = await browser.newContext() as ChromiumBrowserContext; + const context = await browser.newContext(); const page = await context.newPage(); const [worker] = await Promise.all([ context.waitForEvent('serviceworker'), @@ -175,7 +174,7 @@ describe('chromium', (suite, { browserName }) => { const cdpBrowser1 = await browserType.connectOverCDP({ wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, }); - const context = cdpBrowser1.contexts()[0] as ChromiumBrowserContext; + const context = cdpBrowser1.contexts()[0]; const page = await cdpBrowser1.contexts()[0].newPage(); const [worker] = await Promise.all([ context.waitForEvent('serviceworker'), @@ -187,7 +186,7 @@ describe('chromium', (suite, { browserName }) => { const cdpBrowser2 = await browserType.connectOverCDP({ wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, }); - const context2 = cdpBrowser2.contexts()[0] as ChromiumBrowserContext; + const context2 = cdpBrowser2.contexts()[0]; expect(context2.serviceWorkers().length).toBe(1); await cdpBrowser2.close(); } finally { diff --git a/test/chromium/launcher.spec.ts b/test/chromium/launcher.spec.ts index 0a2c27c6b1..c77520b587 100644 --- a/test/chromium/launcher.spec.ts +++ b/test/chromium/launcher.spec.ts @@ -16,7 +16,6 @@ import { it, expect } from '../fixtures'; import path from 'path'; -import type { ChromiumBrowser, ChromiumBrowserContext } from '../..'; it('should throw with remote-debugging-pipe argument', (test, { browserName, mode }) => { test.skip(mode !== 'default' || browserName !== 'chromium'); @@ -66,7 +65,7 @@ it('should return background pages', (test, { browserName }) => { `--load-extension=${extensionPath}`, ], }; - const context = await browserType.launchPersistentContext(userDataDir, extensionOptions) as ChromiumBrowserContext; + const context = await browserType.launchPersistentContext(userDataDir, extensionOptions); const backgroundPages = context.backgroundPages(); const backgroundPage = backgroundPages.length ? backgroundPages[0] @@ -92,7 +91,7 @@ it('should return background pages when recording video', (test, { browserName } dir: testInfo.outputPath(''), }, }; - const context = await browserType.launchPersistentContext(userDataDir, extensionOptions) as ChromiumBrowserContext; + const context = await browserType.launchPersistentContext(userDataDir, extensionOptions); const backgroundPages = context.backgroundPages(); const backgroundPage = backgroundPages.length ? backgroundPages[0] @@ -107,7 +106,7 @@ it('should not create pages automatically', (test, { browserName }) => { test.skip(browserName !== 'chromium'); }, async ({browserType, browserOptions}) => { const browser = await browserType.launch(browserOptions); - const browserSession = await (browser as ChromiumBrowser).newBrowserCDPSession(); + const browserSession = await browser.newBrowserCDPSession(); const targets = []; browserSession.on('Target.targetCreated', async ({targetInfo}) => { if (targetInfo.type !== 'browser') diff --git a/test/chromium/session.spec.ts b/test/chromium/session.spec.ts index 61881ae3fd..6f1de3099a 100644 --- a/test/chromium/session.spec.ts +++ b/test/chromium/session.spec.ts @@ -14,13 +14,12 @@ * limitations under the License. */ import { it, expect, describe } from '../fixtures'; -import type { ChromiumBrowserContext, ChromiumBrowser } from '../../types/types'; describe('session', (suite, { browserName }) => { suite.skip(browserName !== 'chromium'); }, () => { it('should work', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const client = await page.context().newCDPSession(page); await Promise.all([ client.send('Runtime.enable'), @@ -31,7 +30,7 @@ describe('session', (suite, { browserName }) => { }); it('should send events', async function({page, server}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const client = await page.context().newCDPSession(page); await client.send('Network.enable'); const events = []; client.on('Network.requestWillBeSent', event => events.push(event)); @@ -41,12 +40,12 @@ describe('session', (suite, { browserName }) => { it('should only accept a page', async function({page}) { // @ts-expect-error newCDPSession expects a Page - const error = await (page.context() as ChromiumBrowserContext).newCDPSession(page.context()).catch(e => e); + const error = await page.context().newCDPSession(page.context()).catch(e => e); expect(error.message).toContain('page: expected Page'); }); it('should enable and disable domains independently', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const client = await page.context().newCDPSession(page); await client.send('Runtime.enable'); await client.send('Debugger.enable'); // JS coverage enables and then disables Debugger domain. @@ -64,7 +63,7 @@ describe('session', (suite, { browserName }) => { }); it('should be able to detach session', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const client = await page.context().newCDPSession(page); await client.send('Runtime.enable'); const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true}); expect(evalResponse.result.value).toBe(3); @@ -79,7 +78,7 @@ describe('session', (suite, { browserName }) => { }); it('should throw nice errors', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const client = await page.context().newCDPSession(page); const error = await theSourceOfTheProblems().catch(error => error); expect(error.stack).toContain('theSourceOfTheProblems'); expect(error.message).toContain('ThisCommand.DoesNotExist'); @@ -93,14 +92,14 @@ describe('session', (suite, { browserName }) => { it('should not break page.close()', async function({browser}) { const context = await browser.newContext(); const page = await context.newPage(); - const session = await (page.context() as ChromiumBrowserContext).newCDPSession(page); + const session = await page.context().newCDPSession(page); await session.detach(); await page.close(); await context.close(); }); it('should detach when page closes', async function({browser}) { - const context = await browser.newContext() as ChromiumBrowserContext; + const context = await browser.newContext(); const page = await context.newPage(); const session = await context.newCDPSession(page); await page.close(); @@ -111,7 +110,7 @@ describe('session', (suite, { browserName }) => { }); it('should work with newBrowserCDPSession', async function({browser}) { - const session = await (browser as ChromiumBrowser).newBrowserCDPSession(); + const session = await browser.newBrowserCDPSession(); const version = await session.send('Browser.getVersion'); expect(version.userAgent).toBeTruthy(); diff --git a/test/chromium/tracing.spec.ts b/test/chromium/tracing.spec.ts index 546a754910..2ab96965ed 100644 --- a/test/chromium/tracing.spec.ts +++ b/test/chromium/tracing.spec.ts @@ -17,7 +17,6 @@ import { folio } from '../fixtures'; import fs from 'fs'; import path from 'path'; -import type { ChromiumBrowser } from '../..'; const { it, expect, describe } = folio; describe('tracing', (suite, { browserName }) => { @@ -25,24 +24,24 @@ describe('tracing', (suite, { browserName }) => { }, () => { it('should output a trace', async ({browser, page, server, testInfo}) => { const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputTraceFile}); + await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); await page.goto(server.PREFIX + '/grid.html'); - await (browser as ChromiumBrowser).stopTracing(); + await browser.stopTracing(); expect(fs.existsSync(outputTraceFile)).toBe(true); }); it('should create directories as needed', async ({browser, page, server, testInfo}) => { const filePath = testInfo.outputPath(path.join('these', 'are', 'directories', 'trace.json')); - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath}); + await browser.startTracing(page, {screenshots: true, path: filePath}); await page.goto(server.PREFIX + '/grid.html'); - await (browser as ChromiumBrowser).stopTracing(); + await browser.stopTracing(); expect(fs.existsSync(filePath)).toBe(true); }); it('should run with custom categories if provided', async ({browser, page, testInfo}) => { const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await (browser as ChromiumBrowser).startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); - await (browser as ChromiumBrowser).stopTracing(); + await browser.startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); + await browser.stopTracing(); const traceJson = JSON.parse(fs.readFileSync(outputTraceFile).toString()); expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); @@ -50,35 +49,35 @@ describe('tracing', (suite, { browserName }) => { it('should throw if tracing on two pages', async ({browser, page, testInfo}) => { const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await (browser as ChromiumBrowser).startTracing(page, {path: outputTraceFile}); + await browser.startTracing(page, {path: outputTraceFile}); const newPage = await browser.newPage(); let error = null; - await (browser as ChromiumBrowser).startTracing(newPage, {path: outputTraceFile}).catch(e => error = e); + await browser.startTracing(newPage, {path: outputTraceFile}).catch(e => error = e); await newPage.close(); expect(error).toBeTruthy(); - await (browser as ChromiumBrowser).stopTracing(); + await browser.stopTracing(); }); it('should return a buffer', async ({browser, page, server, testInfo}) => { const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputTraceFile}); + await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); + const trace = await browser.stopTracing(); const buf = fs.readFileSync(outputTraceFile); expect(trace.toString()).toEqual(buf.toString()); }); it('should work without options', async ({browser, page, server}) => { - await (browser as ChromiumBrowser).startTracing(page); + await browser.startTracing(page); await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); + const trace = await browser.stopTracing(); expect(trace).toBeTruthy(); }); it('should support a buffer without a path', async ({browser, page, server}) => { - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true}); + await browser.startTracing(page, {screenshots: true}); await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); + const trace = await browser.stopTracing(); expect(trace.toString()).toContain('screenshot'); }); }); diff --git a/test/coverage.js b/test/coverage.js index 4e7f7bb1d1..1d0c2a6e43 100644 --- a/test/coverage.js +++ b/test/coverage.js @@ -40,18 +40,17 @@ function traceAPICoverage(apiCoverage, api, events) { uninstalls.push(() => Reflect.set(classType.prototype, methodName, method)); } if (events[name]) { - const emitClassType = (name === 'BrowserContext' ? api['ChromiumBrowserContext'] : undefined) || classType; for (const event of Object.values(events[name])) { if (typeof event !== 'symbol') apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false); } - const method = Reflect.get(emitClassType.prototype, 'emit'); - Reflect.set(emitClassType.prototype, 'emit', function(event, ...args) { + const method = Reflect.get(classType.prototype, 'emit'); + Reflect.set(classType.prototype, 'emit', function(event, ...args) { if (typeof event !== 'symbol' && this.listenerCount(event)) apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, true); return method.call(this, event, ...args); }); - uninstalls.push(() => Reflect.set(emitClassType.prototype, 'emit', method)); + uninstalls.push(() => Reflect.set(classType.prototype, 'emit', method)); } } return () => uninstalls.forEach(u => u()); diff --git a/test/defaultbrowsercontext-2.spec.ts b/test/defaultbrowsercontext-2.spec.ts index bf014cda11..db4c2430d6 100644 --- a/test/defaultbrowsercontext-2.spec.ts +++ b/test/defaultbrowsercontext-2.spec.ts @@ -206,13 +206,6 @@ it('coverage should work', (test, { browserName }) => { expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); }); -it('coverage should be missing', (test, { browserName }) => { - test.skip(browserName === 'chromium'); -}, async ({launchPersistent}) => { - const {page} = await launchPersistent(); - expect(page.coverage).toBe(null); -}); - it('should respect selectors', async ({playwright, launchPersistent}) => { const {page} = await launchPersistent(); diff --git a/test/pdf.spec.ts b/test/pdf.spec.ts index 46b4bc6079..16c9d39596 100644 --- a/test/pdf.spec.ts +++ b/test/pdf.spec.ts @@ -26,9 +26,3 @@ it('should be able to save file', (test, { browserName, headful }) => { await page.pdf({path: outputFile}); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); }); - -it('should only have pdf in chromium', (test, { browserName }) => { - test.skip(browserName === 'chromium'); -}, async ({page}) => { - expect(page.pdf).toBe(undefined); -}); diff --git a/types/types.d.ts b/types/types.d.ts index 442266c539..6009fe909c 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -1484,10 +1484,11 @@ export interface Page { context(): BrowserContext; /** - * Browser-specific Coverage implementation, only available for Chromium atm. See - * [ChromiumCoverage](#class-chromiumcoverage) for more details. + * > NOTE: Only available for Chromium atm. + * + * Browser-specific Coverage implementation. See [Coverage](#class-coverage) for more details. */ - coverage: null|ChromiumCoverage; + coverage: Coverage; /** * This method double clicks an element matching `selector` by performing the following steps: @@ -4625,6 +4626,18 @@ export interface BrowserContext { */ exposeBinding(name: string, playwrightBinding: (source: BindingSource, arg: JSHandle) => any, options: { handle: true }): Promise; exposeBinding(name: string, playwrightBinding: (source: BindingSource, ...args: any[]) => any, options?: { handle?: boolean }): Promise; + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + on(event: 'backgroundpage', listener: (page: Page) => void): this; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -4656,6 +4669,25 @@ export interface BrowserContext { */ on(event: 'page', listener: (page: Page) => void): this; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + on(event: 'serviceworker', listener: (worker: Worker) => void): this; + + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + once(event: 'backgroundpage', listener: (page: Page) => void): this; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -4687,6 +4719,25 @@ export interface BrowserContext { */ once(event: 'page', listener: (page: Page) => void): this; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + once(event: 'serviceworker', listener: (worker: Worker) => void): this; + + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + addListener(event: 'backgroundpage', listener: (page: Page) => void): this; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -4718,6 +4769,25 @@ export interface BrowserContext { */ addListener(event: 'page', listener: (page: Page) => void): this; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + addListener(event: 'serviceworker', listener: (worker: Worker) => void): this; + + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + removeListener(event: 'backgroundpage', listener: (page: Page) => void): this; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -4749,6 +4819,25 @@ export interface BrowserContext { */ removeListener(event: 'page', listener: (page: Page) => void): this; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + removeListener(event: 'serviceworker', listener: (worker: Worker) => void): this; + + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + off(event: 'backgroundpage', listener: (page: Page) => void): this; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -4780,6 +4869,13 @@ export interface BrowserContext { */ off(event: 'page', listener: (page: Page) => void): this; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + off(event: 'serviceworker', listener: (worker: Worker) => void): this; + /** * Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies can be * obtained via @@ -4875,6 +4971,13 @@ export interface BrowserContext { content?: string; }, arg?: Serializable): Promise; + /** + * > NOTE: Background pages are only supported on Chromium-based browsers. + * + * All existing background pages in the context. + */ + backgroundPages(): Array; + /** * Returns the browser instance of the context. If it was launched as a persistent context null gets returned. */ @@ -4978,6 +5081,14 @@ export interface BrowserContext { origin?: string; }): Promise; + /** + * > NOTE: CDP sessions are only supported on Chromium-based browsers. + * + * Returns the newly created session. + * @param page Page to create new session for. + */ + newCDPSession(page: Page): Promise; + /** * Creates a new page in the browser context. */ @@ -5024,6 +5135,13 @@ export interface BrowserContext { */ route(url: string|RegExp|((url: URL) => boolean), handler: ((route: Route, request: Request) => void)): Promise; + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * All existing service workers in the context. + */ + serviceWorkers(): Array; + /** * This setting will change the default maximum navigation time for the following methods and related shortcuts: * - [page.goBack([options])](https://playwright.dev/docs/api/class-page#pagegobackoptions) @@ -5165,6 +5283,18 @@ export interface BrowserContext { */ unroute(url: string|RegExp|((url: URL) => boolean), handler?: ((route: Route, request: Request) => void)): Promise; + /** + * > NOTE: Only works with Chromium browser's persistent context. + * + * Emitted when new background page is created in the context. + * + * ```js + * const backgroundPage = await context.waitForEvent('backgroundpage'); + * ``` + * + */ + waitForEvent(event: 'backgroundpage', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise; + /** * Emitted when Browser context gets closed. This might happen because of one of the following: * - Browser context is closed. @@ -5195,6 +5325,13 @@ export interface BrowserContext { * to wait until the page gets to a particular state (you should not need it in most cases). */ waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise; + + /** + * > NOTE: Service workers are only supported on Chromium-based browsers. + * + * Emitted when new service worker is created in the context. + */ + waitForEvent(event: 'serviceworker', optionsOrPredicate?: { predicate?: (worker: Worker) => boolean, timeout?: number } | ((worker: Worker) => boolean)): Promise; } /** @@ -6865,112 +7002,6 @@ export interface BrowserType { */ name(): string;} -/** - * - extends: [Browser] - * - * Chromium-specific features including Tracing, service worker support, etc. You can use - * [chromiumBrowser.startTracing([page, options])](https://playwright.dev/docs/api/class-chromiumbrowser#chromiumbrowserstarttracingpage-options) - * and [chromiumBrowser.stopTracing()](https://playwright.dev/docs/api/class-chromiumbrowser#chromiumbrowserstoptracing) to - * create a trace file which can be opened in Chrome DevTools or - * [timeline viewer](https://chromedevtools.github.io/timeline-viewer/). - * - * ```js - * await browser.startTracing(page, {path: 'trace.json'}); - * await page.goto('https://www.google.com'); - * await browser.stopTracing(); - * ``` - * - * [ChromiumBrowser] can also be used for testing Chrome Extensions. - * - * > NOTE: Extensions in Chrome / Chromium currently only work in non-headless mode. - * - * The following is code for getting a handle to the - * [background page](https://developer.chrome.com/extensions/background_pages) of an extension whose source is located in - * `./my-extension`: - * - * ```js - * const { chromium } = require('playwright'); - * - * (async () => { - * const pathToExtension = require('path').join(__dirname, 'my-extension'); - * const userDataDir = '/tmp/test-user-data-dir'; - * const browserContext = await chromium.launchPersistentContext(userDataDir,{ - * headless: false, - * args: [ - * `--disable-extensions-except=${pathToExtension}`, - * `--load-extension=${pathToExtension}` - * ] - * }); - * const backgroundPage = browserContext.backgroundPages()[0]; - * // Test the background page as you would any other page. - * await browserContext.close(); - * })(); - * ``` - * - */ -export interface ChromiumBrowser extends Browser { - /** - * Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts. - * - * ```js - * const browser = await pw.webkit.launch(); - * console.log(browser.contexts().length); // prints `0` - * - * const context = await browser.newContext(); - * console.log(browser.contexts().length); // prints `1` - * ``` - * - */ - contexts(): Array; - /** - * Creates a new browser context. It won't share cookies/cache with other browser contexts. - * - * ```js - * (async () => { - * const browser = await playwright.firefox.launch(); // Or 'chromium' or 'webkit'. - * // Create a new incognito browser context. - * const context = await browser.newContext(); - * // Create a new page in a pristine context. - * const page = await context.newPage(); - * await page.goto('https://example.com'); - * })(); - * ``` - * - * @param options - */ - newContext(options?: BrowserContextOptions): Promise; - /** - * Returns the newly created browser session. - */ - newBrowserCDPSession(): Promise; - - /** - * Only one trace can be active at a time per browser. - * @param page Optional, if specified, tracing includes screenshots of the given page. - * @param options - */ - startTracing(page?: Page, options?: { - /** - * specify custom categories to use instead of default. - */ - categories?: Array; - - /** - * A path to write the trace file to. - */ - path?: string; - - /** - * captures screenshots in the trace. - */ - screenshots?: boolean; - }): Promise; - - /** - * Returns the buffer with trace data. - */ - stopTracing(): Promise;} - /** * - extends: [EventEmitter] * @@ -7817,7 +7848,7 @@ export interface AndroidDevice { */ height: number; }; - }): Promise; + }): Promise; /** * Performs a long tap on the widget defined by `selector`. @@ -8320,6 +8351,13 @@ export interface Browser extends EventEmitter { */ isConnected(): boolean; + /** + * > NOTE: CDP Sessions are only supported on Chromium-based browsers. + * + * Returns the newly created browser session. + */ + newBrowserCDPSession(): Promise; + /** * Creates a new browser context. It won't share cookies/cache with other browser contexts. * @@ -8652,6 +8690,47 @@ export interface Browser extends EventEmitter { }; }): Promise; + /** + * > NOTE: Tracing is only supported on Chromium-based browsers. + * + * You can use + * [browser.startTracing([page, options])](https://playwright.dev/docs/api/class-browser#browserstarttracingpage-options) + * and [browser.stopTracing()](https://playwright.dev/docs/api/class-browser#browserstoptracing) to create a trace file + * that can be opened in Chrome DevTools performance panel. + * + * ```js + * await browser.startTracing(page, {path: 'trace.json'}); + * await page.goto('https://www.google.com'); + * await browser.stopTracing(); + * ``` + * + * @param page Optional, if specified, tracing includes screenshots of the given page. + * @param options + */ + startTracing(page?: Page, options?: { + /** + * specify custom categories to use instead of default. + */ + categories?: Array; + + /** + * A path to write the trace file to. + */ + path?: string; + + /** + * captures screenshots in the trace. + */ + screenshots?: boolean; + }): Promise; + + /** + * > NOTE: Tracing is only supported on Chromium-based browsers. + * + * Returns the buffer with trace data. + */ + stopTracing(): Promise; + /** * Returns the browser version. */ @@ -8710,290 +8789,37 @@ export interface BrowserServer { } /** - * - extends: [BrowserContext] - * - * Chromium-specific features including background pages, service worker support, etc. - * - * ```js - * const backgroundPage = await context.waitForEvent('backgroundpage'); - * ``` - * + * [ConsoleMessage] objects are dispatched by page via the + * [page.on('console')](https://playwright.dev/docs/api/class-page#pageonconsole) event. */ -export interface ChromiumBrowserContext extends BrowserContext { - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - on(event: 'backgroundpage', listener: (page: Page) => void): this; +export interface ConsoleMessage { + args(): Array; + + location(): { + /** + * URL of the resource. + */ + url: string; + + /** + * 0-based line number in the resource. + */ + lineNumber: number; + + /** + * 0-based column number in the resource. + */ + columnNumber: number; + }; + + text(): string; /** - * Emitted when new service worker is created in the context. + * One of the following values: `'log'`, `'debug'`, `'info'`, `'error'`, `'warning'`, `'dir'`, `'dirxml'`, `'table'`, + * `'trace'`, `'clear'`, `'startGroup'`, `'startGroupCollapsed'`, `'endGroup'`, `'assert'`, `'profile'`, `'profileEnd'`, + * `'count'`, `'timeEnd'`. */ - on(event: 'serviceworker', listener: (worker: Worker) => void): this; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - on(event: 'close', listener: (browserContext: BrowserContext) => void): this; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - on(event: 'page', listener: (page: Page) => void): this; - - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - once(event: 'backgroundpage', listener: (page: Page) => void): this; - - /** - * Emitted when new service worker is created in the context. - */ - once(event: 'serviceworker', listener: (worker: Worker) => void): this; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - once(event: 'close', listener: (browserContext: BrowserContext) => void): this; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - once(event: 'page', listener: (page: Page) => void): this; - - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - addListener(event: 'backgroundpage', listener: (page: Page) => void): this; - - /** - * Emitted when new service worker is created in the context. - */ - addListener(event: 'serviceworker', listener: (worker: Worker) => void): this; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - addListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - addListener(event: 'page', listener: (page: Page) => void): this; - - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - removeListener(event: 'backgroundpage', listener: (page: Page) => void): this; - - /** - * Emitted when new service worker is created in the context. - */ - removeListener(event: 'serviceworker', listener: (worker: Worker) => void): this; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - removeListener(event: 'close', listener: (browserContext: BrowserContext) => void): this; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - removeListener(event: 'page', listener: (page: Page) => void): this; - - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - off(event: 'backgroundpage', listener: (page: Page) => void): this; - - /** - * Emitted when new service worker is created in the context. - */ - off(event: 'serviceworker', listener: (worker: Worker) => void): this; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - off(event: 'close', listener: (browserContext: BrowserContext) => void): this; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - off(event: 'page', listener: (page: Page) => void): this; - - /** - * All existing background pages in the context. - */ - backgroundPages(): Array; - - /** - * Returns the newly created session. - * @param page Page to create new session for. - */ - newCDPSession(page: Page): Promise; - - /** - * All existing service workers in the context. - */ - serviceWorkers(): Array; - - /** - * Emitted when new background page is created in the context. - * - * > NOTE: Only works with persistent context. - */ - waitForEvent(event: 'backgroundpage', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise; - - /** - * Emitted when new service worker is created in the context. - */ - waitForEvent(event: 'serviceworker', optionsOrPredicate?: { predicate?: (worker: Worker) => boolean, timeout?: number } | ((worker: Worker) => boolean)): Promise; - - /** - * Emitted when Browser context gets closed. This might happen because of one of the following: - * - Browser context is closed. - * - Browser application is closed or crashed. - * - The [browser.close()](https://playwright.dev/docs/api/class-browser#browserclose) method was called. - */ - waitForEvent(event: 'close', optionsOrPredicate?: { predicate?: (browserContext: BrowserContext) => boolean, timeout?: number } | ((browserContext: BrowserContext) => boolean)): Promise; - - /** - * The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will - * also fire for popup pages. See also [page.on('popup')](https://playwright.dev/docs/api/class-page#pageonpopup) to - * receive events about popups relevant to a specific page. - * - * The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a - * popup with `window.open('http://example.com')`, this event will fire when the network request to "http://example.com" is - * done and its response has started loading in the popup. - * - * ```js - * const [newPage] = await Promise.all([ - * context.waitForEvent('page'), - * page.click('a[target=_blank]'), - * ]); - * console.log(await newPage.evaluate('location.href')); - * ``` - * - * > NOTE: Use - * [page.waitForLoadState([state, options])](https://playwright.dev/docs/api/class-page#pagewaitforloadstatestate-options) - * to wait until the page gets to a particular state (you should not need it in most cases). - */ - waitForEvent(event: 'page', optionsOrPredicate?: { predicate?: (page: Page) => boolean, timeout?: number } | ((page: Page) => boolean)): Promise; - + type(): string; } /** @@ -9001,6 +8827,8 @@ export interface ChromiumBrowserContext extends BrowserContext { * * An example of using JavaScript coverage to produce Istanbul report for page load: * + * > NOTE: Coverage APIs are only supported on Chromium-based browsers. + * * ```js * const { chromium } = require('playwright'); * const v8toIstanbul = require('v8-to-istanbul'); @@ -9022,7 +8850,7 @@ export interface ChromiumBrowserContext extends BrowserContext { * ``` * */ -export interface ChromiumCoverage { +export interface Coverage { /** * Returns coverage is started * @param options @@ -9126,40 +8954,6 @@ export interface ChromiumCoverage { }>>; } -/** - * [ConsoleMessage] objects are dispatched by page via the - * [page.on('console')](https://playwright.dev/docs/api/class-page#pageonconsole) event. - */ -export interface ConsoleMessage { - args(): Array; - - location(): { - /** - * URL of the resource. - */ - url: string; - - /** - * 0-based line number in the resource. - */ - lineNumber: number; - - /** - * 0-based column number in the resource. - */ - columnNumber: number; - }; - - text(): string; - - /** - * One of the following values: `'log'`, `'debug'`, `'info'`, `'error'`, `'warning'`, `'dir'`, `'dirxml'`, `'table'`, - * `'trace'`, `'clear'`, `'startGroup'`, `'startGroupCollapsed'`, `'endGroup'`, `'assert'`, `'profile'`, `'profileEnd'`, - * `'count'`, `'timeEnd'`. - */ - type(): string; -} - /** * [Dialog] objects are dispatched by page via the * [page.on('dialog')](https://playwright.dev/docs/api/class-page#pageondialog) event. @@ -9256,8 +9050,7 @@ export interface Download { /** * Returns path to the downloaded file in case of successful download. The method will wait for the download to finish if - * necessary. The method throws when connected remotely via - * [browserType.connect(params)](https://playwright.dev/docs/api/class-browsertype#browsertypeconnectparams). + * necessary. The method throws when connected remotely. */ path(): Promise; @@ -9438,15 +9231,6 @@ export interface FileChooser { }): Promise; } -/** - * - extends: [Browser] - * - * Firefox browser instance does not expose Firefox-specific features. - */ -export interface FirefoxBrowser extends Browser { - -} - /** * Keyboard provides an api for managing a virtual keyboard. The high level api is * [keyboard.type(text[, options])](https://playwright.dev/docs/api/class-keyboard#keyboardtypetext-options), which takes @@ -10241,8 +10025,7 @@ export interface Video { /** * Returns the file system path this video will be recorded to. The video is guaranteed to be written to the filesystem - * upon closing the browser context. This method throws when connected remotely via - * [browserType.connect(params)](https://playwright.dev/docs/api/class-browsertype#browsertypeconnectparams). + * upon closing the browser context. This method throws when connected remotely. */ path(): Promise; @@ -10254,15 +10037,6 @@ export interface Video { saveAs(path: string): Promise; } -/** - * - extends: [Browser] - * - * WebKit browser instance does not expose WebKit-specific features. - */ -export interface WebKitBrowser extends Browser { - -} - /** * The [WebSocket] class represents websocket connections in the page. */ @@ -11100,3 +10874,9 @@ type Devices = { "Pixel 5 landscape": DeviceDescriptor; [key: string]: DeviceDescriptor; } + +export interface ChromiumBrowserContext extends BrowserContext { } +export interface ChromiumBrowser extends Browser { } +export interface FirefoxBrowser extends Browser { } +export interface WebKitBrowser extends Browser { } +export interface ChromiumCoverage extends Coverage { } diff --git a/utils/generate_types/index.js b/utils/generate_types/index.js index cacf058471..fc56ce4e2e 100644 --- a/utils/generate_types/index.js +++ b/utils/generate_types/index.js @@ -97,6 +97,12 @@ ${overrides} ${classes.map(classDesc => classToString(classDesc)).join('\n')} ${objectDefinitionsToString(overrides)} ${generateDevicesTypes()} + +export interface ChromiumBrowserContext extends BrowserContext { } +export interface ChromiumBrowser extends Browser { } +export interface FirefoxBrowser extends Browser { } +export interface WebKitBrowser extends Browser { } +export interface ChromiumCoverage extends Coverage { } `; for (const [key, value] of Object.entries(exported)) output = output.replace(new RegExp('\\b' + key + '\\b', 'g'), value); diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index a178ff1e7e..53a630c1d4 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -144,11 +144,6 @@ export interface BrowserType { } -export interface ChromiumBrowser extends Browser { - contexts(): Array; - newContext(options?: BrowserContextOptions): Promise; -} - export interface CDPSession { on: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; addListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;