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