diff options
author | Alex Kocharin <alex@kocharin.ru> | 2015-05-03 18:11:33 +0000 |
---|---|---|
committer | Roman Reiss <me@silverwind.io> | 2015-05-10 04:48:50 +0200 |
commit | aed6bce9064915bda28237b1a5fbf7fcdbf439ef (patch) | |
tree | 0196f43cfb6d416c79e569d1333c20c02822d896 /test | |
parent | 64d3210c98acf1d991a413e0993e07886c9e90de (diff) | |
download | android-node-v8-aed6bce9064915bda28237b1a5fbf7fcdbf439ef.tar.gz android-node-v8-aed6bce9064915bda28237b1a5fbf7fcdbf439ef.tar.bz2 android-node-v8-aed6bce9064915bda28237b1a5fbf7fcdbf439ef.zip |
readline: turn emitKeys into a streaming parser
In certain environments escape sequences could be splitted into
multiple chunks. For example, when user presses left arrow,
`\x1b[D` sequence could appear as two keypresses (`\x1b` + `[D`).
PR-URL: https://github.com/iojs/io.js/pull/1601
Fixes: https://github.com/iojs/io.js/issues/1403
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Roman Reiss <me@silverwind.io>
Diffstat (limited to 'test')
-rw-r--r-- | test/parallel/test-readline-interface.js | 25 | ||||
-rw-r--r-- | test/parallel/test-readline-keys.js | 150 |
2 files changed, 150 insertions, 25 deletions
diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index 69eb4bf519..0edee3190c 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -191,31 +191,6 @@ function isWarned(emitter) { assert.equal(callCount, 1); rli.close(); - // keypress - [ - ['a'], - ['\x1b'], - ['\x1b[31m'], - ['\x1b[31m', '\x1b[39m'], - ['\x1b[31m', 'a', '\x1b[39m', 'a'] - ].forEach(function (keypresses) { - fi = new FakeInput(); - callCount = 0; - var remainingKeypresses = keypresses.slice(); - function keypressListener (ch, key) { - callCount++; - if (ch) assert(!key.code); - assert.equal(key.sequence, remainingKeypresses.shift()); - }; - readline.emitKeypressEvents(fi); - fi.on('keypress', keypressListener); - fi.emit('data', keypresses.join('')); - assert.equal(callCount, keypresses.length); - assert.equal(remainingKeypresses.length, 0); - fi.removeListener('keypress', keypressListener); - fi.emit('data', ''); // removes listener - }); - // calling readline without `new` fi = new FakeInput(); rli = readline.Interface({ input: fi, output: fi, terminal: terminal }); diff --git a/test/parallel/test-readline-keys.js b/test/parallel/test-readline-keys.js new file mode 100644 index 0000000000..0bf2673435 --- /dev/null +++ b/test/parallel/test-readline-keys.js @@ -0,0 +1,150 @@ +var EventEmitter = require('events').EventEmitter; +var PassThrough = require('stream').PassThrough; +var assert = require('assert'); +var inherits = require('util').inherits; +var extend = require('util')._extend; +var Interface = require('readline').Interface; + + +function FakeInput() { + PassThrough.call(this); +} +inherits(FakeInput, PassThrough); + + +var fi = new FakeInput(); +var fo = new FakeInput(); +var rli = new Interface({ input: fi, output: fo, terminal: true }); + +var keys = []; +fi.on('keypress', function (s, k) { + keys.push(k); +}); + + +function addTest(sequences, expectedKeys) { + if (!Array.isArray(sequences)) { + sequences = [ sequences ]; + } + + if (!Array.isArray(expectedKeys)) { + expectedKeys = [ expectedKeys ]; + } + + expectedKeys = expectedKeys.map(function (k) { + return k ? extend({ ctrl: false, meta: false, shift: false }, k) : k; + }); + + keys = []; + + sequences.forEach(function (sequence) { + fi.write(sequence); + }); + assert.deepStrictEqual(keys, expectedKeys); +} + +// regular alphanumerics +addTest('io.JS', [ + { name: 'i', sequence: 'i' }, + { name: 'o', sequence: 'o' }, + undefined, // emitted as `emit('keypress', '.', undefined)` + { name: 'j', sequence: 'J', shift: true }, + { name: 's', sequence: 'S', shift: true }, +]); + +// named characters +addTest('\n\r\t', [ + { name: 'enter', sequence: '\n' }, + { name: 'return', sequence: '\r' }, + { name: 'tab', sequence: '\t' }, +]); + +// space and backspace +addTest('\b\x7f\x1b\b\x1b\x7f \x1b ', [ + { name: 'backspace', sequence: '\b' }, + { name: 'backspace', sequence: '\x7f' }, + { name: 'backspace', sequence: '\x1b\b', meta: true }, + { name: 'backspace', sequence: '\x1b\x7f', meta: true }, + { name: 'space', sequence: ' ' }, + { name: 'space', sequence: '\x1b ', meta: true }, +]); + +// control keys +addTest('\x01\x0b\x10', [ + { name: 'a', sequence: '\x01', ctrl: true }, + { name: 'k', sequence: '\x0b', ctrl: true }, + { name: 'p', sequence: '\x10', ctrl: true }, +]); + +// alt keys +addTest('a\x1baA\x1bA', [ + { name: 'a', sequence: 'a' }, + { name: 'a', sequence: '\x1ba', meta: true }, + { name: 'a', sequence: 'A', shift: true }, + { name: 'a', sequence: '\x1bA', meta: true, shift: true }, +]); + +// xterm/gnome +addTest('\x1bOA\x1bOB', [ + { name: 'up', sequence: '\x1bOA', code: 'OA' }, + { name: 'down', sequence: '\x1bOB', code: 'OB' }, +]); + +// old xterm shift-arrows +addTest('\x1bO2A\x1bO2B', [ + { name: 'up', sequence: '\x1bO2A', code: 'OA', shift: true }, + { name: 'down', sequence: '\x1bO2B', code: 'OB', shift: true }, +]); + +// gnome terminal +addTest('\x1b[A\x1b[B\x1b[2A\x1b[2B', [ + { name: 'up', sequence: '\x1b[A', code: '[A' }, + { name: 'down', sequence: '\x1b[B', code: '[B' }, + { name: 'up', sequence: '\x1b[2A', code: '[A', shift: true }, + { name: 'down', sequence: '\x1b[2B', code: '[B', shift: true }, +]); + +// rxvt +addTest('\x1b[20~\x1b[2$\x1b[2^', [ + { name: 'f9', sequence: '\x1b[20~', code: '[20~' }, + { name: 'insert', sequence: '\x1b[2$', code: '[2$', shift: true }, + { name: 'insert', sequence: '\x1b[2^', code: '[2^', ctrl: true }, +]); + +// xterm + modifiers +addTest('\x1b[20;5~\x1b[6;5^', [ + { name: 'f9', sequence: '\x1b[20;5~', code: '[20~', ctrl: true }, + { name: 'pagedown', sequence: '\x1b[6;5^', code: '[6^', ctrl: true }, +]); + +addTest('\x1b[H\x1b[5H\x1b[1;5H', [ + { name: 'home', sequence: '\x1b[H', code: '[H' }, + { name: 'home', sequence: '\x1b[5H', code: '[H', ctrl: true }, + { name: 'home', sequence: '\x1b[1;5H', code: '[H', ctrl: true }, +]); + +// escape sequences broken into multiple data chunks +addTest('\x1b[D\x1b[C\x1b[D\x1b[C'.split(''), [ + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'right', sequence: '\x1b[C', code: '[C' }, + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'right', sequence: '\x1b[C', code: '[C' }, +]); + +// escape sequences mixed with regular ones +addTest('\x1b[DD\x1b[2DD\x1b[2^D', [ + { name: 'left', sequence: '\x1b[D', code: '[D' }, + { name: 'd', sequence: 'D', shift: true }, + { name: 'left', sequence: '\x1b[2D', code: '[D', shift: true }, + { name: 'd', sequence: 'D', shift: true }, + { name: 'insert', sequence: '\x1b[2^', code: '[2^', ctrl: true }, + { name: 'd', sequence: 'D', shift: true }, +]); + +// color sequences +addTest('\x1b[31ma\x1b[39ma', [ + { name: 'undefined', sequence: '\x1b[31m', code: '[31m' }, + { name: 'a', sequence: 'a' }, + { name: 'undefined', sequence: '\x1b[39m', code: '[39m' }, + { name: 'a', sequence: 'a' }, +]); |