summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlex Kocharin <alex@kocharin.ru>2015-05-03 18:11:33 +0000
committerRoman Reiss <me@silverwind.io>2015-05-10 04:48:50 +0200
commitaed6bce9064915bda28237b1a5fbf7fcdbf439ef (patch)
tree0196f43cfb6d416c79e569d1333c20c02822d896 /test
parent64d3210c98acf1d991a413e0993e07886c9e90de (diff)
downloadandroid-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.js25
-rw-r--r--test/parallel/test-readline-keys.js150
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' },
+]);