diff --git a/docs/api.md b/docs/api.md index 4f7c0053..e8b11513 100644 --- a/docs/api.md +++ b/docs/api.md @@ -118,6 +118,7 @@ * [page.coverage](#pagecoverage) * [page.deleteCookie(...cookies)](#pagedeletecookiecookies) * [page.emulate(options)](#pageemulateoptions) + * [page.emulateIdleState(overrides)](#pageemulateidlestateoverrides) * [page.emulateMediaFeatures(features)](#pageemulatemediafeaturesfeatures) * [page.emulateMediaType(type)](#pageemulatemediatypetype) * [page.emulateTimezone(timezoneId)](#pageemulatetimezonetimezoneid) @@ -1342,6 +1343,12 @@ const iPhone = puppeteer.devices['iPhone 6']; List of all available devices is available in the source code: [src/common/DeviceDescriptors.ts](https://github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts). +#### page.emulateIdleState(overrides) +- `overrides` If not set, clears emulation + - `isUserActive` <[boolean]> **required** + - `isScreenUnlocked` <[boolean]> **required** +- returns: <[Promise]> + #### 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/new-docs/puppeteer.page.emulateidlestate.md b/new-docs/puppeteer.page.emulateidlestate.md new file mode 100644 index 00000000..def93c02 --- /dev/null +++ b/new-docs/puppeteer.page.emulateidlestate.md @@ -0,0 +1,42 @@ + + +[Home](./index.md) > [puppeteer](./puppeteer.md) > [Page](./puppeteer.page.md) > [emulateIdleState](./puppeteer.page.emulateidlestate.md) + +## Page.emulateIdleState() method + +Emulates the idle state. If no arguments set, clears idle state emulation. + +Signature: + +```typescript +emulateIdleState(overrides?: { + isUserActive: boolean; + isScreenUnlocked: boolean; + }): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| overrides | { isUserActive: boolean; isScreenUnlocked: boolean; } | Mock idle state. If not set, clears idle overrides | + +Returns: + +Promise<void> + +## Example + + +```js +// set idle emulation +await page.emulateIdleState({isUserActive: true, isScreenUnlocked: false}); + +// do some checks here +... + +// clear idle emulation +await page.emulateIdleState(); + +``` + diff --git a/new-docs/puppeteer.page.md b/new-docs/puppeteer.page.md index 2530230a..a7b33c1f 100644 --- a/new-docs/puppeteer.page.md +++ b/new-docs/puppeteer.page.md @@ -89,6 +89,7 @@ page.off('request', logRequest); | [cookies(urls)](./puppeteer.page.cookies.md) | | If no URLs are specified, this method returns cookies for the current page URL. If URLs are specified, only cookies for those URLs are returned. | | [deleteCookie(cookies)](./puppeteer.page.deletecookie.md) | | | | [emulate(options)](./puppeteer.page.emulate.md) | | | +| [emulateIdleState(overrides)](./puppeteer.page.emulateidlestate.md) | | Emulates the idle state. If no arguments set, clears idle state emulation. | | [emulateMediaFeatures(features)](./puppeteer.page.emulatemediafeatures.md) | | | | [emulateMediaType(type)](./puppeteer.page.emulatemediatype.md) | | | | [emulateTimezone(timezoneId)](./puppeteer.page.emulatetimezone.md) | | | diff --git a/src/common/Page.ts b/src/common/Page.ts index 211b1bcb..d8fcdb1e 100644 --- a/src/common/Page.ts +++ b/src/common/Page.ts @@ -1443,6 +1443,40 @@ export class Page extends EventEmitter { } } + /** + * Emulates the idle state. + * If no arguments set, clears idle state emulation. + * + * @example + * ```js + * // set idle emulation + * await page.emulateIdleState({isUserActive: true, isScreenUnlocked: false}); + * + * // do some checks here + * ... + * + * // clear idle emulation + * await page.emulateIdleState(); + * ``` + * + * @param overrides Mock idle state. If not set, clears idle overrides + * @param isUserActive Mock isUserActive + * @param isScreenUnlocked Mock isScreenUnlocked + */ + async emulateIdleState(overrides?: { + isUserActive: boolean; + isScreenUnlocked: boolean; + }): Promise { + if (overrides) { + await this._client.send('Emulation.setIdleOverride', { + isUserActive: overrides.isUserActive, + isScreenUnlocked: overrides.isScreenUnlocked, + }); + } else { + await this._client.send('Emulation.clearIdleOverride'); + } + } + /** * Simulates the given vision deficiency on the page. * diff --git a/src/node/Launcher.ts b/src/node/Launcher.ts index 720ee20e..4140ce81 100644 --- a/src/node/Launcher.ts +++ b/src/node/Launcher.ts @@ -190,6 +190,9 @@ class ChromeLauncher implements ProductLauncher { '--enable-automation', '--password-store=basic', '--use-mock-keychain', + // TODO(sadym): remove '--enable-blink-features=IdleDetection' + // once IdleDetection is turned on by default. + '--enable-blink-features=IdleDetection', ]; const { devtools = false, diff --git a/test/assets/idle-detector.html b/test/assets/idle-detector.html new file mode 100644 index 00000000..83b496c0 --- /dev/null +++ b/test/assets/idle-detector.html @@ -0,0 +1,23 @@ + +
+ diff --git a/test/idle_override.spec.ts b/test/idle_override.spec.ts new file mode 100644 index 00000000..31b4169a --- /dev/null +++ b/test/idle_override.spec.ts @@ -0,0 +1,94 @@ +/** + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import expect from 'expect'; +import { + getTestState, + setupTestBrowserHooks, + setupTestPageAndContextHooks, + describeFailsFirefox, +} from './mocha-utils'; // eslint-disable-line import/extensions + +describeFailsFirefox('Emulate idle state', () => { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + async function getIdleState() { + const { page } = getTestState(); + + const stateElement = await page.$('#state'); + return await page.evaluate((element: HTMLElement) => { + return element.innerText; + }, stateElement); + } + + async function verifyState(expectedState: string) { + const actualState = await getIdleState(); + expect(actualState).toEqual(expectedState); + } + + it('changing idle state emulation causes change of the IdleDetector state', async () => { + const { page, server, context } = getTestState(); + await context.overridePermissions(server.PREFIX + '/idle-detector.html', [ + 'notifications', + ]); + + await page.goto(server.PREFIX + '/idle-detector.html'); + + // Store initial state, as soon as it is not guaranteed to be `active, unlocked`. + const initialState = await getIdleState(); + + // Emulate Idle states and verify IdleDetector updates state accordingly. + await page.emulateIdleState({ + isUserActive: false, + isScreenUnlocked: false, + }); + await verifyState('Idle state: idle, locked.'); + + await page.emulateIdleState({ + isUserActive: true, + isScreenUnlocked: false, + }); + await verifyState('Idle state: active, locked.'); + + await page.emulateIdleState({ + isUserActive: true, + isScreenUnlocked: true, + }); + await verifyState('Idle state: active, unlocked.'); + + await page.emulateIdleState({ + isUserActive: false, + isScreenUnlocked: true, + }); + await verifyState('Idle state: idle, unlocked.'); + + // Remove Idle emulation and verify IdleDetector is in initial state. + await page.emulateIdleState(); + await verifyState(initialState); + + // Emulate idle state again after removing emulation. + await page.emulateIdleState({ + isUserActive: false, + isScreenUnlocked: false, + }); + await verifyState('Idle state: idle, locked.'); + + // Remove emulation second time. + await page.emulateIdleState(); + await verifyState(initialState); + }); +});