repl: refactor tests to not rely on timing
Tests relying on synchronous timing have been migrated to use events. PR-URL: https://github.com/nodejs/node/pull/17828 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
parent
6007a9cc0e
commit
de848ac1e0
@ -8,7 +8,7 @@ const os = require('os');
|
||||
const util = require('util');
|
||||
const debug = util.debuglog('repl');
|
||||
module.exports = Object.create(REPL);
|
||||
module.exports.createInternalRepl = createRepl;
|
||||
module.exports.createInternalRepl = createInternalRepl;
|
||||
|
||||
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
|
||||
// The debounce is to guard against code pasted into the REPL.
|
||||
@ -19,7 +19,7 @@ function _writeToOutput(repl, message) {
|
||||
repl._refreshLine();
|
||||
}
|
||||
|
||||
function createRepl(env, opts, cb) {
|
||||
function createInternalRepl(env, opts, cb) {
|
||||
if (typeof opts === 'function') {
|
||||
cb = opts;
|
||||
opts = null;
|
||||
|
79
lib/repl.js
79
lib/repl.js
@ -109,6 +109,11 @@ writer.options = Object.assign({},
|
||||
|
||||
exports._builtinLibs = internalModule.builtinLibs;
|
||||
|
||||
const sep = '\u0000\u0000\u0000';
|
||||
const regExMatcher = new RegExp(`^${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
|
||||
`${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
|
||||
`${sep}(.*)$`);
|
||||
|
||||
function REPLServer(prompt,
|
||||
stream,
|
||||
eval_,
|
||||
@ -149,6 +154,7 @@ function REPLServer(prompt,
|
||||
}
|
||||
|
||||
var self = this;
|
||||
replMap.set(self, self);
|
||||
|
||||
self._domain = dom || domain.create();
|
||||
self.useGlobal = !!useGlobal;
|
||||
@ -165,41 +171,6 @@ function REPLServer(prompt,
|
||||
self.rli = this;
|
||||
|
||||
const savedRegExMatches = ['', '', '', '', '', '', '', '', '', ''];
|
||||
const sep = '\u0000\u0000\u0000';
|
||||
const regExMatcher = new RegExp(`^${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
|
||||
`${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
|
||||
`${sep}(.*)$`);
|
||||
|
||||
eval_ = eval_ || defaultEval;
|
||||
|
||||
// Pause taking in new input, and store the keys in a buffer.
|
||||
const pausedBuffer = [];
|
||||
let paused = false;
|
||||
function pause() {
|
||||
paused = true;
|
||||
}
|
||||
function unpause() {
|
||||
if (!paused) return;
|
||||
paused = false;
|
||||
let entry;
|
||||
while (entry = pausedBuffer.shift()) {
|
||||
const [type, payload] = entry;
|
||||
switch (type) {
|
||||
case 'key': {
|
||||
const [d, key] = payload;
|
||||
self._ttyWrite(d, key);
|
||||
break;
|
||||
}
|
||||
case 'close':
|
||||
self.emit('exit');
|
||||
break;
|
||||
}
|
||||
if (paused) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function defaultEval(code, context, file, cb) {
|
||||
var err, result, script, wrappedErr;
|
||||
var wrappedCmd = false;
|
||||
@ -331,7 +302,6 @@ function REPLServer(prompt,
|
||||
|
||||
if (awaitPromise && !err) {
|
||||
let sigintListener;
|
||||
pause();
|
||||
let promise = result;
|
||||
if (self.breakEvalOnSigint) {
|
||||
const interrupt = new Promise((resolve, reject) => {
|
||||
@ -350,7 +320,6 @@ function REPLServer(prompt,
|
||||
prioritizedSigintQueue.delete(sigintListener);
|
||||
|
||||
finishExecution(undefined, result);
|
||||
unpause();
|
||||
}, (err) => {
|
||||
// Remove prioritized SIGINT listener if it was not called.
|
||||
prioritizedSigintQueue.delete(sigintListener);
|
||||
@ -360,7 +329,6 @@ function REPLServer(prompt,
|
||||
Object.defineProperty(err, 'stack', { value: '' });
|
||||
}
|
||||
|
||||
unpause();
|
||||
if (err && process.domain) {
|
||||
debug('not recoverable, send to domain');
|
||||
process.domain.emit('error', err);
|
||||
@ -377,6 +345,36 @@ function REPLServer(prompt,
|
||||
}
|
||||
}
|
||||
|
||||
eval_ = eval_ || defaultEval;
|
||||
|
||||
// Pause taking in new input, and store the keys in a buffer.
|
||||
const pausedBuffer = [];
|
||||
let paused = false;
|
||||
function pause() {
|
||||
paused = true;
|
||||
}
|
||||
function unpause() {
|
||||
if (!paused) return;
|
||||
paused = false;
|
||||
let entry;
|
||||
while (entry = pausedBuffer.shift()) {
|
||||
const [type, payload] = entry;
|
||||
switch (type) {
|
||||
case 'key': {
|
||||
const [d, key] = payload;
|
||||
self._ttyWrite(d, key);
|
||||
break;
|
||||
}
|
||||
case 'close':
|
||||
self.emit('exit');
|
||||
break;
|
||||
}
|
||||
if (paused) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.eval = self._domain.bind(eval_);
|
||||
|
||||
self._domain.on('error', function debugDomainError(e) {
|
||||
@ -405,6 +403,7 @@ function REPLServer(prompt,
|
||||
top.clearBufferedCommand();
|
||||
top.lines.level = [];
|
||||
top.displayPrompt();
|
||||
unpause();
|
||||
});
|
||||
|
||||
if (!input && !output) {
|
||||
@ -593,6 +592,7 @@ function REPLServer(prompt,
|
||||
const evalCmd = self[kBufferedCommandSymbol] + cmd + '\n';
|
||||
|
||||
debug('eval %j', evalCmd);
|
||||
pause();
|
||||
self.eval(evalCmd, self.context, 'repl', finish);
|
||||
|
||||
function finish(e, ret) {
|
||||
@ -605,6 +605,7 @@ function REPLServer(prompt,
|
||||
'(Press Control-D to exit.)\n');
|
||||
self.clearBufferedCommand();
|
||||
self.displayPrompt();
|
||||
unpause();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -642,6 +643,7 @@ function REPLServer(prompt,
|
||||
|
||||
// Display prompt again
|
||||
self.displayPrompt();
|
||||
unpause();
|
||||
}
|
||||
});
|
||||
|
||||
@ -724,7 +726,6 @@ exports.start = function(prompt,
|
||||
ignoreUndefined,
|
||||
replMode);
|
||||
if (!exports.repl) exports.repl = repl;
|
||||
replMap.set(repl, repl);
|
||||
return repl;
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,22 @@ const repl = require('repl');
|
||||
common.globalCheck = false;
|
||||
|
||||
const putIn = new common.ArrayStream();
|
||||
repl.start('', putIn, null, true);
|
||||
|
||||
|
||||
const replserver = repl.start('', putIn, null, true);
|
||||
const callbacks = [];
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
const expected = callbacks.shift();
|
||||
return $eval.call(this, code, context, file, (...args) => {
|
||||
try {
|
||||
expected(cb, ...args);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
test1();
|
||||
|
||||
@ -48,6 +63,11 @@ function test1() {
|
||||
}
|
||||
};
|
||||
assert(!gotWrite);
|
||||
callbacks.push(common.mustCall((cb, err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, require('fs'));
|
||||
cb(err, result);
|
||||
}));
|
||||
putIn.run(['fs']);
|
||||
assert(gotWrite);
|
||||
}
|
||||
@ -66,6 +86,11 @@ function test2() {
|
||||
const val = {};
|
||||
global.url = val;
|
||||
assert(!gotWrite);
|
||||
callbacks.push(common.mustCall((cb, err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, val);
|
||||
cb(err, result);
|
||||
}));
|
||||
putIn.run(['url']);
|
||||
assert(gotWrite);
|
||||
}
|
||||
|
@ -27,40 +27,57 @@ function testContext(repl) {
|
||||
repl.close();
|
||||
}
|
||||
|
||||
testContextSideEffects(repl.start({ input: stream, output: stream }));
|
||||
const replserver = repl.start({ input: stream, output: stream });
|
||||
const callbacks = [];
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
const expected = callbacks.shift();
|
||||
return $eval.call(this, code, context, file, (...args) => {
|
||||
try {
|
||||
expected(cb, ...args);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
testContextSideEffects(replserver);
|
||||
|
||||
function testContextSideEffects(server) {
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 0);
|
||||
|
||||
// an assignment to '_' in the repl server
|
||||
callbacks.push(common.mustCall((cb, ...args) => {
|
||||
assert.ok(server.underscoreAssigned);
|
||||
assert.strictEqual(server.last, 500);
|
||||
cb(...args);
|
||||
assert.strictEqual(server.lines.length, 1);
|
||||
assert.strictEqual(server.lines[0], '_ = 500;');
|
||||
|
||||
// use the server to create a new context
|
||||
const context = server.createContext();
|
||||
|
||||
// ensure that creating a new context does not
|
||||
// have side effects on the server
|
||||
assert.ok(server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 1);
|
||||
assert.strictEqual(server.lines[0], '_ = 500;');
|
||||
assert.strictEqual(server.last, 500);
|
||||
|
||||
// reset the server context
|
||||
server.resetContext();
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 0);
|
||||
|
||||
// ensure that assigning to '_' in the new context
|
||||
// does not change the value in our server.
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
vm.runInContext('_ = 1000;\n', context);
|
||||
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 0);
|
||||
server.close();
|
||||
}));
|
||||
server.write('_ = 500;\n');
|
||||
assert.ok(server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 1);
|
||||
assert.strictEqual(server.lines[0], '_ = 500;');
|
||||
assert.strictEqual(server.last, 500);
|
||||
|
||||
// use the server to create a new context
|
||||
const context = server.createContext();
|
||||
|
||||
// ensure that creating a new context does not
|
||||
// have side effects on the server
|
||||
assert.ok(server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 1);
|
||||
assert.strictEqual(server.lines[0], '_ = 500;');
|
||||
assert.strictEqual(server.last, 500);
|
||||
|
||||
// reset the server context
|
||||
server.resetContext();
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 0);
|
||||
|
||||
// ensure that assigning to '_' in the new context
|
||||
// does not change the value in our server.
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
vm.runInContext('_ = 1000;\n', context);
|
||||
|
||||
assert.ok(!server.underscoreAssigned);
|
||||
assert.strictEqual(server.lines.length, 0);
|
||||
server.close();
|
||||
}
|
||||
|
@ -21,10 +21,7 @@
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
let terminalExit = 0;
|
||||
let regularExit = 0;
|
||||
|
||||
// Create a dummy stream that does nothing
|
||||
const stream = new common.ArrayStream();
|
||||
@ -41,11 +38,10 @@ function testTerminalMode() {
|
||||
stream.emit('data', '\u0004');
|
||||
});
|
||||
|
||||
r1.on('exit', function() {
|
||||
r1.on('exit', common.mustCall(function() {
|
||||
// should be fired from the simulated ^D keypress
|
||||
terminalExit++;
|
||||
testRegularMode();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function testRegularMode() {
|
||||
@ -59,17 +55,11 @@ function testRegularMode() {
|
||||
stream.emit('end');
|
||||
});
|
||||
|
||||
r2.on('exit', function() {
|
||||
r2.on('exit', common.mustCall(function() {
|
||||
// should be fired from the simulated 'end' event
|
||||
regularExit++;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(terminalExit, 1);
|
||||
assert.strictEqual(regularExit, 1);
|
||||
});
|
||||
|
||||
|
||||
// start
|
||||
testTerminalMode();
|
||||
|
@ -3,21 +3,27 @@ const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
|
||||
{
|
||||
const stream = new common.ArrayStream();
|
||||
const options = {
|
||||
eval: common.mustCall((cmd, context) => {
|
||||
assert.strictEqual(cmd, '.scope\n');
|
||||
assert.deepStrictEqual(context, { animal: 'Sterrance' });
|
||||
}),
|
||||
input: stream,
|
||||
output: stream,
|
||||
terminal: true
|
||||
};
|
||||
const exitTests = [];
|
||||
process.on('exit', () => {
|
||||
for (const test of exitTests) test();
|
||||
});
|
||||
const CONTEXT = { animal: 'Sterrance' };
|
||||
const stream = new common.ArrayStream();
|
||||
const options = {
|
||||
eval: common.mustCall((cmd, context) => {
|
||||
// need to escape the domain
|
||||
exitTests.push(common.mustCall(() => {
|
||||
assert.strictEqual(cmd, '.scope');
|
||||
assert.ok(context === CONTEXT);
|
||||
}));
|
||||
}),
|
||||
input: stream,
|
||||
output: stream,
|
||||
terminal: true
|
||||
};
|
||||
|
||||
const r = repl.start(options);
|
||||
r.context = { animal: 'Sterrance' };
|
||||
const r = repl.start(options);
|
||||
r.context = CONTEXT;
|
||||
|
||||
stream.emit('data', '\t');
|
||||
stream.emit('.exit\n');
|
||||
}
|
||||
stream.emit('data', '\t');
|
||||
stream.emit('.exit\n');
|
||||
|
@ -3,31 +3,31 @@ const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
|
||||
{
|
||||
let evalCalledWithExpectedArgs = false;
|
||||
const exitTests = [];
|
||||
process.on('exit', () => {
|
||||
for (const test of exitTests) test();
|
||||
});
|
||||
const options = {
|
||||
eval: common.mustCall((cmd, context) => {
|
||||
// Assertions here will not cause the test to exit with an error code
|
||||
// so set a boolean that is checked later instead.
|
||||
exitTests.push(common.mustCall(() => {
|
||||
assert.strictEqual(cmd, 'function f() {}\n');
|
||||
assert.strictEqual(context.foo, 'bar');
|
||||
}));
|
||||
})
|
||||
};
|
||||
|
||||
const options = {
|
||||
eval: common.mustCall((cmd, context) => {
|
||||
// Assertions here will not cause the test to exit with an error code
|
||||
// so set a boolean that is checked later instead.
|
||||
evalCalledWithExpectedArgs = (cmd === 'function f() {}\n' &&
|
||||
context.foo === 'bar');
|
||||
})
|
||||
};
|
||||
const r = repl.start(options);
|
||||
r.context = { foo: 'bar' };
|
||||
|
||||
const r = repl.start(options);
|
||||
r.context = { foo: 'bar' };
|
||||
|
||||
try {
|
||||
// Default preprocessor transforms
|
||||
// function f() {} to
|
||||
// var f = function f() {}
|
||||
// Test to ensure that original input is preserved.
|
||||
// Reference: https://github.com/nodejs/node/issues/9743
|
||||
r.write('function f() {}\n');
|
||||
} finally {
|
||||
r.write('.exit\n');
|
||||
}
|
||||
|
||||
assert(evalCalledWithExpectedArgs);
|
||||
try {
|
||||
// Default preprocessor transforms
|
||||
// function f() {} to
|
||||
// var f = function f() {}
|
||||
// Test to ensure that original input is preserved.
|
||||
// Reference: https://github.com/nodejs/node/issues/9743
|
||||
r.write('function f() {}\n');
|
||||
} finally {
|
||||
r.write('.exit\n');
|
||||
}
|
||||
|
@ -7,32 +7,47 @@ const stream = require('stream');
|
||||
|
||||
common.globalCheck = false;
|
||||
|
||||
const r = initRepl();
|
||||
const input = new stream();
|
||||
input.write = input.pause = input.resume = () => {};
|
||||
input.readable = true;
|
||||
|
||||
r.input.emit('data', 'function a() { return 42; } (1)\n');
|
||||
r.input.emit('data', 'a\n');
|
||||
r.input.emit('data', '.exit');
|
||||
const output = new stream();
|
||||
output.writable = true;
|
||||
output.accumulator = [];
|
||||
|
||||
const expected = '1\n[Function: a]\n';
|
||||
const got = r.output.accumulator.join('');
|
||||
assert.strictEqual(got, expected);
|
||||
output.write = (data) => output.accumulator.push(data);
|
||||
|
||||
function initRepl() {
|
||||
const input = new stream();
|
||||
input.write = input.pause = input.resume = () => {};
|
||||
input.readable = true;
|
||||
|
||||
const output = new stream();
|
||||
output.writable = true;
|
||||
output.accumulator = [];
|
||||
|
||||
output.write = (data) => output.accumulator.push(data);
|
||||
|
||||
return repl.start({
|
||||
input,
|
||||
output,
|
||||
useColors: false,
|
||||
terminal: false,
|
||||
prompt: ''
|
||||
const replserver = repl.start({
|
||||
input,
|
||||
output,
|
||||
useColors: false,
|
||||
terminal: false,
|
||||
prompt: ''
|
||||
});
|
||||
const callbacks = [];
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
const expected = callbacks.shift();
|
||||
return $eval.call(this, code, context, file, (...args) => {
|
||||
try {
|
||||
expected(...args);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
cb(...args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, 1);
|
||||
}));
|
||||
replserver.input.emit('data', 'function a() { return 42; } (1)\n');
|
||||
callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof result, 'function');
|
||||
assert.strictEqual(result.toString(), 'function a() { return 42; }');
|
||||
}));
|
||||
replserver.input.emit('data', 'a\n');
|
||||
replserver.input.emit('data', '.exit');
|
||||
|
@ -36,5 +36,7 @@ const r = repl.start({
|
||||
});
|
||||
|
||||
r.write(`${command}\n`);
|
||||
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
|
||||
r.on('exit', common.mustCall(() => {
|
||||
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
|
||||
}));
|
||||
r.close();
|
||||
|
@ -19,35 +19,49 @@ tests.forEach(function(test) {
|
||||
function testSloppyMode() {
|
||||
const cli = initRepl(repl.REPL_MODE_SLOPPY);
|
||||
|
||||
cli.callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, 3);
|
||||
}));
|
||||
cli.input.emit('data', 'x = 3\n');
|
||||
assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> ');
|
||||
cli.output.accumulator.length = 0;
|
||||
|
||||
cli.callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, undefined);
|
||||
}));
|
||||
cli.input.emit('data', 'let y = 3\n');
|
||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
||||
}
|
||||
|
||||
function testStrictMode() {
|
||||
const cli = initRepl(repl.REPL_MODE_STRICT);
|
||||
|
||||
cli._domain.once('error', common.mustCall((err) => {
|
||||
assert.ok(err);
|
||||
assert.ok(/ReferenceError: x is not defined/.test(err.message));
|
||||
}));
|
||||
cli.input.emit('data', 'x = 3\n');
|
||||
assert.ok(/ReferenceError: x is not defined/.test(
|
||||
cli.output.accumulator.join('')));
|
||||
cli.output.accumulator.length = 0;
|
||||
|
||||
cli.callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, undefined);
|
||||
}));
|
||||
cli.input.emit('data', 'let y = 3\n');
|
||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
||||
}
|
||||
|
||||
function testAutoMode() {
|
||||
const cli = initRepl(repl.REPL_MODE_MAGIC);
|
||||
|
||||
cli.callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, 3);
|
||||
}));
|
||||
cli.input.emit('data', 'x = 3\n');
|
||||
assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> ');
|
||||
cli.output.accumulator.length = 0;
|
||||
|
||||
cli.callbacks.push(common.mustCall((err, result) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(result, undefined);
|
||||
}));
|
||||
cli.input.emit('data', 'let y = 3\n');
|
||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
||||
}
|
||||
|
||||
function initRepl(mode) {
|
||||
@ -62,11 +76,28 @@ function initRepl(mode) {
|
||||
output.accumulator = [];
|
||||
output.writable = true;
|
||||
|
||||
return repl.start({
|
||||
const replserver = repl.start({
|
||||
input: input,
|
||||
output: output,
|
||||
useColors: false,
|
||||
terminal: false,
|
||||
replMode: mode
|
||||
});
|
||||
const callbacks = [];
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
const expected = callbacks.shift();
|
||||
return $eval.call(this, code, context, file, (...args) => {
|
||||
console.log('EVAL RET', args);
|
||||
try {
|
||||
expected(...args);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
cb(...args);
|
||||
});
|
||||
};
|
||||
replserver.callbacks = callbacks;
|
||||
return replserver;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const repl = require('repl');
|
||||
const assert = require('assert');
|
||||
const Stream = require('stream');
|
||||
@ -18,7 +18,6 @@ const replserver = repl.start({
|
||||
replserver.emit('line', 'process.nextTick(() => { throw null; })');
|
||||
replserver.emit('line', '.exit');
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(text);
|
||||
replserver.on('exit', common.mustCall(() => {
|
||||
assert(text.includes('Thrown: null'));
|
||||
}, 0);
|
||||
}));
|
||||
|
@ -1,9 +1,28 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const repl = require('repl');
|
||||
const assert = require('assert');
|
||||
|
||||
const callbacks = [
|
||||
common.mustCall((err, value) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(value, undefined);
|
||||
})
|
||||
];
|
||||
const replserver = new repl.REPLServer();
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
const expected = callbacks.shift();
|
||||
return $eval.call(this, code, context, file, (...args) => {
|
||||
try {
|
||||
expected(...args);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
cb(...args);
|
||||
});
|
||||
};
|
||||
|
||||
replserver._inTemplateLiteral = true;
|
||||
|
||||
|
@ -22,7 +22,9 @@ function run({ command, expected }) {
|
||||
});
|
||||
|
||||
r.write(`${command}\n`);
|
||||
assert.strictEqual(accum, expected);
|
||||
r.on('exit', common.mustCall(() => {
|
||||
assert.strictEqual(accum, expected);
|
||||
}));
|
||||
r.close();
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,9 @@ function run({ command, expected }) {
|
||||
});
|
||||
|
||||
r.write(`${command}\n`);
|
||||
assert.strictEqual(accum, expected);
|
||||
r.on('exit', common.mustCall(() => {
|
||||
assert.strictEqual(accum, expected);
|
||||
}));
|
||||
r.close();
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,11 @@ const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
|
||||
let evalCount = 0;
|
||||
let recovered = false;
|
||||
let rendered = false;
|
||||
|
||||
function customEval(code, context, file, cb) {
|
||||
evalCount++;
|
||||
|
||||
return cb(evalCount === 1 ? new repl.Recoverable() : null, true);
|
||||
return cb(!recovered ? new repl.Recoverable() : null, true);
|
||||
}
|
||||
|
||||
const putIn = new common.ArrayStream();
|
||||
@ -26,7 +23,7 @@ putIn.write = function(msg) {
|
||||
}
|
||||
};
|
||||
|
||||
repl.start('', putIn, customEval);
|
||||
repl.start('', putIn, common.mustCall(customEval, 2));
|
||||
|
||||
// https://github.com/nodejs/node/issues/2939
|
||||
// Expose recoverable errors to the consumer.
|
||||
@ -36,5 +33,4 @@ putIn.emit('data', '2\n');
|
||||
process.on('exit', function() {
|
||||
assert(recovered, 'REPL never recovered');
|
||||
assert(rendered, 'REPL never rendered the result');
|
||||
assert.strictEqual(evalCount, 2);
|
||||
});
|
||||
|
@ -1,18 +1,20 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
|
||||
// This test ensures that the repl does not
|
||||
// crash or emit error when throwing `null|undefined`
|
||||
// ie `throw null` or `throw undefined`
|
||||
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
|
||||
const r = repl.start();
|
||||
const replserver = repl.start();
|
||||
const $eval = replserver.eval;
|
||||
replserver.eval = function(code, context, file, cb) {
|
||||
return $eval.call(this, code, context, file,
|
||||
common.mustNotCall(
|
||||
'repl crashes/throw error on `throw null|undefined`'));
|
||||
};
|
||||
replserver.write('throw null\n');
|
||||
replserver.write('throw undefined\n');
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
r.write('throw null\n');
|
||||
r.write('throw undefined\n');
|
||||
}, TypeError, 'repl crashes/throw error on `throw null|undefined`');
|
||||
|
||||
r.write('.exit\n');
|
||||
replserver.write('.exit\n');
|
||||
|
@ -28,20 +28,22 @@ function testSloppyMode() {
|
||||
_; // remains 30 from user input
|
||||
`);
|
||||
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'10',
|
||||
'10',
|
||||
'Expression assignment to _ now disabled.',
|
||||
'20',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'40',
|
||||
'30'
|
||||
]);
|
||||
r.on('exit', () => {
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'10',
|
||||
'10',
|
||||
'Expression assignment to _ now disabled.',
|
||||
'20',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'40',
|
||||
'30'
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
function testStrictMode() {
|
||||
@ -61,20 +63,22 @@ function testStrictMode() {
|
||||
_; // remains 30 from user input
|
||||
`);
|
||||
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'undefined',
|
||||
'30',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'30'
|
||||
]);
|
||||
r.on('exit', () => {
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'undefined',
|
||||
'30',
|
||||
'undefined',
|
||||
'undefined',
|
||||
'30'
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
function testMagicMode() {
|
||||
@ -94,20 +98,22 @@ function testMagicMode() {
|
||||
_; // remains 30 from user input
|
||||
`);
|
||||
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'10',
|
||||
'10',
|
||||
'undefined',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'undefined',
|
||||
'30',
|
||||
'undefined',
|
||||
'50',
|
||||
'30'
|
||||
]);
|
||||
r.on('exit', () => {
|
||||
assertOutput(r.output, [
|
||||
'undefined',
|
||||
'10',
|
||||
'10',
|
||||
'undefined',
|
||||
'20',
|
||||
'30',
|
||||
'30',
|
||||
'undefined',
|
||||
'30',
|
||||
'undefined',
|
||||
'50',
|
||||
'30'
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
function testResetContext() {
|
||||
@ -121,15 +127,17 @@ function testResetContext() {
|
||||
_; // expect 20
|
||||
`);
|
||||
|
||||
assertOutput(r.output, [
|
||||
'Expression assignment to _ now disabled.',
|
||||
'10',
|
||||
'10',
|
||||
'Clearing context...',
|
||||
'10',
|
||||
'20',
|
||||
'20'
|
||||
]);
|
||||
r.on('exit', () => {
|
||||
assertOutput(r.output, [
|
||||
'Expression assignment to _ now disabled.',
|
||||
'10',
|
||||
'10',
|
||||
'Clearing context...',
|
||||
'10',
|
||||
'20',
|
||||
'20'
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
function testResetContextGlobal() {
|
||||
@ -141,12 +149,14 @@ function testResetContextGlobal() {
|
||||
_; // remains 10
|
||||
`);
|
||||
|
||||
assertOutput(r.output, [
|
||||
'Expression assignment to _ now disabled.',
|
||||
'10',
|
||||
'10',
|
||||
'10',
|
||||
]);
|
||||
r.on('exit', () => {
|
||||
assertOutput(r.output, [
|
||||
'Expression assignment to _ now disabled.',
|
||||
'10',
|
||||
'10',
|
||||
'10',
|
||||
]);
|
||||
});
|
||||
|
||||
// delete globals leaked by REPL when `useGlobal` is `true`
|
||||
delete global.module;
|
||||
|
@ -7,13 +7,6 @@ const stream = require('stream');
|
||||
const repl = require('internal/repl');
|
||||
const assert = require('assert');
|
||||
|
||||
// Array of [useGlobal, expectedResult] pairs
|
||||
const globalTestCases = [
|
||||
[false, 'undefined'],
|
||||
[true, '\'tacos\''],
|
||||
[undefined, 'undefined']
|
||||
];
|
||||
|
||||
const globalTest = (useGlobal, cb, output) => (err, repl) => {
|
||||
if (err)
|
||||
return cb(err);
|
||||
@ -26,26 +19,12 @@ const globalTest = (useGlobal, cb, output) => (err, repl) => {
|
||||
global.lunch = 'tacos';
|
||||
repl.write('global.lunch;\n');
|
||||
repl.close();
|
||||
delete global.lunch;
|
||||
cb(null, str.trim());
|
||||
repl.on('exit', common.mustCall(() => {
|
||||
delete global.lunch;
|
||||
cb(null, str.trim());
|
||||
}));
|
||||
};
|
||||
|
||||
// Test how the global object behaves in each state for useGlobal
|
||||
for (const [option, expected] of globalTestCases) {
|
||||
runRepl(option, globalTest, common.mustCall((err, output) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(output, expected);
|
||||
}));
|
||||
}
|
||||
|
||||
// Test how shadowing the process object via `let`
|
||||
// behaves in each useGlobal state. Note: we can't
|
||||
// actually test the state when useGlobal is true,
|
||||
// because the exception that's generated is caught
|
||||
// (see below), but errors are printed, and the test
|
||||
// suite is aware of it, causing a failure to be flagged.
|
||||
//
|
||||
const processTestCases = [false, undefined];
|
||||
const processTest = (useGlobal, cb, output) => (err, repl) => {
|
||||
if (err)
|
||||
return cb(err);
|
||||
@ -57,15 +36,37 @@ const processTest = (useGlobal, cb, output) => (err, repl) => {
|
||||
repl.write('let process;\n');
|
||||
repl.write('21 * 2;\n');
|
||||
repl.close();
|
||||
cb(null, str.trim());
|
||||
repl.on('exit', common.mustCall((err) => {
|
||||
assert.ifError(err);
|
||||
cb(null, str.trim());
|
||||
}));
|
||||
};
|
||||
|
||||
for (const option of processTestCases) {
|
||||
runRepl(option, processTest, common.mustCall((err, output) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(output, 'undefined\n42');
|
||||
}));
|
||||
}
|
||||
// Array of [useGlobal, expectedResult, fn] pairs
|
||||
const testCases = [
|
||||
// Test how the global object behaves in each state for useGlobal
|
||||
[false, 'undefined', globalTest],
|
||||
[true, '\'tacos\'', globalTest],
|
||||
[undefined, 'undefined', globalTest],
|
||||
// Test how shadowing the process object via `let`
|
||||
// behaves in each useGlobal state. Note: we can't
|
||||
// actually test the state when useGlobal is true,
|
||||
// because the exception that's generated is caught
|
||||
// (see below), but errors are printed, and the test
|
||||
// suite is aware of it, causing a failure to be flagged.
|
||||
[false, 'undefined\n42', processTest]
|
||||
];
|
||||
|
||||
const next = common.mustCall(() => {
|
||||
if (testCases.length) {
|
||||
const [option, expected, runner] = testCases.shift();
|
||||
runRepl(option, runner, common.mustCall((err, output) => {
|
||||
assert.strictEqual(output, expected);
|
||||
next();
|
||||
}));
|
||||
}
|
||||
}, testCases.length + 1);
|
||||
next();
|
||||
|
||||
function runRepl(useGlobal, testFunc, cb) {
|
||||
const inputStream = new stream.PassThrough();
|
||||
|
Loading…
x
Reference in New Issue
Block a user