• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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