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