From 126ab7b90e475daabc9d595f1d3c98a1473eb0de Mon Sep 17 00:00:00 2001 From: JoelEinbinder Date: Mon, 23 Oct 2017 12:43:45 -0700 Subject: [PATCH] feat(keyboard): Accept codes (#1116) BREAKING CHANGE: This patch lets key names be code in addition to key. When specifying a code, the proper text is generated assuming a standard US keyboard layout. e.g Digit5 -> "5" or "%" depending on Shift. * location is now specified. #777 * Using unknown key names now throws an error. #723 * Typing newlines now correctly presses enter. #681 --- docs/api.md | 9 +- lib/Input.js | 298 ++++++++++++---------------------------- lib/USKeyboardLayout.js | 281 +++++++++++++++++++++++++++++++++++++ test/test.js | 36 +++++ 4 files changed, 407 insertions(+), 217 deletions(-) create mode 100644 lib/USKeyboardLayout.js diff --git a/docs/api.md b/docs/api.md index bd763c11724..e876b1bbdc1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1164,7 +1164,7 @@ page.keyboard.press('Backspace'); ``` #### keyboard.down(key[, options]) -- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/) +- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names. - `options` <[Object]> - `text` <[string]> If specified, generates an input event with this text. - returns: <[Promise]> @@ -1178,7 +1178,7 @@ If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key After the key is pressed once, subsequent calls to [`keyboard.down`](#keyboarddownkey-options) will have [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use [`keyboard.up`](#keyboardupkey). #### keyboard.press(key[, options]) -- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/) +- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names. - `options` <[Object]> - `text` <[string]> If specified, generates an input event with this text. - `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0. @@ -1212,7 +1212,7 @@ page.keyboard.type('World', {delay: 100}); // Types slower, like a user ``` #### keyboard.up(key) -- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/) +- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names. - returns: <[Promise]> Dispatches a `keyup` event. @@ -1783,7 +1783,7 @@ Returns a JSON representation of the object. The JSON is generated by running [` > **NOTE** The method will throw if the referenced object is not stringifiable. #### elementHandle.press(key[, options]) -- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/) +- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names. - `options` <[Object]> - `text` <[string]> If specified, generates an input event with this text. - `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0. @@ -2022,3 +2022,4 @@ Identifies what kind of target this is. Can be `"page"`, `"service_worker"`, or [Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable" [Touchscreen]: #class-touchscreen "Touchscreen" [Target]: #class-target "Target" +[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout" \ No newline at end of file diff --git a/lib/Input.js b/lib/Input.js index 721660a647d..dd3e3054e34 100644 --- a/lib/Input.js +++ b/lib/Input.js @@ -15,6 +15,16 @@ */ const {helper} = require('./helper'); +const keyDefinitions = require('./USKeyboardLayout'); + +/** + * @typedef {Object} KeyDescription + * @property {number} keyCode + * @property {string} key + * @property {string} text + * @property {string} code + * @property {number} location + */ class Keyboard { /** @@ -30,23 +40,25 @@ class Keyboard { * @param {string} key * @param {{text: string}=} options */ - async down(key, options = {text: undefined}) { - let { text } = options; - // If the key is a single character, and no modifiers are pressed except shift, a keypress should be generated by default. - if (text === undefined) - text = (key.length === 1 && !(this._modifiers & ~8)) ? key : ''; - const autoRepeat = this._pressedKeys.has(key); - this._pressedKeys.add(key); - this._modifiers |= this._modifierBit(key); + async down(key, options = { text: undefined }) { + const description = this._keyDescriptionForString(key); + + const autoRepeat = this._pressedKeys.has(description.code); + this._pressedKeys.add(description.code); + this._modifiers |= this._modifierBit(description.key); + + const text = options.text === undefined ? description.text : options.text; await this._client.send('Input.dispatchKeyEvent', { type: text ? 'keyDown' : 'rawKeyDown', modifiers: this._modifiers, - windowsVirtualKeyCode: keyCodeForKey(key), - code: codeForKey(key), - key, - text, + windowsVirtualKeyCode: description.keyCode, + code: description.code, + key: description.key, + text: text, unmodifiedText: text, - autoRepeat + autoRepeat, + location: description.location, + isKeypad: description.location === 3 }); } @@ -66,18 +78,69 @@ class Keyboard { return 0; } + /** + * @param {string} keyString + * @return {KeyDescription} + */ + _keyDescriptionForString(keyString) { + const shift = this._modifiers & 8; + const description = { + key: '', + keyCode: 0, + code: '', + text: '', + location: 0 + }; + + const definition = keyDefinitions[keyString]; + console.assert(definition, `Unknown key: "${keyString}"`); + + if (definition.key) + description.key = definition.key; + if (shift && definition.shiftKey) + description.key = definition.shiftKey; + + if (definition.keyCode) + description.keyCode = definition.keyCode; + if (shift && definition.shiftKeyCode) + description.keyCode = definition.shiftKeyCode; + + if (definition.code) + description.code = definition.code; + + if (definition.location) + description.location = definition.location; + + if (description.key.length === 1) + description.text = description.key; + + if (definition.text) + description.text = definition.text; + if (shift && definition.shiftText) + description.text = definition.shiftText; + + // if any modifiers besides shift are pressed, no text should be sent + if (this._modifiers & ~8) + description.text = ''; + + return description; + } + /** * @param {string} key */ async up(key) { - this._modifiers &= ~this._modifierBit(key); - this._pressedKeys.delete(key); + const description = this._keyDescriptionForString(key); + + this._modifiers &= ~this._modifierBit(description.key); + this._pressedKeys.delete(description.key); await this._client.send('Input.dispatchKeyEvent', { type: 'keyUp', modifiers: this._modifiers, - key, - windowsVirtualKeyCode: keyCodeForKey(key), - code: codeForKey(key) + key: description.key, + windowsVirtualKeyCode: description.keyCode, + code: description.code, + location: description.location }); } @@ -103,7 +166,10 @@ class Keyboard { if (options && options.delay) delay = options.delay; for (const char of text) { - await this.press(char, {text: char, delay}); + if (keyDefinitions[char]) + await this.press(char, {delay}); + else + await this.sendCharacter(char); if (delay) await new Promise(f => setTimeout(f, delay)); } @@ -229,200 +295,6 @@ class Touchscreen { } } -/** - * @type {Object} - */ -const keys = { - 'Cancel': {keyCode: 3, code: 'Abort'}, - 'Help': {keyCode: 6, code: 'Help'}, - 'Backspace': {keyCode: 8, code: 'Backspace'}, - 'Tab': {keyCode: 9, code: 'Tab'}, - 'Clear': {keyCode: 12, code: ''}, - 'Enter': {keyCode: 13, code: 'Enter'}, - 'Shift': {keyCode: 16, code: 'ShiftLeft'}, - 'Control': {keyCode: 17, code: 'ControlLeft'}, - 'Alt': {keyCode: 18, code: 'AltLeft'}, - 'Pause': {keyCode: 19, code: 'Pause'}, - 'CapsLock': {keyCode: 20, code: 'CapsLock'}, - 'Escape': {keyCode: 27, code: 'Escape'}, - 'Convert': {keyCode: 28, code: 'Convert'}, - 'NonConvert': {keyCode: 29, code: 'NonConvert'}, - 'Accept': {keyCode: 30, code: ''}, - 'ModeChange': {keyCode: 31, code: ''}, - 'PageUp': {keyCode: 33, code: 'PageUp'}, - 'PageDown': {keyCode: 34, code: 'PageDown'}, - 'End': {keyCode: 35, code: 'End'}, - 'Home': {keyCode: 36, code: 'Home'}, - 'ArrowLeft': {keyCode: 37, code: 'ArrowLeft'}, - 'ArrowUp': {keyCode: 38, code: 'ArrowUp'}, - 'ArrowRight': {keyCode: 39, code: 'ArrowRight'}, - 'ArrowDown': {keyCode: 40, code: 'ArrowDown'}, - 'Select': {keyCode: 41, code: 'Select'}, - 'Print': {keyCode: 42, code: ''}, - 'Execute': {keyCode: 43, code: 'Open'}, - 'PrintScreen': {keyCode: 44, code: 'PrintScreen'}, - 'Insert': {keyCode: 45, code: 'Insert'}, - 'Delete': {keyCode: 46, code: 'Delete'}, - ')': {keyCode: 48, code: 'Digit0'}, - '!': {keyCode: 49, code: 'Digit1'}, - '@': {keyCode: 50, code: 'Digit2'}, - '#': {keyCode: 51, code: 'Digit3'}, - '$': {keyCode: 52, code: 'Digit4'}, - '%': {keyCode: 53, code: 'Digit5'}, - '^': {keyCode: 54, code: 'Digit6'}, - '&': {keyCode: 55, code: 'Digit7'}, - '*': {keyCode: 56, code: 'Digit8'}, - '(': {keyCode: 57, code: 'Digit9'}, - 'Meta': {keyCode: 91, code: 'MetaLeft'}, - 'ContextMenu': {keyCode: 93, code: 'ContextMenu'}, - 'F1': {keyCode: 112, code: 'F1'}, - 'F2': {keyCode: 113, code: 'F2'}, - 'F3': {keyCode: 114, code: 'F3'}, - 'F4': {keyCode: 115, code: 'F4'}, - 'F5': {keyCode: 116, code: 'F5'}, - 'F6': {keyCode: 117, code: 'F6'}, - 'F7': {keyCode: 118, code: 'F7'}, - 'F8': {keyCode: 119, code: 'F8'}, - 'F9': {keyCode: 120, code: 'F9'}, - 'F10': {keyCode: 121, code: 'F10'}, - 'F11': {keyCode: 122, code: 'F11'}, - 'F12': {keyCode: 123, code: 'F12'}, - 'F13': {keyCode: 124, code: 'F13'}, - 'F14': {keyCode: 125, code: 'F14'}, - 'F15': {keyCode: 126, code: 'F15'}, - 'F16': {keyCode: 127, code: 'F16'}, - 'F17': {keyCode: 128, code: 'F17'}, - 'F18': {keyCode: 129, code: 'F18'}, - 'F19': {keyCode: 130, code: 'F19'}, - 'F20': {keyCode: 131, code: 'F20'}, - 'F21': {keyCode: 132, code: 'F21'}, - 'F22': {keyCode: 133, code: 'F22'}, - 'F23': {keyCode: 134, code: 'F23'}, - 'F24': {keyCode: 135, code: 'F24'}, - 'NumLock': {keyCode: 144, code: 'NumLock'}, - 'ScrollLock': {keyCode: 145, code: 'ScrollLock'}, - 'AudioVolumeMute': {keyCode: 173, code: 'AudioVolumeMute'}, - 'AudioVolumeDown': {keyCode: 174, code: 'AudioVolumeDown'}, - 'AudioVolumeUp': {keyCode: 175, code: 'AudioVolumeUp'}, - 'MediaTrackNext': {keyCode: 176, code: 'MediaTrackNext'}, - 'MediaTrackPrevious': {keyCode: 177, code: 'MediaTrackPrevious'}, - 'MediaStop': {keyCode: 178, code: 'MediaStop'}, - 'MediaPlayPause': {keyCode: 179, code: 'MediaPlayPause'}, - ';': {keyCode: 186, code: 'Semicolon'}, - ':': {keyCode: 186, code: 'Semicolon'}, - '=': {keyCode: 187, code: 'Equal'}, - '+': {keyCode: 187, code: 'Equal'}, - ',': {keyCode: 188, code: 'Comma'}, - '<': {keyCode: 188, code: 'Comma'}, - '-': {keyCode: 189, code: 'Minus'}, - '_': {keyCode: 189, code: 'Minus'}, - '.': {keyCode: 190, code: 'Period'}, - '>': {keyCode: 190, code: 'Period'}, - '/': {keyCode: 191, code: 'Slash'}, - '?': {keyCode: 191, code: 'Slash'}, - '`': {keyCode: 192, code: 'Backquote'}, - '~': {keyCode: 192, code: 'Backquote'}, - '[': {keyCode: 219, code: 'BracketLeft'}, - '{': {keyCode: 219, code: 'BracketLeft'}, - '\\': {keyCode: 220, code: 'Backslash'}, - '|': {keyCode: 220, code: 'Backslash'}, - ']': {keyCode: 221, code: 'BracketRight'}, - '}': {keyCode: 221, code: 'BracketRight'}, - '\'': {keyCode: 222, code: 'Quote'}, - '"': {keyCode: 222, code: 'Quote'}, - 'AltGraph': {keyCode: 225, code: 'AltGraph'}, - 'Attn': {keyCode: 246, code: ''}, - 'CrSel': {keyCode: 247, code: 'Props'}, - 'ExSel': {keyCode: 248, code: ''}, - 'EraseEof': {keyCode: 249, code: ''}, - 'Play': {keyCode: 250, code: ''}, - 'ZoomOut': {keyCode: 251, code: ''}, - '0': { keyCode: 48, code: 'Digit0'}, - '1': { keyCode: 49, code: 'Digit1'}, - '2': { keyCode: 50, code: 'Digit2'}, - '3': { keyCode: 51, code: 'Digit3'}, - '4': { keyCode: 52, code: 'Digit4'}, - '5': { keyCode: 53, code: 'Digit5'}, - '6': { keyCode: 54, code: 'Digit6'}, - '7': { keyCode: 55, code: 'Digit7'}, - '8': { keyCode: 56, code: 'Digit8'}, - '9': { keyCode: 57, code: 'Digit9'}, - 'q': { keyCode: 81, code: 'KeyQ'}, - 'w': { keyCode: 87, code: 'KeyW'}, - 'e': { keyCode: 69, code: 'KeyE'}, - 'r': { keyCode: 82, code: 'KeyR'}, - 't': { keyCode: 84, code: 'KeyT'}, - 'y': { keyCode: 89, code: 'KeyY'}, - 'u': { keyCode: 85, code: 'KeyU'}, - 'i': { keyCode: 73, code: 'KeyI'}, - 'o': { keyCode: 79, code: 'KeyO'}, - 'p': { keyCode: 80, code: 'KeyP'}, - 'a': { keyCode: 65, code: 'KeyA'}, - 's': { keyCode: 83, code: 'KeyS'}, - 'd': { keyCode: 68, code: 'KeyD'}, - 'f': { keyCode: 70, code: 'KeyF'}, - 'g': { keyCode: 71, code: 'KeyG'}, - 'h': { keyCode: 72, code: 'KeyH'}, - 'j': { keyCode: 74, code: 'KeyJ'}, - 'k': { keyCode: 75, code: 'KeyK'}, - 'l': { keyCode: 76, code: 'KeyL'}, - 'z': { keyCode: 90, code: 'KeyZ'}, - 'x': { keyCode: 88, code: 'KeyX'}, - 'c': { keyCode: 67, code: 'KeyC'}, - 'v': { keyCode: 86, code: 'KeyV'}, - 'b': { keyCode: 66, code: 'KeyB'}, - 'n': { keyCode: 78, code: 'KeyN'}, - 'm': { keyCode: 77, code: 'KeyM'}, - 'Q': { keyCode: 81, code: 'KeyQ'}, - 'W': { keyCode: 87, code: 'KeyW'}, - 'E': { keyCode: 69, code: 'KeyE'}, - 'R': { keyCode: 82, code: 'KeyR'}, - 'T': { keyCode: 84, code: 'KeyT'}, - 'Y': { keyCode: 89, code: 'KeyY'}, - 'U': { keyCode: 85, code: 'KeyU'}, - 'I': { keyCode: 73, code: 'KeyI'}, - 'O': { keyCode: 79, code: 'KeyO'}, - 'P': { keyCode: 80, code: 'KeyP'}, - 'A': { keyCode: 65, code: 'KeyA'}, - 'S': { keyCode: 83, code: 'KeyS'}, - 'D': { keyCode: 68, code: 'KeyD'}, - 'F': { keyCode: 70, code: 'KeyF'}, - 'G': { keyCode: 71, code: 'KeyG'}, - 'H': { keyCode: 72, code: 'KeyH'}, - 'J': { keyCode: 74, code: 'KeyJ'}, - 'K': { keyCode: 75, code: 'KeyK'}, - 'L': { keyCode: 76, code: 'KeyL'}, - 'Z': { keyCode: 90, code: 'KeyZ'}, - 'X': { keyCode: 88, code: 'KeyX'}, - 'C': { keyCode: 67, code: 'KeyC'}, - 'V': { keyCode: 86, code: 'KeyV'}, - 'B': { keyCode: 66, code: 'KeyB'}, - 'N': { keyCode: 78, code: 'KeyN'}, - 'M': { keyCode: 77, code: 'KeyM'} -}; - -/** - * @param {string} key - * @return {number} - */ -function keyCodeForKey(key) { - if (keys[key]) - return keys[key].keyCode; - if (key.length === 1) - return key.toUpperCase().charCodeAt(0); - return 0; -} - -/** - * @param {string} key - * @return {string} - */ -function codeForKey(key) { - if (!keys[key]) - return ''; - return keys[key].code; -} - module.exports = { Keyboard, Mouse, Touchscreen}; helper.tracePublicAPI(Keyboard); helper.tracePublicAPI(Mouse); diff --git a/lib/USKeyboardLayout.js b/lib/USKeyboardLayout.js new file mode 100644 index 00000000000..431d0b0f381 --- /dev/null +++ b/lib/USKeyboardLayout.js @@ -0,0 +1,281 @@ +/** + * 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. + */ + +/** + * @typedef {Object} KeyDefinition + * @property {number=} keyCode + * @property {number=} shiftKeyCode + * @property {string=} key + * @property {string=} shiftKey + * @property {string=} code + * @property {string=} text + * @property {string=} shiftText + * @property {number=} location + */ + +/** + * @type {Object} + */ +module.exports = { + '0': {'keyCode': 48, 'key': '0', 'code': 'Digit0'}, + '1': {'keyCode': 49, 'key': '1', 'code': 'Digit1'}, + '2': {'keyCode': 50, 'key': '2', 'code': 'Digit2'}, + '3': {'keyCode': 51, 'key': '3', 'code': 'Digit3'}, + '4': {'keyCode': 52, 'key': '4', 'code': 'Digit4'}, + '5': {'keyCode': 53, 'key': '5', 'code': 'Digit5'}, + '6': {'keyCode': 54, 'key': '6', 'code': 'Digit6'}, + '7': {'keyCode': 55, 'key': '7', 'code': 'Digit7'}, + '8': {'keyCode': 56, 'key': '8', 'code': 'Digit8'}, + '9': {'keyCode': 57, 'key': '9', 'code': 'Digit9'}, + 'Power': {'key': 'Power', 'code': 'Power'}, + 'Eject': {'key': 'Eject', 'code': 'Eject'}, + 'Abort': {'keyCode': 3, 'code': 'Abort', 'key': 'Cancel'}, + 'Help': {'keyCode': 6, 'code': 'Help', 'key': 'Help'}, + 'Backspace': {'keyCode': 8, 'code': 'Backspace', 'key': 'Backspace'}, + 'Tab': {'keyCode': 9, 'code': 'Tab', 'key': 'Tab'}, + 'Numpad5': {'keyCode': 12, 'shiftKeyCode': 101, 'key': 'Clear', 'code': 'Numpad5', 'shiftKey': '5', 'location': 3}, + 'NumpadEnter': {'keyCode': 13, 'code': 'NumpadEnter', 'key': 'Enter', 'text': '\r', 'location': 3}, + 'Enter': {'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r'}, + '\r': {'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r'}, + '\n': {'keyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r'}, + 'ShiftLeft': {'keyCode': 16, 'code': 'ShiftLeft', 'key': 'Shift', 'location': 1}, + 'ShiftRight': {'keyCode': 16, 'code': 'ShiftRight', 'key': 'Shift', 'location': 2}, + 'ControlLeft': {'keyCode': 17, 'code': 'ControlLeft', 'key': 'Control', 'location': 1}, + 'ControlRight': {'keyCode': 17, 'code': 'ControlRight', 'key': 'Control', 'location': 2}, + 'AltLeft': {'keyCode': 18, 'code': 'AltLeft', 'key': 'Alt', 'location': 1}, + 'AltRight': {'keyCode': 18, 'code': 'AltRight', 'key': 'Alt', 'location': 2}, + 'Pause': {'keyCode': 19, 'code': 'Pause', 'key': 'Pause'}, + 'CapsLock': {'keyCode': 20, 'code': 'CapsLock', 'key': 'CapsLock'}, + 'Escape': {'keyCode': 27, 'code': 'Escape', 'key': 'Escape'}, + 'Convert': {'keyCode': 28, 'code': 'Convert', 'key': 'Convert'}, + 'NonConvert': {'keyCode': 29, 'code': 'NonConvert', 'key': 'NonConvert'}, + 'Space': {'keyCode': 32, 'code': 'Space', 'key': ' '}, + 'Numpad9': {'keyCode': 33, 'shiftKeyCode': 105, 'key': 'PageUp', 'code': 'Numpad9', 'shiftKey': '9', 'location': 3}, + 'PageUp': {'keyCode': 33, 'code': 'PageUp', 'key': 'PageUp'}, + 'Numpad3': {'keyCode': 34, 'shiftKeyCode': 99, 'key': 'PageDown', 'code': 'Numpad3', 'shiftKey': '3', 'location': 3}, + 'PageDown': {'keyCode': 34, 'code': 'PageDown', 'key': 'PageDown'}, + 'End': {'keyCode': 35, 'code': 'End', 'key': 'End'}, + 'Numpad1': {'keyCode': 35, 'shiftKeyCode': 97, 'key': 'End', 'code': 'Numpad1', 'shiftKey': '1', 'location': 3}, + 'Home': {'keyCode': 36, 'code': 'Home', 'key': 'Home'}, + 'Numpad7': {'keyCode': 36, 'shiftKeyCode': 103, 'key': 'Home', 'code': 'Numpad7', 'shiftKey': '7', 'location': 3}, + 'ArrowLeft': {'keyCode': 37, 'code': 'ArrowLeft', 'key': 'ArrowLeft'}, + 'Numpad4': {'keyCode': 37, 'shiftKeyCode': 100, 'key': 'ArrowLeft', 'code': 'Numpad4', 'shiftKey': '4', 'location': 3}, + 'Numpad8': {'keyCode': 38, 'shiftKeyCode': 104, 'key': 'ArrowUp', 'code': 'Numpad8', 'shiftKey': '8', 'location': 3}, + 'ArrowUp': {'keyCode': 38, 'code': 'ArrowUp', 'key': 'ArrowUp'}, + 'ArrowRight': {'keyCode': 39, 'code': 'ArrowRight', 'key': 'ArrowRight'}, + 'Numpad6': {'keyCode': 39, 'shiftKeyCode': 102, 'key': 'ArrowRight', 'code': 'Numpad6', 'shiftKey': '6', 'location': 3}, + 'Numpad2': {'keyCode': 40, 'shiftKeyCode': 98, 'key': 'ArrowDown', 'code': 'Numpad2', 'shiftKey': '2', 'location': 3}, + 'ArrowDown': {'keyCode': 40, 'code': 'ArrowDown', 'key': 'ArrowDown'}, + 'Select': {'keyCode': 41, 'code': 'Select', 'key': 'Select'}, + 'Open': {'keyCode': 43, 'code': 'Open', 'key': 'Execute'}, + 'PrintScreen': {'keyCode': 44, 'code': 'PrintScreen', 'key': 'PrintScreen'}, + 'Insert': {'keyCode': 45, 'code': 'Insert', 'key': 'Insert'}, + 'Numpad0': {'keyCode': 45, 'shiftKeyCode': 96, 'key': 'Insert', 'code': 'Numpad0', 'shiftKey': '0', 'location': 3}, + 'Delete': {'keyCode': 46, 'code': 'Delete', 'key': 'Delete'}, + 'NumpadDecimal': {'keyCode': 46, 'shiftKeyCode': 110, 'code': 'NumpadDecimal', 'key': '\u0000', 'shiftKey': '.', 'location': 3}, + 'Digit0': {'keyCode': 48, 'code': 'Digit0', 'shiftKey': ')', 'key': '0'}, + 'Digit1': {'keyCode': 49, 'code': 'Digit1', 'shiftKey': '!', 'key': '1'}, + 'Digit2': {'keyCode': 50, 'code': 'Digit2', 'shiftKey': '@', 'key': '2'}, + 'Digit3': {'keyCode': 51, 'code': 'Digit3', 'shiftKey': '#', 'key': '3'}, + 'Digit4': {'keyCode': 52, 'code': 'Digit4', 'shiftKey': '$', 'key': '4'}, + 'Digit5': {'keyCode': 53, 'code': 'Digit5', 'shiftKey': '%', 'key': '5'}, + 'Digit6': {'keyCode': 54, 'code': 'Digit6', 'shiftKey': '^', 'key': '6'}, + 'Digit7': {'keyCode': 55, 'code': 'Digit7', 'shiftKey': '&', 'key': '7'}, + 'Digit8': {'keyCode': 56, 'code': 'Digit8', 'shiftKey': '*', 'key': '8'}, + 'Digit9': {'keyCode': 57, 'code': 'Digit9', 'shiftKey': '\(', 'key': '9'}, + 'KeyA': {'keyCode': 65, 'code': 'KeyA', 'shiftKey': 'A', 'key': 'a'}, + 'KeyB': {'keyCode': 66, 'code': 'KeyB', 'shiftKey': 'B', 'key': 'b'}, + 'KeyC': {'keyCode': 67, 'code': 'KeyC', 'shiftKey': 'C', 'key': 'c'}, + 'KeyD': {'keyCode': 68, 'code': 'KeyD', 'shiftKey': 'D', 'key': 'd'}, + 'KeyE': {'keyCode': 69, 'code': 'KeyE', 'shiftKey': 'E', 'key': 'e'}, + 'KeyF': {'keyCode': 70, 'code': 'KeyF', 'shiftKey': 'F', 'key': 'f'}, + 'KeyG': {'keyCode': 71, 'code': 'KeyG', 'shiftKey': 'G', 'key': 'g'}, + 'KeyH': {'keyCode': 72, 'code': 'KeyH', 'shiftKey': 'H', 'key': 'h'}, + 'KeyI': {'keyCode': 73, 'code': 'KeyI', 'shiftKey': 'I', 'key': 'i'}, + 'KeyJ': {'keyCode': 74, 'code': 'KeyJ', 'shiftKey': 'J', 'key': 'j'}, + 'KeyK': {'keyCode': 75, 'code': 'KeyK', 'shiftKey': 'K', 'key': 'k'}, + 'KeyL': {'keyCode': 76, 'code': 'KeyL', 'shiftKey': 'L', 'key': 'l'}, + 'KeyM': {'keyCode': 77, 'code': 'KeyM', 'shiftKey': 'M', 'key': 'm'}, + 'KeyN': {'keyCode': 78, 'code': 'KeyN', 'shiftKey': 'N', 'key': 'n'}, + 'KeyO': {'keyCode': 79, 'code': 'KeyO', 'shiftKey': 'O', 'key': 'o'}, + 'KeyP': {'keyCode': 80, 'code': 'KeyP', 'shiftKey': 'P', 'key': 'p'}, + 'KeyQ': {'keyCode': 81, 'code': 'KeyQ', 'shiftKey': 'Q', 'key': 'q'}, + 'KeyR': {'keyCode': 82, 'code': 'KeyR', 'shiftKey': 'R', 'key': 'r'}, + 'KeyS': {'keyCode': 83, 'code': 'KeyS', 'shiftKey': 'S', 'key': 's'}, + 'KeyT': {'keyCode': 84, 'code': 'KeyT', 'shiftKey': 'T', 'key': 't'}, + 'KeyU': {'keyCode': 85, 'code': 'KeyU', 'shiftKey': 'U', 'key': 'u'}, + 'KeyV': {'keyCode': 86, 'code': 'KeyV', 'shiftKey': 'V', 'key': 'v'}, + 'KeyW': {'keyCode': 87, 'code': 'KeyW', 'shiftKey': 'W', 'key': 'w'}, + 'KeyX': {'keyCode': 88, 'code': 'KeyX', 'shiftKey': 'X', 'key': 'x'}, + 'KeyY': {'keyCode': 89, 'code': 'KeyY', 'shiftKey': 'Y', 'key': 'y'}, + 'KeyZ': {'keyCode': 90, 'code': 'KeyZ', 'shiftKey': 'Z', 'key': 'z'}, + 'MetaLeft': {'keyCode': 91, 'code': 'MetaLeft', 'key': 'Meta'}, + 'MetaRight': {'keyCode': 92, 'code': 'MetaRight', 'key': 'Meta'}, + 'ContextMenu': {'keyCode': 93, 'code': 'ContextMenu', 'key': 'ContextMenu'}, + 'NumpadMultiply': {'keyCode': 106, 'code': 'NumpadMultiply', 'key': '*', 'location': 3}, + 'NumpadAdd': {'keyCode': 107, 'code': 'NumpadAdd', 'key': '+', 'location': 3}, + 'NumpadSubtract': {'keyCode': 109, 'code': 'NumpadSubtract', 'key': '-', 'location': 3}, + 'NumpadDivide': {'keyCode': 111, 'code': 'NumpadDivide', 'key': '/', 'location': 3}, + 'F1': {'keyCode': 112, 'code': 'F1', 'key': 'F1'}, + 'F2': {'keyCode': 113, 'code': 'F2', 'key': 'F2'}, + 'F3': {'keyCode': 114, 'code': 'F3', 'key': 'F3'}, + 'F4': {'keyCode': 115, 'code': 'F4', 'key': 'F4'}, + 'F5': {'keyCode': 116, 'code': 'F5', 'key': 'F5'}, + 'F6': {'keyCode': 117, 'code': 'F6', 'key': 'F6'}, + 'F7': {'keyCode': 118, 'code': 'F7', 'key': 'F7'}, + 'F8': {'keyCode': 119, 'code': 'F8', 'key': 'F8'}, + 'F9': {'keyCode': 120, 'code': 'F9', 'key': 'F9'}, + 'F10': {'keyCode': 121, 'code': 'F10', 'key': 'F10'}, + 'F11': {'keyCode': 122, 'code': 'F11', 'key': 'F11'}, + 'F12': {'keyCode': 123, 'code': 'F12', 'key': 'F12'}, + 'F13': {'keyCode': 124, 'code': 'F13', 'key': 'F13'}, + 'F14': {'keyCode': 125, 'code': 'F14', 'key': 'F14'}, + 'F15': {'keyCode': 126, 'code': 'F15', 'key': 'F15'}, + 'F16': {'keyCode': 127, 'code': 'F16', 'key': 'F16'}, + 'F17': {'keyCode': 128, 'code': 'F17', 'key': 'F17'}, + 'F18': {'keyCode': 129, 'code': 'F18', 'key': 'F18'}, + 'F19': {'keyCode': 130, 'code': 'F19', 'key': 'F19'}, + 'F20': {'keyCode': 131, 'code': 'F20', 'key': 'F20'}, + 'F21': {'keyCode': 132, 'code': 'F21', 'key': 'F21'}, + 'F22': {'keyCode': 133, 'code': 'F22', 'key': 'F22'}, + 'F23': {'keyCode': 134, 'code': 'F23', 'key': 'F23'}, + 'F24': {'keyCode': 135, 'code': 'F24', 'key': 'F24'}, + 'NumLock': {'keyCode': 144, 'code': 'NumLock', 'key': 'NumLock'}, + 'ScrollLock': {'keyCode': 145, 'code': 'ScrollLock', 'key': 'ScrollLock'}, + 'AudioVolumeMute': {'keyCode': 173, 'code': 'AudioVolumeMute', 'key': 'AudioVolumeMute'}, + 'AudioVolumeDown': {'keyCode': 174, 'code': 'AudioVolumeDown', 'key': 'AudioVolumeDown'}, + 'AudioVolumeUp': {'keyCode': 175, 'code': 'AudioVolumeUp', 'key': 'AudioVolumeUp'}, + 'MediaTrackNext': {'keyCode': 176, 'code': 'MediaTrackNext', 'key': 'MediaTrackNext'}, + 'MediaTrackPrevious': {'keyCode': 177, 'code': 'MediaTrackPrevious', 'key': 'MediaTrackPrevious'}, + 'MediaStop': {'keyCode': 178, 'code': 'MediaStop', 'key': 'MediaStop'}, + 'MediaPlayPause': {'keyCode': 179, 'code': 'MediaPlayPause', 'key': 'MediaPlayPause'}, + 'Semicolon': {'keyCode': 186, 'code': 'Semicolon', 'shiftKey': ':', 'key': ';'}, + 'Equal': {'keyCode': 187, 'code': 'Equal', 'shiftKey': '+', 'key': '='}, + 'NumpadEqual': {'keyCode': 187, 'code': 'NumpadEqual', 'key': '=', 'location': 3}, + 'Comma': {'keyCode': 188, 'code': 'Comma', 'shiftKey': '\<', 'key': ','}, + 'Minus': {'keyCode': 189, 'code': 'Minus', 'shiftKey': '_', 'key': '-'}, + 'Period': {'keyCode': 190, 'code': 'Period', 'shiftKey': '>', 'key': '.'}, + 'Slash': {'keyCode': 191, 'code': 'Slash', 'shiftKey': '?', 'key': '/'}, + 'Backquote': {'keyCode': 192, 'code': 'Backquote', 'shiftKey': '~', 'key': '`'}, + 'BracketLeft': {'keyCode': 219, 'code': 'BracketLeft', 'shiftKey': '{', 'key': '['}, + 'Backslash': {'keyCode': 220, 'code': 'Backslash', 'shiftKey': '|', 'key': '\\'}, + 'BracketRight': {'keyCode': 221, 'code': 'BracketRight', 'shiftKey': '}', 'key': ']'}, + 'Quote': {'keyCode': 222, 'code': 'Quote', 'shiftKey': '"', 'key': '\''}, + 'AltGraph': {'keyCode': 225, 'code': 'AltGraph', 'key': 'AltGraph'}, + 'Props': {'keyCode': 247, 'code': 'Props', 'key': 'CrSel'}, + 'Cancel': {'keyCode': 3, 'key': 'Cancel', 'code': 'Abort'}, + 'Clear': {'keyCode': 12, 'key': 'Clear', 'code': 'Numpad5', 'location': 3}, + 'Shift': {'keyCode': 16, 'key': 'Shift', 'code': 'ShiftLeft'}, + 'Control': {'keyCode': 17, 'key': 'Control', 'code': 'ControlLeft'}, + 'Alt': {'keyCode': 18, 'key': 'Alt', 'code': 'AltLeft'}, + 'Accept': {'keyCode': 30, 'key': 'Accept'}, + 'ModeChange': {'keyCode': 31, 'key': 'ModeChange'}, + ' ': {'keyCode': 32, 'key': ' ', 'code': 'Space'}, + 'Print': {'keyCode': 42, 'key': 'Print'}, + 'Execute': {'keyCode': 43, 'key': 'Execute', 'code': 'Open'}, + '\u0000': {'keyCode': 46, 'key': '\u0000', 'code': 'NumpadDecimal', 'location': 3}, + 'a': {'keyCode': 65, 'key': 'a', 'code': 'KeyA'}, + 'b': {'keyCode': 66, 'key': 'b', 'code': 'KeyB'}, + 'c': {'keyCode': 67, 'key': 'c', 'code': 'KeyC'}, + 'd': {'keyCode': 68, 'key': 'd', 'code': 'KeyD'}, + 'e': {'keyCode': 69, 'key': 'e', 'code': 'KeyE'}, + 'f': {'keyCode': 70, 'key': 'f', 'code': 'KeyF'}, + 'g': {'keyCode': 71, 'key': 'g', 'code': 'KeyG'}, + 'h': {'keyCode': 72, 'key': 'h', 'code': 'KeyH'}, + 'i': {'keyCode': 73, 'key': 'i', 'code': 'KeyI'}, + 'j': {'keyCode': 74, 'key': 'j', 'code': 'KeyJ'}, + 'k': {'keyCode': 75, 'key': 'k', 'code': 'KeyK'}, + 'l': {'keyCode': 76, 'key': 'l', 'code': 'KeyL'}, + 'm': {'keyCode': 77, 'key': 'm', 'code': 'KeyM'}, + 'n': {'keyCode': 78, 'key': 'n', 'code': 'KeyN'}, + 'o': {'keyCode': 79, 'key': 'o', 'code': 'KeyO'}, + 'p': {'keyCode': 80, 'key': 'p', 'code': 'KeyP'}, + 'q': {'keyCode': 81, 'key': 'q', 'code': 'KeyQ'}, + 'r': {'keyCode': 82, 'key': 'r', 'code': 'KeyR'}, + 's': {'keyCode': 83, 'key': 's', 'code': 'KeyS'}, + 't': {'keyCode': 84, 'key': 't', 'code': 'KeyT'}, + 'u': {'keyCode': 85, 'key': 'u', 'code': 'KeyU'}, + 'v': {'keyCode': 86, 'key': 'v', 'code': 'KeyV'}, + 'w': {'keyCode': 87, 'key': 'w', 'code': 'KeyW'}, + 'x': {'keyCode': 88, 'key': 'x', 'code': 'KeyX'}, + 'y': {'keyCode': 89, 'key': 'y', 'code': 'KeyY'}, + 'z': {'keyCode': 90, 'key': 'z', 'code': 'KeyZ'}, + 'Meta': {'keyCode': 91, 'key': 'Meta', 'code': 'MetaLeft'}, + '*': {'keyCode': 106, 'key': '*', 'code': 'NumpadMultiply', 'location': 3}, + '+': {'keyCode': 107, 'key': '+', 'code': 'NumpadAdd', 'location': 3}, + '-': {'keyCode': 109, 'key': '-', 'code': 'NumpadSubtract', 'location': 3}, + '/': {'keyCode': 111, 'key': '/', 'code': 'NumpadDivide', 'location': 3}, + ';': {'keyCode': 186, 'key': ';', 'code': 'Semicolon'}, + '=': {'keyCode': 187, 'key': '=', 'code': 'Equal'}, + ',': {'keyCode': 188, 'key': ',', 'code': 'Comma'}, + '.': {'keyCode': 190, 'key': '.', 'code': 'Period'}, + '`': {'keyCode': 192, 'key': '`', 'code': 'Backquote'}, + '[': {'keyCode': 219, 'key': '[', 'code': 'BracketLeft'}, + '\\': {'keyCode': 220, 'key': '\\', 'code': 'Backslash'}, + ']': {'keyCode': 221, 'key': ']', 'code': 'BracketRight'}, + '\'': {'keyCode': 222, 'key': '\'', 'code': 'Quote'}, + 'Attn': {'keyCode': 246, 'key': 'Attn'}, + 'CrSel': {'keyCode': 247, 'key': 'CrSel', 'code': 'Props'}, + 'ExSel': {'keyCode': 248, 'key': 'ExSel'}, + 'EraseEof': {'keyCode': 249, 'key': 'EraseEof'}, + 'Play': {'keyCode': 250, 'key': 'Play'}, + 'ZoomOut': {'keyCode': 251, 'key': 'ZoomOut'}, + ')': {'keyCode': 48, 'key': ')', 'code': 'Digit0'}, + '!': {'keyCode': 49, 'key': '!', 'code': 'Digit1'}, + '@': {'keyCode': 50, 'key': '@', 'code': 'Digit2'}, + '#': {'keyCode': 51, 'key': '#', 'code': 'Digit3'}, + '$': {'keyCode': 52, 'key': '$', 'code': 'Digit4'}, + '%': {'keyCode': 53, 'key': '%', 'code': 'Digit5'}, + '^': {'keyCode': 54, 'key': '^', 'code': 'Digit6'}, + '&': {'keyCode': 55, 'key': '&', 'code': 'Digit7'}, + '(': {'keyCode': 57, 'key': '\(', 'code': 'Digit9'}, + 'A': {'keyCode': 65, 'key': 'A', 'code': 'KeyA'}, + 'B': {'keyCode': 66, 'key': 'B', 'code': 'KeyB'}, + 'C': {'keyCode': 67, 'key': 'C', 'code': 'KeyC'}, + 'D': {'keyCode': 68, 'key': 'D', 'code': 'KeyD'}, + 'E': {'keyCode': 69, 'key': 'E', 'code': 'KeyE'}, + 'F': {'keyCode': 70, 'key': 'F', 'code': 'KeyF'}, + 'G': {'keyCode': 71, 'key': 'G', 'code': 'KeyG'}, + 'H': {'keyCode': 72, 'key': 'H', 'code': 'KeyH'}, + 'I': {'keyCode': 73, 'key': 'I', 'code': 'KeyI'}, + 'J': {'keyCode': 74, 'key': 'J', 'code': 'KeyJ'}, + 'K': {'keyCode': 75, 'key': 'K', 'code': 'KeyK'}, + 'L': {'keyCode': 76, 'key': 'L', 'code': 'KeyL'}, + 'M': {'keyCode': 77, 'key': 'M', 'code': 'KeyM'}, + 'N': {'keyCode': 78, 'key': 'N', 'code': 'KeyN'}, + 'O': {'keyCode': 79, 'key': 'O', 'code': 'KeyO'}, + 'P': {'keyCode': 80, 'key': 'P', 'code': 'KeyP'}, + 'Q': {'keyCode': 81, 'key': 'Q', 'code': 'KeyQ'}, + 'R': {'keyCode': 82, 'key': 'R', 'code': 'KeyR'}, + 'S': {'keyCode': 83, 'key': 'S', 'code': 'KeyS'}, + 'T': {'keyCode': 84, 'key': 'T', 'code': 'KeyT'}, + 'U': {'keyCode': 85, 'key': 'U', 'code': 'KeyU'}, + 'V': {'keyCode': 86, 'key': 'V', 'code': 'KeyV'}, + 'W': {'keyCode': 87, 'key': 'W', 'code': 'KeyW'}, + 'X': {'keyCode': 88, 'key': 'X', 'code': 'KeyX'}, + 'Y': {'keyCode': 89, 'key': 'Y', 'code': 'KeyY'}, + 'Z': {'keyCode': 90, 'key': 'Z', 'code': 'KeyZ'}, + ':': {'keyCode': 186, 'key': ':', 'code': 'Semicolon'}, + '<': {'keyCode': 188, 'key': '\<', 'code': 'Comma'}, + '_': {'keyCode': 189, 'key': '_', 'code': 'Minus'}, + '>': {'keyCode': 190, 'key': '>', 'code': 'Period'}, + '?': {'keyCode': 191, 'key': '?', 'code': 'Slash'}, + '~': {'keyCode': 192, 'key': '~', 'code': 'Backquote'}, + '{': {'keyCode': 219, 'key': '{', 'code': 'BracketLeft'}, + '|': {'keyCode': 220, 'key': '|', 'code': 'Backslash'}, + '}': {'keyCode': 221, 'key': '}', 'code': 'BracketRight'}, + '"': {'keyCode': 222, 'key': '"', 'code': 'Quote'} +}; \ No newline at end of file diff --git a/test/test.js b/test/test.js index fae8865d081..ded66436ebb 100644 --- a/test/test.js +++ b/test/test.js @@ -1997,6 +1997,42 @@ describe('Page', function() { await button.click(); expect(await frame.evaluate(() => window.result)).toBe('Clicked'); })); + it('should type all kinds of characters', SX(async function() { + await page.goto(PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + const text = 'This text goes onto two lines.\nThis character is 嗨.'; + await page.keyboard.type(text); + expect(await page.evaluate('result')).toBe(text); + })); + it('should specify location', SX(async function() { + await page.goto(PREFIX + '/input/textarea.html'); + await page.evaluate(() => { + window.addEventListener('keydown', event => window.keyLocation = event.location, true); + }); + const textarea = await page.$('textarea'); + + await textarea.press('Digit5'); + expect(await page.evaluate('keyLocation')).toBe(0); + + await textarea.press('ControlLeft'); + expect(await page.evaluate('keyLocation')).toBe(1); + + await textarea.press('ControlRight'); + expect(await page.evaluate('keyLocation')).toBe(2); + + await textarea.press('NumpadSubtract'); + expect(await page.evaluate('keyLocation')).toBe(3); + })); + it('should throw on unknown keys', SX(async function() { + let error = await page.keyboard.press('NotARealKey').catch(e => e); + expect(error.message).toBe('Unknown key: "NotARealKey"'); + + error = await page.keyboard.press('ё').catch(e => e); + expect(error && error.message).toBe('Unknown key: "ё"'); + + error = await page.keyboard.press('😊').catch(e => e); + expect(error && error.message).toBe('Unknown key: "😊"'); + })); function dimensions() { const rect = document.querySelector('textarea').getBoundingClientRect(); return {