/** * @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 * as ts from 'typescript'; import {createLanguageService} from '../src/language_service'; import {TypeScriptServiceHost} from '../src/typescript_host'; import {MockTypescriptHost} from './test_utils'; const TEST_TEMPLATE = '/app/test.ng'; describe('hover', () => { const mockHost = new MockTypescriptHost(['/app/main.ts']); const tsLS = ts.createLanguageService(mockHost); const ngLSHost = new TypeScriptServiceHost(mockHost, tsLS); const ngLS = createLanguageService(ngLSHost); beforeEach(() => { mockHost.reset(); }); it('should be able to find field in an interpolation', () => { const fileName = mockHost.addCode(` @Component({ template: '{{«name»}}' }) export class MyComponent { name: string; }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) MyComponent.name: string'); }); it('should be able to find a field in a attribute reference', () => { const fileName = mockHost.addCode(` @Component({ template: '' }) export class MyComponent { name: string; }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) MyComponent.name: string'); }); it('should be able to find a method from a call', () => { const fileName = mockHost.addCode(` @Component({ template: '
' }) export class MyComponent { myClick() { } }`); const marker = mockHost.getDefinitionMarkerFor(fileName, 'myClick'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(textSpan.length).toBe('myClick()'.length); expect(toText(displayParts)).toBe('(method) MyComponent.myClick: () => void'); }); it('should be able to find a field reference in an *ngIf', () => { const fileName = mockHost.addCode(` @Component({ template: '
' }) export class MyComponent { include = true; }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'include'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) MyComponent.include: boolean'); }); it('should be able to find a reference to a component', () => { mockHost.override(TEST_TEMPLATE, '<~{cursor}test-comp>'); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); const {displayParts, documentation} = quickInfo !; expect(toText(displayParts)).toBe('(component) AppModule.TestComponent: typeof TestComponent'); expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.'); }); it('should be able to find a reference to a directive', () => { const content = mockHost.override(TEST_TEMPLATE, `
`); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); const {displayParts, textSpan} = quickInfo !; expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: typeof StringModel'); expect(content.substring(textSpan.start, textSpan.start + textSpan.length)) .toBe('string-model'); }); it('should be able to find an event provider', () => { const fileName = mockHost.addCode(` @Component({ template: '' }) export class MyComponent { myHandler() {} }`); const marker = mockHost.getDefinitionMarkerFor(fileName, 'test'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(event) TestComponent.testEvent: EventEmitter'); }); it('should be able to find an input provider', () => { const fileName = mockHost.addCode(` @Component({ template: '' }) export class MyComponent { name = 'my name'; }`); const marker = mockHost.getDefinitionMarkerFor(fileName, 'tcName'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) TestComponent.name: string'); }); it('should be able to find a structural directive', () => { mockHost.override(TEST_TEMPLATE, `
`); const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'ngIf'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) NgIf.ngIf: T'); }); it('should be able to find a reference to a two-way binding', () => { mockHost.override(TEST_TEMPLATE, ``); const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'model'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) StringModel.model: string'); }); it('should be able to ignore a reference declaration', () => { const fileName = mockHost.addCode(` @Component({ template: '
' }) export class MyComponent { }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'chart'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeUndefined(); }); it('should be able to find the NgModule of a component', () => { const fileName = '/app/app.component.ts'; mockHost.override(fileName, ` import {Component} from '@angular/core'; @Component({ template: '
' }) export class «AppComponent» { name: string; }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'AppComponent'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(component) AppModule.AppComponent: class'); }); it('should be able to find the NgModule of a directive', () => { const fileName = '/app/parsing-cases.ts'; const content = mockHost.readFile(fileName) !; const position = content.indexOf('StringModel'); expect(position).toBeGreaterThan(0); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, position); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual({ start: position, length: 'StringModel'.length, }); expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: class'); }); it('should be able to provide quick info for $any() cast function', () => { const content = mockHost.override(TEST_TEMPLATE, '
{{$any(title)}}
'); const position = content.indexOf('$any'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position); expect(quickInfo).toBeDefined(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual({ start: position, length: '$any(title)'.length, }); expect(toText(displayParts)).toBe('(method) $any: $any'); }); it('should provide documentation for a property', () => { mockHost.override(TEST_TEMPLATE, `
{{~{cursor}title}}
`); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); const documentation = toText(quickInfo !.documentation); expect(documentation).toBe('This is the title of the `TemplateReference` Component.'); }); it('should provide documentation for a selector', () => { mockHost.override(TEST_TEMPLATE, `<~{cursor}test-comp>
`); const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor'); const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start); expect(quickInfo).toBeDefined(); const documentation = toText(quickInfo !.documentation); expect(documentation).toBe('This Component provides the `test-comp` selector.'); }); it('should not expand i18n templates', () => { const fileName = mockHost.addCode(` @Component({ template: '
{{«name»}}
' }) export class MyComponent { name: string; }`); const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); const quickInfo = ngLS.getQuickInfoAtPosition(fileName, marker.start); expect(quickInfo).toBeTruthy(); const {textSpan, displayParts} = quickInfo !; expect(textSpan).toEqual(marker); expect(toText(displayParts)).toBe('(property) MyComponent.name: string'); }); }); function toText(displayParts?: ts.SymbolDisplayPart[]): string { return (displayParts || []).map(p => p.text).join(''); }