1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22// Flags: --expose-internals
23'use strict';
24const common = require('../common');
25common.skipIfDumbTerminal();
26
27const assert = require('assert');
28const readline = require('readline');
29const util = require('util');
30const {
31  getStringWidth,
32  stripVTControlCharacters
33} = require('internal/util/inspect');
34const { EventEmitter, getEventListeners } = require('events');
35const { Writable, Readable } = require('stream');
36
37class FakeInput extends EventEmitter {
38  resume() {}
39  pause() {}
40  write() {}
41  end() {}
42}
43
44function isWarned(emitter) {
45  for (const name in emitter) {
46    const listeners = emitter[name];
47    if (listeners.warned) return true;
48  }
49  return false;
50}
51
52function getInterface(options) {
53  const fi = new FakeInput();
54  const rli = new readline.Interface({
55    input: fi,
56    output: fi,
57    ...options,
58  });
59  return [rli, fi];
60}
61
62function assertCursorRowsAndCols(rli, rows, cols) {
63  const cursorPos = rli.getCursorPos();
64  assert.strictEqual(cursorPos.rows, rows);
65  assert.strictEqual(cursorPos.cols, cols);
66}
67
68{
69  const input = new FakeInput();
70  const rl = readline.Interface({ input });
71  assert(rl instanceof readline.Interface);
72}
73
74{
75  const fi = new FakeInput();
76  const rli = new readline.Interface(
77    fi,
78    fi,
79    common.mustCall((line) => [[], line]),
80    true,
81  );
82  assert(rli instanceof readline.Interface);
83  fi.emit('data', 'a\t');
84  rli.close();
85}
86
87[
88  undefined,
89  50,
90  0,
91  100.5,
92  5000,
93].forEach((crlfDelay) => {
94  const [rli] = getInterface({ crlfDelay });
95  assert.strictEqual(rli.crlfDelay, Math.max(crlfDelay || 100, 100));
96  rli.close();
97});
98
99{
100  const input = new FakeInput();
101
102  // Constructor throws if completer is not a function or undefined
103  ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((invalid) => {
104    assert.throws(() => {
105      readline.createInterface({
106        input,
107        completer: invalid
108      });
109    }, {
110      name: 'TypeError',
111      code: 'ERR_INVALID_ARG_VALUE'
112    });
113  });
114
115  // Constructor throws if history is not an array
116  ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((history) => {
117    assert.throws(() => {
118      readline.createInterface({
119        input,
120        history,
121      });
122    }, {
123      name: 'TypeError',
124      code: 'ERR_INVALID_ARG_TYPE'
125    });
126  });
127
128  // Constructor throws if historySize is not a positive number
129  ['not a number', -1, NaN, {}, true, Symbol(), null].forEach((historySize) => {
130    assert.throws(() => {
131      readline.createInterface({
132        input,
133        historySize,
134      });
135    }, {
136      name: 'RangeError',
137      code: 'ERR_INVALID_ARG_VALUE'
138    });
139  });
140
141  // Check for invalid tab sizes.
142  assert.throws(
143    () => new readline.Interface({
144      input,
145      tabSize: 0
146    }),
147    { code: 'ERR_OUT_OF_RANGE' }
148  );
149
150  assert.throws(
151    () => new readline.Interface({
152      input,
153      tabSize: '4'
154    }),
155    { code: 'ERR_INVALID_ARG_TYPE' }
156  );
157
158  assert.throws(
159    () => new readline.Interface({
160      input,
161      tabSize: 4.5
162    }),
163    {
164      code: 'ERR_OUT_OF_RANGE',
165      message: 'The value of "tabSize" is out of range. ' +
166                'It must be an integer. Received 4.5'
167    }
168  );
169}
170
171// Sending a single character with no newline
172{
173  const fi = new FakeInput();
174  const rli = new readline.Interface(fi, {});
175  rli.on('line', common.mustNotCall());
176  fi.emit('data', 'a');
177  rli.close();
178}
179
180// Sending multiple newlines at once that does not end with a new line and a
181// `end` event(last line is). \r should behave like \n when alone.
182{
183  const [rli, fi] = getInterface({ terminal: true });
184  const expectedLines = ['foo', 'bar', 'baz', 'bat'];
185  rli.on('line', common.mustCall((line) => {
186    assert.strictEqual(line, expectedLines.shift());
187  }, expectedLines.length - 1));
188  fi.emit('data', expectedLines.join('\r'));
189  rli.close();
190}
191
192// \r at start of input should output blank line
193{
194  const [rli, fi] = getInterface({ terminal: true });
195  const expectedLines = ['', 'foo' ];
196  rli.on('line', common.mustCall((line) => {
197    assert.strictEqual(line, expectedLines.shift());
198  }, expectedLines.length));
199  fi.emit('data', '\rfoo\r');
200  rli.close();
201}
202
203// \t does not become part of the input when there is a completer function
204{
205  const completer = (line) => [[], line];
206  const [rli, fi] = getInterface({ terminal: true, completer });
207  rli.on('line', common.mustCall((line) => {
208    assert.strictEqual(line, 'foo');
209  }));
210  for (const character of '\tfo\to\t') {
211    fi.emit('data', character);
212  }
213  fi.emit('data', '\n');
214  rli.close();
215}
216
217// \t when there is no completer function should behave like an ordinary
218// character
219{
220  const [rli, fi] = getInterface({ terminal: true });
221  rli.on('line', common.mustCall((line) => {
222    assert.strictEqual(line, '\t');
223  }));
224  fi.emit('data', '\t');
225  fi.emit('data', '\n');
226  rli.close();
227}
228
229// Adding history lines should emit the history event with
230// the history array
231{
232  const [rli, fi] = getInterface({ terminal: true });
233  const expectedLines = ['foo', 'bar', 'baz', 'bat'];
234  rli.on('history', common.mustCall((history) => {
235    const expectedHistory = expectedLines.slice(0, history.length).reverse();
236    assert.deepStrictEqual(history, expectedHistory);
237  }, expectedLines.length));
238  for (const line of expectedLines) {
239    fi.emit('data', `${line}\n`);
240  }
241  rli.close();
242}
243
244// Altering the history array in the listener should not alter
245// the line being processed
246{
247  const [rli, fi] = getInterface({ terminal: true });
248  const expectedLine = 'foo';
249  rli.on('history', common.mustCall((history) => {
250    assert.strictEqual(history[0], expectedLine);
251    history.shift();
252  }));
253  rli.on('line', common.mustCall((line) => {
254    assert.strictEqual(line, expectedLine);
255    assert.strictEqual(rli.history.length, 0);
256  }));
257  fi.emit('data', `${expectedLine}\n`);
258  rli.close();
259}
260
261// Duplicate lines are removed from history when
262// `options.removeHistoryDuplicates` is `true`
263{
264  const [rli, fi] = getInterface({
265    terminal: true,
266    removeHistoryDuplicates: true
267  });
268  const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
269  // ['foo', 'baz', 'bar', bat'];
270  let callCount = 0;
271  rli.on('line', (line) => {
272    assert.strictEqual(line, expectedLines[callCount]);
273    callCount++;
274  });
275  fi.emit('data', `${expectedLines.join('\n')}\n`);
276  assert.strictEqual(callCount, expectedLines.length);
277  fi.emit('keypress', '.', { name: 'up' }); // 'bat'
278  assert.strictEqual(rli.line, expectedLines[--callCount]);
279  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
280  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
281  assert.strictEqual(rli.line, expectedLines[--callCount]);
282  fi.emit('keypress', '.', { name: 'up' }); // 'baz'
283  assert.strictEqual(rli.line, expectedLines[--callCount]);
284  fi.emit('keypress', '.', { name: 'up' }); // 'foo'
285  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
286  assert.strictEqual(rli.line, expectedLines[--callCount]);
287  assert.strictEqual(callCount, 0);
288  fi.emit('keypress', '.', { name: 'down' }); // 'baz'
289  assert.strictEqual(rli.line, 'baz');
290  assert.strictEqual(rli.historyIndex, 2);
291  fi.emit('keypress', '.', { name: 'n', ctrl: true }); // 'bar'
292  assert.strictEqual(rli.line, 'bar');
293  assert.strictEqual(rli.historyIndex, 1);
294  fi.emit('keypress', '.', { name: 'n', ctrl: true });
295  assert.strictEqual(rli.line, 'bat');
296  assert.strictEqual(rli.historyIndex, 0);
297  // Activate the substring history search.
298  fi.emit('keypress', '.', { name: 'down' }); // 'bat'
299  assert.strictEqual(rli.line, 'bat');
300  assert.strictEqual(rli.historyIndex, -1);
301  // Deactivate substring history search.
302  fi.emit('keypress', '.', { name: 'backspace' }); // 'ba'
303  assert.strictEqual(rli.historyIndex, -1);
304  assert.strictEqual(rli.line, 'ba');
305  // Activate the substring history search.
306  fi.emit('keypress', '.', { name: 'down' }); // 'ba'
307  assert.strictEqual(rli.historyIndex, -1);
308  assert.strictEqual(rli.line, 'ba');
309  fi.emit('keypress', '.', { name: 'down' }); // 'ba'
310  assert.strictEqual(rli.historyIndex, -1);
311  assert.strictEqual(rli.line, 'ba');
312  fi.emit('keypress', '.', { name: 'up' }); // 'bat'
313  assert.strictEqual(rli.historyIndex, 0);
314  assert.strictEqual(rli.line, 'bat');
315  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
316  assert.strictEqual(rli.historyIndex, 1);
317  assert.strictEqual(rli.line, 'bar');
318  fi.emit('keypress', '.', { name: 'up' }); // 'baz'
319  assert.strictEqual(rli.historyIndex, 2);
320  assert.strictEqual(rli.line, 'baz');
321  fi.emit('keypress', '.', { name: 'up' }); // 'ba'
322  assert.strictEqual(rli.historyIndex, 4);
323  assert.strictEqual(rli.line, 'ba');
324  fi.emit('keypress', '.', { name: 'up' }); // 'ba'
325  assert.strictEqual(rli.historyIndex, 4);
326  assert.strictEqual(rli.line, 'ba');
327  // Deactivate substring history search and reset history index.
328  fi.emit('keypress', '.', { name: 'right' }); // 'ba'
329  assert.strictEqual(rli.historyIndex, -1);
330  assert.strictEqual(rli.line, 'ba');
331  // Substring history search activated.
332  fi.emit('keypress', '.', { name: 'up' }); // 'ba'
333  assert.strictEqual(rli.historyIndex, 0);
334  assert.strictEqual(rli.line, 'bat');
335  rli.close();
336}
337
338// Duplicate lines are not removed from history when
339// `options.removeHistoryDuplicates` is `false`
340{
341  const [rli, fi] = getInterface({
342    terminal: true,
343    removeHistoryDuplicates: false
344  });
345  const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
346  let callCount = 0;
347  rli.on('line', (line) => {
348    assert.strictEqual(line, expectedLines[callCount]);
349    callCount++;
350  });
351  fi.emit('data', `${expectedLines.join('\n')}\n`);
352  assert.strictEqual(callCount, expectedLines.length);
353  fi.emit('keypress', '.', { name: 'up' }); // 'bat'
354  assert.strictEqual(rli.line, expectedLines[--callCount]);
355  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
356  assert.notStrictEqual(rli.line, expectedLines[--callCount]);
357  assert.strictEqual(rli.line, expectedLines[--callCount]);
358  fi.emit('keypress', '.', { name: 'up' }); // 'baz'
359  assert.strictEqual(rli.line, expectedLines[--callCount]);
360  fi.emit('keypress', '.', { name: 'up' }); // 'bar'
361  assert.strictEqual(rli.line, expectedLines[--callCount]);
362  fi.emit('keypress', '.', { name: 'up' }); // 'foo'
363  assert.strictEqual(rli.line, expectedLines[--callCount]);
364  assert.strictEqual(callCount, 0);
365  rli.close();
366}
367
368// Regression test for repl freeze, #1968:
369// check that nothing fails if 'keypress' event throws.
370{
371  const [rli, fi] = getInterface({ terminal: true });
372  const keys = [];
373  const err = new Error('bad thing happened');
374  fi.on('keypress', (key) => {
375    keys.push(key);
376    if (key === 'X') {
377      throw err;
378    }
379  });
380  assert.throws(
381    () => fi.emit('data', 'fooX'),
382    (e) => {
383      assert.strictEqual(e, err);
384      return true;
385    }
386  );
387  fi.emit('data', 'bar');
388  assert.strictEqual(keys.join(''), 'fooXbar');
389  rli.close();
390}
391
392// History is bound
393{
394  const [rli, fi] = getInterface({ terminal: true, historySize: 2 });
395  const lines = ['line 1', 'line 2', 'line 3'];
396  fi.emit('data', lines.join('\n') + '\n');
397  assert.strictEqual(rli.history.length, 2);
398  assert.strictEqual(rli.history[0], 'line 3');
399  assert.strictEqual(rli.history[1], 'line 2');
400}
401
402// Question
403{
404  const [rli] = getInterface({ terminal: true });
405  const expectedLines = ['foo'];
406  rli.question(expectedLines[0], () => rli.close());
407  assertCursorRowsAndCols(rli, 0, expectedLines[0].length);
408  rli.close();
409}
410
411// Sending a multi-line question
412{
413  const [rli] = getInterface({ terminal: true });
414  const expectedLines = ['foo', 'bar'];
415  rli.question(expectedLines.join('\n'), () => rli.close());
416  assertCursorRowsAndCols(
417    rli, expectedLines.length - 1, expectedLines.slice(-1)[0].length);
418  rli.close();
419}
420
421{
422  // Beginning and end of line
423  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
424  fi.emit('data', 'the quick brown fox');
425  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
426  assertCursorRowsAndCols(rli, 0, 0);
427  fi.emit('keypress', '.', { ctrl: true, name: 'e' });
428  assertCursorRowsAndCols(rli, 0, 19);
429  rli.close();
430}
431
432{
433  // Back and Forward one character
434  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
435  fi.emit('data', 'the quick brown fox');
436  assertCursorRowsAndCols(rli, 0, 19);
437
438  // Back one character
439  fi.emit('keypress', '.', { ctrl: true, name: 'b' });
440  assertCursorRowsAndCols(rli, 0, 18);
441  // Back one character
442  fi.emit('keypress', '.', { ctrl: true, name: 'b' });
443  assertCursorRowsAndCols(rli, 0, 17);
444  // Forward one character
445  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
446  assertCursorRowsAndCols(rli, 0, 18);
447  // Forward one character
448  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
449  assertCursorRowsAndCols(rli, 0, 19);
450  rli.close();
451}
452
453// Back and Forward one astral character
454{
455  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
456  fi.emit('data', '�');
457
458  // Move left one character/code point
459  fi.emit('keypress', '.', { name: 'left' });
460  assertCursorRowsAndCols(rli, 0, 0);
461
462  // Move right one character/code point
463  fi.emit('keypress', '.', { name: 'right' });
464  assertCursorRowsAndCols(rli, 0, 2);
465
466  rli.on('line', common.mustCall((line) => {
467    assert.strictEqual(line, '�');
468  }));
469  fi.emit('data', '\n');
470  rli.close();
471}
472
473// Two astral characters left
474{
475  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
476  fi.emit('data', '�');
477
478  // Move left one character/code point
479  fi.emit('keypress', '.', { name: 'left' });
480  assertCursorRowsAndCols(rli, 0, 0);
481
482  fi.emit('data', '�');
483  assertCursorRowsAndCols(rli, 0, 2);
484
485  rli.on('line', common.mustCall((line) => {
486    assert.strictEqual(line, '��');
487  }));
488  fi.emit('data', '\n');
489  rli.close();
490}
491
492// Two astral characters right
493{
494  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
495  fi.emit('data', '�');
496
497  // Move left one character/code point
498  fi.emit('keypress', '.', { name: 'right' });
499  assertCursorRowsAndCols(rli, 0, 2);
500
501  fi.emit('data', '�');
502  assertCursorRowsAndCols(rli, 0, 4);
503
504  rli.on('line', common.mustCall((line) => {
505    assert.strictEqual(line, '��');
506  }));
507  fi.emit('data', '\n');
508  rli.close();
509}
510
511{
512  // `wordLeft` and `wordRight`
513  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
514  fi.emit('data', 'the quick brown fox');
515  fi.emit('keypress', '.', { ctrl: true, name: 'left' });
516  assertCursorRowsAndCols(rli, 0, 16);
517  fi.emit('keypress', '.', { meta: true, name: 'b' });
518  assertCursorRowsAndCols(rli, 0, 10);
519  fi.emit('keypress', '.', { ctrl: true, name: 'right' });
520  assertCursorRowsAndCols(rli, 0, 16);
521  fi.emit('keypress', '.', { meta: true, name: 'f' });
522  assertCursorRowsAndCols(rli, 0, 19);
523  rli.close();
524}
525
526// `deleteWordLeft`
527[
528  { ctrl: true, name: 'w' },
529  { ctrl: true, name: 'backspace' },
530  { meta: true, name: 'backspace' },
531].forEach((deleteWordLeftKey) => {
532  let [rli, fi] = getInterface({ terminal: true, prompt: '' });
533  fi.emit('data', 'the quick brown fox');
534  fi.emit('keypress', '.', { ctrl: true, name: 'left' });
535  rli.on('line', common.mustCall((line) => {
536    assert.strictEqual(line, 'the quick fox');
537  }));
538  fi.emit('keypress', '.', deleteWordLeftKey);
539  fi.emit('data', '\n');
540  rli.close();
541
542  // No effect if pressed at beginning of line
543  [rli, fi] = getInterface({ terminal: true, prompt: '' });
544  fi.emit('data', 'the quick brown fox');
545  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
546  rli.on('line', common.mustCall((line) => {
547    assert.strictEqual(line, 'the quick brown fox');
548  }));
549  fi.emit('keypress', '.', deleteWordLeftKey);
550  fi.emit('data', '\n');
551  rli.close();
552});
553
554// `deleteWordRight`
555[
556  { ctrl: true, name: 'delete' },
557  { meta: true, name: 'delete' },
558  { meta: true, name: 'd' },
559].forEach((deleteWordRightKey) => {
560  let [rli, fi] = getInterface({ terminal: true, prompt: '' });
561  fi.emit('data', 'the quick brown fox');
562  fi.emit('keypress', '.', { ctrl: true, name: 'left' });
563  fi.emit('keypress', '.', { ctrl: true, name: 'left' });
564  rli.on('line', common.mustCall((line) => {
565    assert.strictEqual(line, 'the quick fox');
566  }));
567  fi.emit('keypress', '.', deleteWordRightKey);
568  fi.emit('data', '\n');
569  rli.close();
570
571  // No effect if pressed at end of line
572  [rli, fi] = getInterface({ terminal: true, prompt: '' });
573  fi.emit('data', 'the quick brown fox');
574  rli.on('line', common.mustCall((line) => {
575    assert.strictEqual(line, 'the quick brown fox');
576  }));
577  fi.emit('keypress', '.', deleteWordRightKey);
578  fi.emit('data', '\n');
579  rli.close();
580});
581
582// deleteLeft
583{
584  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
585  fi.emit('data', 'the quick brown fox');
586  assertCursorRowsAndCols(rli, 0, 19);
587
588  // Delete left character
589  fi.emit('keypress', '.', { ctrl: true, name: 'h' });
590  assertCursorRowsAndCols(rli, 0, 18);
591  rli.on('line', common.mustCall((line) => {
592    assert.strictEqual(line, 'the quick brown fo');
593  }));
594  fi.emit('data', '\n');
595  rli.close();
596}
597
598// deleteLeft astral character
599{
600  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
601  fi.emit('data', '�');
602  assertCursorRowsAndCols(rli, 0, 2);
603  // Delete left character
604  fi.emit('keypress', '.', { ctrl: true, name: 'h' });
605  assertCursorRowsAndCols(rli, 0, 0);
606  rli.on('line', common.mustCall((line) => {
607    assert.strictEqual(line, '');
608  }));
609  fi.emit('data', '\n');
610  rli.close();
611}
612
613// deleteRight
614{
615  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
616  fi.emit('data', 'the quick brown fox');
617
618  // Go to the start of the line
619  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
620  assertCursorRowsAndCols(rli, 0, 0);
621
622  // Delete right character
623  fi.emit('keypress', '.', { ctrl: true, name: 'd' });
624  assertCursorRowsAndCols(rli, 0, 0);
625  rli.on('line', common.mustCall((line) => {
626    assert.strictEqual(line, 'he quick brown fox');
627  }));
628  fi.emit('data', '\n');
629  rli.close();
630}
631
632// deleteRight astral character
633{
634  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
635  fi.emit('data', '�');
636
637  // Go to the start of the line
638  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
639  assertCursorRowsAndCols(rli, 0, 0);
640
641  // Delete right character
642  fi.emit('keypress', '.', { ctrl: true, name: 'd' });
643  assertCursorRowsAndCols(rli, 0, 0);
644  rli.on('line', common.mustCall((line) => {
645    assert.strictEqual(line, '');
646  }));
647  fi.emit('data', '\n');
648  rli.close();
649}
650
651// deleteLineLeft
652{
653  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
654  fi.emit('data', 'the quick brown fox');
655  assertCursorRowsAndCols(rli, 0, 19);
656
657  // Delete from current to start of line
658  fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' });
659  assertCursorRowsAndCols(rli, 0, 0);
660  rli.on('line', common.mustCall((line) => {
661    assert.strictEqual(line, '');
662  }));
663  fi.emit('data', '\n');
664  rli.close();
665}
666
667// deleteLineRight
668{
669  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
670  fi.emit('data', 'the quick brown fox');
671
672  // Go to the start of the line
673  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
674  assertCursorRowsAndCols(rli, 0, 0);
675
676  // Delete from current to end of line
677  fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
678  assertCursorRowsAndCols(rli, 0, 0);
679  rli.on('line', common.mustCall((line) => {
680    assert.strictEqual(line, '');
681  }));
682  fi.emit('data', '\n');
683  rli.close();
684}
685
686// yank
687{
688  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
689  fi.emit('data', 'the quick brown fox');
690  assertCursorRowsAndCols(rli, 0, 19);
691
692  // Go to the start of the line
693  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
694  // Move forward one char
695  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
696  // Delete the right part
697  fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
698  assertCursorRowsAndCols(rli, 0, 1);
699
700  // Yank
701  fi.emit('keypress', '.', { ctrl: true, name: 'y' });
702  assertCursorRowsAndCols(rli, 0, 19);
703
704  rli.on('line', common.mustCall((line) => {
705    assert.strictEqual(line, 'the quick brown fox');
706  }));
707
708  fi.emit('data', '\n');
709  rli.close();
710}
711
712// yank pop
713{
714  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
715  fi.emit('data', 'the quick brown fox');
716  assertCursorRowsAndCols(rli, 0, 19);
717
718  // Go to the start of the line
719  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
720  // Move forward one char
721  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
722  // Delete the right part
723  fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
724  assertCursorRowsAndCols(rli, 0, 1);
725  // Yank
726  fi.emit('keypress', '.', { ctrl: true, name: 'y' });
727  assertCursorRowsAndCols(rli, 0, 19);
728
729  // Go to the start of the line
730  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
731  // Move forward four chars
732  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
733  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
734  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
735  fi.emit('keypress', '.', { ctrl: true, name: 'f' });
736  // Delete the right part
737  fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
738  assertCursorRowsAndCols(rli, 0, 4);
739  // Go to the start of the line
740  fi.emit('keypress', '.', { ctrl: true, name: 'a' });
741  assertCursorRowsAndCols(rli, 0, 0);
742
743  // Yank: 'quick brown fox|the '
744  fi.emit('keypress', '.', { ctrl: true, name: 'y' });
745  // Yank pop: 'he quick brown fox|the'
746  fi.emit('keypress', '.', { meta: true, name: 'y' });
747  assertCursorRowsAndCols(rli, 0, 18);
748
749  rli.on('line', common.mustCall((line) => {
750    assert.strictEqual(line, 'he quick brown foxthe ');
751  }));
752
753  fi.emit('data', '\n');
754  rli.close();
755}
756
757// Close readline interface
758{
759  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
760  fi.emit('keypress', '.', { ctrl: true, name: 'c' });
761  assert(rli.closed);
762}
763
764// Multi-line input cursor position
765{
766  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
767  fi.columns = 10;
768  fi.emit('data', 'multi-line text');
769  assertCursorRowsAndCols(rli, 1, 5);
770  rli.close();
771}
772
773// Multi-line input cursor position and long tabs
774{
775  const [rli, fi] = getInterface({ tabSize: 16, terminal: true, prompt: '' });
776  fi.columns = 10;
777  fi.emit('data', 'multi-line\ttext \t');
778  assert.strictEqual(rli.cursor, 17);
779  assertCursorRowsAndCols(rli, 3, 2);
780  rli.close();
781}
782
783// Check for the default tab size.
784{
785  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
786  fi.emit('data', 'the quick\tbrown\tfox');
787  assert.strictEqual(rli.cursor, 19);
788  // The first tab is 7 spaces long, the second one 3 spaces.
789  assertCursorRowsAndCols(rli, 0, 27);
790}
791
792// Multi-line prompt cursor position
793{
794  const [rli, fi] = getInterface({
795    terminal: true,
796    prompt: '\nfilledline\nwraping text\n> '
797  });
798  fi.columns = 10;
799  fi.emit('data', 't');
800  assertCursorRowsAndCols(rli, 4, 3);
801  rli.close();
802}
803
804// Undo & Redo
805{
806  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
807  fi.emit('data', 'the quick brown fox');
808  assertCursorRowsAndCols(rli, 0, 19);
809
810  // Delete the last eight chars
811  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
812  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
813  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
814  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
815  fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
816
817  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
818  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
819  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
820  fi.emit('keypress', '.', { ctrl: true, shift: false, name: 'b' });
821  fi.emit('keypress', ',', { ctrl: true, shift: false, name: 'k' });
822
823  assertCursorRowsAndCols(rli, 0, 11);
824  // Perform undo twice
825  fi.emit('keypress', ',', { sequence: '\x1F' });
826  assert.strictEqual(rli.line, 'the quick brown');
827  fi.emit('keypress', ',', { sequence: '\x1F' });
828  assert.strictEqual(rli.line, 'the quick brown fox');
829  // Perform redo twice
830  fi.emit('keypress', ',', { sequence: '\x1E' });
831  assert.strictEqual(rli.line, 'the quick brown');
832  fi.emit('keypress', ',', { sequence: '\x1E' });
833  assert.strictEqual(rli.line, 'the quick b');
834  fi.emit('data', '\n');
835  rli.close();
836}
837
838// Clear the whole screen
839{
840  const [rli, fi] = getInterface({ terminal: true, prompt: '' });
841  const lines = ['line 1', 'line 2', 'line 3'];
842  fi.emit('data', lines.join('\n'));
843  fi.emit('keypress', '.', { ctrl: true, name: 'l' });
844  assertCursorRowsAndCols(rli, 0, 6);
845  rli.on('line', common.mustCall((line) => {
846    assert.strictEqual(line, 'line 3');
847  }));
848  fi.emit('data', '\n');
849  rli.close();
850}
851
852// Wide characters should be treated as two columns.
853assert.strictEqual(getStringWidth('a'), 1);
854assert.strictEqual(getStringWidth('あ'), 2);
855assert.strictEqual(getStringWidth('谢'), 2);
856assert.strictEqual(getStringWidth('고'), 2);
857assert.strictEqual(getStringWidth(String.fromCodePoint(0x1f251)), 2);
858assert.strictEqual(getStringWidth('abcde'), 5);
859assert.strictEqual(getStringWidth('古池や'), 6);
860assert.strictEqual(getStringWidth('ノード.js'), 9);
861assert.strictEqual(getStringWidth('你好'), 4);
862assert.strictEqual(getStringWidth('안녕하세요'), 10);
863assert.strictEqual(getStringWidth('A\ud83c\ude00BC'), 5);
864assert.strictEqual(getStringWidth('�‍�‍�‍�'), 8);
865assert.strictEqual(getStringWidth('��あ��'), 9);
866// TODO(BridgeAR): This should have a width of 4.
867assert.strictEqual(getStringWidth('⓬⓪'), 2);
868assert.strictEqual(getStringWidth('\u0301\u200D\u200E'), 0);
869
870// Check if vt control chars are stripped
871assert.strictEqual(stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> ');
872assert.strictEqual(
873  stripVTControlCharacters('\u001b[31m> \u001b[39m> '),
874  '> > '
875);
876assert.strictEqual(stripVTControlCharacters('\u001b[31m\u001b[39m'), '');
877assert.strictEqual(stripVTControlCharacters('> '), '> ');
878assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m'), 2);
879assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m> '), 4);
880assert.strictEqual(getStringWidth('\u001b[31m\u001b[39m'), 0);
881assert.strictEqual(getStringWidth('> '), 2);
882
883// Check EventEmitter memory leak
884for (let i = 0; i < 12; i++) {
885  const rl = readline.createInterface({
886    input: process.stdin,
887    output: process.stdout
888  });
889  rl.close();
890  assert.strictEqual(isWarned(process.stdin._events), false);
891  assert.strictEqual(isWarned(process.stdout._events), false);
892}
893
894[true, false].forEach((terminal) => {
895  // Disable history
896  {
897    const [rli, fi] = getInterface({ terminal, historySize: 0 });
898    assert.strictEqual(rli.historySize, 0);
899
900    fi.emit('data', 'asdf\n');
901    assert.deepStrictEqual(rli.history, []);
902    rli.close();
903  }
904
905  // Default history size 30
906  {
907    const [rli, fi] = getInterface({ terminal });
908    assert.strictEqual(rli.historySize, 30);
909
910    fi.emit('data', 'asdf\n');
911    assert.deepStrictEqual(rli.history, terminal ? ['asdf'] : []);
912    rli.close();
913  }
914
915  // Sending a full line
916  {
917    const [rli, fi] = getInterface({ terminal });
918    rli.on('line', common.mustCall((line) => {
919      assert.strictEqual(line, 'asdf');
920    }));
921    fi.emit('data', 'asdf\n');
922  }
923
924  // Ensure that options.signal.removeEventListener was called
925  {
926    const ac = new AbortController();
927    const signal = ac.signal;
928    const [rli] = getInterface({ terminal });
929    signal.removeEventListener = common.mustCall(
930      (event, onAbortFn) => {
931        assert.strictEqual(event, 'abort');
932        assert.strictEqual(onAbortFn.name, 'onAbort');
933      });
934
935    rli.question('hello?', { signal }, common.mustCall());
936
937    rli.write('bar\n');
938    ac.abort();
939    rli.close();
940  }
941
942  // Sending a blank line
943  {
944    const [rli, fi] = getInterface({ terminal });
945    rli.on('line', common.mustCall((line) => {
946      assert.strictEqual(line, '');
947    }));
948    fi.emit('data', '\n');
949  }
950
951  // Sending a single character with no newline and then a newline
952  {
953    const [rli, fi] = getInterface({ terminal });
954    let called = false;
955    rli.on('line', (line) => {
956      called = true;
957      assert.strictEqual(line, 'a');
958    });
959    fi.emit('data', 'a');
960    assert.ok(!called);
961    fi.emit('data', '\n');
962    assert.ok(called);
963    rli.close();
964  }
965
966  // Sending multiple newlines at once
967  {
968    const [rli, fi] = getInterface({ terminal });
969    const expectedLines = ['foo', 'bar', 'baz'];
970    rli.on('line', common.mustCall((line) => {
971      assert.strictEqual(line, expectedLines.shift());
972    }, expectedLines.length));
973    fi.emit('data', `${expectedLines.join('\n')}\n`);
974    rli.close();
975  }
976
977  // Sending multiple newlines at once that does not end with a new line
978  {
979    const [rli, fi] = getInterface({ terminal });
980    const expectedLines = ['foo', 'bar', 'baz', 'bat'];
981    rli.on('line', common.mustCall((line) => {
982      assert.strictEqual(line, expectedLines.shift());
983    }, expectedLines.length - 1));
984    fi.emit('data', expectedLines.join('\n'));
985    rli.close();
986  }
987
988  // Sending multiple newlines at once that does not end with a new(empty)
989  // line and a `end` event
990  {
991    const [rli, fi] = getInterface({ terminal });
992    const expectedLines = ['foo', 'bar', 'baz', ''];
993    rli.on('line', common.mustCall((line) => {
994      assert.strictEqual(line, expectedLines.shift());
995    }, expectedLines.length - 1));
996    rli.on('close', common.mustCall());
997    fi.emit('data', expectedLines.join('\n'));
998    fi.emit('end');
999    rli.close();
1000  }
1001
1002  // Sending a multi-byte utf8 char over multiple writes
1003  {
1004    const buf = Buffer.from('☮', 'utf8');
1005    const [rli, fi] = getInterface({ terminal });
1006    let callCount = 0;
1007    rli.on('line', (line) => {
1008      callCount++;
1009      assert.strictEqual(line, buf.toString('utf8'));
1010    });
1011    for (const i of buf) {
1012      fi.emit('data', Buffer.from([i]));
1013    }
1014    assert.strictEqual(callCount, 0);
1015    fi.emit('data', '\n');
1016    assert.strictEqual(callCount, 1);
1017    rli.close();
1018  }
1019
1020  // Calling readline without `new`
1021  {
1022    const [rli, fi] = getInterface({ terminal });
1023    rli.on('line', common.mustCall((line) => {
1024      assert.strictEqual(line, 'asdf');
1025    }));
1026    fi.emit('data', 'asdf\n');
1027    rli.close();
1028  }
1029
1030  // Calling the question callback
1031  {
1032    const [rli] = getInterface({ terminal });
1033    rli.question('foo?', common.mustCall((answer) => {
1034      assert.strictEqual(answer, 'bar');
1035    }));
1036    rli.write('bar\n');
1037    rli.close();
1038  }
1039
1040  // Calling the question callback with abort signal
1041  {
1042    const [rli] = getInterface({ terminal });
1043    const { signal } = new AbortController();
1044    rli.question('foo?', { signal }, common.mustCall((answer) => {
1045      assert.strictEqual(answer, 'bar');
1046    }));
1047    rli.write('bar\n');
1048    rli.close();
1049  }
1050
1051  // Calling the question multiple times
1052  {
1053    const [rli] = getInterface({ terminal });
1054    rli.question('foo?', common.mustCall((answer) => {
1055      assert.strictEqual(answer, 'baz');
1056    }));
1057    rli.question('bar?', common.mustNotCall());
1058    rli.write('baz\n');
1059    rli.close();
1060  }
1061
1062  // Calling the promisified question
1063  {
1064    const [rli] = getInterface({ terminal });
1065    const question = util.promisify(rli.question).bind(rli);
1066    question('foo?')
1067    .then(common.mustCall((answer) => {
1068      assert.strictEqual(answer, 'bar');
1069    }));
1070    rli.write('bar\n');
1071    rli.close();
1072  }
1073
1074  // Calling the promisified question with abort signal
1075  {
1076    const [rli] = getInterface({ terminal });
1077    const question = util.promisify(rli.question).bind(rli);
1078    const { signal } = new AbortController();
1079    question('foo?', { signal })
1080    .then(common.mustCall((answer) => {
1081      assert.strictEqual(answer, 'bar');
1082    }));
1083    rli.write('bar\n');
1084    rli.close();
1085  }
1086
1087  // Aborting a question
1088  {
1089    const ac = new AbortController();
1090    const signal = ac.signal;
1091    const [rli] = getInterface({ terminal });
1092    rli.on('line', common.mustCall((line) => {
1093      assert.strictEqual(line, 'bar');
1094    }));
1095    rli.question('hello?', { signal }, common.mustNotCall());
1096    ac.abort();
1097    rli.write('bar\n');
1098    rli.close();
1099  }
1100
1101  // Aborting a promisified question
1102  {
1103    const ac = new AbortController();
1104    const signal = ac.signal;
1105    const [rli] = getInterface({ terminal });
1106    const question = util.promisify(rli.question).bind(rli);
1107    rli.on('line', common.mustCall((line) => {
1108      assert.strictEqual(line, 'bar');
1109    }));
1110    question('hello?', { signal })
1111    .then(common.mustNotCall())
1112    .catch(common.mustCall((error) => {
1113      assert.strictEqual(error.name, 'AbortError');
1114    }));
1115    ac.abort();
1116    rli.write('bar\n');
1117    rli.close();
1118  }
1119
1120  // pre-aborted signal
1121  {
1122    const signal = AbortSignal.abort();
1123    const [rli] = getInterface({ terminal });
1124    rli.pause();
1125    rli.on('resume', common.mustNotCall());
1126    rli.question('hello?', { signal }, common.mustNotCall());
1127    rli.close();
1128  }
1129
1130  // pre-aborted signal promisified question
1131  {
1132    const signal = AbortSignal.abort();
1133    const [rli] = getInterface({ terminal });
1134    const question = util.promisify(rli.question).bind(rli);
1135    rli.on('resume', common.mustNotCall());
1136    rli.pause();
1137    question('hello?', { signal })
1138    .then(common.mustNotCall())
1139    .catch(common.mustCall((error) => {
1140      assert.strictEqual(error.name, 'AbortError');
1141    }));
1142    rli.close();
1143  }
1144
1145  // Call question after close
1146  {
1147    const [rli, fi] = getInterface({ terminal });
1148    rli.question('What\'s your name?', common.mustCall((name) => {
1149      assert.strictEqual(name, 'Node.js');
1150      rli.close();
1151      assert.throws(() => {
1152        rli.question('How are you?', common.mustNotCall());
1153      }, {
1154        name: 'Error',
1155        code: 'ERR_USE_AFTER_CLOSE'
1156      });
1157      assert.notStrictEqual(rli.getPrompt(), 'How are you?');
1158    }));
1159    fi.emit('data', 'Node.js\n');
1160  }
1161
1162  // Call promisified question after close
1163  {
1164    const [rli, fi] = getInterface({ terminal });
1165    const question = util.promisify(rli.question).bind(rli);
1166    question('What\'s your name?').then(common.mustCall((name) => {
1167      assert.strictEqual(name, 'Node.js');
1168      rli.close();
1169      question('How are you?')
1170        .then(common.mustNotCall(), common.expectsError({
1171          code: 'ERR_USE_AFTER_CLOSE',
1172          name: 'Error'
1173        }));
1174      assert.notStrictEqual(rli.getPrompt(), 'How are you?');
1175    }));
1176    fi.emit('data', 'Node.js\n');
1177  }
1178
1179  // Can create a new readline Interface with a null output argument
1180  {
1181    const [rli, fi] = getInterface({ output: null, terminal });
1182    rli.on('line', common.mustCall((line) => {
1183      assert.strictEqual(line, 'asdf');
1184    }));
1185    fi.emit('data', 'asdf\n');
1186
1187    rli.setPrompt('ddd> ');
1188    rli.prompt();
1189    rli.write("really shouldn't be seeing this");
1190    rli.question('What do you think of node.js? ', (answer) => {
1191      console.log('Thank you for your valuable feedback:', answer);
1192      rli.close();
1193    });
1194  }
1195
1196  // Calling the getPrompt method
1197  {
1198    const expectedPrompts = ['$ ', '> '];
1199    const [rli] = getInterface({ terminal });
1200    for (const prompt of expectedPrompts) {
1201      rli.setPrompt(prompt);
1202      assert.strictEqual(rli.getPrompt(), prompt);
1203    }
1204  }
1205
1206  {
1207    const expected = terminal ?
1208      ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] :
1209      ['$ '];
1210
1211    const output = new Writable({
1212      write: common.mustCall((chunk, enc, cb) => {
1213        assert.strictEqual(chunk.toString(), expected.shift());
1214        cb();
1215        rl.close();
1216      }, expected.length)
1217    });
1218
1219    const rl = readline.createInterface({
1220      input: new Readable({ read: common.mustCall() }),
1221      output,
1222      prompt: '$ ',
1223      terminal
1224    });
1225
1226    rl.prompt();
1227
1228    assert.strictEqual(rl.getPrompt(), '$ ');
1229  }
1230
1231  {
1232    const fi = new FakeInput();
1233    assert.deepStrictEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
1234  }
1235
1236  // Emit two line events when the delay
1237  // between \r and \n exceeds crlfDelay
1238  {
1239    const crlfDelay = 200;
1240    const [rli, fi] = getInterface({ terminal, crlfDelay });
1241    let callCount = 0;
1242    rli.on('line', () => {
1243      callCount++;
1244    });
1245    fi.emit('data', '\r');
1246    setTimeout(common.mustCall(() => {
1247      fi.emit('data', '\n');
1248      assert.strictEqual(callCount, 2);
1249      rli.close();
1250    }), crlfDelay + 10);
1251  }
1252
1253  // For the purposes of the following tests, we do not care about the exact
1254  // value of crlfDelay, only that the behaviour conforms to what's expected.
1255  // Setting it to Infinity allows the test to succeed even under extreme
1256  // CPU stress.
1257  const crlfDelay = Infinity;
1258
1259  // Set crlfDelay to `Infinity` is allowed
1260  {
1261    const delay = 200;
1262    const [rli, fi] = getInterface({ terminal, crlfDelay });
1263    let callCount = 0;
1264    rli.on('line', () => {
1265      callCount++;
1266    });
1267    fi.emit('data', '\r');
1268    setTimeout(common.mustCall(() => {
1269      fi.emit('data', '\n');
1270      assert.strictEqual(callCount, 1);
1271      rli.close();
1272    }), delay);
1273  }
1274
1275  // Sending multiple newlines at once that does not end with a new line
1276  // and a `end` event(last line is)
1277
1278  // \r\n should emit one line event, not two
1279  {
1280    const [rli, fi] = getInterface({ terminal, crlfDelay });
1281    const expectedLines = ['foo', 'bar', 'baz', 'bat'];
1282    rli.on('line', common.mustCall((line) => {
1283      assert.strictEqual(line, expectedLines.shift());
1284    }, expectedLines.length - 1));
1285    fi.emit('data', expectedLines.join('\r\n'));
1286    rli.close();
1287  }
1288
1289  // \r\n should emit one line event when split across multiple writes.
1290  {
1291    const [rli, fi] = getInterface({ terminal, crlfDelay });
1292    const expectedLines = ['foo', 'bar', 'baz', 'bat'];
1293    let callCount = 0;
1294    rli.on('line', common.mustCall((line) => {
1295      assert.strictEqual(line, expectedLines[callCount]);
1296      callCount++;
1297    }, expectedLines.length));
1298    expectedLines.forEach((line) => {
1299      fi.emit('data', `${line}\r`);
1300      fi.emit('data', '\n');
1301    });
1302    rli.close();
1303  }
1304
1305  // Emit one line event when the delay between \r and \n is
1306  // over the default crlfDelay but within the setting value.
1307  {
1308    const delay = 125;
1309    const [rli, fi] = getInterface({ terminal, crlfDelay });
1310    let callCount = 0;
1311    rli.on('line', () => callCount++);
1312    fi.emit('data', '\r');
1313    setTimeout(common.mustCall(() => {
1314      fi.emit('data', '\n');
1315      assert.strictEqual(callCount, 1);
1316      rli.close();
1317    }), delay);
1318  }
1319});
1320
1321// Ensure that the _wordLeft method works even for large input
1322{
1323  const input = new Readable({
1324    read() {
1325      this.push('\x1B[1;5D'); // CTRL + Left
1326      this.push(null);
1327    },
1328  });
1329  const output = new Writable({
1330    write: common.mustCall((data, encoding, cb) => {
1331      assert.strictEqual(rl.cursor, rl.line.length - 1);
1332      cb();
1333    }),
1334  });
1335  const rl = new readline.createInterface({
1336    input,
1337    output,
1338    terminal: true,
1339  });
1340  rl.line = `a${' '.repeat(1e6)}a`;
1341  rl.cursor = rl.line.length;
1342}
1343
1344{
1345  const fi = new FakeInput();
1346  const signal = AbortSignal.abort();
1347
1348  const rl = readline.createInterface({
1349    input: fi,
1350    output: fi,
1351    signal,
1352  });
1353  rl.on('close', common.mustCall());
1354  assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
1355}
1356
1357{
1358  const fi = new FakeInput();
1359  const ac = new AbortController();
1360  const { signal } = ac;
1361  const rl = readline.createInterface({
1362    input: fi,
1363    output: fi,
1364    signal,
1365  });
1366  assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
1367  rl.on('close', common.mustCall());
1368  ac.abort();
1369  assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
1370}
1371
1372{
1373  const fi = new FakeInput();
1374  const ac = new AbortController();
1375  const { signal } = ac;
1376  const rl = readline.createInterface({
1377    input: fi,
1378    output: fi,
1379    signal,
1380  });
1381  assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
1382  rl.close();
1383  assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
1384}
1385
1386{
1387  // Constructor throws if signal is not an abort signal
1388  assert.throws(() => {
1389    readline.createInterface({
1390      input: new FakeInput(),
1391      signal: {},
1392    });
1393  }, {
1394    name: 'TypeError',
1395    code: 'ERR_INVALID_ARG_TYPE'
1396  });
1397}
1398