diff --git a/docs/api.md b/docs/api.md index cd7afc2793..eb8ab0b30f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -99,9 +99,7 @@ * [page.dblclick(selector[, options])](#pagedblclickselector-options) * [page.deleteCookie(...cookies)](#pagedeletecookiecookies) * [page.emulate(options)](#pageemulateoptions) - * [page.emulateMedia(type)](#pageemulatemediatype) - * [page.emulateMediaFeatures(features)](#pageemulatemediafeaturesfeatures) - * [page.emulateMediaType(type)](#pageemulatemediatypetype) + * [page.emulateMedia(options)](#pageemulatemediaoptions) * [page.emulateTimezone(timezoneId)](#pageemulatetimezonetimezoneid) * [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args) * [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args) @@ -1252,70 +1250,62 @@ const iPhone = playwright.devices['iPhone 6']; List of all available devices is available in the source code: [DeviceDescriptors.js](https://github.com/Microsoft/playwright/blob/master/lib/DeviceDescriptors.js). -#### page.emulateMedia(type) -- `type` Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation. -- returns: <[Promise]> - -**Note:** This method is deprecated, and only kept around as an alias for backwards compatibility. Use [`page.emulateMediaType(type)`](#pageemulatemediatypetype) instead. - -#### page.emulateMediaFeatures(features) -- `features` > Given an array of media feature objects, emulates CSS media features on the page. Each media feature object must have the following properties: - - `name` <[string]> The CSS media feature name. Supported names are `'prefers-colors-scheme'` and `'prefers-reduced-motion'`. - - `value` <[string]> The value for the given CSS media feature. +#### page.emulateMedia(options) +- `options` <[Object]> + - `type` Optional. Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation. + - `features` > Optional. Given an array of media feature objects, emulates CSS media features on the page. Each media feature object must have the following properties: + - `name` <[string]> The CSS media feature name. Supported names are `'prefers-colors-scheme'` and `'prefers-reduced-motion'`. + - `value` <[string]> The value for the given CSS media feature. - returns: <[Promise]> ```js -await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]); -await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)); +await page.evaluate(() => matchMedia('screen').matches)); +// → true +await page.evaluate(() => matchMedia('print').matches)); // → true -await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)); -// → false -await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); -// → false -await page.emulateMediaFeatures([{ name: 'prefers-reduced-motion', value: 'reduce' }]); -await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)); -// → true -await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); +await page.emulateMedia({ type: 'print' }); +await page.evaluate(() => matchMedia('screen').matches)); // → false +await page.evaluate(() => matchMedia('print').matches)); +// → true -await page.emulateMediaFeatures([ - { name: 'prefers-color-scheme', value: 'dark' }, - { name: 'prefers-reduced-motion', value: 'reduce' }, -]); -await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)); +await page.emulateMedia({}); +await page.evaluate(() => matchMedia('screen').matches)); // → true -await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)); -// → false -await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); -// → false -await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)); +await page.evaluate(() => matchMedia('print').matches)); // → true -await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); -// → false ``` -#### page.emulateMediaType(type) -- `type` Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation. -- returns: <[Promise]> - ```js -await page.evaluate(() => matchMedia('screen').matches)); +await page.emulateMedia({ features: [{ name: 'prefers-color-scheme', value: 'dark' }] }); +await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)); // → true -await page.evaluate(() => matchMedia('print').matches)); -// → true - -await page.emulateMediaType('print'); -await page.evaluate(() => matchMedia('screen').matches)); +await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)); +// → false +await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); // → false -await page.evaluate(() => matchMedia('print').matches)); -// → true -await page.emulateMediaType(null); -await page.evaluate(() => matchMedia('screen').matches)); +await page.emulateMedia({ features: [{ name: 'prefers-reduced-motion', value: 'reduce' }] }); +await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)); // → true -await page.evaluate(() => matchMedia('print').matches)); +await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); +// → false + +await page.emulateMedia({ features: [ + { name: 'prefers-color-scheme', value: 'dark' }, + { name: 'prefers-reduced-motion', value: 'reduce' }, +] }); +await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)); // → true +await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)); +// → false +await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); +// → false +await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)); +// → true +await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)); +// → false ``` #### page.emulateTimezone(timezoneId) @@ -2366,13 +2356,13 @@ Dispatches a `mouseup` event. > **NOTE** Generating a pdf is currently only supported in Chrome headless. -`pdf.generate()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call [page.emulateMedia('screen')](#pageemulatemediamediatype) before calling `pdf.generate()`: +`pdf.generate()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call [page.emulateMedia({ type: 'screen' })](#pageemulatemedia) before calling `pdf.generate()`: > **NOTE** By default, `pdf.generate()` generates a pdf with modified colors for printing. Use the [`-webkit-print-color-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust) property to force rendering of exact colors. ```js // Generates a PDF with 'screen' media type. -await page.emulateMedia('screen'); +await page.emulateMedia({ type: 'screen' }); await page.pdf.generate({path: 'page.pdf'}); ``` diff --git a/src/chromium/Page.ts b/src/chromium/Page.ts index cae5d300ac..946ce3f6b0 100644 --- a/src/chromium/Page.ts +++ b/src/chromium/Page.ts @@ -532,26 +532,15 @@ export class Page extends EventEmitter { await this._client.send('Page.setBypassCSP', { enabled }); } - async emulateMedia(type: string | null) { - return this.emulateMediaType(type); - } - - async emulateMediaType(type: string | null) { - assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type); - await this._client.send('Emulation.setEmulatedMedia', {media: type || ''}); - } - - async emulateMediaFeatures(features: MediaFeature[] | null) { - if (features === null) - await this._client.send('Emulation.setEmulatedMedia', {features: null}); - if (Array.isArray(features)) { - features.every(mediaFeature => { + async emulateMedia(options: { type?: string, features?: MediaFeature[] }) { + assert(options.type === 'screen' || options.type === 'print' || options.type === undefined, 'Unsupported media type: ' + options.type); + if (options.features) { + options.features.forEach(mediaFeature => { const name = mediaFeature.name; assert(/^prefers-(?:color-scheme|reduced-motion)$/.test(name), 'Unsupported media feature: ' + name); - return true; }); - await this._client.send('Emulation.setEmulatedMedia', {features: features}); } + await this._client.send('Emulation.setEmulatedMedia', { media: options.type, features: options.features }); } async emulateTimezone(timezoneId: string | null) { diff --git a/src/firefox/Page.ts b/src/firefox/Page.ts index 5fd33a151c..0d67c6b4a4 100644 --- a/src/firefox/Page.ts +++ b/src/firefox/Page.ts @@ -144,13 +144,10 @@ export class Page extends EventEmitter { await this._networkManager.setExtraHTTPHeaders(headers); } - async emulateMedia(type: string): Promise { - await this.emulateMediaType(type); - } - - async emulateMediaType(type: string | null) { - assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type); - await this._session.send('Page.setEmulatedMedia', {media: type || ''}); + async emulateMedia(options: { type?: string, features?: MediaFeature[] }) { + assert(!options.features, 'Media feature emulation is not supported'); + assert(options.type === 'screen' || options.type === 'print' || options.type === undefined, 'Unsupported media type: ' + options.type); + await this._session.send('Page.setEmulatedMedia', { media: options.type || '' }); } async exposeFunction(name: string, playwrightFunction: Function) { @@ -622,4 +619,9 @@ export type Viewport = { isMobile?: boolean; isLandscape?: boolean; hasTouch?: boolean; -} \ No newline at end of file +} + +type MediaFeature = { + name: string, + value: string +} diff --git a/src/webkit/Page.ts b/src/webkit/Page.ts index d0b1ed3a9a..73187f09cf 100644 --- a/src/webkit/Page.ts +++ b/src/webkit/Page.ts @@ -323,13 +323,10 @@ export class Page extends EventEmitter { ]); } - async emulateMedia(type: string | null) { - return this.emulateMediaType(type); - } - - async emulateMediaType(type: string | null) { - assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type); - await this._session.send('Page.setEmulatedMedia', {media: type || ''}); + async emulateMedia(options: { type?: string, features?: MediaFeature[] }) { + assert(!options.features, 'Media feature emulation is not supported'); + assert(options.type === 'screen' || options.type === 'print' || options.type === undefined, 'Unsupported media type: ' + options.type); + await this._session.send('Page.setEmulatedMedia', { media: options.type }); } async setViewport(viewport: Viewport) { @@ -555,3 +552,8 @@ export class ConsoleMessage { return this._location; } } + +type MediaFeature = { + name: string, + value: string +} diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 7a64cfe565..6bac8f825e 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -15,10 +15,10 @@ */ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME, WEBKIT}) { - const {describe, xdescribe, fdescribe} = testRunner; + let {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - + describe = fdescribe; const iPhone = playwright.devices['iPhone 6']; const iPhoneLandscape = playwright.devices['iPhone 6 landscape']; @@ -97,53 +97,47 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); }); - describe('Page.emulateMedia', function() { - it.skip(WEBKIT)('should be an alias for Page.emulateMediaType', async({page, server}) => { - expect(page.emulateMedia).toEqual(page.emulateMediaType); - }); - }); - - describe.skip(WEBKIT)('Page.emulateMediaType', function() { + describe.skip(WEBKIT)('Page.emulateMedia type', function() { it('should work', async({page, server}) => { expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); - await page.emulateMediaType('print'); + await page.emulateMedia({ type: 'print' }); expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true); - await page.emulateMediaType(null); + await page.emulateMedia({}); expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); }); - it('should throw in case of bad argument', async({page, server}) => { + it('should throw in case of bad type argument', async({page, server}) => { let error = null; - await page.emulateMediaType('bad').catch(e => error = e); + await page.emulateMedia({ type: 'bad' }).catch(e => error = e); expect(error.message).toBe('Unsupported media type: bad'); }); }); - describe.skip(FFOX || WEBKIT)('Page.emulateMediaFeatures', function() { + describe.skip(FFOX || WEBKIT)('Page.emulateMedia features', function() { it('should work', async({page, server}) => { - await page.emulateMediaFeatures([ + await page.emulateMedia({ features: [ { name: 'prefers-reduced-motion', value: 'reduce' }, - ]); + ] }); expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false); - await page.emulateMediaFeatures([ + await page.emulateMedia({ features: [ { name: 'prefers-color-scheme', value: 'light' }, - ]); + ] }); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); - await page.emulateMediaFeatures([ + await page.emulateMedia({ features: [ { name: 'prefers-color-scheme', value: 'dark' }, - ]); + ] }); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); - await page.emulateMediaFeatures([ + await page.emulateMedia({ features: [ { name: 'prefers-reduced-motion', value: 'reduce' }, { name: 'prefers-color-scheme', value: 'light' }, - ]); + ] }); expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); @@ -152,7 +146,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME }); it('should throw in case of bad argument', async({page, server}) => { let error = null; - await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e); + await page.emulateMedia({ features: [{ name: 'bad', value: '' }] }).catch(e => error = e); expect(error.message).toBe('Unsupported media feature: bad'); }); }); diff --git a/test/test.js b/test/test.js index 99d9c1e1ff..b3fa3c9282 100644 --- a/test/test.js +++ b/test/test.js @@ -72,10 +72,6 @@ beforeEach(async({server, httpsServer}) => { httpsServer.reset(); }); -const CHROMIUM_NO_COVERAGE = new Set([ - 'page.emulateMedia', // Legacy alias for `page.emulateMediaType`. -]); - if (process.env.BROWSER === 'firefox') { describe('Firefox', () => { require('./playwright.spec.js').addTests({ @@ -100,7 +96,7 @@ if (process.env.BROWSER === 'firefox') { testRunner, }); if (process.env.COVERAGE) - utils.recordAPICoverage(testRunner, require('../lib/api').Chromium, require('../lib/chromium/events').Events, CHROMIUM_NO_COVERAGE); + utils.recordAPICoverage(testRunner, require('../lib/api').Chromium, require('../lib/chromium/events').Events); }); } diff --git a/test/utils.js b/test/utils.js index c76241f53e..ceea2f0c51 100644 --- a/test/utils.js +++ b/test/utils.js @@ -55,7 +55,7 @@ function traceAPICoverage(apiCoverage, events, className, classType) { } const utils = module.exports = { - recordAPICoverage: function(testRunner, api, events, disabled) { + recordAPICoverage: function(testRunner, api, events) { const coverage = new Map(); for (const [className, classType] of Object.entries(api)) traceAPICoverage(coverage, events, className, classType); @@ -63,7 +63,7 @@ const utils = module.exports = { testRunner.it('should call all API methods', () => { const missingMethods = []; for (const method of coverage.keys()) { - if (!coverage.get(method) && !disabled.has(method)) + if (!coverage.get(method)) missingMethods.push(method); } if (missingMethods.length)