From 25ace3c238bedb46abab68a148be28db7184596d Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Mon, 7 Oct 2019 09:09:00 +0200 Subject: [PATCH 1/6] feat(api): add page.emulateMedia{Type,Features} Requires Chromium v79.0.3929.0+. --- docs/api.md | 69 ++++++++++++++++++++++++++++++++++++++++-- lib/Page.js | 30 +++++++++++++++--- test/emulation.spec.js | 57 ++++++++++++++++++++++++++++------ 3 files changed, 139 insertions(+), 17 deletions(-) diff --git a/docs/api.md b/docs/api.md index aa78657c39865..c69dc74aa0f9c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -108,7 +108,8 @@ * [page.coverage](#pagecoverage) * [page.deleteCookie(...cookies)](#pagedeletecookiecookies) * [page.emulate(options)](#pageemulateoptions) - * [page.emulateMedia(mediaType)](#pageemulatemediamediatype) + * [page.emulateMediaFeatures(features)](#pageemulatemediafeaturesfeatures) + * [page.emulateMediaType(type)](#pageemulatemediatypetype) * [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args) * [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args) * [page.evaluateOnNewDocument(pageFunction[, ...args])](#pageevaluateonnewdocumentpagefunction-args) @@ -1276,10 +1277,72 @@ puppeteer.launch().then(async browser => { List of all available devices is available in the source code: [DeviceDescriptors.js](https://github.com/GoogleChrome/puppeteer/blob/master/lib/DeviceDescriptors.js). -#### page.emulateMedia(mediaType) -- `mediaType` Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables media emulation. +#### 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. - returns: <[Promise]> +```js +await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]); +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.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)); +// → 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)); +// → false + +await page.emulateMediaFeatures([ + { 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.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)); +// → true +await page.evaluate(() => matchMedia('print').matches)); +// → true + +await page.emulateMediaType('print'); +await page.evaluate(() => matchMedia('screen').matches)); +// → false +await page.evaluate(() => matchMedia('print').matches)); +// → true + +await page.emulateMediaType(null); +await page.evaluate(() => matchMedia('screen').matches)); +// → true +await page.evaluate(() => matchMedia('print').matches)); +// → true +``` + #### page.evaluate(pageFunction[, ...args]) - `pageFunction` <[function]|[string]> Function to be evaluated in the page context - `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction` diff --git a/lib/Page.js b/lib/Page.js index 0bdd217c8291f..a0d4ef8196725 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -801,11 +801,27 @@ class Page extends EventEmitter { } /** - * @param {?string} mediaType + * @param {?string} type */ - async emulateMedia(mediaType) { - assert(mediaType === 'screen' || mediaType === 'print' || mediaType === null, 'Unsupported media type: ' + mediaType); - await this._client.send('Emulation.setEmulatedMedia', {media: mediaType || ''}); + async emulateMediaType(type) { + assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type); + await this._client.send('Emulation.setEmulatedMedia', {media: type || ''}); + } + + /** + * @param {?Array} features + */ + async emulateMediaFeatures(features) { + if (features === null) + await this._client.send('Emulation.setEmulatedMedia', {features: null}); + if (Array.isArray(features)) { + features.every(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}); + } } /** @@ -1161,6 +1177,12 @@ class Page extends EventEmitter { * @property {string=} encoding */ +/** + * @typedef {Object} MediaFeature + * @property {string} name + * @property {string} value + */ + /** @type {!Set} */ const supportedMetrics = new Set([ 'Timestamp', diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 485394cc89251..368c8a1b011e5 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -97,21 +97,58 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe('Page.emulateMedia', function() { + describe('Page.emulateMediaType', function() { it('should work', async({page, server}) => { - expect(await page.evaluate(() => window.matchMedia('screen').matches)).toBe(true); - expect(await page.evaluate(() => window.matchMedia('print').matches)).toBe(false); - await page.emulateMedia('print'); - expect(await page.evaluate(() => window.matchMedia('screen').matches)).toBe(false); - expect(await page.evaluate(() => window.matchMedia('print').matches)).toBe(true); - await page.emulateMedia(null); - expect(await page.evaluate(() => window.matchMedia('screen').matches)).toBe(true); - expect(await page.evaluate(() => window.matchMedia('print').matches)).toBe(false); + expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); + expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); + await page.emulateMediaType('print'); + expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false); + expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true); + await page.emulateMediaType(null); + 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}) => { let error = null; - await page.emulateMedia('bad').catch(e => error = e); + await page.emulateMediaType('bad').catch(e => error = e); expect(error.message).toBe('Unsupported media type: bad'); }); }); + + describe('Page.emulateMediaFeatures', function() { + it('should work', async({page, server}) => { + await page.emulateMediaFeatures([ + { 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([ + { 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([ + { 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([ + { 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); + 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); + }); + it('should throw in case of bad argument', async({page, server}) => { + let error = null; + await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e); + expect(error.message).toBe('Unsupported media feature: bad'); + }); + }); + }; From be39b873bde2c0fb1ff854ed6a57bd13343cc02e Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Thu, 17 Oct 2019 09:58:44 +0200 Subject: [PATCH 2/6] Expose Page.emulateMedia as a deprecated alias --- lib/Page.js | 3 +++ test/emulation.spec.js | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lib/Page.js b/lib/Page.js index a0d4ef8196725..a7928a0f97185 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -1132,6 +1132,9 @@ class Page extends EventEmitter { } } +// Expose alias for deprecated method. +Page.prototype.emulateMedia = Page.prototype.emulateMediaType; + /** * @typedef {Object} PDFOptions * @property {number=} scale diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 368c8a1b011e5..3adeeb5c2a9ad 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -97,6 +97,12 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); + describe('Page.emulateMedia', function() { + fit('should be an alias for Page.emulateMediaType', async({page, server}) => { + expect(page.emulateMedia).toEqual(page.emulateMediaType); + }); + }); + describe('Page.emulateMediaType', function() { it('should work', async({page, server}) => { expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); From 36d01d5744ff10b53ec13a6f6aba3340d76b3205 Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Thu, 17 Oct 2019 10:04:50 +0200 Subject: [PATCH 3/6] Document legacy alias --- docs/api.md | 7 +++++++ test/emulation.spec.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index c69dc74aa0f9c..14ade7388e337 100644 --- a/docs/api.md +++ b/docs/api.md @@ -108,6 +108,7 @@ * [page.coverage](#pagecoverage) * [page.deleteCookie(...cookies)](#pagedeletecookiecookies) * [page.emulate(options)](#pageemulateoptions) + * [page.emulateMedia(type)](#pageemulatemediatype) * [page.emulateMediaFeatures(features)](#pageemulatemediafeaturesfeatures) * [page.emulateMediaType(type)](#pageemulatemediatypetype) * [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args) @@ -1277,6 +1278,12 @@ puppeteer.launch().then(async browser => { List of all available devices is available in the source code: [DeviceDescriptors.js](https://github.com/GoogleChrome/puppeteer/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)`](#pageemulatemediamediatypetype) 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'`. diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 3adeeb5c2a9ad..4c65e6e3f5329 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -98,7 +98,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); describe('Page.emulateMedia', function() { - fit('should be an alias for Page.emulateMediaType', async({page, server}) => { + it('should be an alias for Page.emulateMediaType', async({page, server}) => { expect(page.emulateMedia).toEqual(page.emulateMediaType); }); }); From aded03398cbd7c1fcc14ea795bcaeb714a9bce9b Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Wed, 23 Oct 2019 13:10:58 +0200 Subject: [PATCH 4/6] Add coverage exception for legacy alias --- test/test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.js b/test/test.js index 8e70d98886fb8..55c5cc657bf0d 100644 --- a/test/test.js +++ b/test/test.js @@ -73,6 +73,7 @@ beforeEach(async({server, httpsServer}) => { }); const CHROMIUM_NO_COVERAGE = new Set([ + 'page.emulateMedia', // Legacy alias for `page.emulateMediaType`. ]); if (process.env.BROWSER === 'firefox') { From cf791f46f2fe374d6f5779f37f779fa8e18e672c Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Wed, 23 Oct 2019 13:22:58 +0200 Subject: [PATCH 5/6] Fix typo --- docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 14ade7388e337..18106ded657b9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1282,7 +1282,7 @@ List of all available devices is available in the source code: [DeviceDescriptor - `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)`](#pageemulatemediamediatypetype) instead. +**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: From aac21181f9620ab696344efd18d19cd14d342e06 Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Wed, 23 Oct 2019 13:36:21 +0200 Subject: [PATCH 6/6] Update puppeteer-firefox accordingly --- experimental/puppeteer-firefox/lib/Page.js | 11 +++++++---- test/emulation.spec.js | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/experimental/puppeteer-firefox/lib/Page.js b/experimental/puppeteer-firefox/lib/Page.js index ad7019b0ff5fd..9e4fa5e6d46cb 100644 --- a/experimental/puppeteer-firefox/lib/Page.js +++ b/experimental/puppeteer-firefox/lib/Page.js @@ -150,11 +150,11 @@ class Page extends EventEmitter { } /** - * @param {?string} mediaType + * @param {?string} type */ - async emulateMedia(mediaType) { - assert(mediaType === 'screen' || mediaType === 'print' || mediaType === null, 'Unsupported media type: ' + mediaType); - await this._session.send('Page.setEmulatedMedia', {media: mediaType || ''}); + async emulateMediaType(type) { + assert(type === 'screen' || type === 'print' || type === null, 'Unsupported media type: ' + type); + await this._session.send('Page.setEmulatedMedia', {media: type || ''}); } /** @@ -747,6 +747,9 @@ class Page extends EventEmitter { } } +// Expose alias for deprecated method. +Page.prototype.emulateMedia = Page.prototype.emulateMediaType; + class ConsoleMessage { /** * @param {string} type diff --git a/test/emulation.spec.js b/test/emulation.spec.js index 4c65e6e3f5329..6a9bd501c7846 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -121,7 +121,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe('Page.emulateMediaFeatures', function() { + describe_fails_ffox('Page.emulateMediaFeatures', function() { it('should work', async({page, server}) => { await page.emulateMediaFeatures([ { name: 'prefers-reduced-motion', value: 'reduce' },