refactor(language-service): cleanup tests for Hover (#32378)
Move generic test methods to `MockTypescriptHost` so they could be shared across all tests. This is in preparation for adding more tests to Hover when new features get added. PR Close #32378
This commit is contained in:
		
							parent
							
								
									852afb312a
								
							
						
					
					
						commit
						18ce58c2bc
					
				| @ -30,7 +30,7 @@ describe('definitions', () => { | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find field in an interpolation', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '{{«name»}}' | ||||
|       }) | ||||
| @ -38,7 +38,7 @@ describe('definitions', () => { | ||||
|         «ᐱnameᐱ: string;» | ||||
|       }`);
 | ||||
| 
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'name'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| @ -51,11 +51,11 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(fileName); | ||||
|     expect(def.name).toBe('name'); | ||||
|     expect(def.kind).toBe('property'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(fileName, 'name')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(fileName, 'name')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a field in a attribute reference', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<input [(ngModel)]="«name»">' | ||||
|       }) | ||||
| @ -63,7 +63,7 @@ describe('definitions', () => { | ||||
|         «ᐱnameᐱ: string;» | ||||
|       }`);
 | ||||
| 
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'name'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| @ -76,11 +76,11 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(fileName); | ||||
|     expect(def.name).toBe('name'); | ||||
|     expect(def.kind).toBe('property'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(fileName, 'name')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(fileName, 'name')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a method from a call', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div (click)="~{start-my}«myClick»()~{end-my};"></div>' | ||||
|       }) | ||||
| @ -88,12 +88,12 @@ describe('definitions', () => { | ||||
|         «ᐱmyClickᐱ() { }» | ||||
|       }`);
 | ||||
| 
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'myClick'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick'); | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     expect(textSpan).toEqual(getLocationMarkerFor(fileName, 'my')); | ||||
|     expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my')); | ||||
|     expect(definitions).toBeDefined(); | ||||
|     expect(definitions !.length).toBe(1); | ||||
|     const def = definitions ![0]; | ||||
| @ -101,11 +101,11 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(fileName); | ||||
|     expect(def.name).toBe('myClick'); | ||||
|     expect(def.kind).toBe('method'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(fileName, 'myClick')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(fileName, 'myClick')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a field reference in an *ngIf', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div *ngIf="«include»"></div>' | ||||
|       }) | ||||
| @ -113,7 +113,7 @@ describe('definitions', () => { | ||||
|         «ᐱincludeᐱ = true;» | ||||
|       }`);
 | ||||
| 
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'include'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'include'); | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| @ -126,25 +126,25 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(fileName); | ||||
|     expect(def.name).toBe('include'); | ||||
|     expect(def.kind).toBe('property'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(fileName, 'include')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(fileName, 'include')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a reference to a component', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '~{start-my}<«test-comp»></test-comp>~{end-my}' | ||||
|       }) | ||||
|       export class MyComponent { }`);
 | ||||
| 
 | ||||
|     // Get the marker for «test-comp» in the code added above.
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'test-comp'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'test-comp'); | ||||
| 
 | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     // Get the marker for bounded text in the code added above.
 | ||||
|     const boundedText = getLocationMarkerFor(fileName, 'my'); | ||||
|     const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); | ||||
|     expect(textSpan).toEqual(boundedText); | ||||
| 
 | ||||
|     // There should be exactly 1 definition
 | ||||
| @ -156,25 +156,25 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(refFileName); | ||||
|     expect(def.name).toBe('TestComponent'); | ||||
|     expect(def.kind).toBe('component'); | ||||
|     expect(def.textSpan).toEqual(getLocationMarkerFor(refFileName, 'test-comp')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getLocationMarkerFor(refFileName, 'test-comp')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find an event provider', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<test-comp ~{start-my}(«test»)="myHandler()"~{end-my}></div>' | ||||
|       }) | ||||
|       export class MyComponent { myHandler() {} }`);
 | ||||
| 
 | ||||
|     // Get the marker for «test» in the code added above.
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'test'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'test'); | ||||
| 
 | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     // Get the marker for bounded text in the code added above
 | ||||
|     const boundedText = getLocationMarkerFor(fileName, 'my'); | ||||
|     const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); | ||||
|     expect(textSpan).toEqual(boundedText); | ||||
| 
 | ||||
|     // There should be exactly 1 definition
 | ||||
