Skip to content

Commit

Permalink
feat(api): add page.emulateMedia{Type,Features}
Browse files Browse the repository at this point in the history
Requires Chromium v79.0.3929.0+.
  • Loading branch information
mathiasbynens committed Oct 17, 2019
1 parent 11ff374 commit 2d0264d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 18 deletions.
69 changes: 66 additions & 3 deletions docs/api.md
Expand Up @@ -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)
Expand Down Expand Up @@ -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` <?[string]> 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` <?[Array]<[Object]>> 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` <?[string]> 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`
Expand Down
30 changes: 26 additions & 4 deletions lib/Page.js
Expand Up @@ -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<MediaFeature>} 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});
}
}

/**
Expand Down Expand Up @@ -1161,6 +1177,12 @@ class Page extends EventEmitter {
* @property {string=} encoding
*/

/**
* @typedef {Object} MediaFeature
* @property {string} name
* @property {string} value
*/

/** @type {!Set<string>} */
const supportedMetrics = new Set([
'Timestamp',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -8,7 +8,7 @@
"node": ">=8.16.0"
},
"puppeteer": {
"chromium_revision": "686378"
"chromium_revision": "693954"
},
"scripts": {
"unit": "node test/test.js",
Expand Down
57 changes: 47 additions & 10 deletions test/emulation.spec.js
Expand Up @@ -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');
});
});

};

0 comments on commit 2d0264d

Please sign in to comment.