fix(input) Send .code with keyboard events (#1015)

This patch starts sending proper `.code` field with emulated keyboard events.

References #777.
This commit is contained in:
JoelEinbinder 2017-10-11 18:09:43 -07:00 committed by Andrey Lushnikov
parent 23c0ba0727
commit c893bf3684
3 changed files with 208 additions and 131 deletions

View File

@ -38,7 +38,8 @@ class Keyboard {
await this._client.send('Input.dispatchKeyEvent', { await this._client.send('Input.dispatchKeyEvent', {
type: text ? 'keyDown' : 'rawKeyDown', type: text ? 'keyDown' : 'rawKeyDown',
modifiers: this._modifiers, modifiers: this._modifiers,
windowsVirtualKeyCode: codeForKey(key), windowsVirtualKeyCode: keyCodeForKey(key),
code: codeForKey(key),
key, key,
text, text,
unmodifiedText: text, unmodifiedText: text,
@ -72,7 +73,8 @@ class Keyboard {
type: 'keyUp', type: 'keyUp',
modifiers: this._modifiers, modifiers: this._modifiers,
key, key,
windowsVirtualKeyCode: codeForKey(key), windowsVirtualKeyCode: keyCodeForKey(key),
code: codeForKey(key)
}); });
} }
@ -224,125 +226,200 @@ class Touchscreen {
} }
} }
/**
* @type {Object<string, {keyCode: number, code: string}>}
*/
const keys = { const keys = {
'Cancel': 3, 'Cancel': {keyCode: 3, code: 'Abort'},
'Help': 6, 'Help': {keyCode: 6, code: 'Help'},
'Backspace': 8, 'Backspace': {keyCode: 8, code: 'Backspace'},
'Tab': 9, 'Tab': {keyCode: 9, code: 'Tab'},
'Clear': 12, 'Clear': {keyCode: 12, code: ''},
'Enter': 13, 'Enter': {keyCode: 13, code: 'Enter'},
'Shift': 16, 'Shift': {keyCode: 16, code: 'ShiftLeft'},
'Control': 17, 'Control': {keyCode: 17, code: 'ControlLeft'},
'Alt': 18, 'Alt': {keyCode: 18, code: 'AltLeft'},
'Pause': 19, 'Pause': {keyCode: 19, code: 'Pause'},
'CapsLock': 20, 'CapsLock': {keyCode: 20, code: 'CapsLock'},
'Escape': 27, 'Escape': {keyCode: 27, code: 'Escape'},
'Convert': 28, 'Convert': {keyCode: 28, code: 'Convert'},
'NonConvert': 29, 'NonConvert': {keyCode: 29, code: 'NonConvert'},
'Accept': 30, 'Accept': {keyCode: 30, code: ''},
'ModeChange': 31, 'ModeChange': {keyCode: 31, code: ''},
'PageUp': 33, 'PageUp': {keyCode: 33, code: 'PageUp'},
'PageDown': 34, 'PageDown': {keyCode: 34, code: 'PageDown'},
'End': 35, 'End': {keyCode: 35, code: 'End'},
'Home': 36, 'Home': {keyCode: 36, code: 'Home'},
'ArrowLeft': 37, 'ArrowLeft': {keyCode: 37, code: 'ArrowLeft'},
'ArrowUp': 38, 'ArrowUp': {keyCode: 38, code: 'ArrowUp'},
'ArrowRight': 39, 'ArrowRight': {keyCode: 39, code: 'ArrowRight'},
'ArrowDown': 40, 'ArrowDown': {keyCode: 40, code: 'ArrowDown'},
'Select': 41, 'Select': {keyCode: 41, code: 'Select'},
'Print': 42, 'Print': {keyCode: 42, code: ''},
'Execute': 43, 'Execute': {keyCode: 43, code: 'Open'},
'PrintScreen': 44, 'PrintScreen': {keyCode: 44, code: 'PrintScreen'},
'Insert': 45, 'Insert': {keyCode: 45, code: 'Insert'},
'Delete': 46, 'Delete': {keyCode: 46, code: 'Delete'},
')': 48, ')': {keyCode: 48, code: 'Digit0'},
'!': 49, '!': {keyCode: 49, code: 'Digit1'},
'@': 50, '@': {keyCode: 50, code: 'Digit2'},
'#': 51, '#': {keyCode: 51, code: 'Digit3'},
'$': 52, '$': {keyCode: 52, code: 'Digit4'},
'%': 53, '%': {keyCode: 53, code: 'Digit5'},
'^': 54, '^': {keyCode: 54, code: 'Digit6'},
'&': 55, '&': {keyCode: 55, code: 'Digit7'},
'*': 56, '*': {keyCode: 56, code: 'Digit8'},
'(': 57, '(': {keyCode: 57, code: 'Digit9'},
'Meta': 91, 'Meta': {keyCode: 91, code: 'MetaLeft'},
'ContextMenu': 93, 'ContextMenu': {keyCode: 93, code: 'ContextMenu'},
'F1': 112, 'F1': {keyCode: 112, code: 'F1'},
'F2': 113, 'F2': {keyCode: 113, code: 'F2'},
'F3': 114, 'F3': {keyCode: 114, code: 'F3'},
'F4': 115, 'F4': {keyCode: 115, code: 'F4'},
'F5': 116, 'F5': {keyCode: 116, code: 'F5'},
'F6': 117, 'F6': {keyCode: 117, code: 'F6'},
'F7': 118, 'F7': {keyCode: 118, code: 'F7'},
'F8': 119, 'F8': {keyCode: 119, code: 'F8'},
'F9': 120, 'F9': {keyCode: 120, code: 'F9'},
'F10': 121, 'F10': {keyCode: 121, code: 'F10'},
'F11': 122, 'F11': {keyCode: 122, code: 'F11'},
'F12': 123, 'F12': {keyCode: 123, code: 'F12'},
'F13': 124, 'F13': {keyCode: 124, code: 'F13'},
'F14': 125, 'F14': {keyCode: 125, code: 'F14'},
'F15': 126, 'F15': {keyCode: 126, code: 'F15'},
'F16': 127, 'F16': {keyCode: 127, code: 'F16'},
'F17': 128, 'F17': {keyCode: 128, code: 'F17'},
'F18': 129, 'F18': {keyCode: 129, code: 'F18'},
'F19': 130, 'F19': {keyCode: 130, code: 'F19'},
'F20': 131, 'F20': {keyCode: 131, code: 'F20'},
'F21': 132, 'F21': {keyCode: 132, code: 'F21'},
'F22': 133, 'F22': {keyCode: 133, code: 'F22'},
'F23': 134, 'F23': {keyCode: 134, code: 'F23'},
'F24': 135, 'F24': {keyCode: 135, code: 'F24'},
'NumLock': 144, 'NumLock': {keyCode: 144, code: 'NumLock'},
'ScrollLock': 145, 'ScrollLock': {keyCode: 145, code: 'ScrollLock'},
'AudioVolumeMute': 173, 'AudioVolumeMute': {keyCode: 173, code: 'AudioVolumeMute'},
'AudioVolumeDown': 174, 'AudioVolumeDown': {keyCode: 174, code: 'AudioVolumeDown'},
'AudioVolumeUp': 175, 'AudioVolumeUp': {keyCode: 175, code: 'AudioVolumeUp'},
'MediaTrackNext': 176, 'MediaTrackNext': {keyCode: 176, code: 'MediaTrackNext'},
'MediaTrackPrevious': 177, 'MediaTrackPrevious': {keyCode: 177, code: 'MediaTrackPrevious'},
'MediaStop': 178, 'MediaStop': {keyCode: 178, code: 'MediaStop'},
'MediaPlayPause': 179, 'MediaPlayPause': {keyCode: 179, code: 'MediaPlayPause'},
';': 186, ';': {keyCode: 186, code: 'Semicolon'},
':': 186, ':': {keyCode: 186, code: 'Semicolon'},
'=': 187, '=': {keyCode: 187, code: 'Equal'},
'+': 187, '+': {keyCode: 187, code: 'Equal'},
',': 188, ',': {keyCode: 188, code: 'Comma'},
'<': 188, '<': {keyCode: 188, code: 'Comma'},
'-': 189, '-': {keyCode: 189, code: 'Minus'},
'_': 189, '_': {keyCode: 189, code: 'Minus'},
'.': 190, '.': {keyCode: 190, code: 'Period'},
'>': 190, '>': {keyCode: 190, code: 'Period'},
'/': 191, '/': {keyCode: 191, code: 'Slash'},
'?': 191, '?': {keyCode: 191, code: 'Slash'},
'`': 192, '`': {keyCode: 192, code: 'Backquote'},
'~': 192, '~': {keyCode: 192, code: 'Backquote'},
'[': 219, '[': {keyCode: 219, code: 'BracketLeft'},
'{': 219, '{': {keyCode: 219, code: 'BracketLeft'},
'\\': 220, '\\': {keyCode: 220, code: 'Backslash'},
'|': 220, '|': {keyCode: 220, code: 'Backslash'},
']': 221, ']': {keyCode: 221, code: 'BracketRight'},
'}': 221, '}': {keyCode: 221, code: 'BracketRight'},
'\'': 222, '\'': {keyCode: 222, code: 'Quote'},
'"': 222, '"': {keyCode: 222, code: 'Quote'},
'AltGraph': 225, 'AltGraph': {keyCode: 225, code: 'AltGraph'},
'Attn': 246, 'Attn': {keyCode: 246, code: ''},
'CrSel': 247, 'CrSel': {keyCode: 247, code: 'Props'},
'ExSel': 248, 'ExSel': {keyCode: 248, code: ''},
'EraseEof': 249, 'EraseEof': {keyCode: 249, code: ''},
'Play': 250, 'Play': {keyCode: 250, code: ''},
'ZoomOut': 251 '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 * @param {string} key
* @return {number} * @return {number}
*/ */
function codeForKey(key) { function keyCodeForKey(key) {
if (keys[key]) if (keys[key])
return keys[key]; return keys[key].keyCode;
if (key.length === 1) if (key.length === 1)
return key.toUpperCase().charCodeAt(0); return key.toUpperCase().charCodeAt(0);
return 0; return 0;
} }
/**
* @param {string} key
* @return {string}
*/
function codeForKey(key) {
if (!keys[key])
return '';
return keys[key].code;
}
module.exports = { Keyboard, Mouse, Touchscreen}; module.exports = { Keyboard, Mouse, Touchscreen};
helper.tracePublicAPI(Keyboard); helper.tracePublicAPI(Keyboard);
helper.tracePublicAPI(Mouse); helper.tracePublicAPI(Mouse);

View File

@ -10,13 +10,13 @@
let textarea = document.querySelector('textarea'); let textarea = document.querySelector('textarea');
textarea.focus(); textarea.focus();
textarea.addEventListener('keydown', event => { textarea.addEventListener('keydown', event => {
log('Keydown:', event.key, event.which, modifiers(event)); log('Keydown:', event.key, event.code, event.which, modifiers(event));
}); });
textarea.addEventListener('keypress', event => { textarea.addEventListener('keypress', event => {
log('Keypress:', event.key, event.which, event.keyCode, event.charCode, modifiers(event)); log('Keypress:', event.key, event.code, event.which, event.keyCode, event.charCode, modifiers(event));
}); });
textarea.addEventListener('keyup', event => { textarea.addEventListener('keyup', event => {
log('Keyup:', event.key, event.which, modifiers(event)); log('Keyup:', event.key, event.code, event.which, modifiers(event));
}); });
function modifiers(event) { function modifiers(event) {
let m = []; let m = [];

View File

@ -1607,43 +1607,43 @@ describe('Page', function() {
const codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17}; const codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17};
for (const modifierKey in codeForKey) { for (const modifierKey in codeForKey) {
await keyboard.down(modifierKey); await keyboard.down(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + codeForKey[modifierKey] + ' [' + modifierKey + ']'); expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
await keyboard.down('!'); await keyboard.down('!');
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! 49 [' + modifierKey + ']'); expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up('!'); await keyboard.up('!');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! 49 [' + modifierKey + ']'); expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up(modifierKey); await keyboard.up(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + codeForKey[modifierKey] + ' []'); expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
} }
})); }));
it('should report multiple modifiers', SX(async function(){ it('should report multiple modifiers', SX(async function(){
await page.goto(PREFIX + '/input/keyboard.html'); await page.goto(PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard; const keyboard = page.keyboard;
await keyboard.down('Control'); await keyboard.down('Control');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control 17 [Control]'); expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
await keyboard.down('Meta'); await keyboard.down('Meta');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Meta 91 [Control Meta]'); expect(await page.evaluate(() => getResult())).toBe('Keydown: Meta MetaLeft 91 [Control Meta]');
await keyboard.down(';'); await keyboard.down(';');
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; 186 [Control Meta]'); expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Control Meta]');
await keyboard.up(';'); await keyboard.up(';');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; 186 [Control Meta]'); expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Control Meta]');
await keyboard.up('Control'); await keyboard.up('Control');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control 17 [Meta]'); expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Meta]');
await keyboard.up('Meta'); await keyboard.up('Meta');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Meta 91 []'); expect(await page.evaluate(() => getResult())).toBe('Keyup: Meta MetaLeft 91 []');
})); }));
it('should send proper codes while typing', SX(async function(){ it('should send proper codes while typing', SX(async function(){
await page.goto(PREFIX + '/input/keyboard.html'); await page.goto(PREFIX + '/input/keyboard.html');
await page.keyboard.type('!'); await page.keyboard.type('!');
expect(await page.evaluate(() => getResult())).toBe( expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ! 49 []', [ 'Keydown: ! Digit1 49 []',
'Keypress: ! 33 33 33 []', 'Keypress: ! Digit1 33 33 33 []',
'Keyup: ! 49 []'].join('\n')); 'Keyup: ! Digit1 49 []'].join('\n'));
await page.keyboard.type('^'); await page.keyboard.type('^');
expect(await page.evaluate(() => getResult())).toBe( expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ^ 54 []', [ 'Keydown: ^ Digit6 54 []',
'Keypress: ^ 94 94 94 []', 'Keypress: ^ Digit6 94 94 94 []',
'Keyup: ^ 54 []'].join('\n')); 'Keyup: ^ Digit6 54 []'].join('\n'));
})); }));
it('should send proper codes while typing with shift', SX(async function(){ it('should send proper codes while typing with shift', SX(async function(){
await page.goto(PREFIX + '/input/keyboard.html'); await page.goto(PREFIX + '/input/keyboard.html');
@ -1651,10 +1651,10 @@ describe('Page', function() {
await keyboard.down('Shift'); await keyboard.down('Shift');
await page.keyboard.type('~'); await page.keyboard.type('~');
expect(await page.evaluate(() => getResult())).toBe( expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: Shift 16 [Shift]', [ 'Keydown: Shift ShiftLeft 16 [Shift]',
'Keydown: ~ 192 [Shift]', // 192 is ` keyCode 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
'Keypress: ~ 126 126 126 [Shift]', // 126 is ~ charCode 'Keypress: ~ Backquote 126 126 126 [Shift]', // 126 is ~ charCode
'Keyup: ~ 192 [Shift]'].join('\n')); 'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
await keyboard.up('Shift'); await keyboard.up('Shift');
})); }));
it('should not type canceled events', SX(async function(){ it('should not type canceled events', SX(async function(){