nodejs/test/parallel/test-repl-top-level-await.js
Ruben Bridgewater 500720f578 errors: make sure all Node.js errors show their properties
This improves Node.js errors by always showing the attached properties
when inspecting such an error. This applies especially to SystemError.
It did often not show any properties but now all properties will be
visible.

This is done in a mainly backwards compatible way. Instead of using
prototype getters and setters, the property is now set directly on the
error.

PR-URL: https://github.com/nodejs/node/pull/29677
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
2019-10-03 12:34:44 -07:00

178 lines
5.3 KiB
JavaScript

'use strict';
require('../common');
const ArrayStream = require('../common/arraystream');
const assert = require('assert');
const { stripVTControlCharacters } = require('internal/readline/utils');
const repl = require('repl');
// Flags: --expose-internals --experimental-repl-await
const PROMPT = 'await repl > ';
class REPLStream extends ArrayStream {
constructor() {
super();
this.waitingForResponse = false;
this.lines = [''];
}
write(chunk, encoding, callback) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding);
}
const chunkLines = stripVTControlCharacters(chunk).split('\n');
this.lines[this.lines.length - 1] += chunkLines[0];
if (chunkLines.length > 1) {
this.lines.push(...chunkLines.slice(1));
}
this.emit('line');
if (callback) callback();
return true;
}
wait(lookFor = PROMPT) {
if (this.waitingForResponse) {
throw new Error('Currently waiting for response to another command');
}
this.lines = [''];
return new Promise((resolve, reject) => {
const onError = (err) => {
this.removeListener('line', onLine);
reject(err);
};
const onLine = () => {
if (this.lines[this.lines.length - 1].includes(lookFor)) {
this.removeListener('error', onError);
this.removeListener('line', onLine);
resolve(this.lines);
}
};
this.once('error', onError);
this.on('line', onLine);
});
}
}
const putIn = new REPLStream();
const testMe = repl.start({
prompt: PROMPT,
stream: putIn,
terminal: true,
useColors: false,
breakEvalOnSigint: true
});
function runAndWait(cmds, lookFor) {
const promise = putIn.wait(lookFor);
for (const cmd of cmds) {
if (typeof cmd === 'string') {
putIn.run([cmd]);
} else {
testMe.write('', cmd);
}
}
return promise;
}
async function ordinaryTests() {
// These tests were created based on
// https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6
putIn.run([
'function foo(x) { return x; }',
'function koo() { return Promise.resolve(4); }'
]);
const testCases = [
[ 'await Promise.resolve(0)', '0' ],
[ '{ a: await Promise.resolve(1) }', '{ a: 1 }' ],
[ '_', '{ a: 1 }' ],
[ 'let { a, b } = await Promise.resolve({ a: 1, b: 2 }), f = 5;',
'undefined' ],
[ 'a', '1' ],
[ 'b', '2' ],
[ 'f', '5' ],
[ 'let c = await Promise.resolve(2)', 'undefined' ],
[ 'c', '2' ],
[ 'let d;', 'undefined' ],
[ 'd', 'undefined' ],
[ 'let [i, { abc: { k } }] = [0, { abc: { k: 1 } }];', 'undefined' ],
[ 'i', '0' ],
[ 'k', '1' ],
[ 'var l = await Promise.resolve(2);', 'undefined' ],
[ 'l', '2' ],
[ 'foo(await koo())', '4' ],
[ '_', '4' ],
[ 'const m = foo(await koo());', 'undefined' ],
[ 'm', '4' ],
[ 'const n = foo(await\nkoo());', 'undefined' ],
[ 'n', '4' ],
// eslint-disable-next-line no-template-curly-in-string
[ '`status: ${(await Promise.resolve({ status: 200 })).status}`',
"'status: 200'"],
[ 'for (let i = 0; i < 2; ++i) await i', 'undefined' ],
[ 'for (let i = 0; i < 2; ++i) { await i }', 'undefined' ],
[ 'await 0', '0' ],
[ 'await 0; function foo() {}', 'undefined' ],
[ 'foo', '[Function: foo]' ],
[ 'class Foo {}; await 1;', '1' ],
[ 'Foo', '[Function: Foo]' ],
[ 'if (await true) { function bar() {}; }', 'undefined' ],
[ 'bar', '[Function: bar]' ],
[ 'if (await true) { class Bar {}; }', 'undefined' ],
[ 'Bar', 'ReferenceError: Bar is not defined', { line: 1 } ],
[ 'await 0; function* gen(){}', 'undefined' ],
[ 'for (var i = 0; i < 10; ++i) { await i; }', 'undefined' ],
[ 'i', '10' ],
[ 'for (let j = 0; j < 5; ++j) { await j; }', 'undefined' ],
[ 'j', 'ReferenceError: j is not defined', { line: 1 } ],
[ 'gen', '[GeneratorFunction: gen]' ],
[ 'return 42; await 5;', 'SyntaxError: Illegal return statement',
{ line: 4 } ],
[ 'let o = await 1, p', 'undefined' ],
[ 'p', 'undefined' ],
[ 'let q = 1, s = await 2', 'undefined' ],
[ 's', '2' ],
[ 'for await (let i of [1,2,3]) console.log(i)', 'undefined', { line: 3 } ]
];
for (const [input, expected, options = {}] of testCases) {
console.log(`Testing ${input}`);
const toBeRun = input.split('\n');
const lines = await runAndWait(toBeRun);
if ('line' in options) {
assert.strictEqual(lines[toBeRun.length + options.line], expected);
} else {
const echoed = toBeRun.map((a, i) => `${i > 0 ? '... ' : ''}${a}\r`);
assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]);
}
}
}
async function ctrlCTest() {
putIn.run([
`const timeout = (msecs) => new Promise((resolve) => {
setTimeout(resolve, msecs).unref();
});`
]);
console.log('Testing Ctrl+C');
assert.deepStrictEqual(await runAndWait([
'await timeout(100000)',
{ ctrl: true, name: 'c' }
]), [
'await timeout(100000)\r',
'Thrown:',
'[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
'Script execution was interrupted by `SIGINT`] {',
" code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED'",
'}',
PROMPT
]);
}
async function main() {
await ordinaryTests();
await ctrlCTest();
}
main();