test(language-service): Completions test should reuse existing host and services (#33478)
Reusing the single instance of MockHost and language services makes the tests run much faster. PR Close #33478
This commit is contained in:
parent
300d7ca6da
commit
31dccab2da
|
@ -9,7 +9,7 @@
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {createLanguageService} from '../src/language_service';
|
import {createLanguageService} from '../src/language_service';
|
||||||
import {CompletionKind, LanguageService} from '../src/types';
|
import {CompletionKind} from '../src/types';
|
||||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||||
|
|
||||||
import {MockTypescriptHost} from './test_utils';
|
import {MockTypescriptHost} from './test_utils';
|
||||||
|
@ -25,6 +25,8 @@ describe('completions', () => {
|
||||||
const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
|
const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
|
||||||
const ngLS = createLanguageService(ngHost);
|
const ngLS = createLanguageService(ngHost);
|
||||||
|
|
||||||
|
beforeEach(() => { mockHost.reset(); });
|
||||||
|
|
||||||
it('should be able to get entity completions', () => {
|
it('should be able to get entity completions', () => {
|
||||||
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp');
|
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp');
|
||||||
const completions = ngLS.getCompletionsAt(APP_COMPONENT, marker.start);
|
const completions = ngLS.getCompletionsAt(APP_COMPONENT, marker.start);
|
||||||
|
@ -370,192 +372,182 @@ describe('completions', () => {
|
||||||
// expectContain(completions, CompletionKind.PROPERTY, ['innerText']);
|
// expectContain(completions, CompletionKind.PROPERTY, ['innerText']);
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('replace completions correctly', () => {
|
describe('replacement span', () => {
|
||||||
const mockHost = new MockTypescriptHost(['/app/main.ts']);
|
it('should not generate replacement entries for zero-length replacements', () => {
|
||||||
let ngLS: LanguageService;
|
const fileName = mockHost.addCode(`
|
||||||
|
@Component({
|
||||||
|
selector: 'foo-component',
|
||||||
|
template: \`
|
||||||
|
<div>{{obj.~{key}}}</div>
|
||||||
|
\`,
|
||||||
|
})
|
||||||
|
export class FooComponent {
|
||||||
|
obj: {key: 'value'};
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
||||||
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
|
expect(completions).toBeDefined();
|
||||||
|
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
||||||
|
expect(completion).toBeDefined();
|
||||||
|
expect(completion.kind).toBe('property');
|
||||||
|
expect(completion.replacementSpan).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
it('should work for start of template', () => {
|
||||||
mockHost.reset();
|
const fileName = mockHost.addCode(`
|
||||||
const tsLS = ts.createLanguageService(mockHost);
|
@Component({
|
||||||
const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
|
selector: 'foo-component',
|
||||||
ngLS = createLanguageService(ngHost);
|
template: \`~{start}abc\`,
|
||||||
});
|
})
|
||||||
|
export class FooComponent {}
|
||||||
|
`);
|
||||||
|
const location = mockHost.getLocationMarkerFor(fileName, 'start');
|
||||||
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
|
expect(completions).toBeDefined();
|
||||||
|
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
||||||
|
expect(completion).toBeDefined();
|
||||||
|
expect(completion.kind).toBe('html element');
|
||||||
|
expect(completion.replacementSpan).toEqual({start: location.start, length: 3});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not generate replacement entries for zero-length replacements', () => {
|
it('should work for end of template', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`
|
template: \`acro~{end}\`,
|
||||||
<div>{{obj.~{key}}}</div>
|
})
|
||||||
\`,
|
export class FooComponent {}
|
||||||
})
|
`);
|
||||||
export class FooComponent {
|
const location = mockHost.getLocationMarkerFor(fileName, 'end');
|
||||||
obj: {key: 'value'};
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
}
|
expect(completions).toBeDefined();
|
||||||
`);
|
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
expect(completion).toBeDefined();
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
expect(completion.kind).toBe('html element');
|
||||||
expect(completions).toBeDefined();
|
expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4});
|
||||||
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
});
|
||||||
expect(completion).toBeDefined();
|
|
||||||
expect(completion.kind).toBe('property');
|
|
||||||
expect(completion.replacementSpan).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work for start of template', () => {
|
it('should work for middle-word replacements', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`~{start}abc\`,
|
template: \`
|
||||||
})
|
<div>{{obj.ke~{key}key}}</div>
|
||||||
export class FooComponent {}
|
\`,
|
||||||
`);
|
})
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'start');
|
export class FooComponent {
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
obj: {key: 'value'};
|
||||||
expect(completions).toBeDefined();
|
}
|
||||||
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
`);
|
||||||
expect(completion).toBeDefined();
|
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
||||||
expect(completion.kind).toBe('html element');
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start, length: 3});
|
expect(completions).toBeDefined();
|
||||||
});
|
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
||||||
|
expect(completion).toBeDefined();
|
||||||
|
expect(completion.kind).toBe('property');
|
||||||
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5});
|
||||||
|
});
|
||||||
|
|
||||||
it('should work for end of template', () => {
|
it('should work for all kinds of identifier characters', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`acro~{end}\`,
|
template: \`
|
||||||
})
|
<div>{{~{field}$title_1}}</div>
|
||||||
export class FooComponent {}
|
\`,
|
||||||
`);
|
})
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'end');
|
export class FooComponent {
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
$title_1: string;
|
||||||
expect(completions).toBeDefined();
|
}
|
||||||
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
|
`);
|
||||||
expect(completion).toBeDefined();
|
const location = mockHost.getLocationMarkerFor(fileName, 'field');
|
||||||
expect(completion.kind).toBe('html element');
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4});
|
expect(completions).toBeDefined();
|
||||||
});
|
const completion = completions.entries.find(entry => entry.name === '$title_1') !;
|
||||||
|
expect(completion).toBeDefined();
|
||||||
|
expect(completion.kind).toBe('property');
|
||||||
|
expect(completion.replacementSpan).toEqual({start: location.start, length: 8});
|
||||||
|
});
|
||||||
|
|
||||||
it('should work for middle-word replacements', () => {
|
it('should work for attributes', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`
|
template: \`
|
||||||
<div>{{obj.ke~{key}key}}</div>
|
<div cl~{click}></div>
|
||||||
\`,
|
\`,
|
||||||
})
|
})
|
||||||
export class FooComponent {
|
export class FooComponent {}
|
||||||
obj: {key: 'value'};
|
`);
|
||||||
}
|
const location = mockHost.getLocationMarkerFor(fileName, 'click');
|
||||||
`);
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'key');
|
expect(completions).toBeDefined();
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
const completion = completions.entries.find(entry => entry.name === '(click)') !;
|
||||||
expect(completions).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === 'key') !;
|
expect(completion.kind).toBe('attribute');
|
||||||
expect(completion).toBeDefined();
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
||||||
expect(completion.kind).toBe('property');
|
});
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work for all kinds of identifier characters', () => {
|
it('should work for events', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`
|
template: \`
|
||||||
<div>{{~{field}$title_1}}</div>
|
<div (click)="han~{handleClick}"></div>
|
||||||
\`,
|
\`,
|
||||||
})
|
})
|
||||||
export class FooComponent {
|
export class FooComponent {
|
||||||
$title_1: string;
|
handleClick() {}
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'field');
|
const location = mockHost.getLocationMarkerFor(fileName, 'handleClick');
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === '$title_1') !;
|
const completion = completions.entries.find(entry => entry.name === 'handleClick') !;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('property');
|
expect(completion.kind).toBe('method');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start, length: 8});
|
expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work for attributes', () => {
|
it('should work for element names', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`
|
template: \`
|
||||||
<div cl~{click}></div>
|
<di~{div}></div>
|
||||||
\`,
|
\`,
|
||||||
})
|
})
|
||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'click');
|
const location = mockHost.getLocationMarkerFor(fileName, 'div');
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === '(click)') !;
|
const completion = completions.entries.find(entry => entry.name === 'div') !;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('attribute');
|
expect(completion.kind).toBe('html element');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work for events', () => {
|
it('should work for bindings', () => {
|
||||||
const fileName = mockHost.addCode(`
|
const fileName = mockHost.addCode(`
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'foo-component',
|
selector: 'foo-component',
|
||||||
template: \`
|
template: \`
|
||||||
<div (click)="han~{handleClick}"></div>
|
|
||||||
\`,
|
|
||||||
})
|
|
||||||
export class FooComponent {
|
|
||||||
handleClick() {}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'handleClick');
|
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
|
||||||
expect(completions).toBeDefined();
|
|
||||||
const completion = completions.entries.find(entry => entry.name === 'handleClick') !;
|
|
||||||
expect(completion).toBeDefined();
|
|
||||||
expect(completion.kind).toBe('method');
|
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work for element names', () => {
|
|
||||||
const fileName = mockHost.addCode(`
|
|
||||||
@Component({
|
|
||||||
selector: 'foo-component',
|
|
||||||
template: \`
|
|
||||||
<di~{div}></div>
|
|
||||||
\`,
|
|
||||||
})
|
|
||||||
export class FooComponent {}
|
|
||||||
`);
|
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'div');
|
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
|
||||||
expect(completions).toBeDefined();
|
|
||||||
const completion = completions.entries.find(entry => entry.name === 'div') !;
|
|
||||||
expect(completion).toBeDefined();
|
|
||||||
expect(completion.kind).toBe('html element');
|
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work for bindings', () => {
|
|
||||||
const fileName = mockHost.addCode(`
|
|
||||||
@Component({
|
|
||||||
selector: 'foo-component',
|
|
||||||
template: \`
|
|
||||||
<input ngMod~{model} />
|
<input ngMod~{model} />
|
||||||
\`,
|
\`,
|
||||||
})
|
})
|
||||||
export class FooComponent {}
|
export class FooComponent {}
|
||||||
`);
|
`);
|
||||||
const location = mockHost.getLocationMarkerFor(fileName, 'model');
|
const location = mockHost.getLocationMarkerFor(fileName, 'model');
|
||||||
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
const completions = ngLS.getCompletionsAt(fileName, location.start) !;
|
||||||
expect(completions).toBeDefined();
|
expect(completions).toBeDefined();
|
||||||
const completion = completions.entries.find(entry => entry.name === '[(ngModel)]') !;
|
const completion = completions.entries.find(entry => entry.name === '[(ngModel)]') !;
|
||||||
expect(completion).toBeDefined();
|
expect(completion).toBeDefined();
|
||||||
expect(completion.kind).toBe('attribute');
|
expect(completion.kind).toBe('attribute');
|
||||||
expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5});
|
expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue