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', () => { |   it('should be able to find field in an interpolation', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '{{«name»}}' |         template: '{{«name»}}' | ||||||
|       }) |       }) | ||||||
| @ -38,7 +38,7 @@ describe('definitions', () => { | |||||||
|         «ᐱnameᐱ: string;» |         «ᐱnameᐱ: string;» | ||||||
|       }`);
 |       }`);
 | ||||||
| 
 | 
 | ||||||
|     const marker = getReferenceMarkerFor(fileName, 'name'); |     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| @ -51,11 +51,11 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(fileName); |     expect(def.fileName).toBe(fileName); | ||||||
|     expect(def.name).toBe('name'); |     expect(def.name).toBe('name'); | ||||||
|     expect(def.kind).toBe('property'); |     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', () => { |   it('should be able to find a field in a attribute reference', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<input [(ngModel)]="«name»">' |         template: '<input [(ngModel)]="«name»">' | ||||||
|       }) |       }) | ||||||
| @ -63,7 +63,7 @@ describe('definitions', () => { | |||||||
|         «ᐱnameᐱ: string;» |         «ᐱnameᐱ: string;» | ||||||
|       }`);
 |       }`);
 | ||||||
| 
 | 
 | ||||||
|     const marker = getReferenceMarkerFor(fileName, 'name'); |     const marker = mockHost.getReferenceMarkerFor(fileName, 'name'); | ||||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| @ -76,11 +76,11 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(fileName); |     expect(def.fileName).toBe(fileName); | ||||||
|     expect(def.name).toBe('name'); |     expect(def.name).toBe('name'); | ||||||
|     expect(def.kind).toBe('property'); |     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', () => { |   it('should be able to find a method from a call', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<div (click)="~{start-my}«myClick»()~{end-my};"></div>' |         template: '<div (click)="~{start-my}«myClick»()~{end-my};"></div>' | ||||||
|       }) |       }) | ||||||
| @ -88,12 +88,12 @@ describe('definitions', () => { | |||||||
|         «ᐱmyClickᐱ() { }» |         «ᐱmyClickᐱ() { }» | ||||||
|       }`);
 |       }`);
 | ||||||
| 
 | 
 | ||||||
|     const marker = getReferenceMarkerFor(fileName, 'myClick'); |     const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick'); | ||||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| 
 | 
 | ||||||
|     expect(textSpan).toEqual(getLocationMarkerFor(fileName, 'my')); |     expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my')); | ||||||
|     expect(definitions).toBeDefined(); |     expect(definitions).toBeDefined(); | ||||||
|     expect(definitions !.length).toBe(1); |     expect(definitions !.length).toBe(1); | ||||||
|     const def = definitions ![0]; |     const def = definitions ![0]; | ||||||
| @ -101,11 +101,11 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(fileName); |     expect(def.fileName).toBe(fileName); | ||||||
|     expect(def.name).toBe('myClick'); |     expect(def.name).toBe('myClick'); | ||||||
|     expect(def.kind).toBe('method'); |     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', () => { |   it('should be able to find a field reference in an *ngIf', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<div *ngIf="«include»"></div>' |         template: '<div *ngIf="«include»"></div>' | ||||||
|       }) |       }) | ||||||
| @ -113,7 +113,7 @@ describe('definitions', () => { | |||||||
|         «ᐱincludeᐱ = true;» |         «ᐱincludeᐱ = true;» | ||||||
|       }`);
 |       }`);
 | ||||||
| 
 | 
 | ||||||
|     const marker = getReferenceMarkerFor(fileName, 'include'); |     const marker = mockHost.getReferenceMarkerFor(fileName, 'include'); | ||||||
|     const result = ngService.getDefinitionAt(fileName, marker.start); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| @ -126,25 +126,25 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(fileName); |     expect(def.fileName).toBe(fileName); | ||||||
|     expect(def.name).toBe('include'); |     expect(def.name).toBe('include'); | ||||||
|     expect(def.kind).toBe('property'); |     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', () => { |   it('should be able to find a reference to a component', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '~{start-my}<«test-comp»></test-comp>~{end-my}' |         template: '~{start-my}<«test-comp»></test-comp>~{end-my}' | ||||||
|       }) |       }) | ||||||
|       export class MyComponent { }`);
 |       export class MyComponent { }`);
 | ||||||
| 
 | 
 | ||||||
|     // Get the marker for «test-comp» in the code added above.
 |     // 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); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| 
 | 
 | ||||||
|     // Get the marker for bounded text in the code added above.
 |     // 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(textSpan).toEqual(boundedText); | ||||||
| 
 | 
 | ||||||
|     // There should be exactly 1 definition
 |     // There should be exactly 1 definition
 | ||||||
| @ -156,25 +156,25 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(refFileName); |     expect(def.fileName).toBe(refFileName); | ||||||
|     expect(def.name).toBe('TestComponent'); |     expect(def.name).toBe('TestComponent'); | ||||||
|     expect(def.kind).toBe('component'); |     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', () => { |   it('should be able to find an event provider', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<test-comp ~{start-my}(«test»)="myHandler()"~{end-my}></div>' |         template: '<test-comp ~{start-my}(«test»)="myHandler()"~{end-my}></div>' | ||||||
|       }) |       }) | ||||||
|       export class MyComponent { myHandler() {} }`);
 |       export class MyComponent { myHandler() {} }`);
 | ||||||
| 
 | 
 | ||||||
|     // Get the marker for «test» in the code added above.
 |     // 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); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| 
 | 
 | ||||||
|     // Get the marker for bounded text in the code added above
 |     // 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(textSpan).toEqual(boundedText); | ||||||
| 
 | 
 | ||||||
|     // There should be exactly 1 definition
 |     // There should be exactly 1 definition
 | ||||||
| @ -186,12 +186,12 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(refFileName); |     expect(def.fileName).toBe(refFileName); | ||||||
|     expect(def.name).toBe('testEvent'); |     expect(def.name).toBe('testEvent'); | ||||||
|     expect(def.kind).toBe('event'); |     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', () => { |   it('should be able to find an input provider', () => { | ||||||
|     // '/app/parsing-cases.ts', 'tcName',
 |     // '/app/parsing-cases.ts', 'tcName',
 | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<test-comp ~{start-my}[«tcName»]="name"~{end-my}></div>' |         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.
 |     // 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); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| 
 | 
 | ||||||
|     // Get the marker for bounded text in the code added above
 |     // 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(textSpan).toEqual(boundedText); | ||||||
| 
 | 
 | ||||||
|     // There should be exactly 1 definition
 |     // There should be exactly 1 definition
 | ||||||
| @ -219,11 +219,11 @@ describe('definitions', () => { | |||||||
|     expect(def.fileName).toBe(refFileName); |     expect(def.fileName).toBe(refFileName); | ||||||
|     expect(def.name).toBe('name'); |     expect(def.name).toBe('name'); | ||||||
|     expect(def.kind).toBe('property'); |     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', () => { |   it('should be able to find a pipe', () => { | ||||||
|     const fileName = addCode(` |     const fileName = mockHost.addCode(` | ||||||
|       @Component({ |       @Component({ | ||||||
|         template: '<div *ngIf="~{start-my}input | «async»~{end-my}"></div>' |         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.
 |     // 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); |     const result = ngService.getDefinitionAt(fileName, marker.start); | ||||||
|     expect(result).toBeDefined(); |     expect(result).toBeDefined(); | ||||||
|     const {textSpan, definitions} = result !; |     const {textSpan, definitions} = result !; | ||||||
| 
 | 
 | ||||||
|     // Get the marker for bounded text in the code added above
 |     // 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(textSpan).toEqual(boundedText); | ||||||
| 
 | 
 | ||||||
|     expect(definitions).toBeDefined(); |     expect(definitions).toBeDefined(); | ||||||
| @ -253,97 +253,4 @@ describe('definitions', () => { | |||||||
|       // Not asserting the textSpan of definition because it's external file
 |       // 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
 |  * found in the LICENSE file at https://angular.io/license
 | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import 'reflect-metadata'; |  | ||||||
| 
 |  | ||||||
| import * as ts from 'typescript'; | import * as ts from 'typescript'; | ||||||
| 
 | 
 | ||||||
| import {createLanguageService} from '../src/language_service'; | import {createLanguageService} from '../src/language_service'; | ||||||
| import {Hover} from '../src/types'; | import {LanguageService} from '../src/types'; | ||||||
| import {TypeScriptServiceHost} from '../src/typescript_host'; | import {TypeScriptServiceHost} from '../src/typescript_host'; | ||||||
| 
 | 
 | ||||||
| import {toh} from './test_data'; | import {toh} from './test_data'; | ||||||
| import {MockTypescriptHost} from './test_utils'; | import {MockTypescriptHost} from './test_utils'; | ||||||
| 
 | 
 | ||||||
| describe('hover', () => { | describe('hover', () => { | ||||||
|   let documentRegistry = ts.createDocumentRegistry(); |   let mockHost: MockTypescriptHost; | ||||||
|   let mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh); |   let tsLS: ts.LanguageService; | ||||||
|   let service = ts.createLanguageService(mockHost, documentRegistry); |   let ngLSHost: TypeScriptServiceHost; | ||||||
|   let ngHost = new TypeScriptServiceHost(mockHost, service); |   let ngLS: LanguageService; | ||||||
|   let ngService = createLanguageService(ngHost); |  | ||||||
| 
 | 
 | ||||||
|  |   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', () => { |   it('should be able to find field in an interpolation', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '{{«name»}}'}) export class MyComponent { name: string; }`, |       @Component({ | ||||||
|         '(property) MyComponent.name'); |         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', () => { |   it('should be able to find a field in a attribute reference', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<input [(ngModel)]="«name»">'}) export class MyComponent { name: string; }`, |       @Component({ | ||||||
|         '(property) MyComponent.name'); |         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', () => { |   it('should be able to find a method from a call', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<div (click)="«ᐱmyClickᐱ()»;"></div>'}) export class MyComponent { myClick() { }}`, |       @Component({ | ||||||
|         '(method) MyComponent.myClick'); |         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', () => { |   it('should be able to find a field reference in an *ngIf', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<div *ngIf="«include»"></div>'}) export class MyComponent { include = true;}`, |       @Component({ | ||||||
|         '(property) MyComponent.include'); |         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', () => { |   it('should be able to find a reference to a component', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '«<ᐱtestᐱ-comp></test-comp>»'}) export class MyComponent { }`, |       @Component({ | ||||||
|         '(component) TestComponent'); |         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', () => { |   it('should be able to find an event provider', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<test-comp «(ᐱtestᐱ)="myHandler()"»></div>'}) export class MyComponent { myHandler() {} }`, |       @Component({ | ||||||
|         '(event) TestComponent.testEvent'); |         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', () => { |   it('should be able to find an input provider', () => { | ||||||
|     hover( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<test-comp «[ᐱtcNameᐱ]="name"»></div>'}) export class MyComponent { name = 'my name'; }`, |       @Component({ | ||||||
|         '(property) TestComponent.name'); |         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', () => { |   it('should be able to ignore a reference declaration', () => { | ||||||
|     addCode( |     const fileName = mockHost.addCode(` | ||||||
|         ` @Component({template: '<div #«chart»></div>'}) export class MyComponent {  }`, |       @Component({ | ||||||
|         fileName => { |         template: '<div #«chart»></div>' | ||||||
|           const markers = mockHost.getReferenceMarkers(fileName) !; |       }) | ||||||
|           const hover = ngService.getHoverAt(fileName, markers.references.chart[0].start); |       export class MyComponent {  }`);
 | ||||||
|           expect(hover).toBeUndefined(); |     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; |     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>) { | function iterableToArray<T>(iterator: IterableIterator<T>) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user