mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-11 12:33:45 +03:00
feat(emulate): implement emulateMedia color scheme in FF (#71)
This commit is contained in:
parent
860915b9da
commit
2e581f1625
@ -1 +1 @@
|
||||
1001
|
||||
1002
|
||||
|
@ -1,6 +1,6 @@
|
||||
From acb1ee0450ffe7739ed96e556095fb0f1c5d60d0 Mon Sep 17 00:00:00 2001
|
||||
From 533a4ce6515be3665e44ddeae30ecf5052a17191 Mon Sep 17 00:00:00 2001
|
||||
From: Pavel <pavel.feldman@gmail.com>
|
||||
Date: Mon, 25 Nov 2019 13:47:08 -0800
|
||||
Date: Mon, 25 Nov 2019 14:56:33 -0800
|
||||
Subject: [PATCH] chore: bootstrap
|
||||
|
||||
---
|
||||
@ -23,7 +23,7 @@ Subject: [PATCH] chore: bootstrap
|
||||
testing/juggler/content/ContentSession.js | 63 ++
|
||||
testing/juggler/content/FrameTree.js | 232 ++++++
|
||||
testing/juggler/content/NetworkMonitor.js | 62 ++
|
||||
testing/juggler/content/PageAgent.js | 638 +++++++++++++++++
|
||||
testing/juggler/content/PageAgent.js | 644 +++++++++++++++++
|
||||
testing/juggler/content/RuntimeAgent.js | 468 ++++++++++++
|
||||
testing/juggler/content/ScrollbarManager.js | 85 +++
|
||||
.../juggler/content/floating-scrollbars.css | 47 ++
|
||||
@ -37,7 +37,7 @@ Subject: [PATCH] chore: bootstrap
|
||||
testing/juggler/protocol/NetworkHandler.js | 154 ++++
|
||||
testing/juggler/protocol/PageHandler.js | 277 ++++++++
|
||||
testing/juggler/protocol/PrimitiveTypes.js | 143 ++++
|
||||
testing/juggler/protocol/Protocol.js | 669 ++++++++++++++++++
|
||||
testing/juggler/protocol/Protocol.js | 670 ++++++++++++++++++
|
||||
testing/juggler/protocol/RuntimeHandler.js | 41 ++
|
||||
testing/juggler/protocol/TargetHandler.js | 75 ++
|
||||
.../statusfilter/nsBrowserStatusFilter.cpp | 12 +-
|
||||
@ -46,7 +46,7 @@ Subject: [PATCH] chore: bootstrap
|
||||
uriloader/base/nsDocLoader.h | 5 +
|
||||
uriloader/base/nsIWebProgress.idl | 7 +-
|
||||
uriloader/base/nsIWebProgressListener2.idl | 23 +
|
||||
42 files changed, 4579 insertions(+), 7 deletions(-)
|
||||
42 files changed, 4586 insertions(+), 7 deletions(-)
|
||||
create mode 100644 testing/juggler/BrowserContextManager.js
|
||||
create mode 100644 testing/juggler/Helper.js
|
||||
create mode 100644 testing/juggler/NetworkObserver.js
|
||||
@ -1795,10 +1795,10 @@ index 000000000000..2508cce41565
|
||||
+
|
||||
diff --git a/testing/juggler/content/PageAgent.js b/testing/juggler/content/PageAgent.js
|
||||
new file mode 100644
|
||||
index 000000000000..0e47a99eda47
|
||||
index 000000000000..34b799b53113
|
||||
--- /dev/null
|
||||
+++ b/testing/juggler/content/PageAgent.js
|
||||
@@ -0,0 +1,638 @@
|
||||
@@ -0,0 +1,644 @@
|
||||
+"use strict";
|
||||
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
+const Ci = Components.interfaces;
|
||||
@ -1857,12 +1857,18 @@ index 000000000000..0e47a99eda47
|
||||
+ this._scrollbarManager.setFloatingScrollbars(isMobile);
|
||||
+ }
|
||||
+
|
||||
+ async setEmulatedMedia({media}) {
|
||||
+ async setEmulatedMedia({type, colorScheme}) {
|
||||
+ const docShell = this._frameTree.mainFrame().docShell();
|
||||
+ if (media)
|
||||
+ docShell.contentViewer.emulateMedium(media);
|
||||
+ else
|
||||
+ docShell.contentViewer.stopEmulatingMedium();
|
||||
+ const cv = docShell.contentViewer;
|
||||
+ if (type === '')
|
||||
+ cv.stopEmulatingMedium();
|
||||
+ else if (type)
|
||||
+ cv.emulateMedium(type);
|
||||
+ switch (colorScheme) {
|
||||
+ case 'light': cv.emulatePrefersColorScheme(cv.PREFERS_COLOR_SCHEME_LIGHT); break;
|
||||
+ case 'dark': cv.emulatePrefersColorScheme(cv.PREFERS_COLOR_SCHEME_DARK); break;
|
||||
+ case 'no-preference': cv.emulatePrefersColorScheme(cv.PREFERS_COLOR_SCHEME_NO_PREFERENCE); break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ async setUserAgent({userAgent}) {
|
||||
@ -4123,10 +4129,10 @@ index 000000000000..78b6601b91d0
|
||||
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
|
||||
diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js
|
||||
new file mode 100644
|
||||
index 000000000000..0b2044e057a4
|
||||
index 000000000000..1ed27df14a1a
|
||||
--- /dev/null
|
||||
+++ b/testing/juggler/protocol/Protocol.js
|
||||
@@ -0,0 +1,669 @@
|
||||
@@ -0,0 +1,670 @@
|
||||
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
|
||||
+
|
||||
+// Protocol-specific types.
|
||||
@ -4617,7 +4623,8 @@ index 000000000000..0b2044e057a4
|
||||
+ },
|
||||
+ 'setEmulatedMedia': {
|
||||
+ params: {
|
||||
+ media: t.Enum(['screen', 'print', '']),
|
||||
+ type: t.Optional(t.Enum(['screen', 'print', ''])),
|
||||
+ colorScheme: t.Optional(t.Enum(['dark', 'light', 'no-preference'])),
|
||||
+ },
|
||||
+ },
|
||||
+ 'setCacheDisabled': {
|
||||
|
27
docs/api.md
27
docs/api.md
@ -1240,9 +1240,7 @@ List of all available devices is available in the source code: [DeviceDescriptor
|
||||
#### page.emulateMedia(options)
|
||||
- `options` <[Object]>
|
||||
- `type` <?[string]> 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` <?[Array]<[Object]>> 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.
|
||||
- `colorScheme` <?[string]> Optional. Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
|
||||
- returns: <[Promise]>
|
||||
|
||||
```js
|
||||
@ -1265,34 +1263,13 @@ await page.evaluate(() => matchMedia('print').matches));
|
||||
```
|
||||
|
||||
```js
|
||||
await page.emulateMedia({ features: [{ name: 'prefers-color-scheme', value: 'dark' }] });
|
||||
await page.emulateMedia({ colorScheme: '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.emulateMedia({ features: [{ 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.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)
|
||||
|
@ -20,7 +20,7 @@ import * as fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import * as path from 'path';
|
||||
import { assert, debugError, helper } from '../helper';
|
||||
import { ClickOptions, MultiClickOptions, PointerActionOptions, SelectOption } from '../input';
|
||||
import { ClickOptions, MultiClickOptions, PointerActionOptions, SelectOption, mediaTypes, mediaColorSchemes } from '../input';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { Browser } from './Browser';
|
||||
import { BrowserContext } from './BrowserContext';
|
||||
@ -78,6 +78,7 @@ export class Page extends EventEmitter {
|
||||
private _fileChooserInterceptionIsDisabled = false;
|
||||
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||
private _disconnectPromise: Promise<Error> | undefined;
|
||||
private _emulatedMediaType: string | undefined;
|
||||
|
||||
static async create(client: CDPSession, target: Target, ignoreHTTPSErrors: boolean, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
||||
const page = new Page(client, target, ignoreHTTPSErrors, screenshotTaskQueue);
|
||||
@ -534,15 +535,15 @@ export class Page extends EventEmitter {
|
||||
await this._client.send('Page.setBypassCSP', { enabled });
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
await this._client.send('Emulation.setEmulatedMedia', { media: options.type, features: options.features });
|
||||
async emulateMedia(options: {
|
||||
type?: string,
|
||||
colorScheme?: 'dark' | 'light' | 'no-preference' }) {
|
||||
assert(!options.type || mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
||||
assert(!options.colorScheme || mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
||||
const media = typeof options.type === 'undefined' ? this._emulatedMediaType : options.type;
|
||||
const features = typeof options.colorScheme === 'undefined' ? [] : [{ name: 'prefers-color-scheme', value: options.colorScheme }];
|
||||
await this._client.send('Emulation.setEmulatedMedia', { media: media || '', features });
|
||||
this._emulatedMediaType = options.type;
|
||||
}
|
||||
|
||||
async emulateTimezone(timezoneId: string | null) {
|
||||
|
@ -15,7 +15,6 @@ import { Mouse, RawKeyboardImpl } from './Input';
|
||||
import { createHandle, ElementHandle, JSHandle } from './JSHandle';
|
||||
import { NavigationWatchdog } from './NavigationWatchdog';
|
||||
import { NetworkManager, NetworkManagerEvents, Request, Response } from './NetworkManager';
|
||||
import { ClickOptions, MultiClickOptions } from '../input';
|
||||
import * as input from '../input';
|
||||
|
||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||
@ -151,10 +150,12 @@ export class Page extends EventEmitter {
|
||||
await this._networkManager.setExtraHTTPHeaders(headers);
|
||||
}
|
||||
|
||||
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 emulateMedia(options: {
|
||||
type?: string,
|
||||
colorScheme?: 'dark' | 'light' | 'no-preference' }) {
|
||||
assert(!options.type || input.mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
||||
assert(!options.colorScheme || input.mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
||||
await this._session.send('Page.setEmulatedMedia', options);
|
||||
}
|
||||
|
||||
async exposeFunction(name: string, playwrightFunction: Function) {
|
||||
@ -478,15 +479,15 @@ export class Page extends EventEmitter {
|
||||
return this.mainFrame().addStyleTag(options);
|
||||
}
|
||||
|
||||
click(selector: string, options?: ClickOptions) {
|
||||
click(selector: string, options?: input.ClickOptions) {
|
||||
return this.mainFrame().click(selector, options);
|
||||
}
|
||||
|
||||
dblclick(selector: string, options?: MultiClickOptions) {
|
||||
dblclick(selector: string, options?: input.MultiClickOptions) {
|
||||
return this.mainFrame().dblclick(selector, options);
|
||||
}
|
||||
|
||||
tripleclick(selector: string, options?: MultiClickOptions) {
|
||||
tripleclick(selector: string, options?: input.MultiClickOptions) {
|
||||
return this.mainFrame().tripleclick(selector, options);
|
||||
}
|
||||
|
||||
|
@ -310,3 +310,6 @@ export const fillFunction = (element: HTMLElement) => {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const mediaTypes = new Set(['screen', 'print']);
|
||||
export const mediaColorSchemes = new Set(['dark', 'light', 'no-preference']);
|
||||
|
@ -19,7 +19,7 @@ import { EventEmitter } from 'events';
|
||||
import * as fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import { assert, debugError, helper, RegisteredListener } from '../helper';
|
||||
import { ClickOptions, MultiClickOptions } from '../input';
|
||||
import { ClickOptions, mediaColorSchemes, mediaTypes, MultiClickOptions } from '../input';
|
||||
import { TimeoutSettings } from '../TimeoutSettings';
|
||||
import { Browser, BrowserContext } from './Browser';
|
||||
import { TargetSession, TargetSessionEvents } from './Connection';
|
||||
@ -56,6 +56,7 @@ export class Page extends EventEmitter {
|
||||
private _workers = new Map<string, Worker>();
|
||||
private _disconnectPromise: Promise<Error> | undefined;
|
||||
private _sessionListeners: RegisteredListener[] = [];
|
||||
private _emulatedMediaType: string | undefined;
|
||||
|
||||
static async create(session: TargetSession, target: Target, defaultViewport: Viewport | null, screenshotTaskQueue: TaskQueue): Promise<Page> {
|
||||
const page = new Page(session, target, screenshotTaskQueue);
|
||||
@ -326,10 +327,15 @@ export class Page extends EventEmitter {
|
||||
]);
|
||||
}
|
||||
|
||||
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 emulateMedia(options: {
|
||||
type?: string | null,
|
||||
colorScheme?: 'dark' | 'light' | 'no-preference' | null }) {
|
||||
assert(!options.type || mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
||||
assert(!options.colorScheme || mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
||||
assert(!options.colorScheme, 'Media feature emulation is not supported');
|
||||
const media = typeof options.type === 'undefined' ? this._emulatedMediaType : options.type;
|
||||
await this._session.send('Page.setEmulatedMedia', { media: media || '' });
|
||||
this._emulatedMediaType = options.type;
|
||||
}
|
||||
|
||||
async setViewport(viewport: Viewport) {
|
||||
|
@ -104,6 +104,9 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({});
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({ type: '' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
});
|
||||
@ -114,39 +117,25 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX || WEBKIT)('Page.emulateMedia features', function() {
|
||||
describe.skip(WEBKIT)('Page.emulateMedia colorScheme', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
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.emulateMedia({ features: [
|
||||
{ name: 'prefers-color-scheme', value: 'light' },
|
||||
] });
|
||||
await page.emulateMedia({ colorScheme: '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.emulateMedia({ features: [
|
||||
{ name: 'prefers-color-scheme', value: 'dark' },
|
||||
] });
|
||||
await page.emulateMedia({ colorScheme: '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.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);
|
||||
await page.emulateMedia({ colorScheme: 'no-preference' });
|
||||
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);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(true);
|
||||
});
|
||||
it('should throw in case of bad argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ features: [{ name: 'bad', value: '' }] }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported media feature: bad');
|
||||
await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported color scheme: bad');
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user