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