• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23const common = require('../common');
24const fixtures = require('../common/fixtures');
25const tmpdir = require('../common/tmpdir');
26const assert = require('assert');
27const net = require('net');
28const repl = require('repl');
29const { inspect } = require('util');
30
31const message = 'Read, Eval, Print Loop';
32const prompt_unix = 'node via Unix socket> ';
33const prompt_tcp = 'node via TCP socket> ';
34
35// Absolute path to test/fixtures/a.js
36const moduleFilename = fixtures.path('a');
37
38// Function for REPL to run
39global.invoke_me = function(arg) {
40  return `invoked ${arg}`;
41};
42
43// Helpers for describing the expected output:
44const kArrow = /^ *\^+ *$/;  // Arrow of ^ pointing to syntax error location
45const kSource = Symbol('kSource');  // Placeholder standing for input readback
46
47async function runReplTests(socket, prompt, tests) {
48  let lineBuffer = '';
49
50  for (const { send, expect } of tests) {
51    // Expect can be a single line or multiple lines
52    const expectedLines = Array.isArray(expect) ? expect : [ expect ];
53
54    console.error('out:', JSON.stringify(send));
55    socket.write(`${send}\n`);
56
57    for (let expectedLine of expectedLines) {
58      // Special value: kSource refers to last sent source text
59      if (expectedLine === kSource)
60        expectedLine = send;
61
62      while (!lineBuffer.includes('\n')) {
63        lineBuffer += await event(socket, expect);
64
65        // Cut away the initial prompt
66        while (lineBuffer.startsWith(prompt))
67          lineBuffer = lineBuffer.substr(prompt.length);
68
69        // Allow to match partial text if no newline was received, because
70        // sending newlines from the REPL itself would be redundant
71        // (e.g. in the `... ` multiline prompt: The user already pressed
72        // enter for that, so the REPL shouldn't do it again!).
73        if (lineBuffer === expectedLine && !expectedLine.includes('\n'))
74          lineBuffer += '\n';
75      }
76
77      // Split off the current line.
78      const newlineOffset = lineBuffer.indexOf('\n');
79      let actualLine = lineBuffer.substr(0, newlineOffset);
80      lineBuffer = lineBuffer.substr(newlineOffset + 1);
81
82      // This might have been skipped in the loop above because the buffer
83      // already contained a \n to begin with and the entire loop was skipped.
84      while (actualLine.startsWith(prompt))
85        actualLine = actualLine.substr(prompt.length);
86
87      console.error('in:', JSON.stringify(actualLine));
88
89      // Match a string directly, or a RegExp.
90      if (typeof expectedLine === 'string') {
91        assert.strictEqual(actualLine, expectedLine);
92      } else {
93        assert.match(actualLine, expectedLine);
94      }
95    }
96  }
97
98  const remainder = socket.read();
99  assert(remainder === '' || remainder === null);
100}
101
102const unixTests = [
103  {
104    send: '',
105    expect: ''
106  },
107  {
108    send: 'message',
109    expect: `'${message}'`
110  },
111  {
112    send: 'invoke_me(987)',
113    expect: '\'invoked 987\''
114  },
115  {
116    send: 'a = 12345',
117    expect: '12345'
118  },
119  {
120    send: '{a:1}',
121    expect: '{ a: 1 }'
122  },
123];
124
125const strictModeTests = [
126  {
127    send: 'ref = 1',
128    expect: [/^Uncaught ReferenceError:\s/]
129  },
130];
131
132const errorTests = [
133  // Uncaught error throws and prints out
134  {
135    send: 'throw new Error(\'test error\');',
136    expect: ['Uncaught Error: test error']
137  },
138  {
139    send: "throw { foo: 'bar' };",
140    expect: "Uncaught { foo: 'bar' }"
141  },
142  // Common syntax error is treated as multiline command
143  {
144    send: 'function test_func() {',
145    expect: '... '
146  },
147  // You can recover with the .break command
148  {
149    send: '.break',
150    expect: ''
151  },
152  // But passing the same string to eval() should throw
153  {
154    send: 'eval("function test_func() {")',
155    expect: [/^Uncaught SyntaxError: /]
156  },
157  // Can handle multiline template literals
158  {
159    send: '`io.js',
160    expect: '... '
161  },
162  // Special REPL commands still available
163  {
164    send: '.break',
165    expect: ''
166  },
167  // Template expressions
168  {
169    send: '`io.js ${"1.0"',
170    expect: '... '
171  },
172  {
173    send: '+ ".2"}`',
174    expect: '\'io.js 1.0.2\''
175  },
176  {
177    send: '`io.js ${',
178    expect: '... '
179  },
180  {
181    send: '"1.0" + ".2"}`',
182    expect: '\'io.js 1.0.2\''
183  },
184  // Dot prefix in multiline commands aren't treated as commands
185  {
186    send: '("a"',
187    expect: '... '
188  },
189  {
190    send: '.charAt(0))',
191    expect: '\'a\''
192  },
193  // Floating point numbers are not interpreted as REPL commands.
194  {
195    send: '.1234',
196    expect: '0.1234'
197  },
198  // Floating point expressions are not interpreted as REPL commands
199  {
200    send: '.1+.1',
201    expect: '0.2'
202  },
203  // Can parse valid JSON
204  {
205    send: 'JSON.parse(\'{"valid": "json"}\');',
206    expect: '{ valid: \'json\' }'
207  },
208  // Invalid input to JSON.parse error is special case of syntax error,
209  // should throw
210  {
211    send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');',
212    expect: [/^Uncaught SyntaxError: /]
213  },
214  // End of input to JSON.parse error is special case of syntax error,
215  // should throw
216  {
217    send: 'JSON.parse(\'066\');',
218    expect: [/^Uncaught SyntaxError: /]
219  },
220  // should throw
221  {
222    send: 'JSON.parse(\'{\');',
223    expect: [/^Uncaught SyntaxError: /]
224  },
225  // invalid RegExps are a special case of syntax error,
226  // should throw
227  {
228    send: '/(/;',
229    expect: [
230      kSource,
231      kArrow,
232      '',
233      /^Uncaught SyntaxError: /,
234    ]
235  },
236  // invalid RegExp modifiers are a special case of syntax error,
237  // should throw (GH-4012)
238  {
239    send: 'new RegExp("foo", "wrong modifier");',
240    expect: [/^Uncaught SyntaxError: /]
241  },
242  // Strict mode syntax errors should be caught (GH-5178)
243  {
244    send: '(function() { "use strict"; return 0755; })()',
245    expect: [
246      kSource,
247      kArrow,
248      '',
249      /^Uncaught SyntaxError: /,
250    ]
251  },
252  {
253    send: '(function(a, a, b) { "use strict"; return a + b + c; })()',
254    expect: [
255      kSource,
256      kArrow,
257      '',
258      /^Uncaught SyntaxError: /,
259    ]
260  },
261  {
262    send: '(function() { "use strict"; with (this) {} })()',
263    expect: [
264      kSource,
265      kArrow,
266      '',
267      /^Uncaught SyntaxError: /,
268    ]
269  },
270  {
271    send: '(function() { "use strict"; var x; delete x; })()',
272    expect: [
273      kSource,
274      kArrow,
275      '',
276      /^Uncaught SyntaxError: /,
277    ]
278  },
279  {
280    send: '(function() { "use strict"; eval = 17; })()',
281    expect: [
282      kSource,
283      kArrow,
284      '',
285      /^Uncaught SyntaxError: /,
286    ]
287  },
288  {
289    send: '(function() { "use strict"; if (true) function f() { } })()',
290    expect: [
291      kSource,
292      kArrow,
293      '',
294      'Uncaught:',
295      /^SyntaxError: /,
296    ]
297  },
298  // Named functions can be used:
299  {
300    send: 'function blah() { return 1; }',
301    expect: 'undefined'
302  },
303  {
304    send: 'blah()',
305    expect: '1'
306  },
307  // Functions should not evaluate twice (#2773)
308  {
309    send: 'var I = [1,2,3,function() {}]; I.pop()',
310    expect: '[Function (anonymous)]'
311  },
312  // Multiline object
313  {
314    send: '{ a: ',
315    expect: '... '
316  },
317  {
318    send: '1 }',
319    expect: '{ a: 1 }'
320  },
321  // Multiline string-keyed object (e.g. JSON)
322  {
323    send: '{ "a": ',
324    expect: '... '
325  },
326  {
327    send: '1 }',
328    expect: '{ a: 1 }'
329  },
330  // Multiline class with private member.
331  {
332    send: 'class Foo { #private = true ',
333    expect: '... '
334  },
335  // Class field with bigint.
336  {
337    send: 'num = 123456789n',
338    expect: '... '
339  },
340  // Static class features.
341  {
342    send: 'static foo = "bar" }',
343    expect: 'undefined'
344  },
345  // Multiline anonymous function with comment
346  {
347    send: '(function() {',
348    expect: '... '
349  },
350  {
351    send: '// blah',
352    expect: '... '
353  },
354  {
355    send: 'return 1n;',
356    expect: '... '
357  },
358  {
359    send: '})()',
360    expect: '1n'
361  },
362  // Multiline function call
363  {
364    send: 'function f(){}; f(f(1,',
365    expect: '... '
366  },
367  {
368    send: '2)',
369    expect: '... '
370  },
371  {
372    send: ')',
373    expect: 'undefined'
374  },
375  // `npm` prompt error message.
376  {
377    send: 'npm install foobar',
378    expect: [
379      'npm should be run outside of the Node.js REPL, in your normal shell.',
380      '(Press Ctrl+D to exit.)',
381    ]
382  },
383  {
384    send: '(function() {\n\nreturn 1;\n})()',
385    expect: '... ... ... 1'
386  },
387  {
388    send: '{\n\na: 1\n}',
389    expect: '... ... ... { a: 1 }'
390  },
391  {
392    send: 'url.format("http://google.com")',
393    expect: '\'http://google.com/\''
394  },
395  {
396    send: 'var path = 42; path',
397    expect: '42'
398  },
399  // This makes sure that we don't print `undefined` when we actually print
400  // the error message
401  {
402    send: '.invalid_repl_command',
403    expect: 'Invalid REPL keyword'
404  },
405  // This makes sure that we don't crash when we use an inherited property as
406  // a REPL command
407  {
408    send: '.toString',
409    expect: 'Invalid REPL keyword'
410  },
411  // Fail when we are not inside a String and a line continuation is used
412  {
413    send: '[] \\',
414    expect: [
415      kSource,
416      kArrow,
417      '',
418      /^Uncaught SyntaxError: /,
419    ]
420  },
421  // Do not fail when a String is created with line continuation
422  {
423    send: '\'the\\\nfourth\\\neye\'',
424    expect: ['... ... \'thefourtheye\'']
425  },
426  // Don't fail when a partial String is created and line continuation is used
427  // with whitespace characters at the end of the string. We are to ignore it.
428  // This test is to make sure that we properly remove the whitespace
429  // characters at the end of line, unlike the buggy `trimWhitespace` function
430  {
431    send: '  \t    .break  \t  ',
432    expect: ''
433  },
434  // Multiline strings preserve whitespace characters in them
435  {
436    send: '\'the \\\n   fourth\t\t\\\n  eye  \'',
437    expect: '... ... \'the    fourth\\t\\t  eye  \''
438  },
439  // More than one multiline strings also should preserve whitespace chars
440  {
441    send: '\'the \\\n   fourth\' +  \'\t\t\\\n  eye  \'',
442    expect: '... ... \'the    fourth\\t\\t  eye  \''
443  },
444  // using REPL commands within a string literal should still work
445  {
446    send: '\'\\\n.break',
447    expect: '... ' + prompt_unix
448  },
449  // Using REPL command "help" within a string literal should still work
450  {
451    send: '\'thefourth\\\n.help\neye\'',
452    expect: [
453      /\.break/,
454      /\.clear/,
455      /\.exit/,
456      /\.help/,
457      /\.load/,
458      /\.save/,
459      '',
460      'Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL',
461      /'thefourtheye'/,
462    ]
463  },
464  // Check for wrapped objects.
465  {
466    send: '{ a: 1 }.a', // ({ a: 1 }.a);
467    expect: '1'
468  },
469  {
470    send: '{ a: 1 }.a;', // { a: 1 }.a;
471    expect: [
472      kSource,
473      kArrow,
474      '',
475      /^Uncaught SyntaxError: /,
476    ]
477  },
478  {
479    send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1);
480    expect: 'true'
481  },
482  {
483    send: '{ a: 1 }["a"] === 1;', // { a: 1 }; ['a'] === 1;
484    expect: 'false'
485  },
486  // Empty lines in the REPL should be allowed
487  {
488    send: '\n\r\n\r\n',
489    expect: ''
490  },
491  // Empty lines in the string literals should not affect the string
492  {
493    send: '\'the\\\n\\\nfourtheye\'\n',
494    expect: '... ... \'thefourtheye\''
495  },
496  // Regression test for https://github.com/nodejs/node/issues/597
497  {
498    send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n',
499    expect: 'true'
500  },
501  // The following test's result depends on the RegExp's match from the above
502  {
503    send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' +
504          'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n',
505    expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'',
506             '\'7\'', '\'8\'', '\'9\'']
507  },
508  // Regression tests for https://github.com/nodejs/node/issues/2749
509  {
510    send: 'function x() {\nreturn \'\\n\';\n }',
511    expect: '... ... undefined'
512  },
513  {
514    send: 'function x() {\nreturn \'\\\\\';\n }',
515    expect: '... ... undefined'
516  },
517  // Regression tests for https://github.com/nodejs/node/issues/3421
518  {
519    send: 'function x() {\n//\'\n }',
520    expect: '... ... undefined'
521  },
522  {
523    send: 'function x() {\n//"\n }',
524    expect: '... ... undefined'
525  },
526  {
527    send: 'function x() {//\'\n }',
528    expect: '... undefined'
529  },
530  {
531    send: 'function x() {//"\n }',
532    expect: '... undefined'
533  },
534  {
535    send: 'function x() {\nvar i = "\'";\n }',
536    expect: '... ... undefined'
537  },
538  {
539    send: 'function x(/*optional*/) {}',
540    expect: 'undefined'
541  },
542  {
543    send: 'function x(/* // 5 */) {}',
544    expect: 'undefined'
545  },
546  {
547    send: '// /* 5 */',
548    expect: 'undefined'
549  },
550  {
551    send: '"//"',
552    expect: '\'//\''
553  },
554  {
555    send: '"data /*with*/ comment"',
556    expect: '\'data /*with*/ comment\''
557  },
558  {
559    send: 'function x(/*fn\'s optional params*/) {}',
560    expect: 'undefined'
561  },
562  {
563    send: '/* \'\n"\n\'"\'\n*/',
564    expect: '... ... ... undefined'
565  },
566  // REPL should get a normal require() function, not one that allows
567  // access to internal modules without the --expose-internals flag.
568  {
569    send: 'require("internal/repl")',
570    expect: [
571      /^Uncaught Error: Cannot find module 'internal\/repl'/,
572      /^Require stack:/,
573      /^- <repl>/,
574      /^ {4}at .*/,
575      /^ {4}at .*/,
576      /^ {4}at .*/,
577      /^ {4}at .*/,
578      "  code: 'MODULE_NOT_FOUND',",
579      "  requireStack: [ '<repl>' ]",
580      '}',
581    ]
582  },
583  // REPL should handle quotes within regexp literal in multiline mode
584  {
585    send: "function x(s) {\nreturn s.replace(/'/,'');\n}",
586    expect: '... ... undefined'
587  },
588  {
589    send: "function x(s) {\nreturn s.replace(/'/,'');\n}",
590    expect: '... ... undefined'
591  },
592  {
593    send: 'function x(s) {\nreturn s.replace(/"/,"");\n}',
594    expect: '... ... undefined'
595  },
596  {
597    send: 'function x(s) {\nreturn s.replace(/.*/,"");\n}',
598    expect: '... ... undefined'
599  },
600  {
601    send: '{ var x = 4; }',
602    expect: 'undefined'
603  },
604  // Illegal token is not recoverable outside string literal, RegExp literal,
605  // or block comment. https://github.com/nodejs/node/issues/3611
606  {
607    send: 'a = 3.5e',
608    expect: [
609      kSource,
610      kArrow,
611      '',
612      /^Uncaught SyntaxError: /,
613    ]
614  },
615  // Mitigate https://github.com/nodejs/node/issues/548
616  {
617    send: 'function name(){ return "node"; };name()',
618    expect: '\'node\''
619  },
620  {
621    send: 'function name(){ return "nodejs"; };name()',
622    expect: '\'nodejs\''
623  },
624  // Avoid emitting repl:line-number for SyntaxError
625  {
626    send: 'a = 3.5e',
627    expect: [
628      kSource,
629      kArrow,
630      '',
631      /^Uncaught SyntaxError: /,
632    ]
633  },
634  // Avoid emitting stack trace
635  {
636    send: 'a = 3.5e',
637    expect: [
638      kSource,
639      kArrow,
640      '',
641      /^Uncaught SyntaxError: /,
642    ]
643  },
644
645  // https://github.com/nodejs/node/issues/9850
646  {
647    send: 'function* foo() {}; foo().next();',
648    expect: '{ value: undefined, done: true }'
649  },
650
651  {
652    send: 'function *foo() {}; foo().next();',
653    expect: '{ value: undefined, done: true }'
654  },
655
656  {
657    send: 'function*foo() {}; foo().next();',
658    expect: '{ value: undefined, done: true }'
659  },
660
661  {
662    send: 'function * foo() {}; foo().next()',
663    expect: '{ value: undefined, done: true }'
664  },
665
666  // https://github.com/nodejs/node/issues/9300
667  {
668    send: 'function foo() {\nvar bar = 1 / 1; // "/"\n}',
669    expect: '... ... undefined'
670  },
671
672  {
673    send: '(function() {\nreturn /foo/ / /bar/;\n}())',
674    expect: '... ... NaN'
675  },
676
677  {
678    send: '(function() {\nif (false) {} /bar"/;\n}())',
679    expect: '... ... undefined'
680  },
681
682  // https://github.com/nodejs/node/issues/16483
683  {
684    send: 'new Proxy({x:42}, {get(){throw null}});',
685    expect: 'Proxy [ { x: 42 }, { get: [Function: get] } ]'
686  },
687  {
688    send: 'repl.writer.options.showProxy = false, new Proxy({x:42}, {});',
689    expect: '{ x: 42 }'
690  },
691
692  // Newline within template string maintains whitespace.
693  {
694    send: '`foo \n`',
695    expect: '... \'foo \\n\''
696  },
697  // Whitespace is not evaluated.
698  {
699    send: ' \t  \n',
700    expect: 'undefined'
701  },
702  // Do not parse `...[]` as a REPL keyword
703  {
704    send: '...[]',
705    expect: [
706      kSource,
707      kArrow,
708      '',
709      /^Uncaught SyntaxError: /,
710    ]
711  },
712  // Bring back the repl to prompt
713  {
714    send: '.break',
715    expect: ''
716  },
717  {
718    send: 'console.log("Missing comma in arg list" process.version)',
719    expect: [
720      kSource,
721      kArrow,
722      '',
723      /^Uncaught SyntaxError: /,
724    ]
725  },
726  {
727    send: 'x = {\nfield\n{',
728    expect: [
729      '... ... {',
730      kArrow,
731      '',
732      /^Uncaught SyntaxError: /,
733    ]
734  },
735  {
736    send: '(2 + 3))',
737    expect: [
738      kSource,
739      kArrow,
740      '',
741      /^Uncaught SyntaxError: /,
742    ]
743  },
744  {
745    send: 'if (typeof process === "object"); {',
746    expect: '... '
747  },
748  {
749    send: 'console.log("process is defined");',
750    expect: '... '
751  },
752  {
753    send: '} else {',
754    expect: [
755      kSource,
756      kArrow,
757      '',
758      /^Uncaught SyntaxError: /,
759    ]
760  },
761  {
762    send: 'console',
763    expect: [
764      'Object [console] {',
765      '  log: [Function: log],',
766      '  warn: [Function: warn],',
767      '  dir: [Function: dir],',
768      '  time: [Function: time],',
769      '  timeEnd: [Function: timeEnd],',
770      '  timeLog: [Function: timeLog],',
771      '  trace: [Function: trace],',
772      '  assert: [Function: assert],',
773      '  clear: [Function: clear],',
774      '  count: [Function: count],',
775      '  countReset: [Function: countReset],',
776      '  group: [Function: group],',
777      '  groupEnd: [Function: groupEnd],',
778      '  table: [Function: table],',
779      / {2}debug: \[Function: (debug|log)],/,
780      / {2}info: \[Function: (info|log)],/,
781      / {2}dirxml: \[Function: (dirxml|log)],/,
782      / {2}error: \[Function: (error|warn)],/,
783      / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/,
784      / {2}Console: \[Function: Console],?/,
785      ...process.features.inspector ? [
786        '  profile: [Function: profile],',
787        '  profileEnd: [Function: profileEnd],',
788        '  timeStamp: [Function: timeStamp],',
789        '  context: [Function: context]',
790      ] : [],
791      '}',
792    ]
793  },
794];
795
796const tcpTests = [
797  {
798    send: '',
799    expect: ''
800  },
801  {
802    send: 'invoke_me(333)',
803    expect: '\'invoked 333\''
804  },
805  {
806    send: 'a += 1',
807    expect: '12346'
808  },
809  {
810    send: `require(${JSON.stringify(moduleFilename)}).number`,
811    expect: '42'
812  },
813  {
814    send: 'import comeOn from \'fhqwhgads\'',
815    expect: [
816      kSource,
817      kArrow,
818      '',
819      'Uncaught:',
820      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
821alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");',
822    ]
823  },
824  {
825    send: 'import { export1, export2 } from "module-name"',
826    expect: [
827      kSource,
828      kArrow,
829      '',
830      'Uncaught:',
831      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
832alternatively use dynamic import: const { export1, export2 } = await import("module-name");',
833    ]
834  },
835  {
836    send: 'import * as name from "module-name";',
837    expect: [
838      kSource,
839      kArrow,
840      '',
841      'Uncaught:',
842      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
843alternatively use dynamic import: const name = await import("module-name");',
844    ]
845  },
846  {
847    send: 'import "module-name";',
848    expect: [
849      kSource,
850      kArrow,
851      '',
852      'Uncaught:',
853      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
854alternatively use dynamic import: await import("module-name");',
855    ]
856  },
857  {
858    send: 'import { export1 as localName1, export2 } from "bar";',
859    expect: [
860      kSource,
861      kArrow,
862      '',
863      'Uncaught:',
864      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
865alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");',
866    ]
867  },
868  {
869    send: 'import alias from "bar";',
870    expect: [
871      kSource,
872      kArrow,
873      '',
874      'Uncaught:',
875      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
876alternatively use dynamic import: const { default: alias } = await import("bar");',
877    ]
878  },
879  {
880    send: 'import alias, {namedExport} from "bar";',
881    expect: [
882      kSource,
883      kArrow,
884      '',
885      'Uncaught:',
886      'SyntaxError: Cannot use import statement inside the Node.js REPL, \
887alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");',
888    ]
889  },
890];
891
892(async function() {
893  {
894    const [ socket, replServer ] = await startUnixRepl();
895
896    await runReplTests(socket, prompt_unix, unixTests);
897    await runReplTests(socket, prompt_unix, errorTests);
898    replServer.replMode = repl.REPL_MODE_STRICT;
899    await runReplTests(socket, prompt_unix, strictModeTests);
900
901    socket.end();
902  }
903  {
904    const [ socket ] = await startTCPRepl();
905
906    await runReplTests(socket, prompt_tcp, tcpTests);
907
908    socket.end();
909  }
910  common.allowGlobals(global.invoke_me, global.message, global.a, global.blah,
911                      global.I, global.f, global.path, global.x, global.name, global.foo);
912})().then(common.mustCall());
913
914function startTCPRepl() {
915  let resolveSocket, resolveReplServer;
916
917  const server = net.createServer(common.mustCall((socket) => {
918    assert.strictEqual(server, socket.server);
919
920    socket.on('end', common.mustCall(() => {
921      socket.end();
922    }));
923
924    resolveReplServer(repl.start(prompt_tcp, socket));
925  }));
926
927  server.listen(0, common.mustCall(() => {
928    const client = net.createConnection(server.address().port);
929
930    client.setEncoding('utf8');
931
932    client.on('connect', common.mustCall(() => {
933      assert.strictEqual(client.readable, true);
934      assert.strictEqual(client.writable, true);
935
936      resolveSocket(client);
937    }));
938
939    client.on('close', common.mustCall(() => {
940      server.close();
941    }));
942  }));
943
944  return Promise.all([
945    new Promise((resolve) => resolveSocket = resolve),
946    new Promise((resolve) => resolveReplServer = resolve),
947  ]);
948}
949
950function startUnixRepl() {
951  let resolveSocket, resolveReplServer;
952
953  const server = net.createServer(common.mustCall((socket) => {
954    assert.strictEqual(server, socket.server);
955
956    socket.on('end', common.mustCall(() => {
957      socket.end();
958    }));
959
960    const replServer = repl.start({
961      prompt: prompt_unix,
962      input: socket,
963      output: socket,
964      useGlobal: true
965    });
966    replServer.context.message = message;
967    resolveReplServer(replServer);
968  }));
969
970  tmpdir.refresh();
971
972  server.listen(common.PIPE, common.mustCall(() => {
973    const client = net.createConnection(common.PIPE);
974
975    client.setEncoding('utf8');
976
977    client.on('connect', common.mustCall(() => {
978      assert.strictEqual(client.readable, true);
979      assert.strictEqual(client.writable, true);
980
981      resolveSocket(client);
982    }));
983
984    client.on('close', common.mustCall(() => {
985      server.close();
986    }));
987  }));
988
989  return Promise.all([
990    new Promise((resolve) => resolveSocket = resolve),
991    new Promise((resolve) => resolveReplServer = resolve),
992  ]);
993}
994
995function event(ee, expected) {
996  return new Promise((resolve, reject) => {
997    const timeout = setTimeout(() => {
998      const data = inspect(expected, { compact: false });
999      const msg = `The REPL did not reply as expected for:\n\n${data}`;
1000      reject(new Error(msg));
1001    }, common.platformTimeout(9999));
1002    ee.once('data', common.mustCall((...args) => {
1003      clearTimeout(timeout);
1004      resolve(...args);
1005    }));
1006  });
1007}
1008