Skip to content

Commit 3cdf8ec

Browse files
dario-piotrowiczRafaelGSS
authored andcommitted
test: add tests for REPL custom evals
PR-URL: #57691 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent e567952 commit 3cdf8ec

File tree

3 files changed

+135
-34
lines changed

3 files changed

+135
-34
lines changed

lib/repl.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,6 @@ function REPLServer(prompt,
302302
options.useColors = shouldColorize(options.output);
303303
}
304304

305-
// TODO(devsnek): Add a test case for custom eval functions.
306305
const preview = options.terminal &&
307306
(options.preview !== undefined ? !!options.preview : !eval_);
308307

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use strict';
2+
3+
require('../common');
4+
const ArrayStream = require('../common/arraystream');
5+
const assert = require('assert');
6+
const { describe, it } = require('node:test');
7+
8+
const repl = require('repl');
9+
10+
function getReplOutput(input, replOptions, run = true) {
11+
const inputStream = new ArrayStream();
12+
const outputStream = new ArrayStream();
13+
14+
repl.start({
15+
input: inputStream,
16+
output: outputStream,
17+
...replOptions,
18+
});
19+
20+
let output = '';
21+
outputStream.write = (chunk) => (output += chunk);
22+
23+
inputStream.emit('data', input);
24+
25+
if (run) {
26+
inputStream.run(['']);
27+
}
28+
29+
return output;
30+
}
31+
32+
describe('repl with custom eval', { concurrency: true }, () => {
33+
it('uses the custom eval function as expected', () => {
34+
const output = getReplOutput('Convert this to upper case', {
35+
terminal: true,
36+
eval: (code, _ctx, _replRes, cb) => cb(null, code.toUpperCase()),
37+
});
38+
assert.match(
39+
output,
40+
/Convert this to upper case\r\n'CONVERT THIS TO UPPER CASE\\n'/
41+
);
42+
});
43+
44+
it('surfaces errors as expected', () => {
45+
const output = getReplOutput('Convert this to upper case', {
46+
terminal: true,
47+
eval: (_code, _ctx, _replRes, cb) => cb(new Error('Testing Error')),
48+
});
49+
assert.match(output, /Uncaught Error: Testing Error\n/);
50+
});
51+
52+
it('provides a repl context to the eval callback', async () => {
53+
const context = await new Promise((resolve) => {
54+
const r = repl.start({
55+
eval: (_cmd, context) => resolve(context),
56+
});
57+
r.context = { foo: 'bar' };
58+
r.write('\n.exit\n');
59+
});
60+
assert.strictEqual(context.foo, 'bar');
61+
});
62+
63+
it('provides the global context to the eval callback', async () => {
64+
const context = await new Promise((resolve) => {
65+
const r = repl.start({
66+
useGlobal: true,
67+
eval: (_cmd, context) => resolve(context),
68+
});
69+
global.foo = 'global_foo';
70+
r.write('\n.exit\n');
71+
});
72+
73+
assert.strictEqual(context.foo, 'global_foo');
74+
delete global.foo;
75+
});
76+
77+
it('inherits variables from the global context but does not use it afterwords if `useGlobal` is false', async () => {
78+
global.bar = 'global_bar';
79+
const context = await new Promise((resolve) => {
80+
const r = repl.start({
81+
useGlobal: false,
82+
eval: (_cmd, context) => resolve(context),
83+
});
84+
global.baz = 'global_baz';
85+
r.write('\n.exit\n');
86+
});
87+
88+
assert.strictEqual(context.bar, 'global_bar');
89+
assert.notStrictEqual(context.baz, 'global_baz');
90+
delete global.bar;
91+
delete global.baz;
92+
});
93+
94+
/**
95+
* Default preprocessor transforms
96+
* function f() {} to
97+
* var f = function f() {}
98+
* This test ensures that original input is preserved.
99+
* Reference: https://github.com/nodejs/node/issues/9743
100+
*/
101+
it('preserves the original input', async () => {
102+
const cmd = await new Promise((resolve) => {
103+
const r = repl.start({
104+
eval: (cmd) => resolve(cmd),
105+
});
106+
r.write('function f() {}\n.exit\n');
107+
});
108+
assert.strictEqual(cmd, 'function f() {}\n');
109+
});
110+
111+
it("doesn't show previews by default", () => {
112+
const input = "'Hello custom' + ' eval World!'";
113+
const output = getReplOutput(input, {
114+
terminal: true,
115+
eval: (code, _ctx, _replRes, cb) => cb(null, eval(code)),
116+
}, false);
117+
assert.strictEqual(output, input);
118+
assert.doesNotMatch(output, /Hello custom eval World!/);
119+
});
120+
121+
it('does show previews if `preview` is set to `true`', () => {
122+
const input = "'Hello custom' + ' eval World!'";
123+
const output = getReplOutput(input, {
124+
terminal: true,
125+
eval: (code, _ctx, _replRes, cb) => cb(null, eval(code)),
126+
preview: true,
127+
}, false);
128+
129+
const escapedInput = input.replace(/\+/g, '\\+'); // TODO: migrate to `RegExp.escape` when it's available.
130+
assert.match(
131+
output,
132+
new RegExp(`${escapedInput}\n// 'Hello custom eval World!'`)
133+
);
134+
});
135+
});

test/parallel/test-repl-eval.js

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)