• 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';
23
24const common = require('../common');
25const ArrayStream = require('../common/arraystream');
26const {
27  hijackStderr,
28  restoreStderr
29} = require('../common/hijackstdio');
30const assert = require('assert');
31const path = require('path');
32const fixtures = require('../common/fixtures');
33const { builtinModules } = require('module');
34const publicModules = builtinModules.filter((lib) => !lib.startsWith('_'));
35
36const hasInspector = process.features.inspector;
37
38if (!common.isMainThread)
39  common.skip('process.chdir is not available in Workers');
40
41// We have to change the directory to ../fixtures before requiring repl
42// in order to make the tests for completion of node_modules work properly
43// since repl modifies module.paths.
44process.chdir(fixtures.fixturesDir);
45
46const repl = require('repl');
47
48function getNoResultsFunction() {
49  return common.mustSucceed((data) => {
50    assert.deepStrictEqual(data[0], []);
51  });
52}
53
54const works = [['inner.one'], 'inner.o'];
55const putIn = new ArrayStream();
56const testMe = repl.start({
57  prompt: '',
58  input: putIn,
59  output: process.stdout,
60  allowBlockingCompletions: true
61});
62
63// Some errors are passed to the domain, but do not callback
64testMe._domain.on('error', assert.ifError);
65
66// Tab Complete will not break in an object literal
67putIn.run([
68  'var inner = {',
69  'one:1',
70]);
71testMe.complete('inner.o', getNoResultsFunction());
72
73testMe.complete('console.lo', common.mustCall(function(error, data) {
74  assert.deepStrictEqual(data, [['console.log'], 'console.lo']);
75}));
76
77testMe.complete('console?.lo', common.mustCall((error, data) => {
78  assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']);
79}));
80
81testMe.complete('console?.zzz', common.mustCall((error, data) => {
82  assert.deepStrictEqual(data, [[], 'console?.zzz']);
83}));
84
85testMe.complete('console?.', common.mustCall((error, data) => {
86  assert(data[0].includes('console?.log'));
87  assert.strictEqual(data[1], 'console?.');
88}));
89
90// Tab Complete will return globally scoped variables
91putIn.run(['};']);
92testMe.complete('inner.o', common.mustCall(function(error, data) {
93  assert.deepStrictEqual(data, works);
94}));
95
96putIn.run(['.clear']);
97
98// Tab Complete will not break in an ternary operator with ()
99putIn.run([
100  'var inner = ( true ',
101  '?',
102  '{one: 1} : ',
103]);
104testMe.complete('inner.o', getNoResultsFunction());
105
106putIn.run(['.clear']);
107
108// Tab Complete will return a simple local variable
109putIn.run([
110  'var top = function() {',
111  'var inner = {one:1};',
112]);
113testMe.complete('inner.o', getNoResultsFunction());
114
115// When you close the function scope tab complete will not return the
116// locally scoped variable
117putIn.run(['};']);
118testMe.complete('inner.o', getNoResultsFunction());
119
120putIn.run(['.clear']);
121
122// Tab Complete will return a complex local variable
123putIn.run([
124  'var top = function() {',
125  'var inner = {',
126  ' one:1',
127  '};',
128]);
129testMe.complete('inner.o', getNoResultsFunction());
130
131putIn.run(['.clear']);
132
133// Tab Complete will return a complex local variable even if the function
134// has parameters
135putIn.run([
136  'var top = function(one, two) {',
137  'var inner = {',
138  ' one:1',
139  '};',
140]);
141testMe.complete('inner.o', getNoResultsFunction());
142
143putIn.run(['.clear']);
144
145// Tab Complete will return a complex local variable even if the
146// scope is nested inside an immediately executed function
147putIn.run([
148  'var top = function() {',
149  '(function test () {',
150  'var inner = {',
151  ' one:1',
152  '};',
153]);
154testMe.complete('inner.o', getNoResultsFunction());
155
156putIn.run(['.clear']);
157
158// The definition has the params and { on a separate line.
159putIn.run([
160  'var top = function() {',
161  'r = function test (',
162  ' one, two) {',
163  'var inner = {',
164  ' one:1',
165  '};',
166]);
167testMe.complete('inner.o', getNoResultsFunction());
168
169putIn.run(['.clear']);
170
171// Currently does not work, but should not break, not the {
172putIn.run([
173  'var top = function() {',
174  'r = function test ()',
175  '{',
176  'var inner = {',
177  ' one:1',
178  '};',
179]);
180testMe.complete('inner.o', getNoResultsFunction());
181
182putIn.run(['.clear']);
183
184// Currently does not work, but should not break
185putIn.run([
186  'var top = function() {',
187  'r = function test (',
188  ')',
189  '{',
190  'var inner = {',
191  ' one:1',
192  '};',
193]);
194testMe.complete('inner.o', getNoResultsFunction());
195
196putIn.run(['.clear']);
197
198// Make sure tab completion works on non-Objects
199putIn.run([
200  'var str = "test";',
201]);
202testMe.complete('str.len', common.mustCall(function(error, data) {
203  assert.deepStrictEqual(data, [['str.length'], 'str.len']);
204}));
205
206putIn.run(['.clear']);
207
208// Tab completion should be case-insensitive if member part is lower-case
209putIn.run([
210  'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
211]);
212testMe.complete(
213  'foo.b',
214  common.mustCall(function(error, data) {
215    assert.deepStrictEqual(data, [
216      ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
217      'foo.b',
218    ]);
219  })
220);
221
222putIn.run(['.clear']);
223
224// Tab completion should be case-insensitive if member part is upper-case
225putIn.run([
226  'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
227]);
228testMe.complete(
229  'foo.B',
230  common.mustCall(function(error, data) {
231    assert.deepStrictEqual(data, [
232      ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
233      'foo.B',
234    ]);
235  })
236);
237
238putIn.run(['.clear']);
239
240// Tab completion should not break on spaces
241const spaceTimeout = setTimeout(function() {
242  throw new Error('timeout');
243}, 1000);
244
245testMe.complete(' ', common.mustSucceed((data) => {
246  assert.strictEqual(data[1], '');
247  assert.ok(data[0].includes('globalThis'));
248  clearTimeout(spaceTimeout);
249}));
250
251// Tab completion should pick up the global "toString" object, and
252// any other properties up the "global" object's prototype chain
253testMe.complete('toSt', common.mustCall(function(error, data) {
254  assert.deepStrictEqual(data, [['toString'], 'toSt']);
255}));
256
257// Own properties should shadow properties on the prototype
258putIn.run(['.clear']);
259putIn.run([
260  'var x = Object.create(null);',
261  'x.a = 1;',
262  'x.b = 2;',
263  'var y = Object.create(x);',
264  'y.a = 3;',
265  'y.c = 4;',
266]);
267testMe.complete('y.', common.mustCall(function(error, data) {
268  assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']);
269}));
270
271// Tab complete provides built in libs for require()
272putIn.run(['.clear']);
273
274testMe.complete('require(\'', common.mustCall(function(error, data) {
275  assert.strictEqual(error, null);
276  publicModules.forEach((lib) => {
277    assert(
278      data[0].includes(lib) && data[0].includes(`node:${lib}`),
279      `${lib} not found`
280    );
281  });
282  const newModule = 'foobar';
283  assert(!builtinModules.includes(newModule));
284  repl.builtinModules.push(newModule);
285  testMe.complete('require(\'', common.mustCall((_, [modules]) => {
286    assert.strictEqual(data[0].length + 1, modules.length);
287    assert(modules.includes(newModule));
288  }));
289}));
290
291testMe.complete("require\t( 'n", common.mustCall(function(error, data) {
292  assert.strictEqual(error, null);
293  assert.strictEqual(data.length, 2);
294  assert.strictEqual(data[1], 'n');
295  // require(...) completions include `node:`-prefixed modules:
296  let lastIndex = -1;
297
298  publicModules.forEach((lib, index) => {
299    lastIndex = data[0].indexOf(`node:${lib}`);
300    assert.notStrictEqual(lastIndex, -1);
301  });
302  assert.strictEqual(data[0][lastIndex + 1], '');
303  // There is only one Node.js module that starts with n:
304  assert.strictEqual(data[0][lastIndex + 2], 'net');
305  assert.strictEqual(data[0][lastIndex + 3], '');
306  // It's possible to pick up non-core modules too
307  data[0].slice(lastIndex + 4).forEach((completion) => {
308    assert.match(completion, /^n/);
309  });
310}));
311
312{
313  const expected = ['@nodejsscope', '@nodejsscope/'];
314  // Require calls should handle all types of quotation marks.
315  for (const quotationMark of ["'", '"', '`']) {
316    putIn.run(['.clear']);
317    testMe.complete('require(`@nodejs', common.mustCall((err, data) => {
318      assert.strictEqual(err, null);
319      assert.deepStrictEqual(data, [expected, '@nodejs']);
320    }));
321
322    putIn.run(['.clear']);
323    // Completions should not be greedy in case the quotation ends.
324    const input = `require(${quotationMark}@nodejsscope${quotationMark}`;
325    testMe.complete(input, common.mustCall((err, data) => {
326      assert.strictEqual(err, null);
327      assert.deepStrictEqual(data, [[], undefined]);
328    }));
329  }
330}
331
332{
333  putIn.run(['.clear']);
334  // Completions should find modules and handle whitespace after the opening
335  // bracket.
336  testMe.complete('require \t("no_ind', common.mustCall((err, data) => {
337    assert.strictEqual(err, null);
338    assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']);
339  }));
340}
341
342// Test tab completion for require() relative to the current directory
343{
344  putIn.run(['.clear']);
345
346  const cwd = process.cwd();
347  process.chdir(__dirname);
348
349  ['require(\'.', 'require(".'].forEach((input) => {
350    testMe.complete(input, common.mustCall((err, data) => {
351      assert.strictEqual(err, null);
352      assert.strictEqual(data.length, 2);
353      assert.strictEqual(data[1], '.');
354      assert.strictEqual(data[0].length, 2);
355      assert.ok(data[0].includes('./'));
356      assert.ok(data[0].includes('../'));
357    }));
358  });
359
360  ['require(\'..', 'require("..'].forEach((input) => {
361    testMe.complete(input, common.mustCall((err, data) => {
362      assert.strictEqual(err, null);
363      assert.deepStrictEqual(data, [['../'], '..']);
364    }));
365  });
366
367  ['./', './test-'].forEach((path) => {
368    [`require('${path}`, `require("${path}`].forEach((input) => {
369      testMe.complete(input, common.mustCall((err, data) => {
370        assert.strictEqual(err, null);
371        assert.strictEqual(data.length, 2);
372        assert.strictEqual(data[1], path);
373        assert.ok(data[0].includes('./test-repl-tab-complete'));
374      }));
375    });
376  });
377
378  ['../parallel/', '../parallel/test-'].forEach((path) => {
379    [`require('${path}`, `require("${path}`].forEach((input) => {
380      testMe.complete(input, common.mustCall((err, data) => {
381        assert.strictEqual(err, null);
382        assert.strictEqual(data.length, 2);
383        assert.strictEqual(data[1], path);
384        assert.ok(data[0].includes('../parallel/test-repl-tab-complete'));
385      }));
386    });
387  });
388
389  {
390    const path = '../fixtures/repl-folder-extensions/f';
391    testMe.complete(`require('${path}`, common.mustSucceed((data) => {
392      assert.strictEqual(data.length, 2);
393      assert.strictEqual(data[1], path);
394      assert.ok(data[0].includes('../fixtures/repl-folder-extensions/foo.js'));
395    }));
396  }
397
398  process.chdir(cwd);
399}
400
401// Make sure tab completion works on context properties
402putIn.run(['.clear']);
403
404putIn.run([
405  'var custom = "test";',
406]);
407testMe.complete('cus', common.mustCall(function(error, data) {
408  assert.deepStrictEqual(data, [['custom'], 'cus']);
409}));
410
411// Make sure tab completion doesn't crash REPL with half-baked proxy objects.
412// See: https://github.com/nodejs/node/issues/2119
413putIn.run(['.clear']);
414
415putIn.run([
416  'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});',
417]);
418
419testMe.complete('proxy.', common.mustCall(function(error, data) {
420  assert.strictEqual(error, null);
421  assert(Array.isArray(data));
422}));
423
424// Make sure tab completion does not include integer members of an Array
425putIn.run(['.clear']);
426
427putIn.run(['var ary = [1,2,3];']);
428testMe.complete('ary.', common.mustCall(function(error, data) {
429  assert.strictEqual(data[0].includes('ary.0'), false);
430  assert.strictEqual(data[0].includes('ary.1'), false);
431  assert.strictEqual(data[0].includes('ary.2'), false);
432}));
433
434// Make sure tab completion does not include integer keys in an object
435putIn.run(['.clear']);
436putIn.run(['var obj = {1:"a","1a":"b",a:"b"};']);
437
438testMe.complete('obj.', common.mustCall(function(error, data) {
439  assert.strictEqual(data[0].includes('obj.1'), false);
440  assert.strictEqual(data[0].includes('obj.1a'), false);
441  assert(data[0].includes('obj.a'));
442}));
443
444// Don't try to complete results of non-simple expressions
445putIn.run(['.clear']);
446putIn.run(['function a() {}']);
447
448testMe.complete('a().b.', getNoResultsFunction());
449
450// Works when prefixed with spaces
451putIn.run(['.clear']);
452putIn.run(['var obj = {1:"a","1a":"b",a:"b"};']);
453
454testMe.complete(' obj.', common.mustCall((error, data) => {
455  assert.strictEqual(data[0].includes('obj.1'), false);
456  assert.strictEqual(data[0].includes('obj.1a'), false);
457  assert(data[0].includes('obj.a'));
458}));
459
460// Works inside assignments
461putIn.run(['.clear']);
462
463testMe.complete('var log = console.lo', common.mustCall((error, data) => {
464  assert.deepStrictEqual(data, [['console.log'], 'console.lo']);
465}));
466
467// Tab completion for defined commands
468putIn.run(['.clear']);
469
470testMe.complete('.b', common.mustCall((error, data) => {
471  assert.deepStrictEqual(data, [['break'], 'b']);
472}));
473putIn.run(['.clear']);
474putIn.run(['var obj = {"hello, world!": "some string", "key": 123}']);
475testMe.complete('obj.', common.mustCall((error, data) => {
476  assert.strictEqual(data[0].includes('obj.hello, world!'), false);
477  assert(data[0].includes('obj.key'));
478}));
479
480// Make sure tab completion does not include __defineSetter__ and friends.
481putIn.run(['.clear']);
482
483putIn.run(['var obj = {};']);
484testMe.complete('obj.', common.mustCall(function(error, data) {
485  assert.strictEqual(data[0].includes('obj.__defineGetter__'), false);
486  assert.strictEqual(data[0].includes('obj.__defineSetter__'), false);
487  assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false);
488  assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false);
489  assert.strictEqual(data[0].includes('obj.__proto__'), true);
490}));
491
492// Tab completion for files/directories
493{
494  putIn.run(['.clear']);
495  process.chdir(__dirname);
496
497  const readFileSyncs = ['fs.readFileSync("', 'fs.promises.readFileSync("'];
498  if (!common.isWindows) {
499    readFileSyncs.forEach((readFileSync) => {
500      const fixturePath = `${readFileSync}../fixtures/test-repl-tab-completion`;
501      testMe.complete(fixturePath, common.mustCall((err, data) => {
502        assert.strictEqual(err, null);
503        assert.ok(data[0][0].includes('.hiddenfiles'));
504        assert.ok(data[0][1].includes('hellorandom.txt'));
505        assert.ok(data[0][2].includes('helloworld.js'));
506      }));
507
508      testMe.complete(`${fixturePath}/hello`,
509                      common.mustCall((err, data) => {
510                        assert.strictEqual(err, null);
511                        assert.ok(data[0][0].includes('hellorandom.txt'));
512                        assert.ok(data[0][1].includes('helloworld.js'));
513                      })
514      );
515
516      testMe.complete(`${fixturePath}/.h`,
517                      common.mustCall((err, data) => {
518                        assert.strictEqual(err, null);
519                        assert.ok(data[0][0].includes('.hiddenfiles'));
520                      })
521      );
522
523      testMe.complete(`${readFileSync}./xxxRandom/random`,
524                      common.mustCall((err, data) => {
525                        assert.strictEqual(err, null);
526                        assert.strictEqual(data[0].length, 0);
527                      })
528      );
529
530      const testPath = fixturePath.slice(0, -1);
531      testMe.complete(testPath, common.mustCall((err, data) => {
532        assert.strictEqual(err, null);
533        assert.ok(data[0][0].includes('test-repl-tab-completion'));
534        assert.strictEqual(
535          data[1],
536          path.basename(testPath)
537        );
538      }));
539    });
540  }
541}
542
543[
544  Array,
545  Buffer,
546
547  Uint8Array,
548  Uint16Array,
549  Uint32Array,
550
551  Uint8ClampedArray,
552  Int8Array,
553  Int16Array,
554  Int32Array,
555  Float32Array,
556  Float64Array,
557].forEach((type) => {
558  putIn.run(['.clear']);
559
560  if (type === Array) {
561    putIn.run([
562      'var ele = [];',
563      'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;',
564      'ele.biu = 1;',
565    ]);
566  } else if (type === Buffer) {
567    putIn.run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']);
568  } else {
569    putIn.run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]);
570  }
571
572  hijackStderr(common.mustNotCall());
573  testMe.complete('ele.', common.mustCall((err, data) => {
574    restoreStderr();
575    assert.ifError(err);
576
577    const ele = (type === Array) ?
578      [] :
579      (type === Buffer ?
580        Buffer.alloc(0) :
581        new type(0));
582
583    assert.strictEqual(data[0].includes('ele.biu'), true);
584
585    data[0].forEach((key) => {
586      if (!key || key === 'ele.biu') return;
587      assert.notStrictEqual(ele[key.substr(4)], undefined);
588    });
589  }));
590});
591
592// check Buffer.prototype.length not crashing.
593// Refs: https://github.com/nodejs/node/pull/11961
594putIn.run(['.clear']);
595testMe.complete('Buffer.prototype.', common.mustCall());
596
597// Make sure repl gives correct autocomplete on literals
598testMe.complete('``.a', common.mustCall((err, data) => {
599  assert.strictEqual(data[0].includes('``.at'), true);
600}));
601testMe.complete('\'\'.a', common.mustCall((err, data) => {
602  assert.strictEqual(data[0].includes('\'\'.at'), true);
603}));
604testMe.complete('"".a', common.mustCall((err, data) => {
605  assert.strictEqual(data[0].includes('"".at'), true);
606}));
607testMe.complete('("").a', common.mustCall((err, data) => {
608  assert.strictEqual(data[0].includes('("").at'), true);
609}));
610testMe.complete('[].a', common.mustCall((err, data) => {
611  assert.strictEqual(data[0].includes('[].at'), true);
612}));
613testMe.complete('{}.a', common.mustCall((err, data) => {
614  assert.deepStrictEqual(data[0], []);
615}));
616
617const testNonGlobal = repl.start({
618  input: putIn,
619  output: putIn,
620  useGlobal: false
621});
622
623const builtins = [
624  [
625    'if',
626    'import',
627    'in',
628    'instanceof',
629    '',
630    'Infinity',
631    'Int16Array',
632    'Int32Array',
633    'Int8Array',
634    ...(common.hasIntl ? ['Intl'] : []),
635    'inspector',
636    'isFinite',
637    'isNaN',
638    '',
639    'isPrototypeOf',
640  ],
641  'I',
642];
643
644testNonGlobal.complete('I', common.mustCall((error, data) => {
645  assert.deepStrictEqual(data, builtins);
646}));
647
648// To test custom completer function.
649// Sync mode.
650const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' ');
651const testCustomCompleterSyncMode = repl.start({
652  prompt: '',
653  input: putIn,
654  output: putIn,
655  completer: function completer(line) {
656    const hits = customCompletions.filter((c) => c.startsWith(line));
657    // Show all completions if none found.
658    return [hits.length ? hits : customCompletions, line];
659  }
660});
661
662// On empty line should output all the custom completions
663// without complete anything.
664testCustomCompleterSyncMode.complete('', common.mustCall((error, data) => {
665  assert.deepStrictEqual(data, [
666    customCompletions,
667    '',
668  ]);
669}));
670
671// On `a` should output `aaa aa1 aa2` and complete until `aa`.
672testCustomCompleterSyncMode.complete('a', common.mustCall((error, data) => {
673  assert.deepStrictEqual(data, [
674    'aaa aa1 aa2'.split(' '),
675    'a',
676  ]);
677}));
678
679// To test custom completer function.
680// Async mode.
681const testCustomCompleterAsyncMode = repl.start({
682  prompt: '',
683  input: putIn,
684  output: putIn,
685  completer: function completer(line, callback) {
686    const hits = customCompletions.filter((c) => c.startsWith(line));
687    // Show all completions if none found.
688    callback(null, [hits.length ? hits : customCompletions, line]);
689  }
690});
691
692// On empty line should output all the custom completions
693// without complete anything.
694testCustomCompleterAsyncMode.complete('', common.mustCall((error, data) => {
695  assert.deepStrictEqual(data, [
696    customCompletions,
697    '',
698  ]);
699}));
700
701// On `a` should output `aaa aa1 aa2` and complete until `aa`.
702testCustomCompleterAsyncMode.complete('a', common.mustCall((error, data) => {
703  assert.deepStrictEqual(data, [
704    'aaa aa1 aa2'.split(' '),
705    'a',
706  ]);
707}));
708
709// Tab completion in editor mode
710const editorStream = new ArrayStream();
711const editor = repl.start({
712  stream: editorStream,
713  terminal: true,
714  useColors: false
715});
716
717editorStream.run(['.clear']);
718editorStream.run(['.editor']);
719
720editor.completer('Uin', common.mustCall((error, data) => {
721  assert.deepStrictEqual(data, [['Uint'], 'Uin']);
722}));
723
724editorStream.run(['.clear']);
725editorStream.run(['.editor']);
726
727editor.completer('var log = console.l', common.mustCall((error, data) => {
728  assert.deepStrictEqual(data, [['console.log'], 'console.l']);
729}));
730
731{
732  // Tab completion of lexically scoped variables
733  const stream = new ArrayStream();
734  const testRepl = repl.start({ stream });
735
736  stream.run([`
737    let lexicalLet = true;
738    const lexicalConst = true;
739    class lexicalKlass {}
740  `]);
741
742  ['Let', 'Const', 'Klass'].forEach((type) => {
743    const query = `lexical${type[0]}`;
744    const expected = hasInspector ? [[`lexical${type}`], query] :
745      [[], `lexical${type[0]}`];
746    testRepl.complete(query, common.mustCall((error, data) => {
747      assert.deepStrictEqual(data, expected);
748    }));
749  });
750}
751