| @ -186,12 +186,12 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(refFileName); | ||||
|     expect(def.name).toBe('testEvent'); | ||||
|     expect(def.kind).toBe('event'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(refFileName, 'test')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(refFileName, 'test')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find an input provider', () => { | ||||
|     // '/app/parsing-cases.ts', 'tcName',
 | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<test-comp ~{start-my}[«tcName»]="name"~{end-my}></div>' | ||||
|       }) | ||||
| @ -200,14 +200,14 @@ describe('definitions', () => { | ||||
|       }`);
 | ||||
| 
 | ||||
|     // Get the marker for «test» in the code added above.
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'tcName'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'tcName'); | ||||
| 
 | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     // Get the marker for bounded text in the code added above
 | ||||
|     const boundedText = getLocationMarkerFor(fileName, 'my'); | ||||
|     const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); | ||||
|     expect(textSpan).toEqual(boundedText); | ||||
| 
 | ||||
|     // There should be exactly 1 definition
 | ||||
| @ -219,11 +219,11 @@ describe('definitions', () => { | ||||
|     expect(def.fileName).toBe(refFileName); | ||||
|     expect(def.name).toBe('name'); | ||||
|     expect(def.kind).toBe('property'); | ||||
|     expect(def.textSpan).toEqual(getDefinitionMarkerFor(refFileName, 'tcName')); | ||||
|     expect(def.textSpan).toEqual(mockHost.getDefinitionMarkerFor(refFileName, 'tcName')); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a pipe', () => { | ||||
|     const fileName = addCode(` | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div *ngIf="~{start-my}input | «async»~{end-my}"></div>' | ||||
|       }) | ||||
| @ -232,14 +232,14 @@ describe('definitions', () => { | ||||
|       }`);
 | ||||
| 
 | ||||
|     // Get the marker for «test» in the code added above.
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'async'); | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'async'); | ||||
| 
 | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     // Get the marker for bounded text in the code added above
 | ||||
|     const boundedText = getLocationMarkerFor(fileName, 'my'); | ||||
|     const boundedText = mockHost.getLocationMarkerFor(fileName, 'my'); | ||||
|     expect(textSpan).toEqual(boundedText); | ||||
| 
 | ||||
|     expect(definitions).toBeDefined(); | ||||
| @ -253,97 +253,4 @@ describe('definitions', () => { | ||||
|       // Not asserting the textSpan of definition because it's external file
 | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find a template from a url', () => { | ||||
|     const fileName = addCode(` | ||||
|       @Component({ | ||||
|         templateUrl: './«test».ng', | ||||
|       }) | ||||
|       export class MyComponent {}`);
 | ||||
| 
 | ||||
|     const marker = getReferenceMarkerFor(fileName, 'test'); | ||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||
| 
 | ||||
|     expect(result).toBeDefined(); | ||||
|     const {textSpan, definitions} = result !; | ||||
| 
 | ||||
|     expect(textSpan).toEqual({start: marker.start - 2, length: 9}); | ||||
| 
 | ||||
|     expect(definitions).toBeDefined(); | ||||
|     expect(definitions !.length).toBe(1); | ||||
|     const [def] = definitions !; | ||||
|     expect(def.fileName).toBe('/app/test.ng'); | ||||
|     expect(def.textSpan).toEqual({start: 0, length: 0}); | ||||
|   }); | ||||
| 
 | ||||
|   /** | ||||
|    * Append a snippet of code to `app.component.ts` and return the file name. | ||||
|    * There must not be any name collision with existing code. | ||||
|    * @param code Snippet of code | ||||
|    */ | ||||
|   function addCode(code: string) { | ||||
|     const fileName = '/app/app.component.ts'; | ||||
|     const originalContent = mockHost.getFileContent(fileName); | ||||
|     const newContent = originalContent + code; | ||||
|     mockHost.override(fileName, newContent); | ||||
|     return fileName; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the definition marker ᐱselectorᐱ for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   function getDefinitionMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = mockHost.getReferenceMarkers(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     expect(Object.keys(markers !.definitions)).toContain(selector); | ||||
|     expect(markers !.definitions[selector].length).toBe(1); | ||||
|     const marker = markers !.definitions[selector][0]; | ||||
|     expect(marker.start).toBeLessThanOrEqual(marker.end); | ||||
|     return { | ||||
|       start: marker.start, | ||||
|       length: marker.end - marker.start, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the reference marker «selector» for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   function getReferenceMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = mockHost.getReferenceMarkers(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     expect(Object.keys(markers !.references)).toContain(selector); | ||||
|     expect(markers !.references[selector].length).toBe(1); | ||||
|     const marker = markers !.references[selector][0]; | ||||
|     expect(marker.start).toBeLessThanOrEqual(marker.end); | ||||
|     return { | ||||
|       start: marker.start, | ||||
|       length: marker.end - marker.start, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the location marker ~{selector} for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   function getLocationMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = mockHost.getMarkerLocations(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     const start = markers ![`start-${selector}`]; | ||||
|     expect(start).toBeDefined(); | ||||
|     const end = markers ![`end-${selector}`]; | ||||
|     expect(end).toBeDefined(); | ||||
|     expect(start).toBeLessThanOrEqual(end); | ||||
|     return { | ||||
|       start: start, | ||||
|       length: end - start, | ||||
|     }; | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @ -6,114 +6,151 @@ | ||||
|  * 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 {LanguageService} 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); | ||||
|   let mockHost: MockTypescriptHost; | ||||
|   let tsLS: ts.LanguageService; | ||||
|   let ngLSHost: TypeScriptServiceHost; | ||||
|   let ngLS: LanguageService; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh); | ||||
|     tsLS = ts.createLanguageService(mockHost); | ||||
|     ngLSHost = new TypeScriptServiceHost(mockHost, tsLS); | ||||
|     ngLS = createLanguageService(ngLSHost); | ||||
|   }); | ||||
| 
 | ||||
|   it('should be able to find field in an interpolation', () => { | ||||
|     hover( | ||||
|         ` @Component({template: '{{«name»}}'}) export class MyComponent { name: string; }`, | ||||
|         '(property) MyComponent.name'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '{{«name»}}' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         name: string; | ||||
|       }`);
 | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<input [(ngModel)]="«name»">' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         name: string; | ||||
