Skip to content

Commit f30b771

Browse files
BridgeARMylesBorins
authored andcommitted
test: add multiple repl preview tests
This improves the coverage for the preview feature signficantly. Quite a few edge cases get testet here to prevent regressions. PR-URL: #30907 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 02f3fe4 commit f30b771

File tree

3 files changed

+206
-13
lines changed

3 files changed

+206
-13
lines changed

test/parallel/test-repl-history-navigation.js

+203-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const REPL = require('internal/repl');
88
const assert = require('assert');
99
const fs = require('fs');
1010
const path = require('path');
11+
const { inspect } = require('util');
1112

1213
const tmpdir = require('../common/tmpdir');
1314
tmpdir.refresh();
@@ -17,6 +18,7 @@ const defaultHistoryPath = path.join(tmpdir.path, '.node_repl_history');
1718
// Create an input stream specialized for testing an array of actions
1819
class ActionStream extends stream.Stream {
1920
run(data) {
21+
let reallyWait = true;
2022
const _iter = data[Symbol.iterator]();
2123
const doAction = () => {
2224
const next = _iter.next();
@@ -32,24 +34,34 @@ class ActionStream extends stream.Stream {
3234
} else {
3335
this.emit('data', `${action}`);
3436
}
35-
setImmediate(doAction);
37+
if (action === WAIT && reallyWait) {
38+
setTimeout(doAction, common.platformTimeout(50));
39+
reallyWait = false;
40+
} else {
41+
setImmediate(doAction);
42+
}
3643
};
37-
setImmediate(doAction);
44+
doAction();
3845
}
3946
resume() {}
4047
pause() {}
4148
}
4249
ActionStream.prototype.readable = true;
4350

44-
4551
// Mock keys
4652
const ENTER = { name: 'enter' };
4753
const UP = { name: 'up' };
4854
const DOWN = { name: 'down' };
4955
const LEFT = { name: 'left' };
56+
const RIGHT = { name: 'right' };
5057
const DELETE = { name: 'delete' };
58+
const BACKSPACE = { name: 'backspace' };
59+
const WORD_LEFT = { name: 'left', ctrl: true };
60+
const WORD_RIGHT = { name: 'right', ctrl: true };
61+
const GO_TO_END = { name: 'end' };
5162

5263
const prompt = '> ';
64+
const WAIT = '€';
5365

5466
const prev = process.features.inspector;
5567

