@jupyter-lsp/jupyterlab-lsp
Version:
Language Server Protocol integration for JupyterLab
187 lines • 7.29 kB
JavaScript
import { CompletionItem } from './item';
import { LSPCompleterModel } from './model';
describe('LSPCompleterModel', () => {
let model;
function createDummyItem(match, type = 'dummy', source = 'LSP') {
return new CompletionItem({
type,
icon: null,
match,
connection: null,
source: source
});
}
const jupyterIconCompletion = createDummyItem({
label: '<i class="jp-icon-jupyter"></i> Jupyter',
filterText: 'i font icon jupyter Jupyter',
documentation: 'A Jupyter icon implemented with <i> tag'
});
const testCompletionA = createDummyItem({
label: 'test',
sortText: 'a'
});
const testCompletionB = createDummyItem({
label: 'test',
sortText: 'b'
});
const testCompletionC = createDummyItem({
label: 'test',
sortText: 'c'
});
const testCompletionTest = createDummyItem({
label: 'test_test',
sortText: 'test_test'
});
beforeEach(() => {
model = new LSPCompleterModel({
caseSensitive: true,
includePerfectMatches: true
});
});
it('returns escaped when no query', () => {
model.setCompletionItems([jupyterIconCompletion]);
model.query = '';
let markedItems = model.completionItems();
expect(markedItems[0].label).toBe('<i class="jp-icon-jupyter"></i> Jupyter');
});
it('marks html correctly', () => {
model.setCompletionItems([jupyterIconCompletion]);
model.query = 'Jup';
let markedItems = model.completionItems();
expect(markedItems[0].label).toBe('<i class="jp-icon-jupyter"></i> <mark>Jup</mark>yter');
});
it('ties are solved with sortText', () => {
model.setCompletionItems([
testCompletionA,
testCompletionC,
testCompletionB
]);
model.query = 'test';
let sortedItems = model.completionItems();
expect(sortedItems.map(item => item.sortText)).toEqual(['a', 'b', 'c']);
});
describe('pre-filtering', () => {
beforeEach(() => {
// order of cursor/current matters
model.current = model.original = {
text: 'a',
line: 0,
column: 1
};
model.cursor = { start: 0, end: 1 };
});
const prefixA = createDummyItem({
label: 'a'
});
const prefixB = createDummyItem({
label: 'b'
});
const prefixBButNotLSP = createDummyItem({
label: 'b'
}, 'dummy', 'not LSP');
it('filters out non-matching LSP completions', () => {
model.setCompletionItems([prefixA, prefixB]);
let items = model.completionItems();
expect(items.map(item => item.insertText)).toEqual(['a']);
});
it('does not filter out non LSP completions', () => {
model.setCompletionItems([prefixA, prefixBButNotLSP]);
let items = model.completionItems();
expect(items.map(item => item.insertText)).toEqual(['a', 'b']);
});
it('does not filter out when turned off', () => {
model.setCompletionItems([prefixA, prefixB]);
model.settings.preFilterMatches = false;
let items = model.completionItems();
expect(items.map(item => item.insertText)).toEqual(['a']);
});
});
describe('sorting by source', () => {
const testCompletionA = createDummyItem({
label: 'test'
}, 'a', 'LSP');
const testCompletionB = createDummyItem({
label: 'test'
}, 'b', 'kernel');
const testCompletionC = createDummyItem({
label: 'test'
}, 'c', 'context');
const testCompletionD = createDummyItem({
label: 'test'
}, 'd', 'unknown');
const completionsFromDifferentSources = [
testCompletionA,
testCompletionC,
testCompletionB
];
it('completions are sorted by source', () => {
model.setCompletionItems(completionsFromDifferentSources);
model.query = 'test';
let sortedItems = model.completionItems();
expect(sortedItems.map(item => item.type)).toEqual(['a', 'b', 'c']);
});
it('kernel completions can be prioritised', () => {
model.setCompletionItems(completionsFromDifferentSources);
model.query = 'test';
model.settings.kernelCompletionsFirst = true;
let sortedItems = model.completionItems();
expect(sortedItems.map(item => item.type)).toEqual(['b', 'a', 'c']);
});
it('completions from unknown source land at the end', () => {
model.setCompletionItems([
testCompletionD,
...completionsFromDifferentSources
]);
model.query = 'test';
let sortedItems = model.completionItems();
const types = sortedItems.map(item => item.type);
expect(types[types.length - 1]).toEqual('d');
});
});
it('ignores perfect matches when asked', () => {
model = new LSPCompleterModel({
includePerfectMatches: false
});
model.setCompletionItems([testCompletionA, testCompletionTest]);
model.query = 'test';
let items = model.completionItems();
// should not include the perfect match 'test'
expect(items.length).toBe(1);
expect(items.map(item => item.sortText)).toEqual(['test_test']);
});
it('case-sensitivity can be changed', () => {
model = new LSPCompleterModel();
model.setCompletionItems([testCompletionA]);
model.query = 'Test';
model.settings.caseSensitive = true;
let items = model.completionItems();
expect(items.length).toBe(0);
model.settings.caseSensitive = false;
items = model.completionItems();
expect(items.length).toBe(1);
});
it('filters use filterText', () => {
model.setCompletionItems([jupyterIconCompletion]);
// font is in filterText but not in label
model.query = 'font';
let filteredItems = model.completionItems();
expect(filteredItems.length).toBe(1);
// class is in label but not in filterText
model.query = 'class';
filteredItems = model.completionItems();
expect(filteredItems.length).toBe(0);
});
it('marks appropriate part of label when filterText matches', () => {
model.setCompletionItems([jupyterIconCompletion]);
// font is in filterText but not in label
model.query = 'font';
// nothing should get highlighted
let markedItems = model.completionItems();
expect(markedItems[0].label).toBe('<i class="jp-icon-jupyter"></i> Jupyter');
// i is in both label and filterText
model.query = 'i';
markedItems = model.completionItems();
expect(markedItems[0].label).toBe('<<mark>i</mark> class="jp-icon-jupyter"></i> Jupyter');
});
});
//# sourceMappingURL=model.spec.js.map