feat(page): emulate idle state (#6410)

* 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.
This commit is contained in:
Maksim Sadym 2020-09-14 11:31:23 +02:00 committed by GitHub
parent 03e41da465
commit 17960e5d8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 204 additions and 0 deletions

View File

@ -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'`.

View File

@ -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();
```

View File

@ -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) | | |

View File

@ -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.
*

View File

@ -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,

View File

@ -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>

View File

@ -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);
});
});