Intorduce Page.keyboard (#74)
Introduce page.keyboard to provide low-level access to the keyboard.
This commit is contained in:
parent
895f69d17a
commit
bf7698e8f8
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
|
.vscode
|
||||||
|
89
docs/api.md
89
docs/api.md
@ -35,6 +35,7 @@
|
|||||||
* [page.frames()](#pageframes)
|
* [page.frames()](#pageframes)
|
||||||
* [page.httpHeaders()](#pagehttpheaders)
|
* [page.httpHeaders()](#pagehttpheaders)
|
||||||
* [page.injectFile(filePath)](#pageinjectfilefilepath)
|
* [page.injectFile(filePath)](#pageinjectfilefilepath)
|
||||||
|
* [page.keyboard](#pagekeyboard)
|
||||||
* [page.mainFrame()](#pagemainframe)
|
* [page.mainFrame()](#pagemainframe)
|
||||||
* [page.navigate(url, options)](#pagenavigateurl-options)
|
* [page.navigate(url, options)](#pagenavigateurl-options)
|
||||||
* [page.pdf(options)](#pagepdfoptions)
|
* [page.pdf(options)](#pagepdfoptions)
|
||||||
@ -54,6 +55,13 @@
|
|||||||
* [page.userAgent()](#pageuseragent)
|
* [page.userAgent()](#pageuseragent)
|
||||||
* [page.viewport()](#pageviewport)
|
* [page.viewport()](#pageviewport)
|
||||||
* [page.waitFor(selector)](#pagewaitforselector)
|
* [page.waitFor(selector)](#pagewaitforselector)
|
||||||
|
- [class: Keyboard](#class-keyboard)
|
||||||
|
* [keyboard.hold(key[, options])](#keyboardholdkey-options)
|
||||||
|
* [keyboard.modifiers()](#keyboardmodifiers)
|
||||||
|
* [keyboard.press(key[, options])](#keyboardpresskey-options)
|
||||||
|
* [keyboard.release(key)](#keyboardreleasekey)
|
||||||
|
* [keyboard.sendCharacter(char)](#keyboardsendcharacterchar)
|
||||||
|
* [keyboard.type(text)](#keyboardtypetext)
|
||||||
- [class: Dialog](#class-dialog)
|
- [class: Dialog](#class-dialog)
|
||||||
* [dialog.accept([promptText])](#dialogacceptprompttext)
|
* [dialog.accept([promptText])](#dialogacceptprompttext)
|
||||||
* [dialog.dismiss()](#dialogdismiss)
|
* [dialog.dismiss()](#dialogdismiss)
|
||||||
@ -299,6 +307,10 @@ This is a shortcut for [page.mainFrame().evaluate()](#frameevaluatefun-args) met
|
|||||||
- `filePath` <[string]> Path to the javascript file to be injected into page.
|
- `filePath` <[string]> Path to the javascript file to be injected into page.
|
||||||
- returns: <[Promise]> Promise which resolves when file gets successfully evaluated in page.
|
- returns: <[Promise]> Promise which resolves when file gets successfully evaluated in page.
|
||||||
|
|
||||||
|
#### page.keyboard
|
||||||
|
|
||||||
|
- returns: <[Keyboard]>
|
||||||
|
|
||||||
#### page.mainFrame()
|
#### page.mainFrame()
|
||||||
- returns: <[Frame]> returns page's main frame.
|
- returns: <[Frame]> returns page's main frame.
|
||||||
|
|
||||||
@ -481,6 +493,82 @@ This is a shortcut for [page.mainFrame().url()](#frameurl)
|
|||||||
|
|
||||||
Shortcut for [page.mainFrame().waitFor(selector)](#framewaitforselector).
|
Shortcut for [page.mainFrame().waitFor(selector)](#framewaitforselector).
|
||||||
|
|
||||||
|
### class: Keyboard
|
||||||
|
|
||||||
|
Keyboard provides an api for managing a virtual keyboard. The high level api is [`keyboard.type`](#keyboardtypetext), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
|
||||||
|
|
||||||
|
For finer control, you can use press, release, and sendCharacter to manually fire events as if they were generated from a real keyboard.
|
||||||
|
|
||||||
|
An example of holding down `Shift` in order to select and delete some text:
|
||||||
|
```js
|
||||||
|
page.keyboard.type('Hello World!');
|
||||||
|
page.keyboard.press('ArrowLeft');
|
||||||
|
|
||||||
|
page.keyboard.hold('Shift');
|
||||||
|
for (let i = 0; i = 0; i < ' World'.length; i++)
|
||||||
|
page.keyboard.press('ArrowLeft');
|
||||||
|
page.keyboard.release('Shift');
|
||||||
|
|
||||||
|
page.keyboard.press('Backspace');
|
||||||
|
// Result text will end up saying 'Hello!'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### keyboard.hold(key[, options])
|
||||||
|
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `text` <[string]> If specified, generates an input event with this text.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Dispatches a `keydown` event.
|
||||||
|
|
||||||
|
This will not send input events unless `text` is specified.
|
||||||
|
|
||||||
|
If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that modifier active. To release the modifier key, use [`keyboard.release`](#keyboardreleasekey).
|
||||||
|
|
||||||
|
#### keyboard.modifiers()
|
||||||
|
- returns: <[Object]>
|
||||||
|
- `Shift` <[boolean]>
|
||||||
|
- `Meta` <[boolean]>
|
||||||
|
- `Control` <[boolean]>
|
||||||
|
- `Alt` <[boolean]>
|
||||||
|
|
||||||
|
Returns which modifier keys are currently active. Use [`keyboard.hold`](#keyboardholdkey) to activate a modifier key.
|
||||||
|
|
||||||
|
#### keyboard.press(key[, options])
|
||||||
|
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `text` <[string]> If specified, generates an input event with this text.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Shortcut for [`keyboard.hold`](#keyboardholdkey) and [`keyboard.release`](#keyboardreleasekey).
|
||||||
|
|
||||||
|
#### keyboard.release(key)
|
||||||
|
- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Dispatches a `keyup` event.
|
||||||
|
|
||||||
|
#### keyboard.sendCharacter(char)
|
||||||
|
- `char` <[string]> Character to send into the page.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Dispatches a `keypress` and `input` event. This does not send a `keydown` or `keyup` event.
|
||||||
|
|
||||||
|
```js
|
||||||
|
page.keyboard.sendCharacter('嗨');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### keyboard.type(text)
|
||||||
|
- `text` <[string]> Text to type into the page
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
|
||||||
|
This is the suggested way to type printable characters.
|
||||||
|
|
||||||
|
```js
|
||||||
|
page.keyboard.type('Hello World!');
|
||||||
|
```
|
||||||
|
|
||||||
### class: Dialog
|
### class: Dialog
|
||||||
|
|
||||||
[Dialog] objects are dispatched by page via the ['dialog'](#event-dialog) event.
|
[Dialog] objects are dispatched by page via the ['dialog'](#event-dialog) event.
|
||||||
@ -780,4 +868,5 @@ If there's already a header with name `name`, the header gets overwritten.
|
|||||||
[Request]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-request "Request"
|
[Request]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-request "Request"
|
||||||
[Browser]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-browser "Browser"
|
[Browser]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-browser "Browser"
|
||||||
[Body]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-body "Body"
|
[Body]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-body "Body"
|
||||||
|
[Keyboard]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-keyboard "Keyboard"
|
||||||
[Dialog]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-dialog "Dialog"
|
[Dialog]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-dialog "Dialog"
|
||||||
|
238
lib/Keyboard.js
Normal file
238
lib/Keyboard.js
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Keyboard {
|
||||||
|
/**
|
||||||
|
* @param {!Connection} client
|
||||||
|
*/
|
||||||
|
constructor(client) {
|
||||||
|
this._client = client;
|
||||||
|
/** @type {!Set<string>} */
|
||||||
|
this._keys = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {{text: (string|undefined)}} options
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
async hold(key, options) {
|
||||||
|
let {text} = options || {};
|
||||||
|
this._keys.add(key);
|
||||||
|
await this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: text ? 'keyDown' : 'rawKeyDown',
|
||||||
|
modifiers: this._modifiersMask(),
|
||||||
|
windowsVirtualKeyCode: codeForKey(key),
|
||||||
|
key: key,
|
||||||
|
text: text,
|
||||||
|
unmodifiedText: text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{Shift: boolean, Alt: boolean, Meta: boolean, Control: boolean}}
|
||||||
|
*/
|
||||||
|
modifiers() {
|
||||||
|
return {
|
||||||
|
Shift: this._keys.has('Shift'),
|
||||||
|
Alt: this._keys.has('Alt'),
|
||||||
|
Meta: this._keys.has('Meta'),
|
||||||
|
Control: this._keys.has('Control')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
async release(key) {
|
||||||
|
this._keys.delete(key);
|
||||||
|
await this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: 'keyUp',
|
||||||
|
modifiers: this._modifiersMask(),
|
||||||
|
key,
|
||||||
|
windowsVirtualKeyCode: codeForKey(key),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {{text: (string|undefined)}} options
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
async press(key, options) {
|
||||||
|
this.hold(key, options);
|
||||||
|
await this.release(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
async type(text) {
|
||||||
|
let last;
|
||||||
|
for (let char of text)
|
||||||
|
last = this.press(char, {text: char});
|
||||||
|
await last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} char
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
async sendCharacter(char) {
|
||||||
|
await this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: 'char',
|
||||||
|
modifiers: this._modifiersMask(),
|
||||||
|
text: char,
|
||||||
|
key: char,
|
||||||
|
unmodifiedText: char
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
_modifiersMask() {
|
||||||
|
let modifiers = 0;
|
||||||
|
if (this._keys.has('Alt'))
|
||||||
|
modifiers += 1;
|
||||||
|
if (this._keys.has('Control'))
|
||||||
|
modifiers += 2;
|
||||||
|
if (this._keys.has('Meta'))
|
||||||
|
modifiers += 4;
|
||||||
|
if (this._keys.has('Shift'))
|
||||||
|
modifiers += 8;
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys = {
|
||||||
|
'Cancel': 3,
|
||||||
|
'Help': 6,
|
||||||
|
'Backspace': 8,
|
||||||
|
'Tab': 9,
|
||||||
|
'Clear': 12,
|
||||||
|
'Enter': 13,
|
||||||
|
'Shift': 16,
|
||||||
|
'Control': 17,
|
||||||
|
'Alt': 18,
|
||||||
|
'Pause': 19,
|
||||||
|
'CapsLock': 20,
|
||||||
|
'Escape': 27,
|
||||||
|
'Convert': 28,
|
||||||
|
'NonConvert': 29,
|
||||||
|
'Accept': 30,
|
||||||
|
'ModeChange': 31,
|
||||||
|
'PageUp': 33,
|
||||||
|
'PageDown': 34,
|
||||||
|
'End': 35,
|
||||||
|
'Home': 36,
|
||||||
|
'ArrowLeft': 37,
|
||||||
|
'ArrowUp': 38,
|
||||||
|
'ArrowRight': 39,
|
||||||
|
'ArrowDown': 40,
|
||||||
|
'Select': 41,
|
||||||
|
'Print': 42,
|
||||||
|
'Execute': 43,
|
||||||
|
'PrintScreen': 44,
|
||||||
|
'Insert': 45,
|
||||||
|
'Delete': 46,
|
||||||
|
')': 48,
|
||||||
|
'!': 49,
|
||||||
|
'@': 50,
|
||||||
|
'#': 51,
|
||||||
|
'$': 52,
|
||||||
|
'%': 53,
|
||||||
|
'^': 54,
|
||||||
|
'&': 55,
|
||||||
|
'*': 56,
|
||||||
|
'(': 57,
|
||||||
|
'Meta': 91,
|
||||||
|
'ContextMenu': 93,
|
||||||
|
'F1': 112,
|
||||||
|
'F2': 113,
|
||||||
|
'F3': 114,
|
||||||
|
'F4': 115,
|
||||||
|
'F5': 116,
|
||||||
|
'F6': 117,
|
||||||
|
'F7': 118,
|
||||||
|
'F8': 119,
|
||||||
|
'F9': 120,
|
||||||
|
'F10': 121,
|
||||||
|
'F11': 122,
|
||||||
|
'F12': 123,
|
||||||
|
'F13': 124,
|
||||||
|
'F14': 125,
|
||||||
|
'F15': 126,
|
||||||
|
'F16': 127,
|
||||||
|
'F17': 128,
|
||||||
|
'F18': 129,
|
||||||
|
'F19': 130,
|
||||||
|
'F20': 131,
|
||||||
|
'F21': 132,
|
||||||
|
'F22': 133,
|
||||||
|
'F23': 134,
|
||||||
|
'F24': 135,
|
||||||
|
'NumLock': 144,
|
||||||
|
'ScrollLock': 145,
|
||||||
|
'VolumeMute': 181,
|
||||||
|
'VolumeDown': 182,
|
||||||
|
'VolumeUp': 183,
|
||||||
|
';': 186,
|
||||||
|
':': 186,
|
||||||
|
'=': 187,
|
||||||
|
'+': 187,
|
||||||
|
',': 188,
|
||||||
|
'<': 188,
|
||||||
|
'-': 189,
|
||||||
|
'_': 189,
|
||||||
|
'.': 190,
|
||||||
|
'>': 190,
|
||||||
|
'/': 191,
|
||||||
|
'?': 191,
|
||||||
|
'`': 192,
|
||||||
|
'~': 192,
|
||||||
|
'[': 219,
|
||||||
|
'{': 219,
|
||||||
|
'\\': 220,
|
||||||
|
'|': 220,
|
||||||
|
']': 221,
|
||||||
|
'}': 221,
|
||||||
|
'\'': 222,
|
||||||
|
'"': 222,
|
||||||
|
'AltGraph': 225,
|
||||||
|
'Attn': 246,
|
||||||
|
'CrSel': 247,
|
||||||
|
'ExSel': 248,
|
||||||
|
'EraseEof': 249,
|
||||||
|
'Play': 250,
|
||||||
|
'ZoomOut': 251
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
function codeForKey(key) {
|
||||||
|
if (keys[key])
|
||||||
|
return keys[key];
|
||||||
|
if (key.length === 1)
|
||||||
|
return key.toUpperCase().charCodeAt(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Keyboard;
|
28
lib/Page.js
28
lib/Page.js
@ -22,6 +22,7 @@ let Navigator = require('./Navigator');
|
|||||||
let Dialog = require('./Dialog');
|
let Dialog = require('./Dialog');
|
||||||
let EmulationManager = require('./EmulationManager');
|
let EmulationManager = require('./EmulationManager');
|
||||||
let FrameManager = require('./FrameManager');
|
let FrameManager = require('./FrameManager');
|
||||||
|
let Keyboard = require('./Keyboard');
|
||||||
let helper = require('./helper');
|
let helper = require('./helper');
|
||||||
|
|
||||||
class Page extends EventEmitter {
|
class Page extends EventEmitter {
|
||||||
@ -59,6 +60,8 @@ class Page extends EventEmitter {
|
|||||||
/** @type {!Map<string, function>} */
|
/** @type {!Map<string, function>} */
|
||||||
this._inPageCallbacks = new Map();
|
this._inPageCallbacks = new Map();
|
||||||
|
|
||||||
|
this._keyboard = new Keyboard(this._client);
|
||||||
|
|
||||||
this._screenshotTaskChain = Promise.resolve();
|
this._screenshotTaskChain = Promise.resolve();
|
||||||
|
|
||||||
this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event));
|
this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event));
|
||||||
@ -84,6 +87,13 @@ class Page extends EventEmitter {
|
|||||||
return this._frameManager.mainFrame();
|
return this._frameManager.mainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Keyboard}
|
||||||
|
*/
|
||||||
|
get keyboard() {
|
||||||
|
return this._keyboard;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Array<!Frame>}
|
* @return {!Array<!Frame>}
|
||||||
*/
|
*/
|
||||||
@ -558,23 +568,7 @@ class Page extends EventEmitter {
|
|||||||
* @param {!Promise}
|
* @param {!Promise}
|
||||||
*/
|
*/
|
||||||
async type(text) {
|
async type(text) {
|
||||||
for (let i = 0; i < text.length; i++) {
|
return this._keyboard.type(text);
|
||||||
let char = text.charAt(i);
|
|
||||||
this._client.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyDown',
|
|
||||||
key: char
|
|
||||||
});
|
|
||||||
this._client.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'char',
|
|
||||||
text: char,
|
|
||||||
key: char,
|
|
||||||
unmodifiedText: char
|
|
||||||
});
|
|
||||||
await this._client.send('Input.dispatchKeyEvent', {
|
|
||||||
type: 'keyUp',
|
|
||||||
key: char
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,6 +67,18 @@ class WebPage {
|
|||||||
this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message));
|
this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message));
|
||||||
this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog));
|
this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog));
|
||||||
this._pageEvents.on(PageEvents.PageError, error => (this._onError || noop).call(null, error.message, error.stack));
|
this._pageEvents.on(PageEvents.PageError, error => (this._onError || noop).call(null, error.message, error.stack));
|
||||||
|
this.event = {
|
||||||
|
key: {
|
||||||
|
A: 65,
|
||||||
|
B: 66,
|
||||||
|
C: 67,
|
||||||
|
Home: ['Home'],
|
||||||
|
Delete: ['Delete'],
|
||||||
|
Backspace: ['Backspace'],
|
||||||
|
Cut: ['Cut'],
|
||||||
|
Paste: ['Paste']
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -343,6 +355,61 @@ class WebPage {
|
|||||||
await(this._page.uploadFile(selector, ...files));
|
await(this._page.uploadFile(selector, ...files));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} eventType
|
||||||
|
* @param {!Array<*>} args
|
||||||
|
*/
|
||||||
|
sendEvent(eventType, ...args) {
|
||||||
|
if (eventType.startsWith('key'))
|
||||||
|
this._sendKeyboardEvent.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} eventType
|
||||||
|
* @param {string} keyOrKeys
|
||||||
|
* @param {null} nop1
|
||||||
|
* @param {null} nop2
|
||||||
|
* @param {number} modifier
|
||||||
|
*/
|
||||||
|
_sendKeyboardEvent(eventType, keyOrKeys, nop1, nop2, modifier) {
|
||||||
|
switch (eventType) {
|
||||||
|
case 'keyup':
|
||||||
|
if (typeof keyOrKeys === 'number') {
|
||||||
|
await(this._page.keyboard.release(String.fromCharCode(keyOrKeys)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (let key of keyOrKeys)
|
||||||
|
await(this._page.keyboard.release(key));
|
||||||
|
break;
|
||||||
|
case 'keypress':
|
||||||
|
if (modifier & 0x04000000)
|
||||||
|
this._page.keyboard.hold('Control');
|
||||||
|
if (modifier & 0x02000000)
|
||||||
|
this._page.keyboard.hold('Shift');
|
||||||
|
if (keyOrKeys instanceof Array) {
|
||||||
|
this._page.keyboard.hold(keyOrKeys[0]);
|
||||||
|
await(this._page.keyboard.release(keyOrKeys[0]));
|
||||||
|
} else if (typeof keyOrKeys === 'number') {
|
||||||
|
await(this._page.type(String.fromCharCode(keyOrKeys)));
|
||||||
|
} else {
|
||||||
|
await(this._page.type(keyOrKeys));
|
||||||
|
}
|
||||||
|
if (modifier & 0x02000000)
|
||||||
|
this._page.keyboard.release('Shift');
|
||||||
|
if (modifier & 0x04000000)
|
||||||
|
this._page.keyboard.release('Control');
|
||||||
|
break;
|
||||||
|
case 'keydown':
|
||||||
|
if (typeof keyOrKeys === 'number') {
|
||||||
|
await(this._page.keyboard.hold(String.fromCharCode(keyOrKeys)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (let key of keyOrKeys)
|
||||||
|
await(this._page.keyboard.hold(key));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
* @param {function()=} callback
|
* @param {function()=} callback
|
||||||
|
44
test/assets/input/keyboard.html
Normal file
44
test/assets/input/keyboard.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Keyboard test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea></textarea>
|
||||||
|
<script>
|
||||||
|
window.result = "";
|
||||||
|
let textarea = document.querySelector('textarea');
|
||||||
|
textarea.focus();
|
||||||
|
textarea.addEventListener('keydown', event => {
|
||||||
|
log('Keydown:', event.key, event.which, modifiers(event));
|
||||||
|
});
|
||||||
|
textarea.addEventListener('keypress', event => {
|
||||||
|
log('Keypress:', event.key, event.which, event.keyCode, event.charCode, modifiers(event));
|
||||||
|
});
|
||||||
|
textarea.addEventListener('keyup', event => {
|
||||||
|
log('Keyup:', event.key, event.which, modifiers(event));
|
||||||
|
});
|
||||||
|
function modifiers(event) {
|
||||||
|
let m = [];
|
||||||
|
if (event.altKey)
|
||||||
|
m.push('Alt')
|
||||||
|
if (event.ctrlKey)
|
||||||
|
m.push('Control');
|
||||||
|
if (event.metaKey)
|
||||||
|
m.push('Meta')
|
||||||
|
if (event.shiftKey)
|
||||||
|
m.push('Shift')
|
||||||
|
return '[' + m.join(' ') + ']';
|
||||||
|
}
|
||||||
|
function log(...args) {
|
||||||
|
console.log.apply(console, args);
|
||||||
|
result += args.join(' ') + '\n';
|
||||||
|
}
|
||||||
|
function getResult() {
|
||||||
|
let temp = result.trim();
|
||||||
|
result = "";
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
105
test/test.js
105
test/test.js
@ -672,6 +672,111 @@ describe('Puppeteer', function() {
|
|||||||
return promise.then(() => reader.result);
|
return promise.then(() => reader.result);
|
||||||
})).toBe('contents of the file');
|
})).toBe('contents of the file');
|
||||||
}));
|
}));
|
||||||
|
it('should move with the arrow keys', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/textarea.html');
|
||||||
|
await page.focus('textarea');
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
await keyboard.type('Hello World!');
|
||||||
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||||
|
for (let i = 0; i < 'World!'.length; i++)
|
||||||
|
keyboard.press('ArrowLeft');
|
||||||
|
await keyboard.type('inserted ');
|
||||||
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
|
||||||
|
keyboard.hold('Shift');
|
||||||
|
for (let i = 0; i < 'inserted '.length; i++)
|
||||||
|
keyboard.press('ArrowLeft');
|
||||||
|
keyboard.release('Shift');
|
||||||
|
keyboard.hold('Backspace');
|
||||||
|
await keyboard.release('Backspace');
|
||||||
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||||
|
}));
|
||||||
|
it('should report shiftKey', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/keyboard.html');
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
let codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17};
|
||||||
|
for (let modifierKey in codeForKey) {
|
||||||
|
await keyboard.hold(modifierKey);
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
|
||||||
|
await keyboard.hold('!');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! 49 [' + modifierKey + ']');
|
||||||
|
await keyboard.release('!');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! 49 [' + modifierKey + ']');
|
||||||
|
await keyboard.release(modifierKey);
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + codeForKey[modifierKey] + ' []');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
it('should report multiple modifiers', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/keyboard.html');
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
await keyboard.hold('Control');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control 17 [Control]');
|
||||||
|
await keyboard.hold('Meta');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keydown: Meta 91 [Control Meta]');
|
||||||
|
await keyboard.hold(';');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; 186 [Control Meta]');
|
||||||
|
await keyboard.release(';');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; 186 [Control Meta]');
|
||||||
|
await keyboard.release('Control');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control 17 [Meta]');
|
||||||
|
await keyboard.release('Meta');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe('Keyup: Meta 91 []');
|
||||||
|
}));
|
||||||
|
it('should send proper codes while typing', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/keyboard.html');
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
await keyboard.type('!');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
|
[ 'Keydown: ! 49 []',
|
||||||
|
'Keypress: ! 33 33 33 []',
|
||||||
|
'Keyup: ! 49 []'].join('\n'));
|
||||||
|
await keyboard.type('^');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
|
[ 'Keydown: ^ 54 []',
|
||||||
|
'Keypress: ^ 94 94 94 []',
|
||||||
|
'Keyup: ^ 54 []'].join('\n'));
|
||||||
|
}));
|
||||||
|
it('should send propery codes while typing with shift', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/keyboard.html');
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
await keyboard.hold('Shift');
|
||||||
|
await keyboard.type('~');
|
||||||
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
|
[ 'Keydown: Shift 16 [Shift]',
|
||||||
|
'Keydown: ~ 192 [Shift]', // 192 is ` keyCode
|
||||||
|
'Keypress: ~ 126 126 126 [Shift]', // 126 is ~ charCode
|
||||||
|
'Keyup: ~ 192 [Shift]'].join('\n'));
|
||||||
|
await keyboard.release('Shift');
|
||||||
|
}));
|
||||||
|
it('should not type canceled events', SX(async function(){
|
||||||
|
await page.navigate(PREFIX + '/input/textarea.html');
|
||||||
|
await page.focus('textarea');
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.addEventListener('keydown', event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
if (event.key === 'l')
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.key === 'o')
|
||||||
|
Promise.resolve().then(() => event.preventDefault());
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
await keyboard.type('Hello World!');
|
||||||
|
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
|
||||||
|
}));
|
||||||
|
it('keyboard.modifiers()', SX(async function(){
|
||||||
|
let keyboard = page.keyboard;
|
||||||
|
expect(keyboard.modifiers().Shift).toBe(false);
|
||||||
|
expect(keyboard.modifiers().Meta).toBe(false);
|
||||||
|
expect(keyboard.modifiers().Alt).toBe(false);
|
||||||
|
expect(keyboard.modifiers().Control).toBe(false);
|
||||||
|
keyboard.hold('Shift');
|
||||||
|
expect(keyboard.modifiers().Shift).toBe(true);
|
||||||
|
expect(keyboard.modifiers().Alt).toBe(false);
|
||||||
|
keyboard.release('Shift');
|
||||||
|
expect(keyboard.modifiers().Shift).toBe(false);
|
||||||
|
expect(keyboard.modifiers().Alt).toBe(false);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
describe('Page.setUserAgent', function() {
|
describe('Page.setUserAgent', function() {
|
||||||
it('should work', SX(async function() {
|
it('should work', SX(async function() {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! unsupported
|
|
||||||
test(function () {
|
test(function () {
|
||||||
var webpage = require('webpage');
|
var webpage = require('webpage');
|
||||||
|
|
||||||
@ -7,7 +6,7 @@ test(function () {
|
|||||||
page.evaluate(function() {
|
page.evaluate(function() {
|
||||||
window.addEventListener('keydown', function(event) {
|
window.addEventListener('keydown', function(event) {
|
||||||
window.loggedEvent = window.loggedEvent || [];
|
window.loggedEvent = window.loggedEvent || [];
|
||||||
window.loggedEvent.push(event);
|
window.loggedEvent.push(event.which);
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -17,5 +16,5 @@ test(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_equals(loggedEvent.length, 1);
|
assert_equals(loggedEvent.length, 1);
|
||||||
assert_equals(loggedEvent[0].which, page.event.key.A);
|
assert_equals(loggedEvent[0], page.event.key.A);
|
||||||
}, "key-down events");
|
}, "key-down events");
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! unsupported
|
|
||||||
test(function () {
|
test(function () {
|
||||||
var webpage = require('webpage');
|
var webpage = require('webpage');
|
||||||
|
|
||||||
@ -7,7 +6,7 @@ test(function () {
|
|||||||
page.evaluate(function() {
|
page.evaluate(function() {
|
||||||
window.addEventListener('keypress', function(event) {
|
window.addEventListener('keypress', function(event) {
|
||||||
window.loggedEvent = window.loggedEvent || [];
|
window.loggedEvent = window.loggedEvent || [];
|
||||||
window.loggedEvent.push(event);
|
window.loggedEvent.push(event.which);
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ test(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_equals(loggedEvent.length, 1);
|
assert_equals(loggedEvent.length, 1);
|
||||||
assert_equals(loggedEvent[0].which, page.event.key.C);
|
assert_equals(loggedEvent[0], page.event.key.C);
|
||||||
|
|
||||||
|
|
||||||
// Send keypress events to an input element and observe the effect.
|
// Send keypress events to an input element and observe the effect.
|
||||||
@ -54,13 +53,15 @@ test(function () {
|
|||||||
page.sendEvent('keypress', page.event.key.Delete);
|
page.sendEvent('keypress', page.event.key.Delete);
|
||||||
assert_equals(getText(), '');
|
assert_equals(getText(), '');
|
||||||
|
|
||||||
|
|
||||||
|
// Joel: This works, but it causes you to lose your clipboard when running the tests.
|
||||||
// Cut and Paste
|
// Cut and Paste
|
||||||
// 0x04000000 is the Control modifier.
|
// 0x04000000 is the Control modifier.
|
||||||
page.sendEvent('keypress', 'ABCD');
|
// page.sendEvent('keypress', 'ABCD');
|
||||||
assert_equals(getText(), 'ABCD');
|
// assert_equals(getText(), 'ABCD');
|
||||||
page.sendEvent('keypress', page.event.key.Home, null, null, 0x02000000);
|
// page.sendEvent('keypress', page.event.key.Home, null, null, 0x02000000);
|
||||||
page.sendEvent('keypress', 'x', null, null, 0x04000000);
|
// page.sendEvent('keypress', page.event.key.Cut);
|
||||||
assert_equals(getText(), '');
|
// assert_equals(getText(), '');
|
||||||
page.sendEvent('keypress', 'v', null, null, 0x04000000);
|
// page.sendEvent('keypress', page.event.key.Paste);
|
||||||
assert_equals(getText(), 'ABCD');
|
// assert_equals(getText(), 'ABCD');
|
||||||
}, "key press events");
|
}, "key press events");
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! unsupported
|
|
||||||
test(function () {
|
test(function () {
|
||||||
var webpage = require('webpage');
|
var webpage = require('webpage');
|
||||||
|
|
||||||
@ -7,7 +6,7 @@ test(function () {
|
|||||||
page.evaluate(function() {
|
page.evaluate(function() {
|
||||||
window.addEventListener('keyup', function(event) {
|
window.addEventListener('keyup', function(event) {
|
||||||
window.loggedEvent = window.loggedEvent || [];
|
window.loggedEvent = window.loggedEvent || [];
|
||||||
window.loggedEvent.push(event);
|
window.loggedEvent.push(event.which);
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -17,5 +16,5 @@ test(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_equals(loggedEvent.length, 1);
|
assert_equals(loggedEvent.length, 1);
|
||||||
assert_equals(loggedEvent[0].which, page.event.key.B);
|
assert_equals(loggedEvent[0], page.event.key.B);
|
||||||
}, "key-up events");
|
}, "key-up events");
|
||||||
|
@ -27,6 +27,7 @@ let EXCLUDE_METHODS = new Set([
|
|||||||
'Page.create',
|
'Page.create',
|
||||||
'Request.constructor',
|
'Request.constructor',
|
||||||
'Response.constructor',
|
'Response.constructor',
|
||||||
|
'Keyboard.constructor',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user