node/test/parallel/test-repl-top-level-await.js
Rich Trott 330f25ef82 test: prepare for consistent comma-dangle lint rule
Make changes so that tests will pass when the comma-dangle settings
applied to the rest of the code base are also applied to tests.

PR-URL: https://github.com/nodejs/node/pull/37930
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Derek Lewis <DerekNonGeneric@inf.is>
2021-04-01 23:14:29 -07:00

194 lines
5.4 KiB
JavaScript

'use strict';
const common = require('../common');
const ArrayStream = require('../common/arraystream');
const assert = require('assert');
const { stripVTControlCharacters } = require('internal/util/inspect');
const repl = require('repl');
common.skipIfInspectorDisabled();
// 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() {
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(PROMPT)) {
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: true,
breakEvalOnSigint: true
});
function runAndWait(cmds) {
const promise = putIn.wait();
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 { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'],
['aa', '1'],
['bb', '2'],
['f', '5'],
['let cc = await Promise.resolve(2)'],
['cc', '2'],
['let dd;'],
['dd'],
['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'],
['ii', '0'],
['kk', '1'],
['var ll = await Promise.resolve(2);'],
['ll', '2'],
['foo(await koo())', '4'],
['_', '4'],
['const m = foo(await koo());'],
['m', '4'],
['const n = foo(await\nkoo());',
['const n = foo(await\r', '... koo());\r', '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'],
['for (let i = 0; i < 2; ++i) { await i }'],
['await 0', '0'],
['await 0; function foo() {}'],
['foo', '[Function: foo]'],
['class Foo {}; await 1;', '1'],
['Foo', '[class Foo]'],
['if (await true) { function bar() {}; }'],
['bar', '[Function: bar]'],
['if (await true) { class Bar {}; }'],
['Bar', 'Uncaught ReferenceError: Bar is not defined'],
['await 0; function* gen(){}'],
['for (var i = 0; i < 10; ++i) { await i; }'],
['i', '10'],
['for (let j = 0; j < 5; ++j) { await j; }'],
['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }],
['gen', '[GeneratorFunction: gen]'],
['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement',
{ line: 3 }],
['let o = await 1, p'],
['p'],
['let q = 1, s = await 2'],
['s', '2'],
['for await (let i of [1,2,3]) console.log(i)',
[
'for await (let i of [1,2,3]) console.log(i)\r',
'1',
'2',
'3',
'undefined',
],
],
];
for (const [input, expected = [`${input}\r`], options = {}] of testCases) {
console.log(`Testing ${input}`);
const toBeRun = input.split('\n');
const lines = await runAndWait(toBeRun);
if (Array.isArray(expected)) {
if (expected.length === 1)
expected.push('undefined');
if (lines[0] === input)
lines.shift();
assert.deepStrictEqual(lines, [...expected, PROMPT]);
} else 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',
'Uncaught:',
'[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();