• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const assert = require('assert');
5const { REPLServer } = require('repl');
6const { Stream } = require('stream');
7const { inspect } = require('util');
8
9common.skipIfInspectorDisabled();
10
11// Ignore terminal settings. This is so the test can be run intact if TERM=dumb.
12process.env.TERM = '';
13const PROMPT = 'repl > ';
14
15class REPLStream extends Stream {
16  readable = true;
17  writable = true;
18
19  constructor() {
20    super();
21    this.lines = [''];
22  }
23  run(data) {
24    for (const entry of data) {
25      this.emit('data', entry);
26    }
27    this.emit('data', '\n');
28  }
29  write(chunk) {
30    const chunkLines = chunk.toString('utf8').split('\n');
31    this.lines[this.lines.length - 1] += chunkLines[0];
32    if (chunkLines.length > 1) {
33      this.lines.push(...chunkLines.slice(1));
34    }
35    this.emit('line');
36    return true;
37  }
38  wait() {
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  pause() {}
57  resume() {}
58}
59
60function runAndWait(cmds, repl) {
61  const promise = repl.inputStream.wait();
62  for (const cmd of cmds) {
63    repl.inputStream.run(cmd);
64  }
65  return promise;
66}
67
68async function tests(options) {
69  const repl = REPLServer({
70    prompt: PROMPT,
71    stream: new REPLStream(),
72    ignoreUndefined: true,
73    useColors: true,
74    ...options
75  });
76
77  repl.inputStream.run([
78    'function foo(x) { return x; }',
79    'function koo() { console.log("abc"); }',
80    'a = undefined;',
81  ]);
82
83  const testCases = [{
84    input: 'foo',
85    noPreview: '[Function: foo]',
86    preview: [
87      'foo',
88      '\x1B[90m[Function: foo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
89      '\x1B[36m[Function: foo]\x1B[39m',
90    ]
91  }, {
92    input: 'koo',
93    noPreview: '[Function: koo]',
94    preview: [
95      'k\x1B[90moo\x1B[39m\x1B[9G',
96      '\x1B[90m[Function: koo]\x1B[39m\x1B[9G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' +
97        '\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G',
98      '\x1B[90m[Function: koo]\x1B[39m\x1B[10G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' +
99        '\x1B[0Ko',
100      '\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
101      '\x1B[36m[Function: koo]\x1B[39m',
102    ]
103  }, {
104    input: 'a',
105    noPreview: 'repl > ', // No "undefined" output.
106    preview: ['a\r'] // No "undefined" preview.
107  }, {
108    input: " { b: 1 }['b'] === 1",
109    noPreview: '\x1B[33mtrue\x1B[39m',
110    preview: [
111      " { b: 1 }['b']",
112      '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ',
113      '\x1B[90m1\x1B[39m\x1B[23G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1',
114      '\x1B[90mtrue\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
115      '\x1B[33mtrue\x1B[39m',
116    ]
117  }, {
118    input: "{ b: 1 }['b'] === 1;",
119    noPreview: '\x1B[33mfalse\x1B[39m',
120    preview: [
121      "{ b: 1 }['b']",
122      '\x1B[90m1\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ',
123      '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1',
124      '\x1B[90mtrue\x1B[39m\x1B[27G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
125      '\x1B[90mfalse\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
126      '\x1B[33mfalse\x1B[39m',
127    ]
128  }, {
129    input: '{ a: true }',
130    noPreview: '{ a: \x1B[33mtrue\x1B[39m }',
131    preview: [
132      '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke }\r',
133      '{ a: \x1B[33mtrue\x1B[39m }',
134    ]
135  }, {
136    input: '{ a: true };',
137    noPreview: '\x1B[33mtrue\x1B[39m',
138    preview: [
139      '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke };',
140      '\x1B[90mtrue\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
141      '\x1B[33mtrue\x1B[39m',
142    ]
143  }, {
144    input: ' \t { a: true};',
145    noPreview: '\x1B[33mtrue\x1B[39m',
146    preview: [
147      '  { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}',
148      '\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
149      '\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
150      '\x1B[33mtrue\x1B[39m',
151    ]
152  }, {
153    input: '1n + 2n',
154    noPreview: '\x1B[33m3n\x1B[39m',
155    preview: [
156      '1n + 2',
157      '\x1B[90mType[39m\x1B[14G\x1B[1A\x1B[1B\x1B[2K\x1B[1An',
158      '\x1B[90m3n\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
159      '\x1B[33m3n\x1B[39m',
160    ]
161  }, {
162    input: '{};1',
163    noPreview: '\x1B[33m1\x1B[39m',
164    preview: [
165      '{};1',
166      '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
167      '\x1B[33m1\x1B[39m',
168    ]
169  }];
170
171  const hasPreview = repl.terminal &&
172    (options.preview !== undefined ? !!options.preview : true);
173
174  for (const { input, noPreview, preview } of testCases) {
175    console.log(`Testing ${input}`);
176
177    const toBeRun = input.split('\n');
178    let lines = await runAndWait(toBeRun, repl);
179
180    if (hasPreview) {
181      // Remove error messages. That allows the code to run in different
182      // engines.
183      // eslint-disable-next-line no-control-regex
184      lines = lines.map((line) => line.replace(/Error: .+?\x1B/, ''));
185      assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G');
186      assert.deepStrictEqual(lines, preview);
187    } else {
188      assert.ok(lines[0].includes(noPreview), lines.map(inspect));
189      if (preview.length !== 1 || preview[0] !== `${input}\r`)
190        assert.strictEqual(lines.length, 2);
191    }
192  }
193}
194
195tests({ terminal: false }); // No preview
196tests({ terminal: true }); // Preview
197tests({ terminal: false, preview: false }); // No preview
198tests({ terminal: false, preview: true }); // No preview
199tests({ terminal: true, preview: true }); // Preview
200