• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3require('../common');
4const assert = require('assert');
5const repl = require('repl');
6const stream = require('stream');
7
8testSloppyMode();
9testStrictMode();
10testResetContext();
11testResetContextGlobal();
12testMagicMode();
13testError();
14
15function testSloppyMode() {
16  const r = initRepl(repl.REPL_MODE_SLOPPY);
17
18  // Cannot use `let` in sloppy mode
19  r.write(`_;          // initial value undefined
20          var x = 10;  // evaluates to undefined
21          _;           // still undefined
22          y = 10;      // evaluates to 10
23          _;           // 10 from last eval
24          _ = 20;      // explicitly set to 20
25          _;           // 20 from user input
26          _ = 30;      // make sure we can set it twice and no prompt
27          _;           // 30 from user input
28          y = 40;      // make sure eval doesn't change _
29          _;           // remains 30 from user input
30          `);
31
32  assertOutput(r.output, [
33    'undefined',
34    'undefined',
35    'undefined',
36    '10',
37    '10',
38    'Expression assignment to _ now disabled.',
39    '20',
40    '20',
41    '30',
42    '30',
43    '40',
44    '30',
45  ]);
46}
47
48function testStrictMode() {
49  const r = initRepl(repl.REPL_MODE_STRICT);
50
51  r.write(`_;          // initial value undefined
52          var x = 10;  // evaluates to undefined
53          _;           // still undefined
54          let _ = 20;  // use 'let' only in strict mode - evals to undefined
55          _;           // 20 from user input
56          _ = 30;      // make sure we can set it twice and no prompt
57          _;           // 30 from user input
58          var y = 40;  // make sure eval doesn't change _
59          _;           // remains 30 from user input
60          function f() { let _ = 50; } // undefined
61          f();         // undefined
62          _;           // remains 30 from user input
63          `);
64
65  assertOutput(r.output, [
66    'undefined',
67    'undefined',
68    'undefined',
69    'undefined',
70    '20',
71    '30',
72    '30',
73    'undefined',
74    '30',
75    'undefined',
76    'undefined',
77    '30',
78  ]);
79}
80
81function testMagicMode() {
82  const r = initRepl(repl.REPL_MODE_MAGIC);
83
84  r.write(`_;          // initial value undefined
85          x = 10;      //
86          _;           // last eval - 10
87          let _ = 20;  // undefined
88          _;           // 20 from user input
89          _ = 30;      // make sure we can set it twice and no prompt
90          _;           // 30 from user input
91          var y = 40;  // make sure eval doesn't change _
92          _;           // remains 30 from user input
93          function f() { let _ = 50; return _; } // undefined
94          f();         // 50
95          _;           // remains 30 from user input
96          `);
97
98  assertOutput(r.output, [
99    'undefined',
100    '10',
101    '10',
102    'undefined',
103    '20',
104    '30',
105    '30',
106    'undefined',
107    '30',
108    'undefined',
109    '50',
110    '30',
111  ]);
112}
113
114function testResetContext() {
115  const r = initRepl(repl.REPL_MODE_SLOPPY);
116
117  r.write(`_ = 10;     // explicitly set to 10
118          _;           // 10 from user input
119          .clear       // Clearing context...
120          _;           // remains 10
121          x = 20;      // but behavior reverts to last eval
122          _;           // expect 20
123          `);
124
125  assertOutput(r.output, [
126    'Expression assignment to _ now disabled.',
127    '10',
128    '10',
129    'Clearing context...',
130    '10',
131    '20',
132    '20',
133  ]);
134}
135
136function testResetContextGlobal() {
137  const r = initRepl(repl.REPL_MODE_STRICT, true);
138
139  r.write(`_ = 10;     // explicitly set to 10
140          _;           // 10 from user input
141          .clear       // No output because useGlobal is true
142          _;           // remains 10
143          `);
144
145  assertOutput(r.output, [
146    'Expression assignment to _ now disabled.',
147    '10',
148    '10',
149    '10',
150  ]);
151
152  // Delete globals leaked by REPL when `useGlobal` is `true`
153  delete global.module;
154  delete global.require;
155}
156
157function testError() {
158  const r = initRepl(repl.REPL_MODE_STRICT);
159
160  r.write(`_error;                                // initial value undefined
161           throw new Error('foo');                // throws error
162           _error;                                // shows error
163           fs.readdirSync('/nonexistent?');       // throws error, sync
164           _error.code;                           // shows error code
165           _error.syscall;                        // shows error syscall
166           setImmediate(() => { throw new Error('baz'); }); undefined;
167                                                  // throws error, async
168           `);
169
170  setImmediate(() => {
171    const lines = r.output.accum.trim().split('\n');
172    const expectedLines = [
173      'undefined',
174
175      // The error, both from the original throw and the `_error` echo.
176      'Uncaught Error: foo',
177      '[Error: foo]',
178
179      // The sync error, with individual property echoes
180      /^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/,
181      /Object\.readdirSync/,
182      /^ {2}errno: -(2|4058),$/,
183      "  syscall: 'scandir',",
184      "  code: 'ENOENT',",
185      "  path: '/nonexistent?'",
186      '}',
187      "'ENOENT'",
188      "'scandir'",
189
190      // Dummy 'undefined' from the explicit silencer + one from the comment
191      'undefined',
192      'undefined',
193
194      // The message from the original throw
195      'Uncaught Error: baz',
196    ];
197    for (const line of lines) {
198      const expected = expectedLines.shift();
199      if (typeof expected === 'string')
200        assert.strictEqual(line, expected);
201      else
202        assert.match(line, expected);
203    }
204    assert.strictEqual(expectedLines.length, 0);
205
206    // Reset output, check that '_error' is the asynchronously caught error.
207    r.output.accum = '';
208    r.write(`_error.message                 // show the message
209             _error = 0;                    // disable auto-assignment
210             throw new Error('quux');       // new error
211             _error;                        // should not see the new error
212             `);
213
214    assertOutput(r.output, [
215      "'baz'",
216      'Expression assignment to _error now disabled.',
217      '0',
218      'Uncaught Error: quux',
219      '0',
220    ]);
221  });
222}
223
224function initRepl(mode, useGlobal) {
225  const inputStream = new stream.PassThrough();
226  const outputStream = new stream.PassThrough();
227  outputStream.accum = '';
228
229  outputStream.on('data', (data) => {
230    outputStream.accum += data;
231  });
232
233  return repl.start({
234    input: inputStream,
235    output: outputStream,
236    useColors: false,
237    terminal: false,
238    prompt: '',
239    replMode: mode,
240    useGlobal: useGlobal
241  });
242}
243
244function assertOutput(output, expected) {
245  const lines = output.accum.trim().split('\n');
246  assert.deepStrictEqual(lines, expected);
247}
248