PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/parallel/test-readline-interface.js

https://gitlab.com/CORP-RESELLER/node
JavaScript | 439 lines | 364 code | 43 blank | 32 comment | 9 complexity | 135675da526d7f68f514800bb238fbd0 MD5 | raw file
  1. // Flags: --expose_internals
  2. 'use strict';
  3. const common = require('../common');
  4. const assert = require('assert');
  5. const readline = require('readline');
  6. const internalReadline = require('internal/readline');
  7. const EventEmitter = require('events').EventEmitter;
  8. const inherits = require('util').inherits;
  9. const Writable = require('stream').Writable;
  10. const Readable = require('stream').Readable;
  11. function FakeInput() {
  12. EventEmitter.call(this);
  13. }
  14. inherits(FakeInput, EventEmitter);
  15. FakeInput.prototype.resume = function() {};
  16. FakeInput.prototype.pause = function() {};
  17. FakeInput.prototype.write = function() {};
  18. FakeInput.prototype.end = function() {};
  19. function isWarned(emitter) {
  20. for (var name in emitter) {
  21. var listeners = emitter[name];
  22. if (listeners.warned) return true;
  23. }
  24. return false;
  25. }
  26. [ true, false ].forEach(function(terminal) {
  27. var fi;
  28. var rli;
  29. var called;
  30. // disable history
  31. fi = new FakeInput();
  32. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal,
  33. historySize: 0 });
  34. assert.strictEqual(rli.historySize, 0);
  35. fi.emit('data', 'asdf\n');
  36. assert.deepStrictEqual(rli.history, terminal ? [] : undefined);
  37. rli.close();
  38. // default history size 30
  39. fi = new FakeInput();
  40. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal});
  41. assert.strictEqual(rli.historySize, 30);
  42. fi.emit('data', 'asdf\n');
  43. assert.deepStrictEqual(rli.history, terminal ? ['asdf'] : undefined);
  44. rli.close();
  45. // sending a full line
  46. fi = new FakeInput();
  47. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  48. called = false;
  49. rli.on('line', function(line) {
  50. called = true;
  51. assert.equal(line, 'asdf');
  52. });
  53. fi.emit('data', 'asdf\n');
  54. assert.ok(called);
  55. // sending a blank line
  56. fi = new FakeInput();
  57. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  58. called = false;
  59. rli.on('line', function(line) {
  60. called = true;
  61. assert.equal(line, '');
  62. });
  63. fi.emit('data', '\n');
  64. assert.ok(called);
  65. // sending a single character with no newline
  66. fi = new FakeInput();
  67. rli = new readline.Interface(fi, {});
  68. called = false;
  69. rli.on('line', function(line) {
  70. called = true;
  71. });
  72. fi.emit('data', 'a');
  73. assert.ok(!called);
  74. rli.close();
  75. // sending a single character with no newline and then a newline
  76. fi = new FakeInput();
  77. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  78. called = false;
  79. rli.on('line', function(line) {
  80. called = true;
  81. assert.equal(line, 'a');
  82. });
  83. fi.emit('data', 'a');
  84. assert.ok(!called);
  85. fi.emit('data', '\n');
  86. assert.ok(called);
  87. rli.close();
  88. // sending multiple newlines at once
  89. fi = new FakeInput();
  90. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  91. var expectedLines = ['foo', 'bar', 'baz'];
  92. var callCount = 0;
  93. rli.on('line', function(line) {
  94. assert.equal(line, expectedLines[callCount]);
  95. callCount++;
  96. });
  97. fi.emit('data', expectedLines.join('\n') + '\n');
  98. assert.equal(callCount, expectedLines.length);
  99. rli.close();
  100. // sending multiple newlines at once that does not end with a new line
  101. fi = new FakeInput();
  102. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  103. expectedLines = ['foo', 'bar', 'baz', 'bat'];
  104. callCount = 0;
  105. rli.on('line', function(line) {
  106. assert.equal(line, expectedLines[callCount]);
  107. callCount++;
  108. });
  109. fi.emit('data', expectedLines.join('\n'));
  110. assert.equal(callCount, expectedLines.length - 1);
  111. rli.close();
  112. // sending multiple newlines at once that does not end with a new(empty)
  113. // line and a `end` event
  114. fi = new FakeInput();
  115. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  116. expectedLines = ['foo', 'bar', 'baz', ''];
  117. callCount = 0;
  118. rli.on('line', function(line) {
  119. assert.equal(line, expectedLines[callCount]);
  120. callCount++;
  121. });
  122. rli.on('close', function() {
  123. callCount++;
  124. });
  125. fi.emit('data', expectedLines.join('\n'));
  126. fi.emit('end');
  127. assert.equal(callCount, expectedLines.length);
  128. rli.close();
  129. // sending multiple newlines at once that does not end with a new line
  130. // and a `end` event(last line is)
  131. // \r\n should emit one line event, not two
  132. fi = new FakeInput();
  133. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  134. expectedLines = ['foo', 'bar', 'baz', 'bat'];
  135. callCount = 0;
  136. rli.on('line', function(line) {
  137. assert.equal(line, expectedLines[callCount]);
  138. callCount++;
  139. });
  140. fi.emit('data', expectedLines.join('\r\n'));
  141. assert.equal(callCount, expectedLines.length - 1);
  142. rli.close();
  143. // \r\n should emit one line event when split across multiple writes.
  144. fi = new FakeInput();
  145. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  146. expectedLines = ['foo', 'bar', 'baz', 'bat'];
  147. callCount = 0;
  148. rli.on('line', function(line) {
  149. assert.equal(line, expectedLines[callCount]);
  150. callCount++;
  151. });
  152. expectedLines.forEach(function(line) {
  153. fi.emit('data', line + '\r');
  154. fi.emit('data', '\n');
  155. });
  156. assert.equal(callCount, expectedLines.length);
  157. rli.close();
  158. // \r should behave like \n when alone
  159. fi = new FakeInput();
  160. rli = new readline.Interface({ input: fi, output: fi, terminal: true });
  161. expectedLines = ['foo', 'bar', 'baz', 'bat'];
  162. callCount = 0;
  163. rli.on('line', function(line) {
  164. assert.equal(line, expectedLines[callCount]);
  165. callCount++;
  166. });
  167. fi.emit('data', expectedLines.join('\r'));
  168. assert.equal(callCount, expectedLines.length - 1);
  169. rli.close();
  170. // \r at start of input should output blank line
  171. fi = new FakeInput();
  172. rli = new readline.Interface({ input: fi, output: fi, terminal: true });
  173. expectedLines = ['', 'foo' ];
  174. callCount = 0;
  175. rli.on('line', function(line) {
  176. assert.equal(line, expectedLines[callCount]);
  177. callCount++;
  178. });
  179. fi.emit('data', '\rfoo\r');
  180. assert.equal(callCount, expectedLines.length);
  181. rli.close();
  182. // \t when there is no completer function should behave like an ordinary
  183. // character
  184. fi = new FakeInput();
  185. rli = new readline.Interface({ input: fi, output: fi, terminal: true });
  186. called = false;
  187. rli.on('line', function(line) {
  188. assert.equal(line, '\t');
  189. assert.strictEqual(called, false);
  190. called = true;
  191. });
  192. fi.emit('data', '\t');
  193. fi.emit('data', '\n');
  194. assert.ok(called);
  195. rli.close();
  196. // \t does not become part of the input when there is a completer function
  197. fi = new FakeInput();
  198. var completer = function(line) {
  199. return [[], line];
  200. };
  201. rli = new readline.Interface({
  202. input: fi,
  203. output: fi,
  204. terminal: true,
  205. completer: completer
  206. });
  207. called = false;
  208. rli.on('line', function(line) {
  209. assert.equal(line, 'foo');
  210. assert.strictEqual(called, false);
  211. called = true;
  212. });
  213. for (var character of '\tfo\to\t') {
  214. fi.emit('data', character);
  215. }
  216. fi.emit('data', '\n');
  217. assert.ok(called);
  218. rli.close();
  219. // constructor throws if completer is not a function or undefined
  220. fi = new FakeInput();
  221. assert.throws(function() {
  222. readline.createInterface({
  223. input: fi,
  224. completer: 'string is not valid'
  225. });
  226. }, function(err) {
  227. if (err instanceof TypeError) {
  228. if (/Argument "completer" must be a function/.test(err)) {
  229. return true;
  230. }
  231. }
  232. return false;
  233. });
  234. // sending a multi-byte utf8 char over multiple writes
  235. var buf = Buffer.from('☮', 'utf8');
  236. fi = new FakeInput();
  237. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  238. callCount = 0;
  239. rli.on('line', function(line) {
  240. callCount++;
  241. assert.equal(line, buf.toString('utf8'));
  242. });
  243. [].forEach.call(buf, function(i) {
  244. fi.emit('data', Buffer.from([i]));
  245. });
  246. assert.equal(callCount, 0);
  247. fi.emit('data', '\n');
  248. assert.equal(callCount, 1);
  249. rli.close();
  250. // Regression test for repl freeze, #1968:
  251. // check that nothing fails if 'keypress' event throws.
  252. fi = new FakeInput();
  253. rli = new readline.Interface({ input: fi, output: fi, terminal: true });
  254. var keys = [];
  255. fi.on('keypress', function(key) {
  256. keys.push(key);
  257. if (key === 'X') {
  258. throw new Error('bad thing happened');
  259. }
  260. });
  261. try {
  262. fi.emit('data', 'fooX');
  263. } catch (e) { }
  264. fi.emit('data', 'bar');
  265. assert.equal(keys.join(''), 'fooXbar');
  266. rli.close();
  267. // calling readline without `new`
  268. fi = new FakeInput();
  269. rli = readline.Interface({ input: fi, output: fi, terminal: terminal });
  270. called = false;
  271. rli.on('line', function(line) {
  272. called = true;
  273. assert.equal(line, 'asdf');
  274. });
  275. fi.emit('data', 'asdf\n');
  276. assert.ok(called);
  277. rli.close();
  278. if (terminal) {
  279. // question
  280. fi = new FakeInput();
  281. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  282. expectedLines = ['foo'];
  283. rli.question(expectedLines[0], function() {
  284. rli.close();
  285. });
  286. var cursorPos = rli._getCursorPos();
  287. assert.equal(cursorPos.rows, 0);
  288. assert.equal(cursorPos.cols, expectedLines[0].length);
  289. rli.close();
  290. // sending a multi-line question
  291. fi = new FakeInput();
  292. rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
  293. expectedLines = ['foo', 'bar'];
  294. rli.question(expectedLines.join('\n'), function() {
  295. rli.close();
  296. });
  297. cursorPos = rli._getCursorPos();
  298. assert.equal(cursorPos.rows, expectedLines.length - 1);
  299. assert.equal(cursorPos.cols, expectedLines.slice(-1)[0].length);
  300. rli.close();
  301. }
  302. // isFullWidthCodePoint() should return false for non-numeric values
  303. [true, false, null, undefined, {}, [], 'あ'].forEach((v) => {
  304. assert.strictEqual(internalReadline.isFullWidthCodePoint('あ'), false);
  305. });
  306. // wide characters should be treated as two columns.
  307. assert.equal(internalReadline.isFullWidthCodePoint('a'.charCodeAt(0)), false);
  308. assert.equal(internalReadline.isFullWidthCodePoint('あ'.charCodeAt(0)), true);
  309. assert.equal(internalReadline.isFullWidthCodePoint('谢'.charCodeAt(0)), true);
  310. assert.equal(internalReadline.isFullWidthCodePoint('고'.charCodeAt(0)), true);
  311. assert.equal(internalReadline.isFullWidthCodePoint(0x1f251), true);
  312. assert.equal(internalReadline.getStringWidth('abcde'), 5);
  313. assert.equal(internalReadline.getStringWidth('古池や'), 6);
  314. assert.equal(internalReadline.getStringWidth('ノード.js'), 9);
  315. assert.equal(internalReadline.getStringWidth('你好'), 4);
  316. assert.equal(internalReadline.getStringWidth('안녕하세요'), 10);
  317. assert.equal(internalReadline.getStringWidth('A\ud83c\ude00BC'), 5);
  318. // check if vt control chars are stripped
  319. assert.strictEqual(
  320. internalReadline.stripVTControlCharacters('\u001b[31m> \u001b[39m'),
  321. '> '
  322. );
  323. assert.strictEqual(
  324. internalReadline.stripVTControlCharacters('\u001b[31m> \u001b[39m> '),
  325. '> > '
  326. );
  327. assert.strictEqual(
  328. internalReadline.stripVTControlCharacters('\u001b[31m\u001b[39m'),
  329. ''
  330. );
  331. assert.strictEqual(
  332. internalReadline.stripVTControlCharacters('> '),
  333. '> '
  334. );
  335. assert.equal(internalReadline.getStringWidth('\u001b[31m> \u001b[39m'), 2);
  336. assert.equal(internalReadline.getStringWidth('\u001b[31m> \u001b[39m> '), 4);
  337. assert.equal(internalReadline.getStringWidth('\u001b[31m\u001b[39m'), 0);
  338. assert.equal(internalReadline.getStringWidth('> '), 2);
  339. assert.deepStrictEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
  340. // check EventEmitter memory leak
  341. for (var i = 0; i < 12; i++) {
  342. var rl = readline.createInterface({
  343. input: process.stdin,
  344. output: process.stdout
  345. });
  346. rl.close();
  347. assert.equal(isWarned(process.stdin._events), false);
  348. assert.equal(isWarned(process.stdout._events), false);
  349. }
  350. //can create a new readline Interface with a null output arugument
  351. fi = new FakeInput();
  352. rli = new readline.Interface({input: fi, output: null, terminal: terminal });
  353. called = false;
  354. rli.on('line', function(line) {
  355. called = true;
  356. assert.equal(line, 'asdf');
  357. });
  358. fi.emit('data', 'asdf\n');
  359. assert.ok(called);
  360. assert.doesNotThrow(function() {
  361. rli.setPrompt('ddd> ');
  362. });
  363. assert.doesNotThrow(function() {
  364. rli.prompt();
  365. });
  366. assert.doesNotThrow(function() {
  367. rli.write('really shouldnt be seeing this');
  368. });
  369. assert.doesNotThrow(function() {
  370. rli.question('What do you think of node.js? ', function(answer) {
  371. console.log('Thank you for your valuable feedback:', answer);
  372. rli.close();
  373. });
  374. });
  375. {
  376. const expected = terminal
  377. ? ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G']
  378. : ['$ '];
  379. let counter = 0;
  380. const output = new Writable({
  381. write: common.mustCall((chunk, enc, cb) => {
  382. assert.strictEqual(chunk.toString(), expected[counter++]);
  383. cb();
  384. rl.close();
  385. }, expected.length)
  386. });
  387. const rl = readline.createInterface({
  388. input: new Readable({ read: () => {} }),
  389. output: output,
  390. prompt: '$ ',
  391. terminal: terminal
  392. });
  393. rl.prompt();
  394. assert.strictEqual(rl._prompt, '$ ');
  395. }
  396. });