node/test/parallel/test-repl-underscore.js
Anatoli Papirovski e7af9830e9 timers: run nextTicks after each immediate and timer
In order to better match the browser behaviour, run nextTicks (and
subsequently the microtask queue) after each individual Timer and
Immediate, rather than after the whole list is processed. The
current behaviour is somewhat of a performance micro-optimization
and also partly dictated by how timer handles were implemented.

PR-URL: https://github.com/nodejs/node/pull/22842
Fixes: https://github.com/nodejs/node/issues/22257
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
2018-10-17 20:38:07 -07:00

250 lines
6.6 KiB
JavaScript

'use strict';
require('../common');
const assert = require('assert');
const repl = require('repl');
const stream = require('stream');
testSloppyMode();
testStrictMode();
testResetContext();
testResetContextGlobal();
testMagicMode();
testError();
function testSloppyMode() {
const r = initRepl(repl.REPL_MODE_SLOPPY);
// cannot use `let` in sloppy mode
r.write(`_; // initial value undefined
var x = 10; // evaluates to undefined
_; // still undefined
y = 10; // evaluates to 10
_; // 10 from last eval
_ = 20; // explicitly set to 20
_; // 20 from user input
_ = 30; // make sure we can set it twice and no prompt
_; // 30 from user input
y = 40; // make sure eval doesn't change _
_; // remains 30 from user input
`);
assertOutput(r.output, [
'undefined',
'undefined',
'undefined',
'10',
'10',
'Expression assignment to _ now disabled.',
'20',
'20',
'30',
'30',
'40',
'30'
]);
}
function testStrictMode() {
const r = initRepl(repl.REPL_MODE_STRICT);
r.write(`_; // initial value undefined
var x = 10; // evaluates to undefined
_; // still undefined
let _ = 20; // use 'let' only in strict mode - evals to undefined
_; // 20 from user input
_ = 30; // make sure we can set it twice and no prompt
_; // 30 from user input
var y = 40; // make sure eval doesn't change _
_; // remains 30 from user input
function f() { let _ = 50; } // undefined
f(); // undefined
_; // remains 30 from user input
`);
assertOutput(r.output, [
'undefined',
'undefined',
'undefined',
'undefined',
'20',
'30',
'30',
'undefined',
'30',
'undefined',
'undefined',
'30'
]);
}
function testMagicMode() {
const r = initRepl(repl.REPL_MODE_MAGIC);
r.write(`_; // initial value undefined
x = 10; //
_; // last eval - 10
let _ = 20; // undefined
_; // 20 from user input
_ = 30; // make sure we can set it twice and no prompt
_; // 30 from user input
var y = 40; // make sure eval doesn't change _
_; // remains 30 from user input
function f() { let _ = 50; return _; } // undefined
f(); // 50
_; // remains 30 from user input
`);
assertOutput(r.output, [
'undefined',
'10',
'10',
'undefined',
'20',
'30',
'30',
'undefined',
'30',
'undefined',
'50',
'30'
]);
}
function testResetContext() {
const r = initRepl(repl.REPL_MODE_SLOPPY);
r.write(`_ = 10; // explicitly set to 10
_; // 10 from user input
.clear // Clearing context...
_; // remains 10
x = 20; // but behavior reverts to last eval
_; // expect 20
`);
assertOutput(r.output, [
'Expression assignment to _ now disabled.',
'10',
'10',
'Clearing context...',
'10',
'20',
'20'
]);
}
function testResetContextGlobal() {
const r = initRepl(repl.REPL_MODE_STRICT, true);
r.write(`_ = 10; // explicitly set to 10
_; // 10 from user input
.clear // No output because useGlobal is true
_; // remains 10
`);
assertOutput(r.output, [
'Expression assignment to _ now disabled.',
'10',
'10',
'10',
]);
// delete globals leaked by REPL when `useGlobal` is `true`
delete global.module;
delete global.require;
}
function testError() {
const r = initRepl(repl.REPL_MODE_STRICT);
r.write(`_error; // initial value undefined
throw new Error('foo'); // throws error
_error; // shows error
fs.readdirSync('/nonexistent?'); // throws error, sync
_error.code; // shows error code
_error.syscall; // shows error syscall
setImmediate(() => { throw new Error('baz'); }); undefined;
// throws error, async
`);
setImmediate(() => {
const lines = r.output.accum.trim().split('\n');
const expectedLines = [
'undefined',
// The error, both from the original throw and the `_error` echo.
'Error: foo',
'[Error: foo]',
// The sync error, with individual property echoes
/^{ Error: ENOENT: no such file or directory, scandir '.*nonexistent.*'/,
/Object\.readdirSync/,
/^ errno: -(2|4058),$/,
" syscall: 'scandir',",
" code: 'ENOENT',",
" path: '/nonexistent?' }",
"'ENOENT'",
"'scandir'",
// Dummy 'undefined' from the explicit silencer + one from the comment
'undefined',
'undefined',
// The message from the original throw
'Error: baz',
/setImmediate/,
/^ at/,
/^ at/,
];
for (const line of lines) {
const expected = expectedLines.shift();
if (typeof expected === 'string')
assert.strictEqual(line, expected);
else
assert(expected.test(line), `${line} should match ${expected}`);
}
assert.strictEqual(expectedLines.length, 0);
// Reset output, check that '_error' is the asynchronously caught error.
r.output.accum = '';
r.write(`_error.message // show the message
_error = 0; // disable auto-assignment
throw new Error('quux'); // new error
_error; // should not see the new error
`);
assertOutput(r.output, [
"'baz'",
'Expression assignment to _error now disabled.',
'0',
'Error: quux',
'0'
]);
});
}
function initRepl(mode, useGlobal) {
const inputStream = new stream.PassThrough();
const outputStream = new stream.PassThrough();
outputStream.accum = '';
outputStream.on('data', (data) => {
outputStream.accum += data;
});
return repl.start({
input: inputStream,
output: outputStream,
useColors: false,
terminal: false,
prompt: '',
replMode: mode,
useGlobal: useGlobal
});
}
function assertOutput(output, expected) {
const lines = output.accum.trim().split('\n');
assert.deepStrictEqual(lines, expected);
}