| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google LLC 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 {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system'; | 
					
						
							|  |  |  | import {initMockFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; | 
					
						
							|  |  |  | import {LanguageServiceTestEnvironment} from '@angular/language-service/ivy/test/env'; | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | import {createModuleWithDeclarations} from './test_utils'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('getSemanticDiagnostics', () => { | 
					
						
							|  |  |  |   beforeEach(() => { | 
					
						
							|  |  |  |     initMockFileSystem('Native'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should not produce error for a minimal component defintion', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         template: '' | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent {} | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const env = createModuleWithDeclarations([appFile]); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.ts')); | 
					
						
							|  |  |  |     expect(diags.length).toEqual(0); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should report member does not exist', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         template: '{{nope}}' | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent {} | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const env = createModuleWithDeclarations([appFile]); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.ts')); | 
					
						
							|  |  |  |     expect(diags.length).toBe(1); | 
					
						
							|  |  |  |     const {category, file, start, length, messageText} = diags[0]; | 
					
						
							|  |  |  |     expect(category).toBe(ts.DiagnosticCategory.Error); | 
					
						
							|  |  |  |     expect(file?.fileName).toBe('/app.ts'); | 
					
						
							|  |  |  |     expect(messageText).toBe(`Property 'nope' does not exist on type 'AppComponent'.`); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should process external template', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         templateUrl: './app.html' | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent {} | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const templateFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.html'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       Hello world! | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const env = createModuleWithDeclarations([appFile], [templateFile]); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.html')); | 
					
						
							|  |  |  |     expect(diags).toEqual([]); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should report member does not exist in external template', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         templateUrl: './app.html' | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent {} | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const templateFile = {name: absoluteFrom('/app.html'), contents: `{{nope}}`}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const env = createModuleWithDeclarations([appFile], [templateFile]); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.html')); | 
					
						
							|  |  |  |     expect(diags.length).toBe(1); | 
					
						
							|  |  |  |     const {category, file, start, length, messageText} = diags[0]; | 
					
						
							|  |  |  |     expect(category).toBe(ts.DiagnosticCategory.Error); | 
					
						
							|  |  |  |     expect(file?.fileName).toBe('/app.html'); | 
					
						
							|  |  |  |     expect(messageText).toBe(`Property 'nope' does not exist on type 'AppComponent'.`); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('should report a parse error in external template', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         templateUrl: './app.html' | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent { | 
					
						
							|  |  |  |         nope = false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const templateFile = {name: absoluteFrom('/app.html'), contents: `{{nope = true}}`}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const env = createModuleWithDeclarations([appFile], [templateFile]); | 
					
						
							|  |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.html')); | 
					
						
							|  |  |  |     expect(diags.length).toBe(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const {category, file, messageText} = diags[0]; | 
					
						
							|  |  |  |     expect(category).toBe(ts.DiagnosticCategory.Error); | 
					
						
							|  |  |  |     expect(file?.fileName).toBe('/app.html'); | 
					
						
							|  |  |  |     expect(messageText) | 
					
						
							|  |  |  |         .toContain( | 
					
						
							|  |  |  |             `Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = true}}]`); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should report parse errors of components defined in the same ts file', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ templateUrl: './app1.html' }) | 
					
						
							|  |  |  |       export class AppComponent1 { nope = false; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ templateUrl: './app2.html' }) | 
					
						
							|  |  |  |       export class AppComponent2 { nope = false; } | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const templateFile1 = {name: absoluteFrom('/app1.html'), contents: `{{nope = false}}`}; | 
					
						
							|  |  |  |     const templateFile2 = {name: absoluteFrom('/app2.html'), contents: `{{nope = true}}`}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const moduleFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app-module.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |         import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |         import {CommonModule} from '@angular/common'; | 
					
						
							|  |  |  |         import {AppComponent, AppComponent2} from './app'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @NgModule({ | 
					
						
							|  |  |  |           declarations: [AppComponent, AppComponent2], | 
					
						
							|  |  |  |           imports: [CommonModule], | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppModule {} | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |       isRoot: true | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const env = | 
					
						
							|  |  |  |         LanguageServiceTestEnvironment.setup([moduleFile, appFile, templateFile1, templateFile2]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.ts')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(diags.map(x => x.messageText).sort()).toEqual([ | 
					
						
							|  |  |  |       'Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = false}}] in /app1.html@0:0', | 
					
						
							|  |  |  |       'Parser Error: Bindings cannot contain assignments at column 8 in [{{nope = true}}] in /app2.html@0:0' | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2021-01-06 14:17:45 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('reports a diagnostic for a component without a template', () => { | 
					
						
							|  |  |  |     const appFile = { | 
					
						
							|  |  |  |       name: absoluteFrom('/app.ts'), | 
					
						
							|  |  |  |       contents: `
 | 
					
						
							|  |  |  |       import {Component} from '@angular/core'; | 
					
						
							|  |  |  |       @Component({}) | 
					
						
							|  |  |  |       export class MyComponent {} | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const env = createModuleWithDeclarations([appFile]); | 
					
						
							|  |  |  |     const diags = env.ngLS.getSemanticDiagnostics(absoluteFrom('/app.ts')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expect(diags.map(x => x.messageText)).toEqual([ | 
					
						
							|  |  |  |       'component is missing a template', | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-12-03 11:42:46 -08:00
										 |  |  | }); |