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