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