a8e2ee1343
Now that the Angular LS is a proper tsserver plugin, it does not make sense for it to maintain its own language service API. This is part one of the effort to remove our custom LanguageService interface. This interface is cumbersome because we have to do two transformations: ng def -> ts def -> lsp definition The TS LS interface is more comprehensive, so this allows the Angular LS to return more information. PR Close #31972
120 lines
4.4 KiB
TypeScript
120 lines
4.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import 'reflect-metadata';
|
|
|
|
import * as ts from 'typescript';
|
|
|
|
import {createLanguageService} from '../src/language_service';
|
|
import {Hover} from '../src/types';
|
|
import {TypeScriptServiceHost} from '../src/typescript_host';
|
|
|
|
import {toh} from './test_data';
|
|
import {MockTypescriptHost} from './test_utils';
|
|
|
|
describe('hover', () => {
|
|
let documentRegistry = ts.createDocumentRegistry();
|
|
let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);
|
|
let service = ts.createLanguageService(mockHost, documentRegistry);
|
|
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
|
let ngService = createLanguageService(ngHost);
|
|
|
|
|
|
it('should be able to find field in an interpolation', () => {
|
|
hover(
|
|
` @Component({template: '{{«name»}}'}) export class MyComponent { name: string; }`,
|
|
'(property) MyComponent.name');
|
|
});
|
|
|
|
it('should be able to find a field in a attribute reference', () => {
|
|
hover(
|
|
` @Component({template: '<input [(ngModel)]="«name»">'}) export class MyComponent { name: string; }`,
|
|
'(property) MyComponent.name');
|
|
});
|
|
|
|
it('should be able to find a method from a call', () => {
|
|
hover(
|
|
` @Component({template: '<div (click)="«ᐱmyClickᐱ()»;"></div>'}) export class MyComponent { myClick() { }}`,
|
|
'(method) MyComponent.myClick');
|
|
});
|
|
|
|
it('should be able to find a field reference in an *ngIf', () => {
|
|
hover(
|
|
` @Component({template: '<div *ngIf="«include»"></div>'}) export class MyComponent { include = true;}`,
|
|
'(property) MyComponent.include');
|
|
});
|
|
|
|
it('should be able to find a reference to a component', () => {
|
|
hover(
|
|
` @Component({template: '«<ᐱtestᐱ-comp></test-comp>»'}) export class MyComponent { }`,
|
|
'(component) TestComponent');
|
|
});
|
|
|
|
it('should be able to find an event provider', () => {
|
|
hover(
|
|
` @Component({template: '<test-comp «(ᐱtestᐱ)="myHandler()"»></div>'}) export class MyComponent { myHandler() {} }`,
|
|
'(event) TestComponent.testEvent');
|
|
});
|
|
|
|
it('should be able to find an input provider', () => {
|
|
hover(
|
|
` @Component({template: '<test-comp «[ᐱtcNameᐱ]="name"»></div>'}) export class MyComponent { name = 'my name'; }`,
|
|
'(property) TestComponent.name');
|
|
});
|
|
|
|
it('should be able to ignore a reference declaration', () => {
|
|
addCode(
|
|
` @Component({template: '<div #«chart»></div>'}) export class MyComponent { }`,
|
|
fileName => {
|
|
const markers = mockHost.getReferenceMarkers(fileName) !;
|
|
const hover = ngService.getHoverAt(fileName, markers.references.chart[0].start);
|
|
expect(hover).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
function hover(code: string, hoverText: string) {
|
|
addCode(code, fileName => {
|
|
let tests = 0;
|
|
const markers = mockHost.getReferenceMarkers(fileName) !;
|
|
const keys = Object.keys(markers.references).concat(Object.keys(markers.definitions));
|
|
for (const referenceName of keys) {
|
|
const references = (markers.references[referenceName] ||
|
|
[]).concat(markers.definitions[referenceName] || []);
|
|
for (const reference of references) {
|
|
tests++;
|
|
const quickInfo = ngService.getHoverAt(fileName, reference.start);
|
|
if (!quickInfo) throw new Error(`Expected a hover at location ${reference.start}`);
|
|
expect(quickInfo.textSpan).toEqual({
|
|
start: reference.start,
|
|
length: reference.end - reference.start,
|
|
});
|
|
expect(toText(quickInfo)).toEqual(hoverText);
|
|
}
|
|
}
|
|
expect(tests).toBeGreaterThan(0); // If this fails the test is wrong.
|
|
});
|
|
}
|
|
|
|
function addCode(code: string, cb: (fileName: string, content?: string) => void) {
|
|
const fileName = '/app/app.component.ts';
|
|
const originalContent = mockHost.getFileContent(fileName);
|
|
const newContent = originalContent + code;
|
|
mockHost.override(fileName, originalContent + code);
|
|
try {
|
|
cb(fileName, newContent);
|
|
} finally {
|
|
mockHost.override(fileName, undefined !);
|
|
}
|
|
}
|
|
|
|
function toText(quickInfo: ts.QuickInfo): string {
|
|
const displayParts = quickInfo.displayParts || [];
|
|
return displayParts.map(p => p.text).join('');
|
|
}
|
|
});
|