diff --git a/docs/api.md b/docs/api.md index 1923cda5ea..692a292ca2 100644 --- a/docs/api.md +++ b/docs/api.md @@ -220,7 +220,8 @@ Indicates that the browser is connected. - `password` <[string]> - `colorScheme` <"light"|"dark"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. - `logger` <[Logger]> Logger sink for Playwright logging. - - `_recordVideos` <[Object]> **experimental** Enables automatic video recording for new pages. The video will have frames with the provided dimensions. Actual picture of the page will be scaled down if necessary to fit specified size. + - `_recordVideos` <[boolean]> **experimental** Enables automatic video recording for new pages. + - `_videoSize` <[Object]> **experimental** Specifies dimensions of the automatically recorded video. Can only be used if `_recordVideos` is true. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size. - `width` <[number]> Video frame width. - `height` <[number]> Video frame height. - returns: <[Promise]<[BrowserContext]>> @@ -265,7 +266,8 @@ Creates a new browser context. It won't share cookies/cache with other browser c - `password` <[string]> - `colorScheme` <"light"|"dark"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. - `logger` <[Logger]> Logger sink for Playwright logging. - - `_recordVideos` <[Object]> **experimental** Enables automatic video recording for the new page. The video will have frames with the provided dimensions. Actual picture of the page will be scaled down if necessary to fit specified size. + - `_recordVideos` <[boolean]> **experimental** Enables automatic video recording for the new page. + - `_videoSize` <[Object]> **experimental** Specifies dimensions of the automatically recorded video. Can only be used if `_recordVideos` is true. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size. - `width` <[number]> Video frame width. - `height` <[number]> Video frame height. - returns: <[Promise]<[Page]>> @@ -792,7 +794,8 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'. _videosPath: __dirname // Save videos to custom directory }); const context = await browser.newContext({ - _recordVideos: { width: 640, height: 360 } + _recordVideos: true, + _videoSize: { width: 640, height: 360 } }); const page = await context.newPage(); const video = await page.waitForEvent('_videostarted'); @@ -4268,7 +4271,8 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'. - `password` <[string]> - `colorScheme` <"light"|"dark"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. - `_videosPath` <[string]> **experimental** If specified, recorded videos are saved into this folder. Otherwise, temporary folder is created and is deleted when browser is closed. - - `_recordVideos` <[Object]> **experimental** Enables automatic video recording for the new page. The video will have frames with the provided dimensions. Actual picture of the page will be scaled down if necessary to fit specified size. + - `_recordVideos` <[boolean]> **experimental** Enables automatic video recording for new pages. + - `_videoSize` <[Object]> **experimental** Specifies dimensions of the automatically recorded video. Can only be used if `_recordVideos` is true. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size. - `width` <[number]> Video frame width. - `height` <[number]> Video frame height. - returns: <[Promise]<[BrowserContext]>> Promise that resolves to the persistent browser context instance. diff --git a/packages/installation-tests/screencast.js b/packages/installation-tests/screencast.js index b3c2658659..23376835e5 100644 --- a/packages/installation-tests/screencast.js +++ b/packages/installation-tests/screencast.js @@ -37,7 +37,8 @@ const fs = require('fs'); _videosPath: __dirname, }); const context = await browser.newContext({ - _recordVideos: {width: 320, height: 240}, + _recordVideos: true, + _videoSize: {width: 320, height: 240}, }); const page = await context.newPage(); const video = await page.waitForEvent('_videostarted'); diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index 580b77a455..5dfa3930ad 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -371,7 +371,8 @@ export type BrowserNewContextParams = { hasTouch?: boolean, colorScheme?: 'dark' | 'light' | 'no-preference', acceptDownloads?: boolean, - _recordVideos?: { + _recordVideos?: boolean, + _videoSize?: { width: number, height: number, }, @@ -408,7 +409,8 @@ export type BrowserNewContextOptions = { hasTouch?: boolean, colorScheme?: 'dark' | 'light' | 'no-preference', acceptDownloads?: boolean, - _recordVideos?: { + _recordVideos?: boolean, + _videoSize?: { width: number, height: number, }, diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 0722f73cdd..985f26c13e 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -367,7 +367,8 @@ Browser: - light - no-preference acceptDownloads: boolean? - _recordVideos: + _recordVideos: boolean? + _videoSize: type: object? properties: width: number diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index 189aa2a34e..4129106eae 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -217,7 +217,8 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { hasTouch: tOptional(tBoolean), colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])), acceptDownloads: tOptional(tBoolean), - _recordVideos: tOptional(tObject({ + _recordVideos: tOptional(tBoolean), + _videoSize: tOptional(tObject({ width: tNumber, height: tNumber, })), diff --git a/src/server/chromium/crPage.ts b/src/server/chromium/crPage.ts index a7ee7495c3..fb9d928d35 100644 --- a/src/server/chromium/crPage.ts +++ b/src/server/chromium/crPage.ts @@ -459,11 +459,13 @@ class FrameSession { for (const source of this._crPage._page._evaluateOnNewDocumentSources) promises.push(this._evaluateOnNewDocument(source)); if (this._crPage._browserContext._options._recordVideos) { - const contextOptions = this._crPage._browserContext._options._recordVideos; + const size = this._crPage._browserContext._options._videoSize || this._crPage._browserContext._options.viewport || { width: 1280, height: 720 }; const screencastId = createGuid(); const outputFile = path.join(this._crPage._browserContext._browser._options._videosPath!, screencastId + '.webm'); - const options = Object.assign({}, contextOptions, {outputFile}); - promises.push(this._startScreencast(screencastId, options)); + promises.push(this._startScreencast(screencastId, { + ...size, + outputFile, + })); } promises.push(this._client.send('Runtime.runIfWaitingForDebugger')); promises.push(this._firstNonInitialNavigationCommittedPromise); diff --git a/src/server/firefox/ffBrowser.ts b/src/server/firefox/ffBrowser.ts index c5607f039a..eda6f1f6fd 100644 --- a/src/server/firefox/ffBrowser.ts +++ b/src/server/firefox/ffBrowser.ts @@ -223,8 +223,9 @@ export class FFBrowserContext extends BrowserContext { if (this._options.colorScheme) promises.push(this._browser._connection.send('Browser.setColorScheme', { browserContextId, colorScheme: this._options.colorScheme })); if (this._options._recordVideos) { + const size = this._options._videoSize || this._options.viewport || { width: 1280, height: 720 }; await this._browser._connection.send('Browser.setScreencastOptions', { - ...this._options._recordVideos, + ...size, dir: this._browser._options._videosPath!, browserContextId: this._browserContextId }); diff --git a/src/server/types.ts b/src/server/types.ts index 08a6f178b9..ecbb88fa46 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -238,10 +238,8 @@ export type BrowserContextOptions = { hasTouch?: boolean, colorScheme?: ColorScheme, acceptDownloads?: boolean, - _recordVideos?: { - width: number, - height: number - } + _recordVideos?: boolean, + _videoSize?: Size, }; export type EnvArray = { name: string, value: string }[]; diff --git a/src/server/webkit/wkPage.ts b/src/server/webkit/wkPage.ts index 1c57640fff..0be4526994 100644 --- a/src/server/webkit/wkPage.ts +++ b/src/server/webkit/wkPage.ts @@ -114,10 +114,12 @@ export class WKPage implements PageDelegate { this._grantPermissions(key, value); } if (this._browserContext._options._recordVideos) { - const contextOptions = this._browserContext._options._recordVideos; + const size = this._browserContext._options._videoSize || this._browserContext._options.viewport || { width: 1280, height: 720 }; const outputFile = path.join(this._browserContext._browser._options._videosPath!, createGuid() + '.webm'); - const options = Object.assign({}, contextOptions, {outputFile}); - promises.push(this.startScreencast(options)); + promises.push(this.startScreencast({ + ...size, + outputFile, + })); } await Promise.all(promises); } diff --git a/test/screencast.spec.ts b/test/screencast.spec.ts index aef3255605..1c189868e1 100644 --- a/test/screencast.spec.ts +++ b/test/screencast.spec.ts @@ -235,7 +235,7 @@ describe('screencast', suite => { it('should automatically start/finish when new page is created/closed', async ({browserType, defaultBrowserOptions, tmpDir}) => { const browser = await browserType.launch({ ...defaultBrowserOptions, _videosPath: tmpDir }); - const context = await browser.newContext({_recordVideos: {width: 320, height: 240}}); + const context = await browser.newContext({ _recordVideos: true, _videoSize: { width: 320, height: 240 }}); const [screencast, newPage] = await Promise.all([ new Promise(r => context.on('page', page => page.on('_videostarted', r))), context.newPage(), @@ -252,7 +252,7 @@ describe('screencast', suite => { it('should finish when contex closes', async ({browserType, defaultBrowserOptions, tmpDir}) => { const browser = await browserType.launch({ ...defaultBrowserOptions, _videosPath: tmpDir }); - const context = await browser.newContext({_recordVideos: {width: 320, height: 240}}); + const context = await browser.newContext({ _recordVideos: true, _videoSize: { width: 320, height: 240 } }); const [video] = await Promise.all([ new Promise(r => context.on('page', page => page.on('_videostarted', r))), @@ -269,7 +269,7 @@ describe('screencast', suite => { }); it('should fire striclty after context.newPage', async ({browser}) => { - const context = await browser.newContext({_recordVideos: {width: 320, height: 240}}); + const context = await browser.newContext({ _recordVideos: true, _videoSize: { width: 320, height: 240 } }); const page = await context.newPage(); // Should not hang. await page.waitForEvent('_videostarted'); @@ -278,7 +278,7 @@ describe('screencast', suite => { it('should fire start event for popups', async ({browserType, defaultBrowserOptions, tmpDir, server}) => { const browser = await browserType.launch({ ...defaultBrowserOptions, _videosPath: tmpDir }); - const context = await browser.newContext({_recordVideos: {width: 320, height: 240}}); + const context = await browser.newContext({ _recordVideos: true, _videoSize: { width: 320, height: 240 } }); const [page] = await Promise.all([ context.newPage(), @@ -339,4 +339,43 @@ describe('screencast', suite => { expectAll(pixels, almostRed); } }); + + it('should use viewport as default size', async ({browser, page, tmpDir, videoPlayer, toImpl}) => { + const size = {width: 800, height: 600}; + const context = await browser.newContext({_recordVideos: true, viewport: size}); + + const [video] = await Promise.all([ + new Promise(r => context.on('page', page => page.on('_videostarted', r))), + context.newPage(), + ]); + await new Promise(r => setTimeout(r, 1000)); + const [videoFile] = await Promise.all([ + video.path(), + context.close(), + ]); + + expect(fs.existsSync(videoFile)).toBe(true); + await videoPlayer.load(videoFile); + expect(await videoPlayer.videoWidth()).toBe(size.width); + expect(await videoPlayer.videoHeight()).toBe(size.height); + }); + + it('should be 1280x720 by default', async ({browser, page, tmpDir, videoPlayer, toImpl}) => { + const context = await browser.newContext({_recordVideos: true}); + + const [video] = await Promise.all([ + new Promise(r => context.on('page', page => page.on('_videostarted', r))), + context.newPage(), + ]); + await new Promise(r => setTimeout(r, 1000)); + const [videoFile] = await Promise.all([ + video.path(), + context.close(), + ]); + + expect(fs.existsSync(videoFile)).toBe(true); + await videoPlayer.load(videoFile); + expect(await videoPlayer.videoWidth()).toBe(1280); + expect(await videoPlayer.videoHeight()).toBe(720); + }); }); \ No newline at end of file