summaryrefslogtreecommitdiff
path: root/cli/tests/node_compat/test/parallel/test-readline-interface.js
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tests/node_compat/test/parallel/test-readline-interface.js')
-rw-r--r--cli/tests/node_compat/test/parallel/test-readline-interface.js1217
1 files changed, 0 insertions, 1217 deletions
diff --git a/cli/tests/node_compat/test/parallel/test-readline-interface.js b/cli/tests/node_compat/test/parallel/test-readline-interface.js
deleted file mode 100644
index e8e48dd1e..000000000
--- a/cli/tests/node_compat/test/parallel/test-readline-interface.js
+++ /dev/null
@@ -1,1217 +0,0 @@
-// deno-fmt-ignore-file
-// deno-lint-ignore-file
-
-// Copyright Joyent and Node contributors. All rights reserved. MIT license.
-// Taken from Node 16.13.0
-// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
-
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-// Flags: --expose-internals
-'use strict';
-const common = require('../common');
-common.skipIfDumbTerminal();
-
-const assert = require('assert');
-const readline = require('readline');
-const util = require('util');
-const {
- getStringWidth,
- stripVTControlCharacters
-} = require('internal/util/inspect');
-const { EventEmitter, getEventListeners } = require('events');
-const { Writable, Readable } = require('stream');
-
-class FakeInput extends EventEmitter {
- resume() {}
- pause() {}
- write() {}
- end() {}
-}
-
-function isWarned(emitter) {
- for (const name in emitter) {
- const listeners = emitter[name];
- if (listeners.warned) return true;
- }
- return false;
-}
-
-function getInterface(options) {
- const fi = new FakeInput();
- const rli = new readline.Interface({
- input: fi,
- output: fi,
- ...options,
- });
- return [rli, fi];
-}
-
-function assertCursorRowsAndCols(rli, rows, cols) {
- const cursorPos = rli.getCursorPos();
- assert.strictEqual(cursorPos.rows, rows);
- assert.strictEqual(cursorPos.cols, cols);
-}
-
-{
- const input = new FakeInput();
- const rl = readline.Interface({ input });
- assert(rl instanceof readline.Interface);
-}
-
-[
- undefined,
- 50,
- 0,
- 100.5,
- 5000,
-].forEach((crlfDelay) => {
- const [rli] = getInterface({ crlfDelay });
- assert.strictEqual(rli.crlfDelay, Math.max(crlfDelay || 100, 100));
- rli.close();
-});
-
-{
- const input = new FakeInput();
-
- // Constructor throws if completer is not a function or undefined
- ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((invalid) => {
- assert.throws(() => {
- readline.createInterface({
- input,
- completer: invalid
- });
- }, {
- name: 'TypeError',
- code: 'ERR_INVALID_ARG_VALUE'
- });
- });
-
- // Constructor throws if history is not an array
- ['not an array', 123, 123n, {}, true, Symbol(), null].forEach((history) => {
- assert.throws(() => {
- readline.createInterface({
- input,
- history,
- });
- }, {
- name: 'TypeError',
- code: 'ERR_INVALID_ARG_TYPE'
- });
- });
-
- // Constructor throws if historySize is not a positive number
- ['not a number', -1, NaN, {}, true, Symbol(), null].forEach((historySize) => {
- assert.throws(() => {
- readline.createInterface({
- input,
- historySize,
- });
- }, {
- name: 'RangeError',
- code: 'ERR_INVALID_ARG_VALUE'
- });
- });
-
- // Check for invalid tab sizes.
- assert.throws(
- () => new readline.Interface({
- input,
- tabSize: 0
- }),
- {
- message: 'The value of "tabSize" is out of range. ' +
- 'It must be >= 1 && < 4294967296. Received 0',
- code: 'ERR_OUT_OF_RANGE'
- }
- );
-
- assert.throws(
- () => new readline.Interface({
- input,
- tabSize: '4'
- }),
- { code: 'ERR_INVALID_ARG_TYPE' }
- );
-
- assert.throws(
- () => new readline.Interface({
- input,
- tabSize: 4.5
- }),
- {
- code: 'ERR_OUT_OF_RANGE',
- message: 'The value of "tabSize" is out of range. ' +
- 'It must be an integer. Received 4.5'
- }
- );
-}
-
-// Sending a single character with no newline
-{
- const fi = new FakeInput();
- const rli = new readline.Interface(fi, {});
- rli.on('line', common.mustNotCall());
- fi.emit('data', 'a');
- rli.close();
-}
-
-// Sending multiple newlines at once that does not end with a new line and a
-// `end` event(last line is). \r should behave like \n when alone.
-{
- const [rli, fi] = getInterface({ terminal: true });
- const expectedLines = ['foo', 'bar', 'baz', 'bat'];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length - 1));
- fi.emit('data', expectedLines.join('\r'));
- rli.close();
-}
-
-// \r at start of input should output blank line
-{
- const [rli, fi] = getInterface({ terminal: true });
- const expectedLines = ['', 'foo' ];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length));
- fi.emit('data', '\rfoo\r');
- rli.close();
-}
-
-// \t does not become part of the input when there is a completer function
-{
- const completer = (line) => [[], line];
- const [rli, fi] = getInterface({ terminal: true, completer });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'foo');
- }));
- for (const character of '\tfo\to\t') {
- fi.emit('data', character);
- }
- fi.emit('data', '\n');
- rli.close();
-}
-
-// \t when there is no completer function should behave like an ordinary
-// character
-{
- const [rli, fi] = getInterface({ terminal: true });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '\t');
- }));
- fi.emit('data', '\t');
- fi.emit('data', '\n');
- rli.close();
-}
-
-// Adding history lines should emit the history event with
-// the history array
-{
- const [rli, fi] = getInterface({ terminal: true });
- const expectedLines = ['foo', 'bar', 'baz', 'bat'];
- rli.on('history', common.mustCall((history) => {
- const expectedHistory = expectedLines.slice(0, history.length).reverse();
- assert.deepStrictEqual(history, expectedHistory);
- }, expectedLines.length));
- for (const line of expectedLines) {
- fi.emit('data', `${line}\n`);
- }
- rli.close();
-}
-
-// Altering the history array in the listener should not alter
-// the line being processed
-{
- const [rli, fi] = getInterface({ terminal: true });
- const expectedLine = 'foo';
- rli.on('history', common.mustCall((history) => {
- assert.strictEqual(history[0], expectedLine);
- history.shift();
- }));
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLine);
- assert.strictEqual(rli.history.length, 0);
- }));
- fi.emit('data', `${expectedLine}\n`);
- rli.close();
-}
-
-// Duplicate lines are removed from history when
-// `options.removeHistoryDuplicates` is `true`
-{
- const [rli, fi] = getInterface({
- terminal: true,
- removeHistoryDuplicates: true
- });
- const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
- // ['foo', 'baz', 'bar', bat'];
- let callCount = 0;
- rli.on('line', (line) => {
- assert.strictEqual(line, expectedLines[callCount]);
- callCount++;
- });
- fi.emit('data', `${expectedLines.join('\n')}\n`);
- assert.strictEqual(callCount, expectedLines.length);
- fi.emit('keypress', '.', { name: 'up' }); // 'bat'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'bar'
- assert.notStrictEqual(rli.line, expectedLines[--callCount]);
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'baz'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'foo'
- assert.notStrictEqual(rli.line, expectedLines[--callCount]);
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- assert.strictEqual(callCount, 0);
- fi.emit('keypress', '.', { name: 'down' }); // 'baz'
- assert.strictEqual(rli.line, 'baz');
- assert.strictEqual(rli.historyIndex, 2);
- fi.emit('keypress', '.', { name: 'n', ctrl: true }); // 'bar'
- assert.strictEqual(rli.line, 'bar');
- assert.strictEqual(rli.historyIndex, 1);
- fi.emit('keypress', '.', { name: 'n', ctrl: true });
- assert.strictEqual(rli.line, 'bat');
- assert.strictEqual(rli.historyIndex, 0);
- // Activate the substring history search.
- fi.emit('keypress', '.', { name: 'down' }); // 'bat'
- assert.strictEqual(rli.line, 'bat');
- assert.strictEqual(rli.historyIndex, -1);
- // Deactivate substring history search.
- fi.emit('keypress', '.', { name: 'backspace' }); // 'ba'
- assert.strictEqual(rli.historyIndex, -1);
- assert.strictEqual(rli.line, 'ba');
- // Activate the substring history search.
- fi.emit('keypress', '.', { name: 'down' }); // 'ba'
- assert.strictEqual(rli.historyIndex, -1);
- assert.strictEqual(rli.line, 'ba');
- fi.emit('keypress', '.', { name: 'down' }); // 'ba'
- assert.strictEqual(rli.historyIndex, -1);
- assert.strictEqual(rli.line, 'ba');
- fi.emit('keypress', '.', { name: 'up' }); // 'bat'
- assert.strictEqual(rli.historyIndex, 0);
- assert.strictEqual(rli.line, 'bat');
- fi.emit('keypress', '.', { name: 'up' }); // 'bar'
- assert.strictEqual(rli.historyIndex, 1);
- assert.strictEqual(rli.line, 'bar');
- fi.emit('keypress', '.', { name: 'up' }); // 'baz'
- assert.strictEqual(rli.historyIndex, 2);
- assert.strictEqual(rli.line, 'baz');
- fi.emit('keypress', '.', { name: 'up' }); // 'ba'
- assert.strictEqual(rli.historyIndex, 4);
- assert.strictEqual(rli.line, 'ba');
- fi.emit('keypress', '.', { name: 'up' }); // 'ba'
- assert.strictEqual(rli.historyIndex, 4);
- assert.strictEqual(rli.line, 'ba');
- // Deactivate substring history search and reset history index.
- fi.emit('keypress', '.', { name: 'right' }); // 'ba'
- assert.strictEqual(rli.historyIndex, -1);
- assert.strictEqual(rli.line, 'ba');
- // Substring history search activated.
- fi.emit('keypress', '.', { name: 'up' }); // 'ba'
- assert.strictEqual(rli.historyIndex, 0);
- assert.strictEqual(rli.line, 'bat');
- rli.close();
-}
-
-// Duplicate lines are not removed from history when
-// `options.removeHistoryDuplicates` is `false`
-{
- const [rli, fi] = getInterface({
- terminal: true,
- removeHistoryDuplicates: false
- });
- const expectedLines = ['foo', 'bar', 'baz', 'bar', 'bat', 'bat'];
- let callCount = 0;
- rli.on('line', (line) => {
- assert.strictEqual(line, expectedLines[callCount]);
- callCount++;
- });
- fi.emit('data', `${expectedLines.join('\n')}\n`);
- assert.strictEqual(callCount, expectedLines.length);
- fi.emit('keypress', '.', { name: 'up' }); // 'bat'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'bar'
- assert.notStrictEqual(rli.line, expectedLines[--callCount]);
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'baz'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'bar'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- fi.emit('keypress', '.', { name: 'up' }); // 'foo'
- assert.strictEqual(rli.line, expectedLines[--callCount]);
- assert.strictEqual(callCount, 0);
- rli.close();
-}
-
-// Regression test for repl freeze, #1968:
-// check that nothing fails if 'keypress' event throws.
-{
- const [rli, fi] = getInterface({ terminal: true });
- const keys = [];
- const err = new Error('bad thing happened');
- fi.on('keypress', (key) => {
- keys.push(key);
- if (key === 'X') {
- throw err;
- }
- });
- assert.throws(
- () => fi.emit('data', 'fooX'),
- (e) => {
- assert.strictEqual(e, err);
- return true;
- }
- );
- fi.emit('data', 'bar');
- assert.strictEqual(keys.join(''), 'fooXbar');
- rli.close();
-}
-
-// History is bound
-{
- const [rli, fi] = getInterface({ terminal: true, historySize: 2 });
- const lines = ['line 1', 'line 2', 'line 3'];
- fi.emit('data', lines.join('\n') + '\n');
- assert.strictEqual(rli.history.length, 2);
- assert.strictEqual(rli.history[0], 'line 3');
- assert.strictEqual(rli.history[1], 'line 2');
-}
-
-// Question
-{
- const [rli] = getInterface({ terminal: true });
- const expectedLines = ['foo'];
- rli.question(expectedLines[0], () => rli.close());
- assertCursorRowsAndCols(rli, 0, expectedLines[0].length);
- rli.close();
-}
-
-// Sending a multi-line question
-{
- const [rli] = getInterface({ terminal: true });
- const expectedLines = ['foo', 'bar'];
- rli.question(expectedLines.join('\n'), () => rli.close());
- assertCursorRowsAndCols(
- rli, expectedLines.length - 1, expectedLines.slice(-1)[0].length);
- rli.close();
-}
-
-{
- // Beginning and end of line
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- fi.emit('keypress', '.', { ctrl: true, name: 'a' });
- assertCursorRowsAndCols(rli, 0, 0);
- fi.emit('keypress', '.', { ctrl: true, name: 'e' });
- assertCursorRowsAndCols(rli, 0, 19);
- rli.close();
-}
-
-{
- // Back and Forward one character
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- assertCursorRowsAndCols(rli, 0, 19);
-
- // Back one character
- fi.emit('keypress', '.', { ctrl: true, name: 'b' });
- assertCursorRowsAndCols(rli, 0, 18);
- // Back one character
- fi.emit('keypress', '.', { ctrl: true, name: 'b' });
- assertCursorRowsAndCols(rli, 0, 17);
- // Forward one character
- fi.emit('keypress', '.', { ctrl: true, name: 'f' });
- assertCursorRowsAndCols(rli, 0, 18);
- // Forward one character
- fi.emit('keypress', '.', { ctrl: true, name: 'f' });
- assertCursorRowsAndCols(rli, 0, 19);
- rli.close();
-}
-
-// Back and Forward one astral character
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', '💻');
-
- // Move left one character/code point
- fi.emit('keypress', '.', { name: 'left' });
- assertCursorRowsAndCols(rli, 0, 0);
-
- // Move right one character/code point
- fi.emit('keypress', '.', { name: 'right' });
- assertCursorRowsAndCols(rli, 0, 2);
-
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '💻');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// Two astral characters left
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', '💻');
-
- // Move left one character/code point
- fi.emit('keypress', '.', { name: 'left' });
- assertCursorRowsAndCols(rli, 0, 0);
-
- fi.emit('data', '🐕');
- assertCursorRowsAndCols(rli, 0, 2);
-
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '🐕💻');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// Two astral characters right
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', '💻');
-
- // Move left one character/code point
- fi.emit('keypress', '.', { name: 'right' });
- assertCursorRowsAndCols(rli, 0, 2);
-
- fi.emit('data', '🐕');
- assertCursorRowsAndCols(rli, 0, 4);
-
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '💻🐕');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-{
- // `wordLeft` and `wordRight`
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- fi.emit('keypress', '.', { ctrl: true, name: 'left' });
- assertCursorRowsAndCols(rli, 0, 16);
- fi.emit('keypress', '.', { meta: true, name: 'b' });
- assertCursorRowsAndCols(rli, 0, 10);
- fi.emit('keypress', '.', { ctrl: true, name: 'right' });
- assertCursorRowsAndCols(rli, 0, 16);
- fi.emit('keypress', '.', { meta: true, name: 'f' });
- assertCursorRowsAndCols(rli, 0, 19);
- rli.close();
-}
-
-// `deleteWordLeft`
-[
- { ctrl: true, name: 'w' },
- { ctrl: true, name: 'backspace' },
- { meta: true, name: 'backspace' },
-].forEach((deleteWordLeftKey) => {
- let [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- fi.emit('keypress', '.', { ctrl: true, name: 'left' });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'the quick fox');
- }));
- fi.emit('keypress', '.', deleteWordLeftKey);
- fi.emit('data', '\n');
- rli.close();
-
- // No effect if pressed at beginning of line
- [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- fi.emit('keypress', '.', { ctrl: true, name: 'a' });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'the quick brown fox');
- }));
- fi.emit('keypress', '.', deleteWordLeftKey);
- fi.emit('data', '\n');
- rli.close();
-});
-
-// `deleteWordRight`
-[
- { ctrl: true, name: 'delete' },
- { meta: true, name: 'delete' },
- { meta: true, name: 'd' },
-].forEach((deleteWordRightKey) => {
- let [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- fi.emit('keypress', '.', { ctrl: true, name: 'left' });
- fi.emit('keypress', '.', { ctrl: true, name: 'left' });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'the quick fox');
- }));
- fi.emit('keypress', '.', deleteWordRightKey);
- fi.emit('data', '\n');
- rli.close();
-
- // No effect if pressed at end of line
- [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'the quick brown fox');
- }));
- fi.emit('keypress', '.', deleteWordRightKey);
- fi.emit('data', '\n');
- rli.close();
-});
-
-// deleteLeft
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- assertCursorRowsAndCols(rli, 0, 19);
-
- // Delete left character
- fi.emit('keypress', '.', { ctrl: true, name: 'h' });
- assertCursorRowsAndCols(rli, 0, 18);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'the quick brown fo');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// deleteLeft astral character
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', '💻');
- assertCursorRowsAndCols(rli, 0, 2);
- // Delete left character
- fi.emit('keypress', '.', { ctrl: true, name: 'h' });
- assertCursorRowsAndCols(rli, 0, 0);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// deleteRight
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
-
- // Go to the start of the line
- fi.emit('keypress', '.', { ctrl: true, name: 'a' });
- assertCursorRowsAndCols(rli, 0, 0);
-
- // Delete right character
- fi.emit('keypress', '.', { ctrl: true, name: 'd' });
- assertCursorRowsAndCols(rli, 0, 0);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'he quick brown fox');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// deleteRight astral character
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', '💻');
-
- // Go to the start of the line
- fi.emit('keypress', '.', { ctrl: true, name: 'a' });
- assertCursorRowsAndCols(rli, 0, 0);
-
- // Delete right character
- fi.emit('keypress', '.', { ctrl: true, name: 'd' });
- assertCursorRowsAndCols(rli, 0, 0);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// deleteLineLeft
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
- assertCursorRowsAndCols(rli, 0, 19);
-
- // Delete from current to start of line
- fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'backspace' });
- assertCursorRowsAndCols(rli, 0, 0);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// deleteLineRight
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick brown fox');
-
- // Go to the start of the line
- fi.emit('keypress', '.', { ctrl: true, name: 'a' });
- assertCursorRowsAndCols(rli, 0, 0);
-
- // Delete from current to end of line
- fi.emit('keypress', '.', { ctrl: true, shift: true, name: 'delete' });
- assertCursorRowsAndCols(rli, 0, 0);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// Close readline interface
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('keypress', '.', { ctrl: true, name: 'c' });
- assert(rli.closed);
-}
-
-// Multi-line input cursor position
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.columns = 10;
- fi.emit('data', 'multi-line text');
- assertCursorRowsAndCols(rli, 1, 5);
- rli.close();
-}
-
-// Multi-line input cursor position and long tabs
-{
- const [rli, fi] = getInterface({ tabSize: 16, terminal: true, prompt: '' });
- fi.columns = 10;
- fi.emit('data', 'multi-line\ttext \t');
- assert.strictEqual(rli.cursor, 17);
- assertCursorRowsAndCols(rli, 3, 2);
- rli.close();
-}
-
-// Check for the default tab size.
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- fi.emit('data', 'the quick\tbrown\tfox');
- assert.strictEqual(rli.cursor, 19);
- // The first tab is 7 spaces long, the second one 3 spaces.
- assertCursorRowsAndCols(rli, 0, 27);
-}
-
-// Multi-line prompt cursor position
-{
- const [rli, fi] = getInterface({
- terminal: true,
- prompt: '\nfilledline\nwraping text\n> '
- });
- fi.columns = 10;
- fi.emit('data', 't');
- assertCursorRowsAndCols(rli, 4, 3);
- rli.close();
-}
-
-// Clear the whole screen
-{
- const [rli, fi] = getInterface({ terminal: true, prompt: '' });
- const lines = ['line 1', 'line 2', 'line 3'];
- fi.emit('data', lines.join('\n'));
- fi.emit('keypress', '.', { ctrl: true, name: 'l' });
- assertCursorRowsAndCols(rli, 0, 6);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'line 3');
- }));
- fi.emit('data', '\n');
- rli.close();
-}
-
-// Wide characters should be treated as two columns.
-assert.strictEqual(getStringWidth('a'), 1);
-assert.strictEqual(getStringWidth('あ'), 2);
-assert.strictEqual(getStringWidth('č°˘'), 2);
-assert.strictEqual(getStringWidth('ęł '), 2);
-assert.strictEqual(getStringWidth(String.fromCodePoint(0x1f251)), 2);
-assert.strictEqual(getStringWidth('abcde'), 5);
-assert.strictEqual(getStringWidth('古池や'), 6);
-assert.strictEqual(getStringWidth('ノード.js'), 9);
-assert.strictEqual(getStringWidth('你弽'), 4);
-assert.strictEqual(getStringWidth('안녕하세요'), 10);
-assert.strictEqual(getStringWidth('A\ud83c\ude00BC'), 5);
-assert.strictEqual(getStringWidth('👨‍👩‍👦‍👦'), 8);
-assert.strictEqual(getStringWidth('🐕𐐷あ💻😀'), 9);
-// TODO(BridgeAR): This should have a width of 4.
-assert.strictEqual(getStringWidth('⓬⓪'), 2);
-assert.strictEqual(getStringWidth('\u0301\u200D\u200E'), 0);
-
-// Check if vt control chars are stripped
-assert.strictEqual(stripVTControlCharacters('\u001b[31m> \u001b[39m'), '> ');
-assert.strictEqual(
- stripVTControlCharacters('\u001b[31m> \u001b[39m> '),
- '> > '
-);
-assert.strictEqual(stripVTControlCharacters('\u001b[31m\u001b[39m'), '');
-assert.strictEqual(stripVTControlCharacters('> '), '> ');
-assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m'), 2);
-assert.strictEqual(getStringWidth('\u001b[31m> \u001b[39m> '), 4);
-assert.strictEqual(getStringWidth('\u001b[31m\u001b[39m'), 0);
-assert.strictEqual(getStringWidth('> '), 2);
-
-// FIXME(bartlomieju): this causes hang
-// Check EventEmitter memory leak
-// for (let i = 0; i < 12; i++) {
-// const rl = readline.createInterface({
-// input: process.stdin,
-// output: process.stdout
-// });
-// rl.close();
-// assert.strictEqual(isWarned(process.stdin._events), false);
-// assert.strictEqual(isWarned(process.stdout._events), false);
-// }
-
-[true, false].forEach((terminal) => {
- // Disable history
- {
- const [rli, fi] = getInterface({ terminal, historySize: 0 });
- assert.strictEqual(rli.historySize, 0);
-
- fi.emit('data', 'asdf\n');
- assert.deepStrictEqual(rli.history, []);
- rli.close();
- }
-
- // Default history size 30
- {
- const [rli, fi] = getInterface({ terminal });
- assert.strictEqual(rli.historySize, 30);
-
- fi.emit('data', 'asdf\n');
- assert.deepStrictEqual(rli.history, terminal ? ['asdf'] : []);
- rli.close();
- }
-
- // Sending a full line
- {
- const [rli, fi] = getInterface({ terminal });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'asdf');
- }));
- fi.emit('data', 'asdf\n');
- }
-
- // Sending a blank line
- {
- const [rli, fi] = getInterface({ terminal });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, '');
- }));
- fi.emit('data', '\n');
- }
-
- // Sending a single character with no newline and then a newline
- {
- const [rli, fi] = getInterface({ terminal });
- let called = false;
- rli.on('line', (line) => {
- called = true;
- assert.strictEqual(line, 'a');
- });
- fi.emit('data', 'a');
- assert.ok(!called);
- fi.emit('data', '\n');
- assert.ok(called);
- rli.close();
- }
-
- // Sending multiple newlines at once
- {
- const [rli, fi] = getInterface({ terminal });
- const expectedLines = ['foo', 'bar', 'baz'];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length));
- fi.emit('data', `${expectedLines.join('\n')}\n`);
- rli.close();
- }
-
- // Sending multiple newlines at once that does not end with a new line
- {
- const [rli, fi] = getInterface({ terminal });
- const expectedLines = ['foo', 'bar', 'baz', 'bat'];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length - 1));
- fi.emit('data', expectedLines.join('\n'));
- rli.close();
- }
-
- // Sending multiple newlines at once that does not end with a new(empty)
- // line and a `end` event
- {
- const [rli, fi] = getInterface({ terminal });
- const expectedLines = ['foo', 'bar', 'baz', ''];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length - 1));
- rli.on('close', common.mustCall());
- fi.emit('data', expectedLines.join('\n'));
- fi.emit('end');
- rli.close();
- }
-
- // Sending a multi-byte utf8 char over multiple writes
- {
- const buf = Buffer.from('☎', 'utf8');
- const [rli, fi] = getInterface({ terminal });
- let callCount = 0;
- rli.on('line', (line) => {
- callCount++;
- assert.strictEqual(line, buf.toString('utf8'));
- });
- for (const i of buf) {
- fi.emit('data', Buffer.from([i]));
- }
- assert.strictEqual(callCount, 0);
- fi.emit('data', '\n');
- assert.strictEqual(callCount, 1);
- rli.close();
- }
-
- // Calling readline without `new`
- {
- const [rli, fi] = getInterface({ terminal });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'asdf');
- }));
- fi.emit('data', 'asdf\n');
- rli.close();
- }
-
- // Calling the question callback
- {
- const [rli] = getInterface({ terminal });
- rli.question('foo?', common.mustCall((answer) => {
- assert.strictEqual(answer, 'bar');
- }));
- rli.write('bar\n');
- rli.close();
- }
-
- // Calling the question multiple times
- {
- const [rli] = getInterface({ terminal });
- rli.question('foo?', common.mustCall((answer) => {
- assert.strictEqual(answer, 'baz');
- }));
- rli.question('bar?', common.mustNotCall(() => {
- }));
- rli.write('baz\n');
- rli.close();
- }
-
- // Calling the promisified question
- {
- const [rli] = getInterface({ terminal });
- const question = util.promisify(rli.question).bind(rli);
- question('foo?')
- .then(common.mustCall((answer) => {
- assert.strictEqual(answer, 'bar');
- }));
- rli.write('bar\n');
- rli.close();
- }
-
- // Aborting a question
- {
- const ac = new AbortController();
- const signal = ac.signal;
- const [rli] = getInterface({ terminal });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'bar');
- }));
- rli.question('hello?', { signal }, common.mustNotCall());
- ac.abort();
- rli.write('bar\n');
- rli.close();
- }
-
- // Aborting a promisified question
- {
- const ac = new AbortController();
- const signal = ac.signal;
- const [rli] = getInterface({ terminal });
- const question = util.promisify(rli.question).bind(rli);
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'bar');
- }));
- question('hello?', { signal })
- .then(common.mustNotCall())
- .catch(common.mustCall((error) => {
- assert.strictEqual(error.name, 'AbortError');
- }));
- ac.abort();
- rli.write('bar\n');
- rli.close();
- }
-
- // pre-aborted signal
- {
- const signal = AbortSignal.abort();
- const [rli] = getInterface({ terminal });
- rli.pause();
- rli.on('resume', common.mustNotCall());
- rli.question('hello?', { signal }, common.mustNotCall());
- rli.close();
- }
-
- // pre-aborted signal promisified question
- {
- const signal = AbortSignal.abort();
- const [rli] = getInterface({ terminal });
- const question = util.promisify(rli.question).bind(rli);
- rli.on('resume', common.mustNotCall());
- rli.pause();
- question('hello?', { signal })
- .then(common.mustNotCall())
- .catch(common.mustCall((error) => {
- assert.strictEqual(error.name, 'AbortError');
- }));
- rli.close();
- }
-
- // Can create a new readline Interface with a null output argument
- {
- const [rli, fi] = getInterface({ output: null, terminal });
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, 'asdf');
- }));
- fi.emit('data', 'asdf\n');
-
- rli.setPrompt('ddd> ');
- rli.prompt();
- rli.write("really shouldn't be seeing this");
- rli.question('What do you think of node.js? ', (answer) => {
- console.log('Thank you for your valuable feedback:', answer);
- rli.close();
- });
- }
-
- // Calling the getPrompt method
- {
- const expectedPrompts = ['$ ', '> '];
- const [rli] = getInterface({ terminal });
- for (const prompt of expectedPrompts) {
- rli.setPrompt(prompt);
- assert.strictEqual(rli.getPrompt(), prompt);
- }
- }
-
- {
- const expected = terminal ?
- ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] :
- ['$ '];
-
- const output = new Writable({
- write: common.mustCall((chunk, enc, cb) => {
- assert.strictEqual(chunk.toString(), expected.shift());
- cb();
- rl.close();
- }, expected.length)
- });
-
- const rl = readline.createInterface({
- input: new Readable({ read: common.mustCall() }),
- output,
- prompt: '$ ',
- terminal
- });
-
- rl.prompt();
-
- assert.strictEqual(rl.getPrompt(), '$ ');
- }
-
- {
- const fi = new FakeInput();
- assert.deepStrictEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
- }
-
- // Emit two line events when the delay
- // between \r and \n exceeds crlfDelay
- {
- const crlfDelay = 200;
- const [rli, fi] = getInterface({ terminal, crlfDelay });
- let callCount = 0;
- rli.on('line', () => {
- callCount++;
- });
- fi.emit('data', '\r');
- setTimeout(common.mustCall(() => {
- fi.emit('data', '\n');
- assert.strictEqual(callCount, 2);
- rli.close();
- }), crlfDelay + 10);
- }
-
- // For the purposes of the following tests, we do not care about the exact
- // value of crlfDelay, only that the behaviour conforms to what's expected.
- // Setting it to Infinity allows the test to succeed even under extreme
- // CPU stress.
- const crlfDelay = Infinity;
-
- // Set crlfDelay to `Infinity` is allowed
- {
- const delay = 200;
- const [rli, fi] = getInterface({ terminal, crlfDelay });
- let callCount = 0;
- rli.on('line', () => {
- callCount++;
- });
- fi.emit('data', '\r');
- setTimeout(common.mustCall(() => {
- fi.emit('data', '\n');
- assert.strictEqual(callCount, 1);
- rli.close();
- }), delay);
- }
-
- // Sending multiple newlines at once that does not end with a new line
- // and a `end` event(last line is)
-
- // \r\n should emit one line event, not two
- {
- const [rli, fi] = getInterface({ terminal, crlfDelay });
- const expectedLines = ['foo', 'bar', 'baz', 'bat'];
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines.shift());
- }, expectedLines.length - 1));
- fi.emit('data', expectedLines.join('\r\n'));
- rli.close();
- }
-
- // \r\n should emit one line event when split across multiple writes.
- {
- const [rli, fi] = getInterface({ terminal, crlfDelay });
- const expectedLines = ['foo', 'bar', 'baz', 'bat'];
- let callCount = 0;
- rli.on('line', common.mustCall((line) => {
- assert.strictEqual(line, expectedLines[callCount]);
- callCount++;
- }, expectedLines.length));
- expectedLines.forEach((line) => {
- fi.emit('data', `${line}\r`);
- fi.emit('data', '\n');
- });
- rli.close();
- }
-
- // Emit one line event when the delay between \r and \n is
- // over the default crlfDelay but within the setting value.
- {
- const delay = 125;
- const [rli, fi] = getInterface({ terminal, crlfDelay });
- let callCount = 0;
- rli.on('line', () => callCount++);
- fi.emit('data', '\r');
- setTimeout(common.mustCall(() => {
- fi.emit('data', '\n');
- assert.strictEqual(callCount, 1);
- rli.close();
- }), delay);
- }
-});
-
-// Ensure that the _wordLeft method works even for large input
-{
- const input = new Readable({
- read() {
- this.push('\x1B[1;5D'); // CTRL + Left
- this.push(null);
- },
- });
- const output = new Writable({
- write: common.mustCall((data, encoding, cb) => {
- assert.strictEqual(rl.cursor, rl.line.length - 1);
- cb();
- }),
- });
- const rl = new readline.createInterface({
- input,
- output,
- terminal: true,
- });
- rl.line = `a${' '.repeat(1e6)}a`;
- rl.cursor = rl.line.length;
-}
-
-// FIXME(bartlomieju): these tests depend on "event_target" module
-// {
-// const fi = new FakeInput();
-// const signal = AbortSignal.abort();
-
-// const rl = readline.createInterface({
-// input: fi,
-// output: fi,
-// signal,
-// });
-// rl.on('close', common.mustCall());
-// assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
-// }
-
-// {
-// const fi = new FakeInput();
-// const ac = new AbortController();
-// const { signal } = ac;
-// const rl = readline.createInterface({
-// input: fi,
-// output: fi,
-// signal,
-// });
-// assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
-// rl.on('close', common.mustCall());
-// ac.abort();
-// assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
-// }
-
-// {
-// const fi = new FakeInput();
-// const ac = new AbortController();
-// const { signal } = ac;
-// const rl = readline.createInterface({
-// input: fi,
-// output: fi,
-// signal,
-// });
-// assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
-// rl.close();
-// assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
-// }
-
-{
- // Constructor throws if signal is not an abort signal
- assert.throws(() => {
- readline.createInterface({
- input: new FakeInput(),
- signal: {},
- });
- }, {
- name: 'TypeError',
- code: 'ERR_INVALID_ARG_TYPE'
- });
-}