1'use strict'; 2 3const common = require('../common'); 4const ArrayStream = require('../common/arraystream'); 5const assert = require('assert'); 6const events = require('events'); 7const { stripVTControlCharacters } = require('internal/util/inspect'); 8const repl = require('repl'); 9 10common.skipIfInspectorDisabled(); 11 12// Flags: --expose-internals 13 14const PROMPT = 'await repl > '; 15 16class REPLStream extends ArrayStream { 17 constructor() { 18 super(); 19 this.waitingForResponse = false; 20 this.lines = ['']; 21 } 22 write(chunk, encoding, callback) { 23 if (Buffer.isBuffer(chunk)) { 24 chunk = chunk.toString(encoding); 25 } 26 const chunkLines = stripVTControlCharacters(chunk).split('\n'); 27 this.lines[this.lines.length - 1] += chunkLines[0]; 28 if (chunkLines.length > 1) { 29 this.lines.push(...chunkLines.slice(1)); 30 } 31 this.emit('line', this.lines[this.lines.length - 1]); 32 if (callback) callback(); 33 return true; 34 } 35 36 async wait() { 37 if (this.waitingForResponse) { 38 throw new Error('Currently waiting for response to another command'); 39 } 40 this.lines = ['']; 41 for await (const [line] of events.on(this, 'line')) { 42 if (line.includes(PROMPT)) { 43 return this.lines; 44 } 45 } 46 } 47} 48 49const putIn = new REPLStream(); 50const testMe = repl.start({ 51 prompt: PROMPT, 52 stream: putIn, 53 terminal: true, 54 useColors: true, 55 breakEvalOnSigint: true 56}); 57 58function runAndWait(cmds) { 59 const promise = putIn.wait(); 60 for (const cmd of cmds) { 61 if (typeof cmd === 'string') { 62 putIn.run([cmd]); 63 } else { 64 testMe.write('', cmd); 65 } 66 } 67 return promise; 68} 69 70async function ordinaryTests() { 71 // These tests were created based on 72 // https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6 73 putIn.run([ 74 'function foo(x) { return x; }', 75 'function koo() { return Promise.resolve(4); }', 76 ]); 77 const testCases = [ 78 ['await Promise.resolve(0)', '0'], 79 ['{ a: await Promise.resolve(1) }', '{ a: 1 }'], 80 ['_', '{ a: 1 }'], 81 ['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'], 82 ['aa', '1'], 83 ['bb', '2'], 84 ['f', '5'], 85 ['let cc = await Promise.resolve(2)'], 86 ['cc', '2'], 87 ['let dd;'], 88 ['dd'], 89 ['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'], 90 ['ii', '0'], 91 ['kk', '1'], 92 ['var ll = await Promise.resolve(2);'], 93 ['ll', '2'], 94 ['foo(await koo())', '4'], 95 ['_', '4'], 96 ['const m = foo(await koo());'], 97 ['m', '4'], 98 ['const n = foo(await\nkoo());', 99 ['const n = foo(await\r', '... koo());\r', 'undefined']], 100 ['n', '4'], 101 // eslint-disable-next-line no-template-curly-in-string 102 ['`status: ${(await Promise.resolve({ status: 200 })).status}`', 103 "'status: 200'"], 104 ['for (let i = 0; i < 2; ++i) await i'], 105 ['for (let i = 0; i < 2; ++i) { await i }'], 106 ['await 0', '0'], 107 ['await 0; function foo() {}'], 108 ['foo', '[Function: foo]'], 109 ['class Foo {}; await 1;', '1'], 110 ['Foo', '[class Foo]'], 111 ['if (await true) { function bar() {}; }'], 112 ['bar', '[Function: bar]'], 113 ['if (await true) { class Bar {}; }'], 114 ['Bar', 'Uncaught ReferenceError: Bar is not defined'], 115 ['await 0; function* gen(){}'], 116 ['for (var i = 0; i < 10; ++i) { await i; }'], 117 ['i', '10'], 118 ['for (let j = 0; j < 5; ++j) { await j; }'], 119 ['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }], 120 ['gen', '[GeneratorFunction: gen]'], 121 ['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', 122 { line: 3 }], 123 ['let o = await 1, p'], 124 ['p'], 125 ['let q = 1, s = await 2'], 126 ['s', '2'], 127 ['for await (let i of [1,2,3]) console.log(i)', 128 [ 129 'for await (let i of [1,2,3]) console.log(i)\r', 130 '1', 131 '2', 132 '3', 133 'undefined', 134 ], 135 ], 136 ['await Promise..resolve()', 137 [ 138 'await Promise..resolve()\r', 139 'Uncaught SyntaxError: ', 140 'await Promise..resolve()', 141 ' ^', 142 '', 143 'Unexpected token \'.\'', 144 ], 145 ], 146 ['for (const x of [1,2,3]) {\nawait x\n}', [ 147 'for (const x of [1,2,3]) {\r', 148 '... await x\r', 149 '... }\r', 150 'undefined', 151 ]], 152 ['for (const x of [1,2,3]) {\nawait x;\n}', [ 153 'for (const x of [1,2,3]) {\r', 154 '... await x;\r', 155 '... }\r', 156 'undefined', 157 ]], 158 ['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [ 159 'for await (const x of [1,2,3]) {\r', 160 '... console.log(x)\r', 161 '... }\r', 162 '1', 163 '2', 164 '3', 165 'undefined', 166 ]], 167 ['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [ 168 'for await (const x of [1,2,3]) {\r', 169 '... console.log(x);\r', 170 '... }\r', 171 '1', 172 '2', 173 '3', 174 'undefined', 175 ]], 176 // Regression test for https://github.com/nodejs/node/issues/43777. 177 ['await Promise.resolve(123), Promise.resolve(456)', 'Promise {', { line: 0 }], 178 ['await Promise.resolve(123), await Promise.resolve(456)', '456'], 179 ['await (Promise.resolve(123), Promise.resolve(456))', '456'], 180 ]; 181 182 for (const [input, expected = [`${input}\r`], options = {}] of testCases) { 183 console.log(`Testing ${input}`); 184 const toBeRun = input.split('\n'); 185 const lines = await runAndWait(toBeRun); 186 if (Array.isArray(expected)) { 187 if (expected.length === 1) 188 expected.push('undefined'); 189 if (lines[0] === input) 190 lines.shift(); 191 assert.deepStrictEqual(lines, [...expected, PROMPT]); 192 } else if ('line' in options) { 193 assert.strictEqual(lines[toBeRun.length + options.line], expected); 194 } else { 195 const echoed = toBeRun.map((a, i) => `${i > 0 ? '... ' : ''}${a}\r`); 196 assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]); 197 } 198 } 199} 200 201async function ctrlCTest() { 202 console.log('Testing Ctrl+C'); 203 const output = await runAndWait([ 204 'await new Promise(() => {})', 205 { ctrl: true, name: 'c' }, 206 ]); 207 assert.deepStrictEqual(output.slice(0, 3), [ 208 'await new Promise(() => {})\r', 209 'Uncaught:', 210 'Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + 211 'Script execution was interrupted by `SIGINT`', 212 ]); 213 assert.deepStrictEqual(output.slice(-2), [ 214 '}', 215 PROMPT, 216 ]); 217} 218 219async function main() { 220 await ordinaryTests(); 221 await ctrlCTest(); 222} 223 224main().then(common.mustCall()); 225