Skip to content

Commit

Permalink
feat(page): emulate idle state (#6410)
Browse files Browse the repository at this point in the history
* https://bugs.chromium.org/p/chromium/issues/detail?id=1090802
* added `page.emulateIdleState(...)` allowing emulate or remove emulation of the idle state;
* added test `emulate idle` -> `remove emulation` -> `emulate idle` -> `remove emulation`;
* added launch argument `--enable-blink-features=IdleDetection` to turn IdleDetection on.
  • Loading branch information
sadym-chromium committed Sep 14, 2020
1 parent 03e41da commit 17960e5
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/api.md
Expand Up @@ -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)
Expand Down Expand Up @@ -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'`.
Expand Down
42 changes: 42 additions & 0 deletions new-docs/puppeteer.page.emulateidlestate.md
@@ -0,0 +1,42 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [Page](./puppeteer.page.md) &gt; [emulateIdleState](./puppeteer.page.emulateidlestate.md)

## Page.emulateIdleState() method

Emulates the idle state. If no arguments set, clears idle state emulation.

<b>Signature:</b>

```typescript
emulateIdleState(overrides?: {
isUserActive: boolean;
isScreenUnlocked: boolean;
}): Promise<void>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| overrides | { isUserActive: boolean; isScreenUnlocked: boolean; } | Mock idle state. If not set, clears idle overrides |

<b>Returns:</b>

Promise&lt;void&gt;

## Example


```js
// set idle emulation
await page.emulateIdleState({isUserActive: true, isScreenUnlocked: false});

// do some checks here
...

// clear idle emulation
await page.emulateIdleState();

```

1 change: 1 addition & 0 deletions new-docs/puppeteer.page.md
Expand Up @@ -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) | | |
Expand Down
34 changes: 34 additions & 0 deletions src/common/Page.ts
Expand Up @@ -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<void> {
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.
*
Expand Down
3 changes: 3 additions & 0 deletions src/node/Launcher.ts
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions test/assets/idle-detector.html
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<div id="state"></div>
<script>
const elState = document.querySelector('#state');
function setState(msg) {
elState.textContent = msg;
}
async function main() {
const controller = new AbortController();
const signal = controller.signal;
const idleDetector = new IdleDetector({
threshold: 60000,
signal,
});
idleDetector.addEventListener('change', () => {
const userState = idleDetector.userState;
const screenState = idleDetector.screenState;
setState(`Idle state: ${userState}, ${screenState}.`);
});
idleDetector.start();
}
main();
</script>
94 changes: 94 additions & 0 deletions 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);
});
});

0 comments on commit 17960e5

Please sign in to comment.