• 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\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G\x1B[0Ko',
96      '\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
97      '\x1B[36m[Function: koo]\x1B[39m'
98    ]
99  }, {
100    input: 'a',
101    noPreview: 'repl > ', // No "undefined" output.
102    preview: ['a\r'] // No "undefined" preview.
103  }, {
104    input: " { b: 1 }['b'] === 1",
105    noPreview: '\x1B[33mtrue\x1B[39m',
106    preview: [
107      " { b: 1 }['b']",
108      '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ',
109      '\x1B[90m1\x1B[39m\x1B[23G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1',
110      '\x1B[90mtrue\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
111      '\x1B[33mtrue\x1B[39m'
112    ]
113  }, {
114    input: "{ b: 1 }['b'] === 1;",
115    noPreview: '\x1B[33mfalse\x1B[39m',
116    preview: [
117      "{ b: 1 }['b']",
118      '\x1B[90m1\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ',
119      '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1',
120      '\x1B[90mtrue\x1B[39m\x1B[27G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
121      '\x1B[90mfalse\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
122      '\x1B[33mfalse\x1B[39m'
123    ]
124  }, {
125    input: '{ a: true }',
126    noPreview: '{ a: \x1B[33mtrue\x1B[39m }',
127    preview: [
128      '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke }\r',
129      '{ a: \x1B[33mtrue\x1B[39m }'
130    ]
131  }, {
132    input: '{ a: true };',
133    noPreview: '\x1B[33mtrue\x1B[39m',
134    preview: [
135      '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke };',
136      '\x1B[90mtrue\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
137      '\x1B[33mtrue\x1B[39m'
138    ]
139  }, {
140    input: ' \t { a: true};',
141    noPreview: '\x1B[33mtrue\x1B[39m',
142    preview: [
143      '  { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}',
144      '\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;',
145      '\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
146      '\x1B[33mtrue\x1B[39m'
147    ]
148  }, {
149    input: '1n + 2n',
150    noPreview: '\x1B[33m3n\x1B[39m',
151    preview: [
152      '1n + 2',
153      '\x1B[90mType[39m\x1B[14G\x1B[1A\x1B[1B\x1B[2K\x1B[1An',
154      '\x1B[90m3n\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
155      '\x1B[33m3n\x1B[39m'
156    ]
157  }, {
158    input: '{};1',
159    noPreview: '\x1B[33m1\x1B[39m',
160    preview: [
161      '{};1',
162      '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r',
163      '\x1B[33m1\x1B[39m'
164    ]
165  }];
166
167  const hasPreview = repl.terminal &&
168    (options.preview !== undefined ? !!options.preview : true);
169
170  for (const { input, noPreview, preview } of testCases) {
171    console.log(`Testing ${input}`);
172
173    const toBeRun = input.split('\n');
174    let lines = await runAndWait(toBeRun, repl);
175
176    if (hasPreview) {
177      // Remove error messages. That allows the code to run in different
178      // engines.
179      // eslint-disable-next-line no-control-regex
180      lines = lines.map((line) => line.replace(/Error: .+?\x1B/, ''));
181      assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G');
182      assert.deepStrictEqual(lines, preview);
183    } else {
184      assert.ok(lines[0].includes(noPreview), lines.map(inspect));
185      if (preview.length !== 1 || preview[0] !== `${input}\r`)
186        assert.strictEqual(lines.length, 2);
187    }
188  }
189}
190
191tests({ terminal: false }); // No preview
192tests({ terminal: true }); // Preview
193tests({ terminal: false, preview: false }); // No preview
194tests({ terminal: false, preview: true }); // No preview
195tests({ terminal: true, preview: true }); // Preview
196