@@ -91,6 +103,162 @@ const tests = [
91103
' 2025, 2116, 2209, ...',
92104
prompt].filter((e) => typeof e === 'string'),
93105
clean: true
106+
},
107+
{
108+
env: { NODE_REPL_HISTORY: defaultHistoryPath },
109+
skip: !process.features.inspector,
110+
test: [
111+
`const ${'veryLongName'.repeat(30)} = 'I should not be previewed'`,
112+
ENTER,
113+
'const e = new RangeError("visible\\ninvisible")',
114+
ENTER,
115+
'e',
116+
ENTER,
117+
'veryLongName'.repeat(30),
118+
ENTER,
119+
`${'\x1B[90m \x1B[39m'.repeat(235)} fun`,
120+
ENTER,
121+
`${' '.repeat(236)} fun`,
122+
ENTER
123+
],
124+
expected: [],
125+
clean: false
126+
},
127+
{
128+
env: { NODE_REPL_HISTORY: defaultHistoryPath },
129+
columns: 250,
130+
skip: !process.features.inspector,
131+
test: [
132+
UP,
133+
UP,
134+
UP,
135+
UP,
136+
BACKSPACE
137+
],
138+
expected: [
139+
prompt,
140+
// This exceeds the maximum columns (250):
141+
// Whitespace + prompt + ' // '.length + 'function'.length
142+
// 236 + 2 + 4 + 8
143+
`${prompt}${' '.repeat(236)} fun`,
144+
`${prompt}${' '.repeat(235)} fun`,
145+
' // ction',
146+
' // ction',
147+
`${prompt}${'veryLongName'.repeat(30)}`,
148+
`${prompt}e`,
149+
'\n// RangeError: visible',
150+
prompt
151+
],
152+
clean: true
153+
},
154+
{
155+
env: { NODE_REPL_HISTORY: defaultHistoryPath },
156+
showEscapeCodes: true,
157+
skip: !process.features.inspector,
158+
test: [
159+
'fun',
160+
RIGHT,
161+
BACKSPACE,
162+
LEFT,
163+
LEFT,
164+
'A',
165+
BACKSPACE,
166+
GO_TO_END,
167+
BACKSPACE,
168+
WORD_LEFT,
169+
WORD_RIGHT,
170+
ENTER
171+
],
172+
// C = Cursor forward
173+
// D = Cursor back
174+
// G = Cursor to column n
175+
// J = Erase in screen
176+
// K = Erase in line
177+
expected: [
178+
// 0.
179+
// 'f'
180+
'\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'f',
181+
// 'u'
182+
'u', ' // nction', '\x1B[5G',
183+
// 'n' - Cleanup
184+
'\x1B[0K',
185+
'n', ' // ction', '\x1B[6G',
186+
// 1. Right. Cleanup
187+
'\x1B[0K',
188+
'ction',
189+
// 2. Backspace. Refresh
190+
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
191+
// Autocomplete and refresh?
192+
' // n', '\x1B[10G', ' // n', '\x1B[10G',
193+
// 3. Left. Cleanup
194+
'\x1B[0K',
195+
'\x1B[1D', '\x1B[10G', ' // n', '\x1B[9G',
196+
// 4. Left. Cleanup
197+
'\x1B[10G', '\x1B[0K', '\x1B[9G',
198+
'\x1B[1D', '\x1B[10G', ' // n', '\x1B[8G',
199+
// 5. 'A' - Cleanup
200+
'\x1B[10G', '\x1B[0K', '\x1B[8G',
201+
// Refresh
202+
'\x1B[1G', '\x1B[0J', `${prompt}functAio`, '\x1B[9G',
203+
// 6. Backspace. Refresh
204+
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[8G', '\x1B[10G', ' // n',
205+
'\x1B[8G', '\x1B[10G', ' // n',
206+
'\x1B[8G', '\x1B[10G',
207+
// 7. Go to end. Cleanup
208+
'\x1B[0K', '\x1B[8G', '\x1B[2C',
209+
'n',
210+
// 8. Backspace. Refresh
211+
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
212+
// Autocomplete
213+
' // n', '\x1B[10G', ' // n', '\x1B[10G',
214+
// 9. Word left. Cleanup
215+
'\x1B[0K', '\x1B[7D', '\x1B[10G', ' // n', '\x1B[3G', '\x1B[10G',
216+
// 10. Word right. Cleanup
217+
'\x1B[0K', '\x1B[3G', '\x1B[7C', ' // n', '\x1B[10G',
218+
'\x1B[0K',
219+
],
220+
clean: true
221+
},
222+
{
223+
// Check that the completer ignores completions that are outdated.
224+
env: { NODE_REPL_HISTORY: defaultHistoryPath },
225+
completer(line, callback) {
226+
if (line.endsWith(WAIT)) {
227+
setTimeout(
228+
callback,
229+
common.platformTimeout(40),
230+
null,
231+
[[`${WAIT}WOW`], line]
232+
);
233+
} else {
234+
callback(null, [[' Always visible'], line]);
235+
}
236+
},
237+
skip: !process.features.inspector,
238+
test: [
239+
WAIT, // The first call is awaited before new input is triggered!
240+
BACKSPACE,
241+
's',
242+
BACKSPACE,
243+
WAIT, // The second call is not awaited. It won't trigger the preview.
244+
BACKSPACE,
245+
's',
246+
BACKSPACE
247+
],
248+
expected: [
249+
prompt,
250+
WAIT,
251+
' // WOW',
252+
prompt,
253+
's',
254+
' // Always visible',
255+
prompt,
256+
WAIT,
257+
prompt,
258+
's',
259+
' // Always visible',
260+
],
261+
clean: true
94262
}
95263
];
96264
const numtests = tests.length;
@@ -112,29 +280,47 @@ function runTest() {
112280
const opts = tests.shift();
113281
if (!opts) return; // All done
114282

115-
const env = opts.env;
116-
const test = opts.test;
117-
const expected = opts.expected;
283+
const { expected, skip } = opts;
284+
285+
// Test unsupported on platform.
286+
if (skip) {
287+
setImmediate(runTestWrap, true);
288+
return;
289+
}
290+
291+
const lastChunks = [];
118292

119-
REPL.createInternalRepl(env, {
293+
REPL.createInternalRepl(opts.env, {
120294
input: new ActionStream(),
121295
output: new stream.Writable({
122296
write(chunk, _, next) {
123297
const output = chunk.toString();
124298

125-
if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) {
299+
if (!opts.showEscapeCodes &&
300+
output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) {
126301
return next();
127302
}
128303

304+
lastChunks.push(inspect(output));
305+
129306
if (expected.length) {
130-
assert.strictEqual(output, expected[0]);
307+
try {
308+
assert.strictEqual(output, expected[0]);
309+
} catch (e) {
310+
console.error(`Failed test # ${numtests - tests.length}`);
311+
console.error('Last outputs: ' + inspect(lastChunks, {
312+
breakLength: 5, colors: true
313+
}));
314+
throw e;
315+
}
131316
expected.shift();
132317
}
133318

134319
next();
135320
}
136321
}),
137-
prompt: prompt,
322+
completer: opts.completer,
323+
prompt,
138324
useColors: false,
139325
terminal: true
140326
}, function(err, repl) {
@@ -153,7 +339,13 @@ function runTest() {
153339
setImmediate(runTestWrap, true);
154340
});
155341

156-
repl.inputStream.run(test);
342+
if (opts.columns) {
343+
Object.defineProperty(repl, 'columns', {
344+
value: opts.columns,
345+
enumerable: true
346+
});
347+
}
348+
repl.inputStream.run(opts.test);
157349
});
158350
}
159351

test/parallel/test-repl-preview.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const common = require('../common');
44
const ArrayStream = require('../common/arraystream');
55
const assert = require('assert');
6-
const Repl = require('repl');
6+
const { REPLServer } = require('repl');
77

88
common.skipIfInspectorDisabled();
99

@@ -52,7 +52,7 @@ function runAndWait(cmds, repl) {
5252
}
5353

5454
async function tests(options) {
55-
const repl = Repl.start({
55+
const repl = REPLServer({
5656
prompt: PROMPT,
5757
stream: new REPLStream(),
5858
ignoreUndefined: true,

test/root.status

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ parallel/test-next-tick-fixed-queue-regression: SLOW
6666
parallel/test-npm-install: SLOW
6767
parallel/test-preload: SLOW
6868
parallel/test-repl: SLOW
69+
parallel/test-repl-history-navigation.js: SLOW
6970
parallel/test-repl-tab-complete: SLOW
7071
parallel/test-repl-top-level-await: SLOW
7172
parallel/test-stdio-pipe-access: SLOW

0 commit comments

Comments
 (0)