Skip to content

test: refactor repl tab complete tests #58636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions test/parallel/test-repl-tab-complete-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict';

const common = require('../common');
const ArrayStream = require('../common/arraystream');
const { hijackStderr, restoreStderr } = require('../common/hijackstdio');
const assert = require('assert');

const repl = require('repl');

const input = new ArrayStream();
const replServer = repl.start({
prompt: '',
input,
output: process.stdout,
allowBlockingCompletions: true,
});

// Some errors are passed to the domain, but do not callback
replServer._domain.on('error', assert.ifError);

for (const type of [
Array,
Buffer,

Uint8Array,
Uint16Array,
Uint32Array,

Uint8ClampedArray,
Int8Array,
Int16Array,
Int32Array,
Float32Array,
Float64Array,
]) {
input.run(['.clear']);

if (type === Array) {
input.run([
'var ele = [];',
'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;',
'ele.biu = 1;',
]);
} else if (type === Buffer) {
input.run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']);
} else {
input.run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]);
}

hijackStderr(common.mustNotCall());
replServer.complete(
'ele.',
common.mustCall((err, data) => {
restoreStderr();
assert.ifError(err);

const ele =
type === Array ? [] : type === Buffer ? Buffer.alloc(0) : new type(0);

assert.strictEqual(data[0].includes('ele.biu'), true);

data[0].forEach((key) => {
if (!key || key === 'ele.biu') return;
assert.notStrictEqual(ele[key.slice(4)], undefined);
});
})
);
}

// check Buffer.prototype.length not crashing.
// Refs: https://github.com/nodejs/node/pull/11961
input.run(['.clear']);
replServer.complete('Buffer.prototype.', common.mustCall());
75 changes: 75 additions & 0 deletions test/parallel/test-repl-tab-complete-custom-completer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const common = require('../common');
const ArrayStream = require('../common/arraystream');
const assert = require('assert');

const repl = require('repl');

const putIn = new ArrayStream();

// To test custom completer function.
// Sync mode.
{
const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' ');
const testCustomCompleterSyncMode = repl.start({
prompt: '',
input: putIn,
output: putIn,
completer: function completer(line) {
const hits = customCompletions.filter((c) => c.startsWith(line));
// Show all completions if none found.
return [hits.length ? hits : customCompletions, line];
}
});

// On empty line should output all the custom completions
// without complete anything.
testCustomCompleterSyncMode.complete('', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [
customCompletions,
'',
]);
}));

// On `a` should output `aaa aa1 aa2` and complete until `aa`.
testCustomCompleterSyncMode.complete('a', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [
'aaa aa1 aa2'.split(' '),
'a',
]);
}));
}

// To test custom completer function.
// Async mode.
{
const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' ');
const testCustomCompleterAsyncMode = repl.start({
prompt: '',
input: putIn,
output: putIn,
completer: function completer(line, callback) {
const hits = customCompletions.filter((c) => c.startsWith(line));
// Show all completions if none found.
callback(null, [hits.length ? hits : customCompletions, line]);
}
});

// On empty line should output all the custom completions
// without complete anything.
testCustomCompleterAsyncMode.complete('', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [
customCompletions,
'',
]);
}));

// On `a` should output `aaa aa1 aa2` and complete until `aa`.
testCustomCompleterAsyncMode.complete('a', common.mustCall((error, data) => {
assert.deepStrictEqual(data, [
'aaa aa1 aa2'.split(' '),
'a',
]);
}));
}
80 changes: 80 additions & 0 deletions test/parallel/test-repl-tab-complete-files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use strict';

const common = require('../common');
const ArrayStream = require('../common/arraystream');
const assert = require('assert');
const path = require('path');

const { isMainThread } = require('worker_threads');

if (!isMainThread) {
common.skip('process.chdir is not available in Workers');
}

const repl = require('repl');

const replServer = repl.start({
prompt: '',
input: new ArrayStream(),
output: process.stdout,
allowBlockingCompletions: true,
});

// Some errors are passed to the domain, but do not callback
replServer._domain.on('error', assert.ifError);

// Tab completion for files/directories
{
process.chdir(__dirname);

const readFileSyncs = ['fs.readFileSync("', 'fs.promises.readFileSync("'];
if (!common.isWindows) {
readFileSyncs.forEach((readFileSync) => {
const fixturePath = `${readFileSync}../fixtures/test-repl-tab-completion`;
replServer.complete(
fixturePath,
common.mustCall((err, data) => {
assert.strictEqual(err, null);
assert.ok(data[0][0].includes('.hiddenfiles'));
assert.ok(data[0][1].includes('hellorandom.txt'));
assert.ok(data[0][2].includes('helloworld.js'));
})
);

replServer.complete(
`${fixturePath}/hello`,
common.mustCall((err, data) => {
assert.strictEqual(err, null);
assert.ok(data[0][0].includes('hellorandom.txt'));
assert.ok(data[0][1].includes('helloworld.js'));
})
);

replServer.complete(
`${fixturePath}/.h`,
common.mustCall((err, data) => {
assert.strictEqual(err, null);
assert.ok(data[0][0].includes('.hiddenfiles'));
})
);

replServer.complete(
`${readFileSync}./xxxRandom/random`,
common.mustCall((err, data) => {
assert.strictEqual(err, null);
assert.strictEqual(data[0].length, 0);
})
);

const testPath = fixturePath.slice(0, -1);
replServer.complete(
testPath,
common.mustCall((err, data) => {
assert.strictEqual(err, null);
assert.ok(data[0][0].includes('test-repl-tab-completion'));
assert.strictEqual(data[1], path.basename(testPath));
})
);
});
}
}
51 changes: 39 additions & 12 deletions test/parallel/test-repl-tab-complete-on-editor-mode.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
'use strict';

require('../common');
const common = require('../common');
const assert = require('assert');
const ArrayStream = require('../common/arraystream');
const repl = require('repl');

const stream = new ArrayStream();
const replServer = repl.start({
input: stream,
output: stream,
terminal: true,
});
// Tab completion in editor mode
{
const editorStream = new ArrayStream();
const editor = repl.start({
stream: editorStream,
terminal: true,
useColors: false
});

// Editor mode
replServer.write('.editor\n');
editorStream.run(['.clear']);
editorStream.run(['.editor']);

editor.completer('Uin', common.mustCall((_error, data) => {
assert.deepStrictEqual(data, [['Uint'], 'Uin']);
}));

editorStream.run(['.clear']);
editorStream.run(['.editor']);

editor.completer('var log = console.l', common.mustCall((_error, data) => {
assert.deepStrictEqual(data, [['console.log'], 'console.l']);
}));
}

// Regression test for https://github.com/nodejs/node/issues/43528
replServer.write('a');
replServer.write(null, { name: 'tab' }); // Should not throw
{
const stream = new ArrayStream();
const replServer = repl.start({
input: stream,
output: stream,
terminal: true,
});

// Editor mode
replServer.write('.editor\n');

replServer.write('a');
replServer.write(null, { name: 'tab' }); // Should not throw

replServer.close();
replServer.close();
}
Loading
Loading