| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-03 15:32:08 -07:00
										 |  |  | import {setup} from '@angular/compiler/test/aot/test_util'; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | import {compile, expectEmit} from './mock_compile'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('mock_compiler', () => { | 
					
						
							|  |  |  |   // This produces a MockDirectory of the file needed to compile an Angular application.
 | 
					
						
							|  |  |  |   // This setup is performed in a beforeAll which populates the map returned.
 | 
					
						
							|  |  |  |   const angularFiles = setup({ | 
					
						
							| 
									
										
										
										
											2018-08-01 09:52:34 +02:00
										 |  |  |     compileAngular: false, | 
					
						
							|  |  |  |     compileFakeCore: true, | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     compileAnimations: false, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('compiling', () => { | 
					
						
							|  |  |  |     // To use compile you need to supply the files in a MockDirectory that can be merged
 | 
					
						
							|  |  |  |     // with a set of "environment" files such as the angular files.
 | 
					
						
							|  |  |  |     it('should be able to compile a simple application', () => { | 
					
						
							|  |  |  |       const files = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'hello.component.ts': `
 | 
					
						
							|  |  |  |             import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @Component({template: 'Hello {{name}}!'}) | 
					
						
							|  |  |  |             export class HelloComponent { | 
					
						
							|  |  |  |               @Input() name: string = 'world'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           `,
 | 
					
						
							|  |  |  |           'hello.module.ts': `
 | 
					
						
							|  |  |  |             import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |             import {HelloComponent} from './hello.component'; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |             @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |             export class HelloModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // result.source contains just the emitted factory declarations regardless of the original
 | 
					
						
							|  |  |  |       // module.
 | 
					
						
							|  |  |  |       expect(result.source).toContain('Hello'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('expecting emitted output', () => { | 
					
						
							|  |  |  |     it('should be able to find a simple expression in the output', () => { | 
					
						
							|  |  |  |       const files = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'hello.component.ts': `
 | 
					
						
							|  |  |  |             import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'}) | 
					
						
							|  |  |  |             export class HelloComponent { | 
					
						
							|  |  |  |               @Input() name: string = 'world'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           `,
 | 
					
						
							|  |  |  |           'hello.module.ts': `
 | 
					
						
							|  |  |  |             import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |             import {HelloComponent} from './hello.component'; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |             @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |             export class HelloModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // The expression can expected directly.
 | 
					
						
							|  |  |  |       expectEmit(result.source, 'name.length', 'name length expression not found'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Whitespace is not significant
 | 
					
						
							|  |  |  |       expectEmit( | 
					
						
							|  |  |  |           result.source, 'name   \n\n   .  \n    length', | 
					
						
							|  |  |  |           'name length expression not found (whitespace)'); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-21 21:59:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should throw if the expected output contains unknown characters', () => { | 
					
						
							|  |  |  |       const files = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'test.ts': `ɵsayHello();`, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(() => { | 
					
						
							|  |  |  |         expectEmit(result.source, `ΔsayHello();`, 'Output does not match.'); | 
					
						
							|  |  |  |       }).toThrowError(/Invalid test, no token found for "Δ"/); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should be able to properly handle string literals with escaped quote', () => { | 
					
						
							|  |  |  |       const files = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'test.ts': String.raw `const identifier = "\"quoted\"";`, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(() => { | 
					
						
							|  |  |  |         expectEmit(result.source, String.raw `const $a$ = "\"quoted\"";`, 'Output does not match.'); | 
					
						
							|  |  |  |       }).not.toThrow(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  |   it('should be able to skip untested regions (… and // ...)', () => { | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     const files = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'hello.component.ts': `
 | 
					
						
							|  |  |  |           import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'}) | 
					
						
							|  |  |  |           export class HelloComponent { | 
					
						
							|  |  |  |             @Input() name: string = 'world'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'hello.module.ts': `
 | 
					
						
							|  |  |  |           import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |           import {HelloComponent} from './hello.component'; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |           @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |           export class HelloModule {} | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The special character … means anything can be generated between the two sections allowing
 | 
					
						
							|  |  |  |     // skipping sections of the output that are not under test. The ellipsis unicode char (…) is
 | 
					
						
							|  |  |  |     // used instead of '...' because '...' is legal JavaScript (the spread operator) and might
 | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  |     // need to be tested. `// ...` could also be used in place of `…`.
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     expectEmit(result.source, 'ctx.name … ctx.name.length', 'could not find correct length access'); | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  |     expectEmit( | 
					
						
							|  |  |  |         result.source, 'ctx.name // ... ctx.name.length', 'could not find correct length access'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should be able to skip TODO comments (// TODO)', () => { | 
					
						
							|  |  |  |     const files = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'hello.component.ts': `
 | 
					
						
							|  |  |  |           import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({template: 'Hello!'}) | 
					
						
							|  |  |  |           export class HelloComponent { } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'hello.module.ts': `
 | 
					
						
							|  |  |  |           import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |           import {HelloComponent} from './hello.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |           export class HelloModule {} | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     expectEmit( | 
					
						
							|  |  |  |         result.source, `
 | 
					
						
							|  |  |  |     // TODO: this comment should not be taken into account
 | 
					
						
							| 
									
										
										
										
											2019-05-17 18:49:21 -07:00
										 |  |  |     $r3$.ɵɵtext(0, "Hello!"); | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  |     // TODO: this comment should not be taken into account
 | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |         'todo comments should be ignored'); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   it('should be able to enforce consistent identifiers', () => { | 
					
						
							|  |  |  |     const files = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'hello.component.ts': `
 | 
					
						
							|  |  |  |           import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'}) | 
					
						
							|  |  |  |           export class HelloComponent { | 
					
						
							|  |  |  |             @Input() name: string = 'world'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'hello.module.ts': `
 | 
					
						
							|  |  |  |           import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |           import {HelloComponent} from './hello.component'; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |           @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |           export class HelloModule {} | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // IDENT can be used a wild card for any identifier
 | 
					
						
							|  |  |  |     expectEmit(result.source, 'IDENT.name', 'could not find context access'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // $<ident>$ can be used as a wild-card but all the content matched by the identifiers must
 | 
					
						
							|  |  |  |     // match each other.
 | 
					
						
							|  |  |  |     // This is useful if the code generator is free to invent a name but should use the name
 | 
					
						
							|  |  |  |     // consistently.
 | 
					
						
							|  |  |  |     expectEmit( | 
					
						
							|  |  |  |         result.source, '$ctx$.$name$ … $ctx$.$name$.length', | 
					
						
							|  |  |  |         'could not find correct length access'); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('should be able to enforce that identifiers match a regexp', () => { | 
					
						
							|  |  |  |     const files = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'hello.component.ts': `
 | 
					
						
							|  |  |  |           import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({template: 'Hello {{name}}! Your name as {{name.length}} characters'}) | 
					
						
							|  |  |  |           export class HelloComponent { | 
					
						
							|  |  |  |             @Input() name: string = 'world'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'hello.module.ts': `
 | 
					
						
							|  |  |  |           import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |           import {HelloComponent} from './hello.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @NgModule({declarations: [HelloComponent]}) | 
					
						
							|  |  |  |           export class HelloModule {} | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const result = compile(files, angularFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Pass: `$n$` ends with `ME` in the generated code
 | 
					
						
							|  |  |  |     expectEmit(result.source, '$ctx$.$n$ … $ctx$.$n$.length', 'Match names', {'$n$': /ME$/i}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fail: `$n$` does not match `/(not)_(\1)/` in the generated code
 | 
					
						
							|  |  |  |     expect(() => { | 
					
						
							|  |  |  |       expectEmit( | 
					
						
							|  |  |  |           result.source, '$ctx$.$n$ … $ctx$.$n$.length', 'Match names', {'$n$': /(not)_(\1)/}); | 
					
						
							|  |  |  |     }).toThrowError(/"\$n\$" is "name" which doesn't match \/\(not\)_\(\\1\)\//); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-04-04 11:41:52 -07:00
										 |  |  | }); |