feat: added reduced motion media query emulation (#6646)
This commit is contained in:
parent
af2fec6bcf
commit
ba29e99ace
|
|
@ -8,12 +8,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1265",
|
"revision": "1266",
|
||||||
"installByDefault": true
|
"installByDefault": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox-stable",
|
"name": "firefox-stable",
|
||||||
"revision": "1255",
|
"revision": "1256",
|
||||||
"installByDefault": false
|
"installByDefault": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -970,6 +970,11 @@ Passing `null` disables CSS media emulation.
|
||||||
Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. Passing
|
Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. Passing
|
||||||
`null` disables color scheme emulation.
|
`null` disables color scheme emulation.
|
||||||
|
|
||||||
|
### option: Page.emulateMedia.reducedMotion
|
||||||
|
- `reducedMotion` <null|[ReducedMotion]<"reduce"|"no-preference">>
|
||||||
|
|
||||||
|
Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. Passing `null` disables reduced motion emulation.
|
||||||
|
|
||||||
## async method: Page.evalOnSelector
|
## async method: Page.evalOnSelector
|
||||||
* langs:
|
* langs:
|
||||||
- alias-python: eval_on_selector
|
- alias-python: eval_on_selector
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,12 @@ Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/W
|
||||||
Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
|
Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
|
||||||
[`method: Page.emulateMedia`] for more details. Defaults to `'light'`.
|
[`method: Page.emulateMedia`] for more details. Defaults to `'light'`.
|
||||||
|
|
||||||
|
## context-option-reducedMotion
|
||||||
|
- `reducedMotion` <[ReducedMotion]<"reduce"|"no-preference">>
|
||||||
|
|
||||||
|
Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See [`method: Page.emulateMedia`] for more details. Defaults
|
||||||
|
to `'no-preference'`.
|
||||||
|
|
||||||
## context-option-logger
|
## context-option-logger
|
||||||
* langs: js
|
* langs: js
|
||||||
- `logger` <[Logger]>
|
- `logger` <[Logger]>
|
||||||
|
|
@ -578,6 +584,7 @@ using the [`method: AndroidDevice.setDefaultTimeout`] method.
|
||||||
- %%-context-option-offline-%%
|
- %%-context-option-offline-%%
|
||||||
- %%-context-option-httpcredentials-%%
|
- %%-context-option-httpcredentials-%%
|
||||||
- %%-context-option-colorscheme-%%
|
- %%-context-option-colorscheme-%%
|
||||||
|
- %%-context-option-reducedMotion-%%
|
||||||
- %%-context-option-logger-%%
|
- %%-context-option-logger-%%
|
||||||
- %%-context-option-videospath-%%
|
- %%-context-option-videospath-%%
|
||||||
- %%-context-option-videosize-%%
|
- %%-context-option-videosize-%%
|
||||||
|
|
|
||||||
|
|
@ -423,11 +423,12 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async emulateMedia(options: { media?: 'screen' | 'print' | null, colorScheme?: 'dark' | 'light' | 'no-preference' | null } = {}) {
|
async emulateMedia(options: { media?: 'screen' | 'print' | null, colorScheme?: 'dark' | 'light' | 'no-preference' | null, reducedMotion?: 'reduce' | 'no-preference' | null } = {}) {
|
||||||
return this._wrapApiCall('page.emulateMedia', async (channel: channels.PageChannel) => {
|
return this._wrapApiCall('page.emulateMedia', async (channel: channels.PageChannel) => {
|
||||||
await channel.emulateMedia({
|
await channel.emulateMedia({
|
||||||
media: options.media === null ? 'null' : options.media,
|
media: options.media === null ? 'null' : options.media,
|
||||||
colorScheme: options.colorScheme === null ? 'null' : options.colorScheme,
|
colorScheme: options.colorScheme === null ? 'null' : options.colorScheme,
|
||||||
|
reducedMotion: options.reducedMotion === null ? 'null' : options.reducedMotion,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> i
|
||||||
await this._page.emulateMedia({
|
await this._page.emulateMedia({
|
||||||
media: params.media === 'null' ? null : params.media,
|
media: params.media === 'null' ? null : params.media,
|
||||||
colorScheme: params.colorScheme === 'null' ? null : params.colorScheme,
|
colorScheme: params.colorScheme === 'null' ? null : params.colorScheme,
|
||||||
|
reducedMotion: params.reducedMotion === 'null' ? null : params.reducedMotion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ export type BrowserTypeLaunchPersistentContextParams = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
@ -382,6 +383,7 @@ export type BrowserTypeLaunchPersistentContextOptions = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
@ -471,6 +473,7 @@ export type BrowserNewContextParams = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
@ -527,6 +530,7 @@ export type BrowserNewContextOptions = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
@ -973,10 +977,12 @@ export type PageCloseResult = void;
|
||||||
export type PageEmulateMediaParams = {
|
export type PageEmulateMediaParams = {
|
||||||
media?: 'screen' | 'print' | 'null',
|
media?: 'screen' | 'print' | 'null',
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference' | 'null',
|
colorScheme?: 'dark' | 'light' | 'no-preference' | 'null',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference' | 'null',
|
||||||
};
|
};
|
||||||
export type PageEmulateMediaOptions = {
|
export type PageEmulateMediaOptions = {
|
||||||
media?: 'screen' | 'print' | 'null',
|
media?: 'screen' | 'print' | 'null',
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference' | 'null',
|
colorScheme?: 'dark' | 'light' | 'no-preference' | 'null',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference' | 'null',
|
||||||
};
|
};
|
||||||
export type PageEmulateMediaResult = void;
|
export type PageEmulateMediaResult = void;
|
||||||
export type PageExposeBindingParams = {
|
export type PageExposeBindingParams = {
|
||||||
|
|
@ -2934,6 +2940,7 @@ export type AndroidDeviceLaunchBrowserParams = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
@ -2978,6 +2985,7 @@ export type AndroidDeviceLaunchBrowserOptions = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
|
reducedMotion?: 'reduce' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_debugName?: string,
|
_debugName?: string,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,11 @@ ContextOptions:
|
||||||
- dark
|
- dark
|
||||||
- light
|
- light
|
||||||
- no-preference
|
- no-preference
|
||||||
|
reducedMotion:
|
||||||
|
type: enum?
|
||||||
|
literals:
|
||||||
|
- reduce
|
||||||
|
- no-preference
|
||||||
acceptDownloads: boolean?
|
acceptDownloads: boolean?
|
||||||
_debugName: string?
|
_debugName: string?
|
||||||
recordVideo:
|
recordVideo:
|
||||||
|
|
@ -717,6 +722,13 @@ Page:
|
||||||
- no-preference
|
- no-preference
|
||||||
# Reset emulated value to the system default.
|
# Reset emulated value to the system default.
|
||||||
- null
|
- null
|
||||||
|
reducedMotion:
|
||||||
|
type: enum?
|
||||||
|
literals:
|
||||||
|
- reduce
|
||||||
|
- no-preference
|
||||||
|
# Reset emulated value to the system default.
|
||||||
|
- null
|
||||||
|
|
||||||
exposeBinding:
|
exposeBinding:
|
||||||
parameters:
|
parameters:
|
||||||
|
|
@ -2374,6 +2386,11 @@ AndroidDevice:
|
||||||
- dark
|
- dark
|
||||||
- light
|
- light
|
||||||
- no-preference
|
- no-preference
|
||||||
|
reducedMotion:
|
||||||
|
type: enum?
|
||||||
|
literals:
|
||||||
|
- reduce
|
||||||
|
- no-preference
|
||||||
acceptDownloads: boolean?
|
acceptDownloads: boolean?
|
||||||
_debugName: string?
|
_debugName: string?
|
||||||
recordVideo:
|
recordVideo:
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
isMobile: tOptional(tBoolean),
|
isMobile: tOptional(tBoolean),
|
||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
||||||
|
reducedMotion: tOptional(tEnum(['reduce', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_debugName: tOptional(tString),
|
_debugName: tOptional(tString),
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
|
|
@ -289,6 +290,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
isMobile: tOptional(tBoolean),
|
isMobile: tOptional(tBoolean),
|
||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
||||||
|
reducedMotion: tOptional(tEnum(['reduce', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_debugName: tOptional(tString),
|
_debugName: tOptional(tString),
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
|
|
@ -410,6 +412,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
scheme.PageEmulateMediaParams = tObject({
|
scheme.PageEmulateMediaParams = tObject({
|
||||||
media: tOptional(tEnum(['screen', 'print', 'null'])),
|
media: tOptional(tEnum(['screen', 'print', 'null'])),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'null'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference', 'null'])),
|
||||||
|
reducedMotion: tOptional(tEnum(['reduce', 'no-preference', 'null'])),
|
||||||
});
|
});
|
||||||
scheme.PageExposeBindingParams = tObject({
|
scheme.PageExposeBindingParams = tObject({
|
||||||
name: tString,
|
name: tString,
|
||||||
|
|
@ -1128,6 +1131,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
isMobile: tOptional(tBoolean),
|
isMobile: tOptional(tBoolean),
|
||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
||||||
|
reducedMotion: tOptional(tEnum(['reduce', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_debugName: tOptional(tString),
|
_debugName: tOptional(tString),
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
|
|
|
||||||
|
|
@ -1006,8 +1006,13 @@ class FrameSession {
|
||||||
async _updateEmulateMedia(initial: boolean): Promise<void> {
|
async _updateEmulateMedia(initial: boolean): Promise<void> {
|
||||||
if (this._crPage._browserContext._browser.isClank())
|
if (this._crPage._browserContext._browser.isClank())
|
||||||
return;
|
return;
|
||||||
const colorScheme = this._page._state.colorScheme || this._crPage._browserContext._options.colorScheme || 'light';
|
const colorScheme = this._page._state.colorScheme === null ? '' : this._page._state.colorScheme;
|
||||||
const features = colorScheme ? [{ name: 'prefers-color-scheme', value: colorScheme }] : [];
|
const reducedMotion = this._page._state.reducedMotion === null ? '' : this._page._state.reducedMotion;
|
||||||
|
const features = [
|
||||||
|
{ name: 'prefers-color-scheme', value: colorScheme },
|
||||||
|
{ name: 'prefers-reduced-motion', value: reducedMotion },
|
||||||
|
];
|
||||||
|
// Empty string disables the override.
|
||||||
await this._client.send('Emulation.setEmulatedMedia', { media: this._page._state.mediaType || '', features });
|
await this._client.send('Emulation.setEmulatedMedia', { media: this._page._state.mediaType || '', features });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,14 @@ export class FFBrowserContext extends BrowserContext {
|
||||||
promises.push(this.setGeolocation(this._options.geolocation));
|
promises.push(this.setGeolocation(this._options.geolocation));
|
||||||
if (this._options.offline)
|
if (this._options.offline)
|
||||||
promises.push(this.setOffline(this._options.offline));
|
promises.push(this.setOffline(this._options.offline));
|
||||||
if (this._options.colorScheme)
|
promises.push(this._browser._connection.send('Browser.setColorScheme', {
|
||||||
promises.push(this._browser._connection.send('Browser.setColorScheme', { browserContextId, colorScheme: this._options.colorScheme }));
|
browserContextId,
|
||||||
|
colorScheme: this._options.colorScheme !== undefined ? this._options.colorScheme : 'light',
|
||||||
|
}));
|
||||||
|
promises.push(this._browser._connection.send('Browser.setReducedMotion', {
|
||||||
|
browserContextId,
|
||||||
|
reducedMotion: this._options.reducedMotion !== undefined ? this._options.reducedMotion : 'no-preference',
|
||||||
|
}));
|
||||||
if (this._options.recordVideo) {
|
if (this._options.recordVideo) {
|
||||||
promises.push(this._ensureVideosPath().then(() => {
|
promises.push(this._ensureVideosPath().then(() => {
|
||||||
return this._browser._connection.send('Browser.setVideoRecordingOptions', {
|
return this._browser._connection.send('Browser.setVideoRecordingOptions', {
|
||||||
|
|
|
||||||
|
|
@ -352,11 +352,13 @@ export class FFPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEmulateMedia(): Promise<void> {
|
async updateEmulateMedia(): Promise<void> {
|
||||||
const colorScheme = this._page._state.colorScheme || this._browserContext._options.colorScheme || 'light';
|
const colorScheme = this._page._state.colorScheme === null ? undefined : this._page._state.colorScheme;
|
||||||
|
const reducedMotion = this._page._state.reducedMotion === null ? undefined : this._page._state.reducedMotion;
|
||||||
await this._session.send('Page.setEmulatedMedia', {
|
await this._session.send('Page.setEmulatedMedia', {
|
||||||
// Empty string means reset.
|
// Empty string means reset.
|
||||||
type: this._page._state.mediaType === null ? '' : this._page._state.mediaType,
|
type: this._page._state.mediaType === null ? '' : this._page._state.mediaType,
|
||||||
colorScheme
|
colorScheme,
|
||||||
|
reducedMotion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ type PageState = {
|
||||||
emulatedSize: { screen: types.Size, viewport: types.Size } | null;
|
emulatedSize: { screen: types.Size, viewport: types.Size } | null;
|
||||||
mediaType: types.MediaType | null;
|
mediaType: types.MediaType | null;
|
||||||
colorScheme: types.ColorScheme | null;
|
colorScheme: types.ColorScheme | null;
|
||||||
|
reducedMotion: types.ReducedMotion | null;
|
||||||
extraHTTPHeaders: types.HeadersArray | null;
|
extraHTTPHeaders: types.HeadersArray | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -159,7 +160,8 @@ export class Page extends SdkObject {
|
||||||
this._state = {
|
this._state = {
|
||||||
emulatedSize: browserContext._options.viewport ? { viewport: browserContext._options.viewport, screen: browserContext._options.screen || browserContext._options.viewport } : null,
|
emulatedSize: browserContext._options.viewport ? { viewport: browserContext._options.viewport, screen: browserContext._options.screen || browserContext._options.viewport } : null,
|
||||||
mediaType: null,
|
mediaType: null,
|
||||||
colorScheme: null,
|
colorScheme: browserContext._options.colorScheme !== undefined ? browserContext._options.colorScheme : 'light',
|
||||||
|
reducedMotion: browserContext._options.reducedMotion !== undefined ? browserContext._options.reducedMotion : 'no-preference',
|
||||||
extraHTTPHeaders: null,
|
extraHTTPHeaders: null,
|
||||||
};
|
};
|
||||||
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
||||||
|
|
@ -359,15 +361,13 @@ export class Page extends SdkObject {
|
||||||
}), this._timeoutSettings.navigationTimeout(options));
|
}), this._timeoutSettings.navigationTimeout(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
async emulateMedia(options: { media?: types.MediaType | null, colorScheme?: types.ColorScheme | null }) {
|
async emulateMedia(options: { media?: types.MediaType | null, colorScheme?: types.ColorScheme | null, reducedMotion?: types.ReducedMotion | null }) {
|
||||||
if (options.media !== undefined)
|
|
||||||
assert(options.media === null || types.mediaTypes.has(options.media), 'media: expected one of (screen|print|null)');
|
|
||||||
if (options.colorScheme !== undefined)
|
|
||||||
assert(options.colorScheme === null || types.colorSchemes.has(options.colorScheme), 'colorScheme: expected one of (dark|light|no-preference|null)');
|
|
||||||
if (options.media !== undefined)
|
if (options.media !== undefined)
|
||||||
this._state.mediaType = options.media;
|
this._state.mediaType = options.media;
|
||||||
if (options.colorScheme !== undefined)
|
if (options.colorScheme !== undefined)
|
||||||
this._state.colorScheme = options.colorScheme;
|
this._state.colorScheme = options.colorScheme;
|
||||||
|
if (options.reducedMotion !== undefined)
|
||||||
|
this._state.reducedMotion = options.reducedMotion;
|
||||||
await this._delegate.updateEmulateMedia();
|
await this._delegate.updateEmulateMedia();
|
||||||
await this._doSlowMo();
|
await this._doSlowMo();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ export const mediaTypes: Set<MediaType> = new Set(['screen', 'print']);
|
||||||
export type ColorScheme = 'dark' | 'light' | 'no-preference';
|
export type ColorScheme = 'dark' | 'light' | 'no-preference';
|
||||||
export const colorSchemes: Set<ColorScheme> = new Set(['dark', 'light', 'no-preference']);
|
export const colorSchemes: Set<ColorScheme> = new Set(['dark', 'light', 'no-preference']);
|
||||||
|
|
||||||
|
export type ReducedMotion = 'no-preference' | 'reduce';
|
||||||
|
export const reducedMotions: Set<ReducedMotion> = new Set(['no-preference', 'reduce']);
|
||||||
|
|
||||||
export type DeviceDescriptor = {
|
export type DeviceDescriptor = {
|
||||||
userAgent: string,
|
userAgent: string,
|
||||||
viewport: Size,
|
viewport: Size,
|
||||||
|
|
@ -237,6 +240,7 @@ export type BrowserContextOptions = {
|
||||||
isMobile?: boolean,
|
isMobile?: boolean,
|
||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: ColorScheme,
|
colorScheme?: ColorScheme,
|
||||||
|
reducedMotion?: ReducedMotion,
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
|
|
|
||||||
|
|
@ -180,8 +180,8 @@ export class WKPage implements PageDelegate {
|
||||||
const contextOptions = this._browserContext._options;
|
const contextOptions = this._browserContext._options;
|
||||||
if (contextOptions.userAgent)
|
if (contextOptions.userAgent)
|
||||||
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
|
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
|
||||||
if (this._page._state.mediaType || this._page._state.colorScheme)
|
if (this._page._state.mediaType || this._page._state.colorScheme || this._page._state.reducedMotion)
|
||||||
promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
|
promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme, this._page._state.reducedMotion));
|
||||||
for (const world of ['main', 'utility'] as const) {
|
for (const world of ['main', 'utility'] as const) {
|
||||||
const bootstrapScript = this._calculateBootstrapScript(world);
|
const bootstrapScript = this._calculateBootstrapScript(world);
|
||||||
if (bootstrapScript.length)
|
if (bootstrapScript.length)
|
||||||
|
|
@ -580,17 +580,21 @@ export class WKPage implements PageDelegate {
|
||||||
await this._page._onFileChooserOpened(handle);
|
await this._page._onFileChooserOpened(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async _setEmulateMedia(session: WKSession, mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> {
|
private static async _setEmulateMedia(session: WKSession, mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null, reducedMotion: types.ReducedMotion | null): Promise<void> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
|
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
|
||||||
if (colorScheme !== null) {
|
let appearance: any = undefined;
|
||||||
let appearance: any = '';
|
switch (colorScheme) {
|
||||||
switch (colorScheme) {
|
case 'light': appearance = 'Light'; break;
|
||||||
case 'light': appearance = 'Light'; break;
|
case 'dark': appearance = 'Dark'; break;
|
||||||
case 'dark': appearance = 'Dark'; break;
|
|
||||||
}
|
|
||||||
promises.push(session.send('Page.setForcedAppearance', { appearance }));
|
|
||||||
}
|
}
|
||||||
|
promises.push(session.send('Page.setForcedAppearance', { appearance }));
|
||||||
|
let reducedMotionWk: any = undefined;
|
||||||
|
switch (reducedMotion) {
|
||||||
|
case 'reduce': reducedMotionWk = 'Reduce'; break;
|
||||||
|
case 'no-preference': reducedMotionWk = 'NoPreference'; break;
|
||||||
|
}
|
||||||
|
promises.push(session.send('Page.setForcedReducedMotion', { reducedMotion: reducedMotionWk }));
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -609,8 +613,9 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEmulateMedia(): Promise<void> {
|
async updateEmulateMedia(): Promise<void> {
|
||||||
const colorScheme = this._page._state.colorScheme || this._browserContext._options.colorScheme || 'light';
|
const colorScheme = this._page._state.colorScheme;
|
||||||
await this._forAllSessions(session => WKPage._setEmulateMedia(session, this._page._state.mediaType, colorScheme));
|
const reducedMotion = this._page._state.reducedMotion;
|
||||||
|
await this._forAllSessions(session => WKPage._setEmulateMedia(session, this._page._state.mediaType, colorScheme, reducedMotion));
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void> {
|
async setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,12 @@ it('should support colorScheme option', async ({launchPersistent}) => {
|
||||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support reducedMotion option', async ({launchPersistent}) => {
|
||||||
|
const {page} = await launchPersistent({reducedMotion: '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);
|
||||||
|
});
|
||||||
|
|
||||||
it('should support timezoneId option', async ({launchPersistent}) => {
|
it('should support timezoneId option', async ({launchPersistent}) => {
|
||||||
const {page} = await launchPersistent({locale: 'en-US', timezoneId: 'America/Jamaica'});
|
const {page} = await launchPersistent({locale: 'en-US', timezoneId: 'America/Jamaica'});
|
||||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||||
|
|
|
||||||
|
|
@ -114,3 +114,14 @@ it('should change the actual colors in css', async ({page}) => {
|
||||||
await page.emulateMedia({ colorScheme: 'light' });
|
await page.emulateMedia({ colorScheme: 'light' });
|
||||||
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emulate reduced motion', async ({page}) => {
|
||||||
|
expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(true);
|
||||||
|
await page.emulateMedia({ reducedMotion: '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({ reducedMotion: 'no-preference' });
|
||||||
|
expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: reduce)').matches)).toBe(false);
|
||||||
|
expect(await page.evaluate(() => matchMedia('(prefers-reduced-motion: no-preference)').matches)).toBe(true);
|
||||||
|
await page.emulateMedia({ reducedMotion: null });
|
||||||
|
});
|
||||||
|
|
|
||||||
34
types/types.d.ts
vendored
34
types/types.d.ts
vendored
|
|
@ -1679,6 +1679,12 @@ export interface Page {
|
||||||
* disables CSS media emulation.
|
* disables CSS media emulation.
|
||||||
*/
|
*/
|
||||||
media?: null|"screen"|"print";
|
media?: null|"screen"|"print";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. Passing `null`
|
||||||
|
* disables reduced motion emulation.
|
||||||
|
*/
|
||||||
|
reducedMotion?: null|"reduce"|"no-preference";
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -7174,6 +7180,13 @@ export interface BrowserType<Unused = {}> {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See
|
||||||
|
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#pageemulatemediaoptions) for more details.
|
||||||
|
* Defaults to `'no-preference'`.
|
||||||
|
*/
|
||||||
|
reducedMotion?: "reduce"|"no-preference";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
||||||
* is set.
|
* is set.
|
||||||
|
|
@ -8197,6 +8210,13 @@ export interface AndroidDevice {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See
|
||||||
|
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#pageemulatemediaoptions) for more details.
|
||||||
|
* Defaults to `'no-preference'`.
|
||||||
|
*/
|
||||||
|
reducedMotion?: "reduce"|"no-preference";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
||||||
* is set.
|
* is set.
|
||||||
|
|
@ -8974,6 +8994,13 @@ export interface Browser extends EventEmitter {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See
|
||||||
|
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#pageemulatemediaoptions) for more details.
|
||||||
|
* Defaults to `'no-preference'`.
|
||||||
|
*/
|
||||||
|
reducedMotion?: "reduce"|"no-preference";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
||||||
* is set.
|
* is set.
|
||||||
|
|
@ -11019,6 +11046,13 @@ export interface BrowserContextOptions {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See
|
||||||
|
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#pageemulatemediaoptions) for more details.
|
||||||
|
* Defaults to `'no-preference'`.
|
||||||
|
*/
|
||||||
|
reducedMotion?: "reduce"|"no-preference";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
* Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the `viewport`
|
||||||
* is set.
|
* is set.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue