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` [Object]> If not set, clears emulation
+ - `isUserActive` <[boolean]> **required**
+ - `isScreenUnlocked` <[boolean]> **required**
+- returns: <[Promise]>
+
#### 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'`.
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);
+ });
+});