diff --git a/lib/internal/repl.js b/lib/internal/repl.js index 812af56ed2e046..76412c3b118a8c 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -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; diff --git a/lib/repl.js b/lib/repl.js index 84682b1b63e6cc..2227953fa86468 100644 --- a/lib/repl.js +++ b/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; }; diff --git a/test/parallel/test-repl-autolibs.js b/test/parallel/test-repl-autolibs.js index 52234deb5e732e..68f8402ddbfdaa 100644 --- a/test/parallel/test-repl-autolibs.js +++ b/test/parallel/test-repl-autolibs.js @@ -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); } diff --git a/test/parallel/test-repl-context.js b/test/parallel/test-repl-context.js index 9d18067bc2aca4..90364cee694bb5 100644 --- a/test/parallel/test-repl-context.js +++ b/test/parallel/test-repl-context.js @@ -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 - 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); + 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(); + // 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); + // 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); + // 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); + // 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(); + assert.ok(!server.underscoreAssigned); + assert.strictEqual(server.lines.length, 0); + server.close(); + })); + server.write('_ = 500;\n'); } diff --git a/test/parallel/test-repl-end-emits-exit.js b/test/parallel/test-repl-end-emits-exit.js index 67f667eeb3d8db..dd9ad5c1eb0e56 100644 --- a/test/parallel/test-repl-end-emits-exit.js +++ b/test/parallel/test-repl-end-emits-exit.js @@ -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(); diff --git a/test/parallel/test-repl-eval-scope.js b/test/parallel/test-repl-eval-scope.js index 00b577cba73f76..83311fd92cd67b 100644 --- a/test/parallel/test-repl-eval-scope.js +++ b/test/parallel/test-repl-eval-scope.js @@ -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'); diff --git a/test/parallel/test-repl-eval.js b/test/parallel/test-repl-eval.js index d775423fb74a52..d7290a1583da58 100644 --- a/test/parallel/test-repl-eval.js +++ b/test/parallel/test-repl-eval.js @@ -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'); } diff --git a/test/parallel/test-repl-function-definition-edge-case.js b/test/parallel/test-repl-function-definition-edge-case.js index 1e3063e3db53ff..bda40594a8876a 100644 --- a/test/parallel/test-repl-function-definition-edge-case.js +++ b/test/parallel/test-repl-function-definition-edge-case.js @@ -7,32 +7,47 @@ const stream = require('stream'); common.globalCheck = false; -const r = initRepl(); - -r.input.emit('data', 'function a() { return 42; } (1)\n'); -r.input.emit('data', 'a\n'); -r.input.emit('data', '.exit'); - -const expected = '1\n[Function: a]\n'; -const got = r.output.accumulator.join(''); -assert.strictEqual(got, expected); - -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 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); + +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'); diff --git a/test/parallel/test-repl-load-multiline.js b/test/parallel/test-repl-load-multiline.js index 8ab878ae768ddd..922aef3d493901 100644 --- a/test/parallel/test-repl-load-multiline.js +++ b/test/parallel/test-repl-load-multiline.js @@ -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(); diff --git a/test/parallel/test-repl-mode.js b/test/parallel/test-repl-mode.js index 60b430d8c7ee31..39d7ff88cce905 100644 --- a/test/parallel/test-repl-mode.js +++ b/test/parallel/test-repl-mode.js @@ -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; } diff --git a/test/parallel/test-repl-null-thrown.js b/test/parallel/test-repl-null-thrown.js index 1fe5d30396d534..9b78b0b1dc4d6d 100644 --- a/test/parallel/test-repl-null-thrown.js +++ b/test/parallel/test-repl-null-thrown.js @@ -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); +})); diff --git a/test/parallel/test-repl-null.js b/test/parallel/test-repl-null.js index 66d09b28f28b84..2748cfa780ee16 100644 --- a/test/parallel/test-repl-null.js +++ b/test/parallel/test-repl-null.js @@ -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; diff --git a/test/parallel/test-repl-pretty-custom-stack.js b/test/parallel/test-repl-pretty-custom-stack.js index be102c1d677a9c..0ca4039a5f8e36 100644 --- a/test/parallel/test-repl-pretty-custom-stack.js +++ b/test/parallel/test-repl-pretty-custom-stack.js @@ -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(); } diff --git a/test/parallel/test-repl-pretty-stack.js b/test/parallel/test-repl-pretty-stack.js index 0fc6b3ada04c79..55f37e1703e52d 100644 --- a/test/parallel/test-repl-pretty-stack.js +++ b/test/parallel/test-repl-pretty-stack.js @@ -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(); } diff --git a/test/parallel/test-repl-recoverable.js b/test/parallel/test-repl-recoverable.js index 6788d84595066c..c1767bbc4db03b 100644 --- a/test/parallel/test-repl-recoverable.js +++ b/test/parallel/test-repl-recoverable.js @@ -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); }); diff --git a/test/parallel/test-repl-throw-null-or-undefined.js b/test/parallel/test-repl-throw-null-or-undefined.js index fd2fd202b5bcb6..ef25dbe015de88 100644 --- a/test/parallel/test-repl-throw-null-or-undefined.js +++ b/test/parallel/test-repl-throw-null-or-undefined.js @@ -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'); diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index 91f32223e180b9..1b22dcc7221a7f 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -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; diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js index c76505272b2682..75646a9c00aae7 100644 --- a/test/parallel/test-repl-use-global.js +++ b/test/parallel/test-repl-use-global.js @@ -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()); -}; - -// 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); + repl.on('exit', common.mustCall(() => { + delete global.lunch; + cb(null, str.trim()); })); -} +}; -// 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()); -}; - -for (const option of processTestCases) { - runRepl(option, processTest, common.mustCall((err, output) => { + repl.on('exit', common.mustCall((err) => { assert.ifError(err); - assert.strictEqual(output, 'undefined\n42'); + cb(null, str.trim()); })); -} +}; + +// 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();