|       }`);
 | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div (click)="«ᐱmyClickᐱ()»;"></div>' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         myClick() { } | ||||
|       }`);
 | ||||
|     const marker = mockHost.getDefinitionMarkerFor(fileName, 'myClick'); | ||||
|     const quickInfo = ngLS.getHoverAt(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'); | ||||
|   }); | ||||
| 
 | ||||
|   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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div *ngIf="«include»"></div>' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         include = true; | ||||
|       }`);
 | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'include'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '«<ᐱtestᐱ-comp></test-comp>»' | ||||
|       }) | ||||
|       export class MyComponent { }`);
 | ||||
|     const marker = mockHost.getDefinitionMarkerFor(fileName, 'test'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<test-comp «(ᐱtestᐱ)="myHandler()"»></div>' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         myHandler() {} | ||||
|       }`);
 | ||||
|     const marker = mockHost.getDefinitionMarkerFor(fileName, 'test'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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'); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<test-comp «[ᐱtcNameᐱ]="name"»></div>' | ||||
|       }) | ||||
|       export class MyComponent { | ||||
|         name = 'my name'; | ||||
|       }`);
 | ||||
|     const marker = mockHost.getDefinitionMarkerFor(fileName, 'tcName'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).toBeTruthy(); | ||||
|     const {textSpan, displayParts} = quickInfo !; | ||||
|     expect(textSpan).toEqual(marker); | ||||
|     expect(toText(displayParts)).toBe('(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(); | ||||
|     const fileName = mockHost.addCode(` | ||||
|       @Component({ | ||||
|         template: '<div #«chart»></div>' | ||||
|       }) | ||||
|       export class MyComponent {  }`);
 | ||||
|     const marker = mockHost.getReferenceMarkerFor(fileName, 'chart'); | ||||
|     const quickInfo = ngLS.getHoverAt(fileName, marker.start); | ||||
|     expect(quickInfo).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(''); | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| function toText(displayParts?: ts.SymbolDisplayPart[]): string { | ||||
|   return (displayParts || []).map(p => p.text).join(''); | ||||
| } | ||||
|  | ||||
| @ -239,6 +239,78 @@ export class MockTypescriptHost implements ts.LanguageServiceHost { | ||||
|     } | ||||
|     return name; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   /** | ||||
|    * Append a snippet of code to `app.component.ts` and return the file name. | ||||
|    * There must not be any name collision with existing code. | ||||
|    * @param code Snippet of code | ||||
|    */ | ||||
|   addCode(code: string) { | ||||
|     const fileName = '/app/app.component.ts'; | ||||
|     const originalContent = this.getFileContent(fileName); | ||||
|     const newContent = originalContent + code; | ||||
|     this.override(fileName, newContent); | ||||
|     return fileName; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the definition marker ᐱselectorᐱ for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   getDefinitionMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = this.getReferenceMarkers(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     expect(Object.keys(markers !.definitions)).toContain(selector); | ||||
|     expect(markers !.definitions[selector].length).toBe(1); | ||||
|     const marker = markers !.definitions[selector][0]; | ||||
|     expect(marker.start).toBeLessThanOrEqual(marker.end); | ||||
|     return { | ||||
|       start: marker.start, | ||||
|       length: marker.end - marker.start, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the reference marker «selector» for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   getReferenceMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = this.getReferenceMarkers(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     expect(Object.keys(markers !.references)).toContain(selector); | ||||
|     expect(markers !.references[selector].length).toBe(1); | ||||
|     const marker = markers !.references[selector][0]; | ||||
|     expect(marker.start).toBeLessThanOrEqual(marker.end); | ||||
|     return { | ||||
|       start: marker.start, | ||||
|       length: marker.end - marker.start, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Returns the location marker ~{selector} for the specified 'selector'. | ||||
|    * Asserts that marker exists. | ||||
|    * @param fileName name of the file | ||||
|    * @param selector name of the marker | ||||
|    */ | ||||
|   getLocationMarkerFor(fileName: string, selector: string): ts.TextSpan { | ||||
|     const markers = this.getMarkerLocations(fileName); | ||||
|     expect(markers).toBeDefined(); | ||||
|     const start = markers ![`start-${selector}`]; | ||||
|     expect(start).toBeDefined(); | ||||
|     const end = markers ![`end-${selector}`]; | ||||
|     expect(end).toBeDefined(); | ||||
|     expect(start).toBeLessThanOrEqual(end); | ||||
|     return { | ||||
|       start: start, | ||||
|       length: end - start, | ||||
|     }; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function iterableToArray<T>(iterator: IterableIterator<T>) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user