• 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 through .test().
90      if (typeof expectedLine === 'string') {
91        assert.strictEqual(actualLine, expectedLine);
92      } else {
93        assert(expectedLine.test(actualLine),
94               `${actualLine} match ${expectedLine}`);
95      }
96    }
97  }
98
99  const remainder = socket.read();
100  assert(remainder === '' || remainder === null);
101}
102
103const unixTests = [
104  {
105    send: '',
106    expect: ''
107  },
108  {
109    send: 'message',
110    expect: `'${message}'`
111  },
112  {
113    send: 'invoke_me(987)',
114    expect: '\'invoked 987\''
115  },
116  {
117    send: 'a = 12345',
118    expect: '12345'
119  },
120  {
121    send: '{a:1}',
122    expect: '{ a: 1 }'
123  },
124];
125
126const strictModeTests = [
127  {
128    send: 'ref = 1',
129    expect: [/^Uncaught ReferenceError:\s/]
130  },
131];
132
133const errorTests = [
134  // Uncaught error throws and prints out
135  {
136    send: 'throw new Error(\'test error\');',
137    expect: ['Uncaught Error: test error']
138  },
139  {
140    send: "throw { foo: 'bar' };",
141    expect: "Uncaught { foo: 'bar' }"
142  },
143  // Common syntax error is treated as multiline command
144  {
145    send: 'function test_func() {',
146    expect: '... '
147  },
148  // You can recover with the .break command
149  {
150    send: '.break',
151    expect: ''
152  },
153  // But passing the same string to eval() should throw
154  {
155    send: 'eval("function test_func() {")',
156    expect: [/^Uncaught SyntaxError: /]
157  },
158  // Can handle multiline template literals
159  {
160    send: '`io.js',
161    expect: '... '
162  },
163  // Special REPL commands still available
164  {
165    send: '.break',
166    expect: ''
167  },
168  // Template expressions
169  {
170    send: '`io.js ${"1.0"',
171    expect: '... '
172  },
173  {
174    send: '+ ".2"}`',
175    expect: '\'io.js 1.0.2\''
176  },
177  {
178    send: '`io.js ${',
179    expect: '... '
180  },
181  {
182    send: '"1.0" + ".2"}`',
183    expect: '\'io.js 1.0.2\''
184  },
185  // Dot prefix in multiline commands aren't treated as commands
186  {
187    send: '("a"',
188    expect: '... '
189  },
190  {
191    send: '.charAt(0))',
192    expect: '\'a\''
193  },
194  // Floating point numbers are not interpreted as REPL commands.
195  {
196    send: '.1234',
197    expect: '0.1234'
198  },
199  // Floating point expressions are not interpreted as REPL commands
200  {
201    send: '.1+.1',
202    expect: '0.2'
203  },
204  // Can parse valid JSON
205  {
206    send: 'JSON.parse(\'{"valid": "json"}\');',
207    expect: '{ valid: \'json\' }'
208  },
209  // Invalid input to JSON.parse error is special case of syntax error,
210  // should throw
211  {
212    send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');',
213    expect: [/^Uncaught SyntaxError: /]
214  },
215  // End of input to JSON.parse error is special case of syntax error,
216  // should throw
217  {
218    send: 'JSON.parse(\'066\');',
219    expect: [/^Uncaught SyntaxError: /]
220  },
221  // should throw
222  {
223    send: 'JSON.parse(\'{\');',
224    expect: [/^Uncaught SyntaxError: /]
225  },
226  // invalid RegExps are a special case of syntax error,
227  // should throw
228  {
229    send: '/(/;',
230    expect: [/^Uncaught SyntaxError: /]
231  },
232  // invalid RegExp modifiers are a special case of syntax error,
233  // should throw (GH-4012)
234  {
235    send: 'new RegExp("foo", "wrong modifier");',
236    expect: [/^Uncaught SyntaxError: /]
237  },
238  // Strict mode syntax errors should be caught (GH-5178)
239  {
240    send: '(function() { "use strict"; return 0755; })()',
241    expect: [
242      kSource,
243      kArrow,
244      '',
245      /^Uncaught SyntaxError: /,
246    ]
247  },
248  {
249    send: '(function(a, a, b) { "use strict"; return a + b + c; })()',
250    expect: [
251      kSource,
252      kArrow,
253      '',
254      /^Uncaught SyntaxError: /,
255    ]
256  },
257  {
258    send: '(function() { "use strict"; with (this) {} })()',
259    expect: [
260      kSource,
261      kArrow,
262      '',
263      /^Uncaught SyntaxError: /,
264    ]
265  },
266  {
267    send: '(function() { "use strict"; var x; delete x; })()',
268    expect: [
269      kSource,
270      kArrow,
271      '',
272      /^Uncaught SyntaxError: /,
273    ]
274  },
275  {
276    send: '(function() { "use strict"; eval = 17; })()',
277    expect: [
278      kSource,
279      kArrow,
280      '',
281      /^Uncaught SyntaxError: /,
282    ]
283  },
284  {
285    send: '(function() { "use strict"; if (true) function f() { } })()',
286    expect: [
287      kSource,
288      kArrow,
289      '',
290      'Uncaught:',
291      /^SyntaxError: /,
292    ]
293  },
294  // Named functions can be used:
295  {
296    send: 'function blah() { return 1; }',
297    expect: 'undefined'
298  },
299  {
300    send: 'blah()',
301    expect: '1'
302  },
303  // Functions should not evaluate twice (#2773)
304  {
305    send: 'var I = [1,2,3,function() {}]; I.pop()',
306    expect: '[Function (anonymous)]'
307  },
308  // Multiline object
309  {
310    send: '{ a: ',
311    expect: '... '
312  },
313  {
314    send: '1 }',
315    expect: '{ a: 1 }'
316  },
317  // Multiline string-keyed object (e.g. JSON)
318  {
319    send: '{ "a": ',
320    expect: '... '
321  },
322  {
323    send: '1 }',
324    expect: '{ a: 1 }'
325  },
326  // Multiline class with private member.
327  {
328    send: 'class Foo { #private = true ',
329    expect: '... '
330  },
331  // Class field with bigint.
332  {
333    send: 'num = 123456789n',
334    expect: '... '
335  },
336  // Static class features.
337  {
338    send: 'static foo = "bar" }',
339    expect: 'undefined'
340  },
341  // Multiline anonymous function with comment
342  {
343    send: '(function() {',
344    expect: '... '
345  },
346  {
347    send: '// blah',
348    expect: '... '
349  },
350  {
351    send: 'return 1n;',
352    expect: '... '
353  },
354  {
355    send: '})()',
356    expect: '1n'
357  },
358  // Multiline function call
359  {
360    send: 'function f(){}; f(f(1,',
361    expect: '... '
362  },
363  {
364    send: '2)',
365    expect: '... '
366  },
367  {
368    send: ')',
369    expect: 'undefined'
370  },
371  // `npm` prompt error message.
372  {
373    send: 'npm install foobar',
374    expect: [
375      'npm should be run outside of the Node.js REPL, in your normal shell.',
376      '(Press Ctrl+D to exit.)',
377    ]
378  },
379  {
380    send: '(function() {\n\nreturn 1;\n})()',
381    expect: '... ... ... 1'
382  },
383  {
384    send: '{\n\na: 1\n}',
385    expect: '... ... ... { a: 1 }'
386  },
387  {
388    send: 'url.format("http://google.com")',
389    expect: '\'http://google.com/\''
390  },
391  {
392    send: 'var path = 42; path',
393    expect: '42'
394  },
395  // This makes sure that we don't print `undefined` when we actually print
396  // the error message
397  {
398    send: '.invalid_repl_command',
399    expect: 'Invalid REPL keyword'
400  },
401  // This makes sure that we don't crash when we use an inherited property as
402  // a REPL command
403  {
404    send: '.toString',
405    expect: 'Invalid REPL keyword'
406  },
407  // Fail when we are not inside a String and a line continuation is used
408  {
409    send: '[] \\',
410    expect: [
411      kSource,
412      kArrow,
413      '',
414      /^Uncaught SyntaxError: /,
415    ]
416  },
417  // Do not fail when a String is created with line continuation
418  {
419    send: '\'the\\\nfourth\\\neye\'',
420    expect: ['... ... \'thefourtheye\'']
421  },
422  // Don't fail when a partial String is created and line continuation is used
423  // with whitespace characters at the end of the string. We are to ignore it.
424  // This test is to make sure that we properly remove the whitespace
425  // characters at the end of line, unlike the buggy `trimWhitespace` function
426  {
427    send: '  \t    .break  \t  ',
428    expect: ''
429  },
430  // Multiline strings preserve whitespace characters in them
431  {
432    send: '\'the \\\n   fourth\t\t\\\n  eye  \'',
433    expect: '... ... \'the    fourth\\t\\t  eye  \''
434  },
435  // More than one multiline strings also should preserve whitespace chars
436  {
437    send: '\'the \\\n   fourth\' +  \'\t\t\\\n  eye  \'',
438    expect: '... ... \'the    fourth\\t\\t  eye  \''
439  },
440  // using REPL commands within a string literal should still work
441  {
442    send: '\'\\\n.break',
443    expect: '... ' + prompt_unix
444  },
445  // Using REPL command "help" within a string literal should still work
446  {
447    send: '\'thefourth\\\n.help\neye\'',
448    expect: [
449      /\.break/,
450      /\.clear/,
451      /\.exit/,
452      /\.help/,
453      /\.load/,
454      /\.save/,
455      '',
456      'Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL',
457      /'thefourtheye'/,
458    ]
459  },
460  // Check for wrapped objects.
461  {
462    send: '{ a: 1 }.a', // ({ a: 1 }.a);
463    expect: '1'
464  },
465  {
466    send: '{ a: 1 }.a;', // { a: 1 }.a;
467    expect: [
468      kSource,
469      kArrow,
470      '',
471      /^Uncaught SyntaxError: /,
472    ]
473  },
474  {
475    send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1);
476    expect: 'true'
477  },
478  {
479    send: '{ a: 1 }["a"] === 1;', // { a: 1 }; ['a'] === 1;
480    expect: 'false'
481  },
482  // Empty lines in the REPL should be allowed
483  {
484    send: '\n\r\n\r\n',
485    expect: ''
486  },
487  // Empty lines in the string literals should not affect the string
488  {
489    send: '\'the\\\n\\\nfourtheye\'\n',
490    expect: '... ... \'thefourtheye\''
491  },
492  // Regression test for https://github.com/nodejs/node/issues/597
493  {
494    send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n',
495    expect: 'true'
496  },
497  // The following test's result depends on the RegExp's match from the above
498  {
499    send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' +
500          'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n',
501    expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'',
502             '\'7\'', '\'8\'', '\'9\'']
503  },
504  // Regression tests for https://github.com/nodejs/node/issues/2749
505  {
506    send: 'function x() {\nreturn \'\\n\';\n }',
507    expect: '... ... undefined'
508  },
509  {
510    send: 'function x() {\nreturn \'\\\\\';\n }',
511    expect: '... ... undefined'
512  },
513  // Regression tests for https://github.com/nodejs/node/issues/3421
514  {
515    send: 'function x() {\n//\'\n }',
516    expect: '... ... undefined'
517  },
518  {
519    send: 'function x() {\n//"\n }',
520    expect: '... ... undefined'
521  },
522  {
523    send: 'function x() {//\'\n }',
524    expect: '... undefined'
525  },
526  {
527    send: 'function x() {//"\n }',
528    expect: '... undefined'
529  },
530  {
531    send: 'function x() {\nvar i = "\'";\n }',
532    expect: '... ... undefined'
533  },
534  {
535    send: 'function x(/*optional*/) {}',
536    expect: 'undefined'
537  },
538  {
539    send: 'function x(/* // 5 */) {}',
540    expect: 'undefined'
541  },
542  {
543    send: '// /* 5 */',
544    expect: 'undefined'
545  },
546  {
547    send: '"//"',
548    expect: '\'//\''
549  },
550  {
551    send: '"data /*with*/ comment"',
552    expect: '\'data /*with*/ comment\''
553  },
554  {
555    send: 'function x(/*fn\'s optional params*/) {}',
556    expect: 'undefined'
557  },
558  {
559    send: '/* \'\n"\n\'"\'\n*/',
560    expect: '... ... ... undefined'
561  },
562  // REPL should get a normal require() function, not one that allows
563  // access to internal modules without the --expose-internals flag.
564  {
565    send: 'require("internal/repl")',
566    expect: [
567      /^Uncaught Error: Cannot find module 'internal\/repl'/,
568      /^Require stack:/,
569      /^- <repl>/,
570      /^    at .*/,
571      /^    at .*/,
572      /^    at .*/,
573      /^    at .*/,
574      "  code: 'MODULE_NOT_FOUND',",
575      "  requireStack: [ '<repl>' ]",
576      '}',
577    ]
578  },
579  // REPL should handle quotes within regexp literal in multiline mode
580  {
581    send: "function x(s) {\nreturn s.replace(/'/,'');\n}",
582    expect: '... ... undefined'
583  },
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: '{ var x = 4; }',
598    expect: 'undefined'
599  },
600  // Illegal token is not recoverable outside string literal, RegExp literal,
601  // or block comment. https://github.com/nodejs/node/issues/3611
602  {
603    send: 'a = 3.5e',
604    expect: [
605      kSource,
606      kArrow,
607      '',
608      /^Uncaught SyntaxError: /,
609    ]
610  },
611  // Mitigate https://github.com/nodejs/node/issues/548
612  {
613    send: 'function name(){ return "node"; };name()',
614    expect: '\'node\''
615  },
616  {
617    send: 'function name(){ return "nodejs"; };name()',
618    expect: '\'nodejs\''
619  },
620  // Avoid emitting repl:line-number for SyntaxError
621  {
622    send: 'a = 3.5e',
623    expect: [
624      kSource,
625      kArrow,
626      '',
627      /^Uncaught SyntaxError: /,
628    ]
629  },
630  // Avoid emitting stack trace
631  {
632    send: 'a = 3.5e',
633    expect: [
634      kSource,
635      kArrow,
636      '',
637      /^Uncaught SyntaxError: /,
638    ]
639  },
640
641  // https://github.com/nodejs/node/issues/9850
642  {
643    send: 'function* foo() {}; foo().next();',
644    expect: '{ value: undefined, done: true }'
645  },
646
647  {
648    send: 'function *foo() {}; foo().next();',
649    expect: '{ value: undefined, done: true }'
650  },
651
652  {
653    send: 'function*foo() {}; foo().next();',
654    expect: '{ value: undefined, done: true }'
655  },
656
657  {
658    send: 'function * foo() {}; foo().next()',
659    expect: '{ value: undefined, done: true }'
660  },
661
662  // https://github.com/nodejs/node/issues/9300
663  {
664    send: 'function foo() {\nvar bar = 1 / 1; // "/"\n}',
665    expect: '... ... undefined'
666  },
667
668  {
669    send: '(function() {\nreturn /foo/ / /bar/;\n}())',
670    expect: '... ... NaN'
671  },
672
673  {
674    send: '(function() {\nif (false) {} /bar"/;\n}())',
675    expect: '... ... undefined'
676  },
677
678  // https://github.com/nodejs/node/issues/16483
679  {
680    send: 'new Proxy({x:42}, {get(){throw null}});',
681    expect: 'Proxy [ { x: 42 }, { get: [Function: get] } ]'
682  },
683  {
684    send: 'repl.writer.options.showProxy = false, new Proxy({x:42}, {});',
685    expect: '{ x: 42 }'
686  },
687
688  // Newline within template string maintains whitespace.
689  {
690    send: '`foo \n`',
691    expect: '... \'foo \\n\''
692  },
693  // Whitespace is not evaluated.
694  {
695    send: ' \t  \n',
696    expect: 'undefined'
697  },
698  // Do not parse `...[]` as a REPL keyword
699  {
700    send: '...[]',
701    expect: [
702      kSource,
703      kArrow,
704      '',
705      /^Uncaught SyntaxError: /,
706    ]
707  },
708  // Bring back the repl to prompt
709  {
710    send: '.break',
711    expect: ''
712  },
713  {
714    send: 'console.log("Missing comma in arg list" process.version)',
715    expect: [
716      kSource,
717      kArrow,
718      '',
719      /^Uncaught SyntaxError: /,
720    ]
721  },
722  {
723    send: 'x = {\nfield\n{',
724    expect: [
725      '... ... {',
726      kArrow,
727      '',
728      /^Uncaught SyntaxError: /,
729    ]
730  },
731  {
732    send: '(2 + 3))',
733    expect: [
734      kSource,
735      kArrow,
736      '',
737      /^Uncaught SyntaxError: /,
738    ]
739  },
740  {
741    send: 'if (typeof process === "object"); {',
742    expect: '... '
743  },
744  {
745    send: 'console.log("process is defined");',
746    expect: '... '
747  },
748  {
749    send: '} else {',
750    expect: [
751      kSource,
752      kArrow,
753      '',
754      /^Uncaught SyntaxError: /,
755    ]
756  },
757  {
758    send: 'console',
759    expect: [
760      'Object [console] {',
761      '  log: [Function: log],',
762      '  warn: [Function: warn],',
763      '  dir: [Function: dir],',
764      '  time: [Function: time],',
765      '  timeEnd: [Function: timeEnd],',
766      '  timeLog: [Function: timeLog],',
767      '  trace: [Function: trace],',
768      '  assert: [Function: assert],',
769      '  clear: [Function: clear],',
770      '  count: [Function: count],',
771      '  countReset: [Function: countReset],',
772      '  group: [Function: group],',
773      '  groupEnd: [Function: groupEnd],',
774      '  table: [Function: table],',
775      /  debug: \[Function: (debug|log)],/,
776      /  info: \[Function: (info|log)],/,
777      /  dirxml: \[Function: (dirxml|log)],/,
778      /  error: \[Function: (error|warn)],/,
779      /  groupCollapsed: \[Function: (groupCollapsed|group)],/,
780      /  Console: \[Function: Console],?/,
781      ...process.features.inspector ? [
782        '  profile: [Function: profile],',
783        '  profileEnd: [Function: profileEnd],',
784        '  timeStamp: [Function: timeStamp],',
785        '  context: [Function: context]',
786      ] : [],
787      '}',
788    ]
789  },
790];
791
792const tcpTests = [
793  {
794    send: '',
795    expect: ''
796  },
797  {
798    send: 'invoke_me(333)',
799    expect: '\'invoked 333\''
800  },
801  {
802    send: 'a += 1',
803    expect: '12346'
804  },
805  {
806    send: `require(${JSON.stringify(moduleFilename)}).number`,
807    expect: '42'
808  },
809  {
810    send: 'import comeOn from \'fhqwhgads\'',
811    expect: [
812      kSource,
813      kArrow,
814      '',
815      'Uncaught:',
816      /^SyntaxError: .* dynamic import/,
817    ]
818  },
819];
820
821(async function() {
822  {
823    const [ socket, replServer ] = await startUnixRepl();
824
825    await runReplTests(socket, prompt_unix, unixTests);
826    await runReplTests(socket, prompt_unix, errorTests);
827    replServer.replMode = repl.REPL_MODE_STRICT;
828    await runReplTests(socket, prompt_unix, strictModeTests);
829
830    socket.end();
831  }
832  {
833    const [ socket ] = await startTCPRepl();
834
835    await runReplTests(socket, prompt_tcp, tcpTests);
836
837    socket.end();
838  }
839  common.allowGlobals(...Object.values(global));
840})().then(common.mustCall());
841
842function startTCPRepl() {
843  let resolveSocket, resolveReplServer;
844
845  const server = net.createServer(common.mustCall((socket) => {
846    assert.strictEqual(server, socket.server);
847
848    socket.on('end', common.mustCall(() => {
849      socket.end();
850    }));
851
852    resolveReplServer(repl.start(prompt_tcp, socket));
853  }));
854
855  server.listen(0, common.mustCall(() => {
856    const client = net.createConnection(server.address().port);
857
858    client.setEncoding('utf8');
859
860    client.on('connect', common.mustCall(() => {
861      assert.strictEqual(client.readable, true);
862      assert.strictEqual(client.writable, true);
863
864      resolveSocket(client);
865    }));
866
867    client.on('close', common.mustCall(() => {
868      server.close();
869    }));
870  }));
871
872  return Promise.all([
873    new Promise((resolve) => resolveSocket = resolve),
874    new Promise((resolve) => resolveReplServer = resolve),
875  ]);
876}
877
878function startUnixRepl() {
879  let resolveSocket, resolveReplServer;
880
881  const server = net.createServer(common.mustCall((socket) => {
882    assert.strictEqual(server, socket.server);
883
884    socket.on('end', common.mustCall(() => {
885      socket.end();
886    }));
887
888    const replServer = repl.start({
889      prompt: prompt_unix,
890      input: socket,
891      output: socket,
892      useGlobal: true
893    });
894    replServer.context.message = message;
895    resolveReplServer(replServer);
896  }));
897
898  tmpdir.refresh();
899
900  server.listen(common.PIPE, common.mustCall(() => {
901    const client = net.createConnection(common.PIPE);
902
903    client.setEncoding('utf8');
904
905    client.on('connect', common.mustCall(() => {
906      assert.strictEqual(client.readable, true);
907      assert.strictEqual(client.writable, true);
908
909      resolveSocket(client);
910    }));
911
912    client.on('close', common.mustCall(() => {
913      server.close();
914    }));
915  }));
916
917  return Promise.all([
918    new Promise((resolve) => resolveSocket = resolve),
919    new Promise((resolve) => resolveReplServer = resolve),
920  ]);
921}
922
923function event(ee, expected) {
924  return new Promise((resolve, reject) => {
925    const timeout = setTimeout(() => {
926      const data = inspect(expected, { compact: false });
927      const msg = `The REPL did not reply as expected for:\n\n${data}`;
928      reject(new Error(msg));
929    }, common.platformTimeout(9999));
930    ee.once('data', common.mustCall((...args) => {
931      clearTimeout(timeout);
932      resolve(...args);
933    }));
934  });
935}
936