• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2
3
4import '../common/index.mjs';
5import assert from 'assert';
6import { Readline } from 'readline/promises';
7import { setImmediate } from 'timers/promises';
8import { Writable } from 'stream';
9
10import utils from 'internal/readline/utils';
11const { CSI } = utils;
12
13const INVALID_ARG = {
14  name: 'TypeError',
15  code: 'ERR_INVALID_ARG_TYPE',
16};
17
18class TestWritable extends Writable {
19  data = '';
20  _write(chunk, encoding, callback) {
21    this.data += chunk.toString();
22    callback();
23  }
24}
25
26[
27  undefined, null,
28  0, 1, 1n, 1.1, NaN, Infinity,
29  true, false,
30  Symbol(),
31  '', '1',
32  [], {}, () => {},
33].forEach((arg) =>
34  assert.throws(() => new Readline(arg), INVALID_ARG)
35);
36
37{
38  const writable = new TestWritable();
39  const readline = new Readline(writable);
40
41  await readline.clearScreenDown().commit();
42  assert.deepStrictEqual(writable.data, CSI.kClearScreenDown);
43  await readline.clearScreenDown().commit();
44
45  writable.data = '';
46  await readline.clearScreenDown().rollback();
47  assert.strictEqual(writable.data, '');
48
49  writable.data = '';
50  await readline.clearLine(-1).commit();
51  assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning);
52
53  writable.data = '';
54  await readline.clearLine(1).commit();
55  assert.deepStrictEqual(writable.data, CSI.kClearToLineEnd);
56
57  writable.data = '';
58  await readline.clearLine(0).commit();
59  assert.deepStrictEqual(writable.data, CSI.kClearLine);
60
61  writable.data = '';
62  await readline.clearLine(-1).commit();
63  assert.deepStrictEqual(writable.data, CSI.kClearToLineBeginning);
64
65  await readline.clearLine(0, null).commit();
66
67  // Nothing is written when moveCursor 0, 0
68  for (const set of
69    [
70      [0, 0, ''],
71      [1, 0, '\x1b[1C'],
72      [-1, 0, '\x1b[1D'],
73      [0, 1, '\x1b[1B'],
74      [0, -1, '\x1b[1A'],
75      [1, 1, '\x1b[1C\x1b[1B'],
76      [-1, 1, '\x1b[1D\x1b[1B'],
77      [-1, -1, '\x1b[1D\x1b[1A'],
78      [1, -1, '\x1b[1C\x1b[1A'],
79    ]) {
80    writable.data = '';
81    await readline.moveCursor(set[0], set[1]).commit();
82    assert.deepStrictEqual(writable.data, set[2]);
83    writable.data = '';
84    await readline.moveCursor(set[0], set[1]).commit();
85    assert.deepStrictEqual(writable.data, set[2]);
86  }
87
88
89  await readline.moveCursor(1, 1, null).commit();
90
91  writable.data = '';
92  [
93    undefined, null,
94    true, false,
95    Symbol(),
96    '', '1',
97    [], {}, () => {},
98  ].forEach((arg) =>
99    assert.throws(() => readline.cursorTo(arg), INVALID_ARG)
100  );
101  assert.strictEqual(writable.data, '');
102
103  writable.data = '';
104  assert.throws(() => readline.cursorTo('a', 'b'), INVALID_ARG);
105  assert.strictEqual(writable.data, '');
106
107  writable.data = '';
108  assert.throws(() => readline.cursorTo('a', 1), INVALID_ARG);
109  assert.strictEqual(writable.data, '');
110
111  writable.data = '';
112  assert.throws(() => readline.cursorTo(1, 'a'), INVALID_ARG);
113  assert.strictEqual(writable.data, '');
114
115  writable.data = '';
116  await readline.cursorTo(1).commit();
117  assert.strictEqual(writable.data, '\x1b[2G');
118
119  writable.data = '';
120  await readline.cursorTo(1, 2).commit();
121  assert.strictEqual(writable.data, '\x1b[3;2H');
122
123  writable.data = '';
124  await readline.cursorTo(1, 2).commit();
125  assert.strictEqual(writable.data, '\x1b[3;2H');
126
127  writable.data = '';
128  await readline.cursorTo(1).cursorTo(1, 2).commit();
129  assert.strictEqual(writable.data, '\x1b[2G\x1b[3;2H');
130
131  writable.data = '';
132  await readline.cursorTo(1).commit();
133  assert.strictEqual(writable.data, '\x1b[2G');
134
135  // Verify that cursorTo() rejects if x or y is NaN.
136  [1.1, NaN, Infinity].forEach((arg) => {
137    assert.throws(() => readline.cursorTo(arg), {
138      code: 'ERR_OUT_OF_RANGE',
139      name: 'RangeError',
140    });
141  });
142
143  [1.1, NaN, Infinity].forEach((arg) => {
144    assert.throws(() => readline.cursorTo(1, arg), {
145      code: 'ERR_OUT_OF_RANGE',
146      name: 'RangeError',
147    });
148  });
149
150  assert.throws(() => readline.cursorTo(NaN, NaN), {
151    code: 'ERR_OUT_OF_RANGE',
152    name: 'RangeError',
153  });
154}
155
156{
157  const error = new Error();
158  const writable = new class extends Writable {
159    _write() { throw error; }
160  }();
161  const readline = new Readline(writable);
162
163  await assert.rejects(readline.cursorTo(1).commit(), error);
164}
165
166{
167  const writable = new TestWritable();
168  const readline = new Readline(writable, { autoCommit: true });
169
170  await readline.clearScreenDown();
171  await setImmediate(); // Wait for next tick as auto commit is asynchronous.
172  assert.deepStrictEqual(writable.data, CSI.kClearScreenDown);
173}
174
175{
176  const writable = new TestWritable();
177  const readline = new Readline(writable, { autoCommit: true });
178  for (const [dir, data] of
179    [
180      [-1, CSI.kClearToLineBeginning],
181      [1, CSI.kClearToLineEnd],
182      [0, CSI.kClearLine],
183    ]) {
184    writable.data = '';
185    readline.clearLine(dir);
186    await setImmediate(); // Wait for next tick as auto commit is asynchronous.
187    assert.deepStrictEqual(writable.data, data);
188  }
189}
190
191{
192  const writable = new TestWritable();
193  const readline = new Readline(writable, { autoCommit: true });
194  for (const [x, y, data] of
195    [
196      [0, 0, ''],
197      [1, 0, '\x1b[1C'],
198      [-1, 0, '\x1b[1D'],
199      [0, 1, '\x1b[1B'],
200      [0, -1, '\x1b[1A'],
201      [1, 1, '\x1b[1C\x1b[1B'],
202      [-1, 1, '\x1b[1D\x1b[1B'],
203      [-1, -1, '\x1b[1D\x1b[1A'],
204      [1, -1, '\x1b[1C\x1b[1A'],
205    ]) {
206    writable.data = '';
207    readline.moveCursor(x, y);
208    await setImmediate(); // Wait for next tick as auto commit is asynchronous.
209    assert.deepStrictEqual(writable.data, data);
210  }
211}
212
213{
214  const writable = new TestWritable();
215  const readline = new Readline(writable, { autoCommit: true });
216  for (const [x, y, data] of
217    [
218      [1, undefined, '\x1b[2G'],
219      [1, 2, '\x1b[3;2H'],
220    ]) {
221    writable.data = '';
222    readline.cursorTo(x, y);
223    await setImmediate(); // Wait for next tick as auto commit is asynchronous.
224    assert.deepStrictEqual(writable.data, data);
225  }
226}
227