• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const ArrayStream = require('../common/arraystream');
5const fixtures = require('../common/fixtures');
6const assert = require('assert');
7const { builtinModules } = require('module');
8const publicModules = builtinModules.filter((lib) => !lib.startsWith('_'));
9
10if (!common.isMainThread)
11  common.skip('process.chdir is not available in Workers');
12
13// We have to change the directory to ../fixtures before requiring repl
14// in order to make the tests for completion of node_modules work properly
15// since repl modifies module.paths.
16process.chdir(fixtures.fixturesDir);
17
18const repl = require('repl');
19
20const putIn = new ArrayStream();
21const testMe = repl.start({
22  prompt: '',
23  input: putIn,
24  output: process.stdout,
25  allowBlockingCompletions: true
26});
27
28// Some errors are passed to the domain, but do not callback
29testMe._domain.on('error', assert.ifError);
30
31// Tab complete provides built in libs for import()
32testMe.complete('import(\'', common.mustCall((error, data) => {
33  assert.strictEqual(error, null);
34  publicModules.forEach((lib) => {
35    assert(
36      data[0].includes(lib) && data[0].includes(`node:${lib}`),
37      `${lib} not found`,
38    );
39  });
40  const newModule = 'foobar';
41  assert(!builtinModules.includes(newModule));
42  repl.builtinModules.push(newModule);
43  testMe.complete('import(\'', common.mustCall((_, [modules]) => {
44    assert.strictEqual(data[0].length + 1, modules.length);
45    assert(modules.includes(newModule) &&
46      !modules.includes(`node:${newModule}`));
47  }));
48}));
49
50testMe.complete("import\t( 'n", common.mustCall((error, data) => {
51  assert.strictEqual(error, null);
52  assert.strictEqual(data.length, 2);
53  assert.strictEqual(data[1], 'n');
54  const completions = data[0];
55  // import(...) completions include `node:` URL modules:
56  let lastIndex = -1;
57
58  publicModules.forEach((lib, index) => {
59    lastIndex = completions.indexOf(`node:${lib}`);
60    assert.notStrictEqual(lastIndex, -1);
61  });
62  assert.strictEqual(completions[lastIndex + 1], '');
63  // There is only one Node.js module that starts with n:
64  assert.strictEqual(completions[lastIndex + 2], 'net');
65  assert.strictEqual(completions[lastIndex + 3], '');
66  // It's possible to pick up non-core modules too
67  completions.slice(lastIndex + 4).forEach((completion) => {
68    assert.match(completion, /^n/);
69  });
70}));
71
72{
73  const expected = ['@nodejsscope', '@nodejsscope/'];
74  // Import calls should handle all types of quotation marks.
75  for (const quotationMark of ["'", '"', '`']) {
76    putIn.run(['.clear']);
77    testMe.complete('import(`@nodejs', common.mustCall((err, data) => {
78      assert.strictEqual(err, null);
79      assert.deepStrictEqual(data, [expected, '@nodejs']);
80    }));
81
82    putIn.run(['.clear']);
83    // Completions should not be greedy in case the quotation ends.
84    const input = `import(${quotationMark}@nodejsscope${quotationMark}`;
85    testMe.complete(input, common.mustCall((err, data) => {
86      assert.strictEqual(err, null);
87      assert.deepStrictEqual(data, [[], undefined]);
88    }));
89  }
90}
91
92{
93  putIn.run(['.clear']);
94  // Completions should find modules and handle whitespace after the opening
95  // bracket.
96  testMe.complete('import \t("no_ind', common.mustCall((err, data) => {
97    assert.strictEqual(err, null);
98    assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']);
99  }));
100}
101
102// Test tab completion for import() relative to the current directory
103{
104  putIn.run(['.clear']);
105
106  const cwd = process.cwd();
107  process.chdir(__dirname);
108
109  ['import(\'.', 'import(".'].forEach((input) => {
110    testMe.complete(input, common.mustCall((err, data) => {
111      assert.strictEqual(err, null);
112      assert.strictEqual(data.length, 2);
113      assert.strictEqual(data[1], '.');
114      assert.strictEqual(data[0].length, 2);
115      assert.ok(data[0].includes('./'));
116      assert.ok(data[0].includes('../'));
117    }));
118  });
119
120  ['import(\'..', 'import("..'].forEach((input) => {
121    testMe.complete(input, common.mustCall((err, data) => {
122      assert.strictEqual(err, null);
123      assert.deepStrictEqual(data, [['../'], '..']);
124    }));
125  });
126
127  ['./', './test-'].forEach((path) => {
128    [`import('${path}`, `import("${path}`].forEach((input) => {
129      testMe.complete(input, common.mustCall((err, data) => {
130        assert.strictEqual(err, null);
131        assert.strictEqual(data.length, 2);
132        assert.strictEqual(data[1], path);
133        assert.ok(data[0].includes('./test-repl-tab-complete.js'));
134      }));
135    });
136  });
137
138  ['../parallel/', '../parallel/test-'].forEach((path) => {
139    [`import('${path}`, `import("${path}`].forEach((input) => {
140      testMe.complete(input, common.mustCall((err, data) => {
141        assert.strictEqual(err, null);
142        assert.strictEqual(data.length, 2);
143        assert.strictEqual(data[1], path);
144        assert.ok(data[0].includes('../parallel/test-repl-tab-complete.js'));
145      }));
146    });
147  });
148
149  {
150    const path = '../fixtures/repl-folder-extensions/f';
151    testMe.complete(`import('${path}`, common.mustSucceed((data) => {
152      assert.strictEqual(data.length, 2);
153      assert.strictEqual(data[1], path);
154      assert.ok(data[0].includes(
155        '../fixtures/repl-folder-extensions/foo.js/'));
156    }));
157  }
158
159  process.chdir(cwd);
160}
161