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 util = require('util');
|
||||||
const debug = util.debuglog('repl');
|
const debug = util.debuglog('repl');
|
||||||
module.exports = Object.create(REPL);
|
module.exports = Object.create(REPL);
|
||||||
module.exports.createInternalRepl = createRepl;
|
module.exports.createInternalRepl = createInternalRepl;
|
||||||
|
|
||||||
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
|
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
|
||||||
// The debounce is to guard against code pasted into the REPL.
|
// The debounce is to guard against code pasted into the REPL.
|
||||||
@ -19,7 +19,7 @@ function _writeToOutput(repl, message) {
|
|||||||
repl._refreshLine();
|
repl._refreshLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRepl(env, opts, cb) {
|
function createInternalRepl(env, opts, cb) {
|
||||||
if (typeof opts === 'function') {
|
if (typeof opts === 'function') {
|
||||||
cb = opts;
|
cb = opts;
|
||||||
opts = null;
|
opts = null;
|
||||||
|
79
lib/repl.js
79
lib/repl.js
@ -109,6 +109,11 @@ writer.options = Object.assign({},
|
|||||||
|
|
||||||
exports._builtinLibs = internalModule.builtinLibs;
|
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,
|
function REPLServer(prompt,
|
||||||
stream,
|
stream,
|
||||||
eval_,
|
eval_,
|
||||||
@ -149,6 +154,7 @@ function REPLServer(prompt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
replMap.set(self, self);
|
||||||
|
|
||||||
self._domain = dom || domain.create();
|
self._domain = dom || domain.create();
|
||||||
self.useGlobal = !!useGlobal;
|
self.useGlobal = !!useGlobal;
|
||||||
@ -165,41 +171,6 @@ function REPLServer(prompt,
|
|||||||
self.rli = this;
|
self.rli = this;
|
||||||
|
|
||||||
const savedRegExMatches = ['', '', '', '', '', '', '', '', '', ''];
|
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) {
|
function defaultEval(code, context, file, cb) {
|
||||||
var err, result, script, wrappedErr;
|
var err, result, script, wrappedErr;
|
||||||
var wrappedCmd = false;
|
var wrappedCmd = false;
|
||||||
@ -331,7 +302,6 @@ function REPLServer(prompt,
|
|||||||
|
|
||||||
if (awaitPromise && !err) {
|
if (awaitPromise && !err) {
|
||||||
let sigintListener;
|
let sigintListener;
|
||||||
pause();
|
|
||||||
let promise = result;
|
let promise = result;
|
||||||
if (self.breakEvalOnSigint) {
|
if (self.breakEvalOnSigint) {
|
||||||
const interrupt = new Promise((resolve, reject) => {
|
const interrupt = new Promise((resolve, reject) => {
|
||||||
@ -350,7 +320,6 @@ function REPLServer(prompt,
|
|||||||
prioritizedSigintQueue.delete(sigintListener);
|
prioritizedSigintQueue.delete(sigintListener);
|
||||||
|
|
||||||
finishExecution(undefined, result);
|
finishExecution(undefined, result);
|
||||||
unpause();
|
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
// Remove prioritized SIGINT listener if it was not called.
|
// Remove prioritized SIGINT listener if it was not called.
|
||||||
prioritizedSigintQueue.delete(sigintListener);
|
prioritizedSigintQueue.delete(sigintListener);
|
||||||
@ -360,7 +329,6 @@ function REPLServer(prompt,
|
|||||||
Object.defineProperty(err, 'stack', { value: '' });
|
Object.defineProperty(err, 'stack', { value: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
unpause();
|
|
||||||
if (err && process.domain) {
|
if (err && process.domain) {
|
||||||
debug('not recoverable, send to domain');
|
debug('not recoverable, send to domain');
|
||||||
process.domain.emit('error', err);
|
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.eval = self._domain.bind(eval_);
|
||||||
|
|
||||||
self._domain.on('error', function debugDomainError(e) {
|
self._domain.on('error', function debugDomainError(e) {
|
||||||
@ -405,6 +403,7 @@ function REPLServer(prompt,
|
|||||||
top.clearBufferedCommand();
|
top.clearBufferedCommand();
|
||||||
top.lines.level = [];
|
top.lines.level = [];
|
||||||
top.displayPrompt();
|
top.displayPrompt();
|
||||||
|
unpause();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!input && !output) {
|
if (!input && !output) {
|
||||||
@ -593,6 +592,7 @@ function REPLServer(prompt,
|
|||||||
const evalCmd = self[kBufferedCommandSymbol] + cmd + '\n';
|
const evalCmd = self[kBufferedCommandSymbol] + cmd + '\n';
|
||||||
|
|
||||||
debug('eval %j', evalCmd);
|
debug('eval %j', evalCmd);
|
||||||
|
pause();
|
||||||
self.eval(evalCmd, self.context, 'repl', finish);
|
self.eval(evalCmd, self.context, 'repl', finish);
|
||||||
|
|
||||||
function finish(e, ret) {
|
function finish(e, ret) {
|
||||||
@ -605,6 +605,7 @@ function REPLServer(prompt,
|
|||||||
'(Press Control-D to exit.)\n');
|
'(Press Control-D to exit.)\n');
|
||||||
self.clearBufferedCommand();
|
self.clearBufferedCommand();
|
||||||
self.displayPrompt();
|
self.displayPrompt();
|
||||||
|
unpause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,6 +643,7 @@ function REPLServer(prompt,
|
|||||||
|
|
||||||
// Display prompt again
|
// Display prompt again
|
||||||
self.displayPrompt();
|
self.displayPrompt();
|
||||||
|
unpause();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -724,7 +726,6 @@ exports.start = function(prompt,
|
|||||||
ignoreUndefined,
|
ignoreUndefined,
|
||||||
replMode);
|
replMode);
|
||||||
if (!exports.repl) exports.repl = repl;
|
if (!exports.repl) exports.repl = repl;
|
||||||
replMap.set(repl, repl);
|
|
||||||
return repl;
|
return repl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +29,22 @@ const repl = require('repl');
|
|||||||
common.globalCheck = false;
|
common.globalCheck = false;
|
||||||
|
|
||||||
const putIn = new common.ArrayStream();
|
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();
|
test1();
|
||||||
|
|
||||||
@ -48,6 +63,11 @@ function test1() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert(!gotWrite);
|
assert(!gotWrite);
|
||||||
|
callbacks.push(common.mustCall((cb, err, result) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(result, require('fs'));
|
||||||
|
cb(err, result);
|
||||||
|
}));
|
||||||
putIn.run(['fs']);
|
putIn.run(['fs']);
|
||||||
assert(gotWrite);
|
assert(gotWrite);
|
||||||
}
|
}
|
||||||
@ -66,6 +86,11 @@ function test2() {
|
|||||||
const val = {};
|
const val = {};
|
||||||
global.url = val;
|
global.url = val;
|
||||||
assert(!gotWrite);
|
assert(!gotWrite);
|
||||||
|
callbacks.push(common.mustCall((cb, err, result) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(result, val);
|
||||||
|
cb(err, result);
|
||||||
|
}));
|
||||||
putIn.run(['url']);
|
putIn.run(['url']);
|
||||||
assert(gotWrite);
|
assert(gotWrite);
|
||||||
}
|
}
|
||||||
|
@ -27,40 +27,57 @@ function testContext(repl) {
|
|||||||
repl.close();
|
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) {
|
function testContextSideEffects(server) {
|
||||||
assert.ok(!server.underscoreAssigned);
|
assert.ok(!server.underscoreAssigned);
|
||||||
assert.strictEqual(server.lines.length, 0);
|
assert.strictEqual(server.lines.length, 0);
|
||||||
|
|
||||||
// an assignment to '_' in the repl server
|
// 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');
|
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';
|
'use strict';
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const assert = require('assert');
|
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
let terminalExit = 0;
|
|
||||||
let regularExit = 0;
|
|
||||||
|
|
||||||
// Create a dummy stream that does nothing
|
// Create a dummy stream that does nothing
|
||||||
const stream = new common.ArrayStream();
|
const stream = new common.ArrayStream();
|
||||||
@ -41,11 +38,10 @@ function testTerminalMode() {
|
|||||||
stream.emit('data', '\u0004');
|
stream.emit('data', '\u0004');
|
||||||
});
|
});
|
||||||
|
|
||||||
r1.on('exit', function() {
|
r1.on('exit', common.mustCall(function() {
|
||||||
// should be fired from the simulated ^D keypress
|
// should be fired from the simulated ^D keypress
|
||||||
terminalExit++;
|
|
||||||
testRegularMode();
|
testRegularMode();
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testRegularMode() {
|
function testRegularMode() {
|
||||||
@ -59,17 +55,11 @@ function testRegularMode() {
|
|||||||
stream.emit('end');
|
stream.emit('end');
|
||||||
});
|
});
|
||||||
|
|
||||||
r2.on('exit', function() {
|
r2.on('exit', common.mustCall(function() {
|
||||||
// should be fired from the simulated 'end' event
|
// should be fired from the simulated 'end' event
|
||||||
regularExit++;
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('exit', function() {
|
|
||||||
assert.strictEqual(terminalExit, 1);
|
|
||||||
assert.strictEqual(regularExit, 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// start
|
// start
|
||||||
testTerminalMode();
|
testTerminalMode();
|
||||||
|
@ -3,21 +3,27 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
|
|
||||||
{
|
const exitTests = [];
|
||||||
const stream = new common.ArrayStream();
|
process.on('exit', () => {
|
||||||
const options = {
|
for (const test of exitTests) test();
|
||||||
eval: common.mustCall((cmd, context) => {
|
});
|
||||||
assert.strictEqual(cmd, '.scope\n');
|
const CONTEXT = { animal: 'Sterrance' };
|
||||||
assert.deepStrictEqual(context, { animal: 'Sterrance' });
|
const stream = new common.ArrayStream();
|
||||||
}),
|
const options = {
|
||||||
input: stream,
|
eval: common.mustCall((cmd, context) => {
|
||||||
output: stream,
|
// need to escape the domain
|
||||||
terminal: true
|
exitTests.push(common.mustCall(() => {
|
||||||
};
|
assert.strictEqual(cmd, '.scope');
|
||||||
|
assert.ok(context === CONTEXT);
|
||||||
|
}));
|
||||||
|
}),
|
||||||
|
input: stream,
|
||||||
|
output: stream,
|
||||||
|
terminal: true
|
||||||
|
};
|
||||||
|
|
||||||
const r = repl.start(options);
|
const r = repl.start(options);
|
||||||
r.context = { animal: 'Sterrance' };
|
r.context = CONTEXT;
|
||||||
|
|
||||||
stream.emit('data', '\t');
|
stream.emit('data', '\t');
|
||||||
stream.emit('.exit\n');
|
stream.emit('.exit\n');
|
||||||
}
|
|
||||||
|
@ -3,31 +3,31 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
|
|
||||||
{
|
const exitTests = [];
|
||||||
let evalCalledWithExpectedArgs = false;
|
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 = {
|
const r = repl.start(options);
|
||||||
eval: common.mustCall((cmd, context) => {
|
r.context = { foo: 'bar' };
|
||||||
// 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);
|
try {
|
||||||
r.context = { foo: 'bar' };
|
// Default preprocessor transforms
|
||||||
|
// function f() {} to
|
||||||
try {
|
// var f = function f() {}
|
||||||
// Default preprocessor transforms
|
// Test to ensure that original input is preserved.
|
||||||
// function f() {} to
|
// Reference: https://github.com/nodejs/node/issues/9743
|
||||||
// var f = function f() {}
|
r.write('function f() {}\n');
|
||||||
// Test to ensure that original input is preserved.
|
} finally {
|
||||||
// Reference: https://github.com/nodejs/node/issues/9743
|
r.write('.exit\n');
|
||||||
r.write('function f() {}\n');
|
|
||||||
} finally {
|
|
||||||
r.write('.exit\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(evalCalledWithExpectedArgs);
|
|
||||||
}
|
}
|
||||||
|
@ -7,32 +7,47 @@ const stream = require('stream');
|
|||||||
|
|
||||||
common.globalCheck = false;
|
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');
|
const output = new stream();
|
||||||
r.input.emit('data', 'a\n');
|
output.writable = true;
|
||||||
r.input.emit('data', '.exit');
|
output.accumulator = [];
|
||||||
|
|
||||||
const expected = '1\n[Function: a]\n';
|
output.write = (data) => output.accumulator.push(data);
|
||||||
const got = r.output.accumulator.join('');
|
|
||||||
assert.strictEqual(got, expected);
|
|
||||||
|
|
||||||
function initRepl() {
|
const replserver = repl.start({
|
||||||
const input = new stream();
|
input,
|
||||||
input.write = input.pause = input.resume = () => {};
|
output,
|
||||||
input.readable = true;
|
useColors: false,
|
||||||
|
terminal: false,
|
||||||
const output = new stream();
|
prompt: ''
|
||||||
output.writable = true;
|
});
|
||||||
output.accumulator = [];
|
const callbacks = [];
|
||||||
|
const $eval = replserver.eval;
|
||||||
output.write = (data) => output.accumulator.push(data);
|
replserver.eval = function(code, context, file, cb) {
|
||||||
|
const expected = callbacks.shift();
|
||||||
return repl.start({
|
return $eval.call(this, code, context, file, (...args) => {
|
||||||
input,
|
try {
|
||||||
output,
|
expected(...args);
|
||||||
useColors: false,
|
} catch (e) {
|
||||||
terminal: false,
|
console.error(e);
|
||||||
prompt: ''
|
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`);
|
r.write(`${command}\n`);
|
||||||
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
|
r.on('exit', common.mustCall(() => {
|
||||||
|
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
|
||||||
|
}));
|
||||||
r.close();
|
r.close();
|
||||||
|
@ -19,35 +19,49 @@ tests.forEach(function(test) {
|
|||||||
function testSloppyMode() {
|
function testSloppyMode() {
|
||||||
const cli = initRepl(repl.REPL_MODE_SLOPPY);
|
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');
|
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');
|
cli.input.emit('data', 'let y = 3\n');
|
||||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStrictMode() {
|
function testStrictMode() {
|
||||||
const cli = initRepl(repl.REPL_MODE_STRICT);
|
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');
|
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');
|
cli.input.emit('data', 'let y = 3\n');
|
||||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testAutoMode() {
|
function testAutoMode() {
|
||||||
const cli = initRepl(repl.REPL_MODE_MAGIC);
|
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');
|
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');
|
cli.input.emit('data', 'let y = 3\n');
|
||||||
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRepl(mode) {
|
function initRepl(mode) {
|
||||||
@ -62,11 +76,28 @@ function initRepl(mode) {
|
|||||||
output.accumulator = [];
|
output.accumulator = [];
|
||||||
output.writable = true;
|
output.writable = true;
|
||||||
|
|
||||||
return repl.start({
|
const replserver = repl.start({
|
||||||
input: input,
|
input: input,
|
||||||
output: output,
|
output: output,
|
||||||
useColors: false,
|
useColors: false,
|
||||||
terminal: false,
|
terminal: false,
|
||||||
replMode: mode
|
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';
|
'use strict';
|
||||||
require('../common');
|
const common = require('../common');
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const Stream = require('stream');
|
const Stream = require('stream');
|
||||||
@ -18,7 +18,6 @@ const replserver = repl.start({
|
|||||||
replserver.emit('line', 'process.nextTick(() => { throw null; })');
|
replserver.emit('line', 'process.nextTick(() => { throw null; })');
|
||||||
replserver.emit('line', '.exit');
|
replserver.emit('line', '.exit');
|
||||||
|
|
||||||
setTimeout(() => {
|
replserver.on('exit', common.mustCall(() => {
|
||||||
console.log(text);
|
|
||||||
assert(text.includes('Thrown: null'));
|
assert(text.includes('Thrown: null'));
|
||||||
}, 0);
|
}));
|
||||||
|
@ -1,9 +1,28 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
require('../common');
|
const common = require('../common');
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const callbacks = [
|
||||||
|
common.mustCall((err, value) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.strictEqual(value, undefined);
|
||||||
|
})
|
||||||
|
];
|
||||||
const replserver = new repl.REPLServer();
|
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;
|
replserver._inTemplateLiteral = true;
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ function run({ command, expected }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
r.write(`${command}\n`);
|
r.write(`${command}\n`);
|
||||||
assert.strictEqual(accum, expected);
|
r.on('exit', common.mustCall(() => {
|
||||||
|
assert.strictEqual(accum, expected);
|
||||||
|
}));
|
||||||
r.close();
|
r.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ function run({ command, expected }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
r.write(`${command}\n`);
|
r.write(`${command}\n`);
|
||||||
assert.strictEqual(accum, expected);
|
r.on('exit', common.mustCall(() => {
|
||||||
|
assert.strictEqual(accum, expected);
|
||||||
|
}));
|
||||||
r.close();
|
r.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,11 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const repl = require('repl');
|
const repl = require('repl');
|
||||||
|
|
||||||
let evalCount = 0;
|
|
||||||
let recovered = false;
|
let recovered = false;
|
||||||
let rendered = false;
|
let rendered = false;
|
||||||
|
|
||||||
function customEval(code, context, file, cb) {
|
function customEval(code, context, file, cb) {
|
||||||
evalCount++;
|
return cb(!recovered ? new repl.Recoverable() : null, true);
|
||||||
|
|
||||||
return cb(evalCount === 1 ? new repl.Recoverable() : null, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const putIn = new common.ArrayStream();
|
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
|
// https://github.com/nodejs/node/issues/2939
|
||||||
// Expose recoverable errors to the consumer.
|
// Expose recoverable errors to the consumer.
|
||||||
@ -36,5 +33,4 @@ putIn.emit('data', '2\n');
|
|||||||
process.on('exit', function() {
|
process.on('exit', function() {
|
||||||
assert(recovered, 'REPL never recovered');
|
assert(recovered, 'REPL never recovered');
|
||||||
assert(rendered, 'REPL never rendered the result');
|
assert(rendered, 'REPL never rendered the result');
|
||||||
assert.strictEqual(evalCount, 2);
|
|
||||||
});
|
});
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
require('../common');
|
const common = require('../common');
|
||||||
|
|
||||||
// This test ensures that the repl does not
|
// This test ensures that the repl does not
|
||||||
// crash or emit error when throwing `null|undefined`
|
// crash or emit error when throwing `null|undefined`
|
||||||
// ie `throw null` or `throw undefined`
|
// ie `throw null` or `throw undefined`
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
const repl = require('repl');
|
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(() => {
|
replserver.write('.exit\n');
|
||||||
r.write('throw null\n');
|
|
||||||
r.write('throw undefined\n');
|
|
||||||
}, TypeError, 'repl crashes/throw error on `throw null|undefined`');
|
|
||||||
|
|
||||||
r.write('.exit\n');
|
|
||||||
|
@ -28,20 +28,22 @@ function testSloppyMode() {
|
|||||||
_; // remains 30 from user input
|
_; // remains 30 from user input
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assertOutput(r.output, [
|
r.on('exit', () => {
|
||||||
'undefined',
|
assertOutput(r.output, [
|
||||||
'undefined',
|
'undefined',
|
||||||
'undefined',
|
'undefined',
|
||||||
'10',
|
'undefined',
|
||||||
'10',
|
'10',
|
||||||
'Expression assignment to _ now disabled.',
|
'10',
|
||||||
'20',
|
'Expression assignment to _ now disabled.',
|
||||||
'20',
|
'20',
|
||||||
'30',
|
'20',
|
||||||
'30',
|
'30',
|
||||||
'40',
|
'30',
|
||||||
'30'
|
'40',
|
||||||
]);
|
'30'
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testStrictMode() {
|
function testStrictMode() {
|
||||||
@ -61,20 +63,22 @@ function testStrictMode() {
|
|||||||
_; // remains 30 from user input
|
_; // remains 30 from user input
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assertOutput(r.output, [
|
r.on('exit', () => {
|
||||||
'undefined',
|
assertOutput(r.output, [
|
||||||
'undefined',
|
'undefined',
|
||||||
'undefined',
|
'undefined',
|
||||||
'undefined',
|
'undefined',
|
||||||
'20',
|
'undefined',
|
||||||
'30',
|
'20',
|
||||||
'30',
|
'30',
|
||||||
'undefined',
|
'30',
|
||||||
'30',
|
'undefined',
|
||||||
'undefined',
|
'30',
|
||||||
'undefined',
|
'undefined',
|
||||||
'30'
|
'undefined',
|
||||||
]);
|
'30'
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMagicMode() {
|
function testMagicMode() {
|
||||||
@ -94,20 +98,22 @@ function testMagicMode() {
|
|||||||
_; // remains 30 from user input
|
_; // remains 30 from user input
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assertOutput(r.output, [
|
r.on('exit', () => {
|
||||||
'undefined',
|
assertOutput(r.output, [
|
||||||
'10',
|
'undefined',
|
||||||
'10',
|
'10',
|
||||||
'undefined',
|
'10',
|
||||||
'20',
|
'undefined',
|
||||||
'30',
|
'20',
|
||||||
'30',
|
'30',
|
||||||
'undefined',
|
'30',
|
||||||
'30',
|
'undefined',
|
||||||
'undefined',
|
'30',
|
||||||
'50',
|
'undefined',
|
||||||
'30'
|
'50',
|
||||||
]);
|
'30'
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testResetContext() {
|
function testResetContext() {
|
||||||
@ -121,15 +127,17 @@ function testResetContext() {
|
|||||||
_; // expect 20
|
_; // expect 20
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assertOutput(r.output, [
|
r.on('exit', () => {
|
||||||
'Expression assignment to _ now disabled.',
|
assertOutput(r.output, [
|
||||||
'10',
|
'Expression assignment to _ now disabled.',
|
||||||
'10',
|
'10',
|
||||||
'Clearing context...',
|
'10',
|
||||||
'10',
|
'Clearing context...',
|
||||||
'20',
|
'10',
|
||||||
'20'
|
'20',
|
||||||
]);
|
'20'
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testResetContextGlobal() {
|
function testResetContextGlobal() {
|
||||||
@ -141,12 +149,14 @@ function testResetContextGlobal() {
|
|||||||
_; // remains 10
|
_; // remains 10
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assertOutput(r.output, [
|
r.on('exit', () => {
|
||||||
'Expression assignment to _ now disabled.',
|
assertOutput(r.output, [
|
||||||
'10',
|
'Expression assignment to _ now disabled.',
|
||||||
'10',
|
'10',
|
||||||
'10',
|
'10',
|
||||||
]);
|
'10',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// delete globals leaked by REPL when `useGlobal` is `true`
|
// delete globals leaked by REPL when `useGlobal` is `true`
|
||||||
delete global.module;
|
delete global.module;
|
||||||
|
@ -7,13 +7,6 @@ const stream = require('stream');
|
|||||||
const repl = require('internal/repl');
|
const repl = require('internal/repl');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
// Array of [useGlobal, expectedResult] pairs
|
|
||||||
const globalTestCases = [
|
|
||||||
[false, 'undefined'],
|
|
||||||
[true, '\'tacos\''],
|
|
||||||
[undefined, 'undefined']
|
|
||||||
];
|
|
||||||
|
|
||||||
const globalTest = (useGlobal, cb, output) => (err, repl) => {
|
const globalTest = (useGlobal, cb, output) => (err, repl) => {
|
||||||
if (err)
|
if (err)
|
||||||
return cb(err);
|
return cb(err);
|
||||||
@ -26,26 +19,12 @@ const globalTest = (useGlobal, cb, output) => (err, repl) => {
|
|||||||
global.lunch = 'tacos';
|
global.lunch = 'tacos';
|
||||||
repl.write('global.lunch;\n');
|
repl.write('global.lunch;\n');
|
||||||
repl.close();
|
repl.close();
|
||||||
delete global.lunch;
|
repl.on('exit', common.mustCall(() => {
|
||||||
cb(null, str.trim());
|
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) => {
|
const processTest = (useGlobal, cb, output) => (err, repl) => {
|
||||||
if (err)
|
if (err)
|
||||||
return cb(err);
|
return cb(err);
|
||||||
@ -57,15 +36,37 @@ const processTest = (useGlobal, cb, output) => (err, repl) => {
|
|||||||
repl.write('let process;\n');
|
repl.write('let process;\n');
|
||||||
repl.write('21 * 2;\n');
|
repl.write('21 * 2;\n');
|
||||||
repl.close();
|
repl.close();
|
||||||
cb(null, str.trim());
|
repl.on('exit', common.mustCall((err) => {
|
||||||
|
assert.ifError(err);
|
||||||
|
cb(null, str.trim());
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const option of processTestCases) {
|
// Array of [useGlobal, expectedResult, fn] pairs
|
||||||
runRepl(option, processTest, common.mustCall((err, output) => {
|
const testCases = [
|
||||||
assert.ifError(err);
|
// Test how the global object behaves in each state for useGlobal
|
||||||
assert.strictEqual(output, 'undefined\n42');
|
[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) {
|
function runRepl(useGlobal, testFunc, cb) {
|
||||||
const inputStream = new stream.PassThrough();
|
const inputStream = new stream.PassThrough();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user