node/test/parallel/test-repl-tab-complete-computed-props.js
Dario Piotrowicz 8ba66c5e7b
repl: improve tab completion on computed properties
improve the tab completion capabilities around computed properties
by replacing the use of brittle and error prone Regex checks with
more robust AST based analysis

PR-URL: https://github.com/nodejs/node/pull/58775
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2025-06-26 01:50:46 +00:00

159 lines
4.5 KiB
JavaScript

'use strict';
const common = require('../common');
const ArrayStream = require('../common/arraystream');
const { describe, it, before, after } = require('node:test');
const assert = require('assert');
const repl = require('repl');
function prepareREPL() {
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);
return { replServer, input };
}
function testCompletion(replServer, { input, expectedCompletions }) {
replServer.complete(
input,
common.mustCall((_error, data) => {
assert.deepStrictEqual(data, [expectedCompletions, input]);
}),
);
};
describe('REPL tab object completion on computed properties', () => {
describe('simple string cases', () => {
let replServer;
before(() => {
const { replServer: server, input } = prepareREPL();
replServer = server;
input.run([
`
const obj = {
one: 1,
innerObj: { two: 2 },
'inner object': { three: 3 },
};
const oneStr = 'one';
`,
]);
});
after(() => {
replServer.close();
});
it('works with double quoted strings', () => testCompletion(replServer, {
input: 'obj["one"].toFi',
expectedCompletions: ['obj["one"].toFixed'],
}));
it('works with single quoted strings', () => testCompletion(replServer, {
input: "obj['one'].toFi",
expectedCompletions: ["obj['one'].toFixed"],
}));
it('works with template strings', () => testCompletion(replServer, {
input: 'obj[`one`].toFi',
expectedCompletions: ['obj[`one`].toFixed'],
}));
it('works with nested objects', () => {
testCompletion(replServer, {
input: 'obj["innerObj"].tw',
expectedCompletions: ['obj["innerObj"].two'],
});
testCompletion(replServer, {
input: 'obj["innerObj"].two.tofi',
expectedCompletions: ['obj["innerObj"].two.toFixed'],
});
});
it('works with nested objects combining different type of strings', () => testCompletion(replServer, {
input: 'obj["innerObj"][`two`].tofi',
expectedCompletions: ['obj["innerObj"][`two`].toFixed'],
}));
it('works with strings with spaces', () => testCompletion(replServer, {
input: 'obj["inner object"].th',
expectedCompletions: ['obj["inner object"].three'],
}));
});
describe('variables as indexes', () => {
let replServer;
before(() => {
const { replServer: server, input } = prepareREPL();
replServer = server;
input.run([
`
const oneStr = 'One';
const helloWorldStr = 'Hello' + ' ' + 'World';
const obj = {
[oneStr]: 1,
['Hello World']: 'hello world!',
};
const lookupObj = {
stringLookup: helloWorldStr,
['number lookup']: oneStr,
};
`,
]);
});
after(() => {
replServer.close();
});
it('works with a simple variable', () => testCompletion(replServer, {
input: 'obj[oneStr].toFi',
expectedCompletions: ['obj[oneStr].toFixed'],
}));
it('works with a computed variable', () => testCompletion(replServer, {
input: 'obj[helloWorldStr].tolocaleup',
expectedCompletions: ['obj[helloWorldStr].toLocaleUpperCase'],
}));
it('works with a simple inlined computed property', () => testCompletion(replServer, {
input: 'obj["Hello " + "World"].tolocaleup',
expectedCompletions: ['obj["Hello " + "World"].toLocaleUpperCase'],
}));
it('works with a ternary inlined computed property', () => testCompletion(replServer, {
input: 'obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase',
expectedCompletions: ['obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase'],
}));
it('works with an inlined computed property with a nested property lookup', () =>
testCompletion(replServer, {
input: 'obj[lookupObj.stringLookup].tolocaleupp',
expectedCompletions: ['obj[lookupObj.stringLookup].toLocaleUpperCase'],
})
);
it('works with an inlined computed property with a nested inlined computer property lookup', () =>
testCompletion(replServer, {
input: 'obj[lookupObj["number" + " lookup"]].toFi',
expectedCompletions: ['obj[lookupObj["number" + " lookup"]].toFixed'],
})
);
});
});