| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  | import {AotSummaryResolver, GeneratedFile, StaticSymbolCache, StaticSymbolResolver, toTypeScript} from '@angular/compiler'; | 
					
						
							| 
									
										
										
										
											2017-09-13 16:55:42 -07:00
										 |  |  | import {MetadataBundler} from '@angular/compiler-cli/src/metadata/bundler'; | 
					
						
							|  |  |  | import {privateEntriesToIndex} from '@angular/compiler-cli/src/metadata/index_writer'; | 
					
						
							| 
									
										
										
										
											2017-12-16 11:35:47 -08:00
										 |  |  | import {extractSourceMap, originalPositionFor} from '@angular/compiler/testing/src/output/source_map_util'; | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  | import {NodeFlags} from '@angular/core/src/view/index'; | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | import * as ts from 'typescript'; | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  | import {arrayToMockDir, compile, EmittingCompilerHost, expectNoDiagnostics, isInBazel, MockAotCompilerHost, MockCompilerHost, MockDirectory, MockMetadataBundlerHost, settings, setup, toMockFileArray} from './test_util'; | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 13:30:40 -08:00
										 |  |  | describe('compiler (unbundled Angular)', () => { | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |   let angularFiles = setup(); | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  |   describe('Quickstart', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should compile', () => { | 
					
						
							|  |  |  |       const {genFiles} = compile([QUICKSTART, angularFiles]); | 
					
						
							| 
									
										
										
										
											2017-10-12 16:09:49 -07:00
										 |  |  |       expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							|  |  |  |       expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-02-15 13:30:40 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   describe('aot source mapping', () => { | 
					
						
							|  |  |  |     const componentPath = '/app/app.component.ts'; | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |     const ngFactoryPath = '/app/app.component.ngfactory.ts'; | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     let rootDir: MockDirectory; | 
					
						
							|  |  |  |     let appDir: MockDirectory; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       appDir = { | 
					
						
							|  |  |  |         'app.module.ts': `
 | 
					
						
							|  |  |  |               import { NgModule }      from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               import { AppComponent }  from './app.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               @NgModule({ | 
					
						
							|  |  |  |                 declarations: [ AppComponent ], | 
					
						
							|  |  |  |                 bootstrap:    [ AppComponent ] | 
					
						
							|  |  |  |               }) | 
					
						
							|  |  |  |               export class AppModule { } | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       rootDir = {'app': appDir}; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     function compileApp(): GeneratedFile { | 
					
						
							|  |  |  |       const {genFiles} = compile([rootDir, angularFiles]); | 
					
						
							|  |  |  |       return genFiles.find( | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'))!; | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-24 09:59:58 -07:00
										 |  |  |     function findLineAndColumn( | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         file: string, token: string): {line: number|null, column: number|null} { | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |       const index = file.indexOf(token); | 
					
						
							|  |  |  |       if (index === -1) { | 
					
						
							|  |  |  |         return {line: null, column: null}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const linesUntilToken = file.slice(0, index).split('\n'); | 
					
						
							|  |  |  |       const line = linesUntilToken.length; | 
					
						
							|  |  |  |       const column = linesUntilToken[linesUntilToken.length - 1].length; | 
					
						
							|  |  |  |       return {line, column}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function createComponentSource(componentDecorator: string) { | 
					
						
							|  |  |  |       return `
 | 
					
						
							|  |  |  |         import { NgModule, Component } from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           ${componentDecorator} | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppComponent { | 
					
						
							|  |  |  |           someMethod() {} | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       `;
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('inline templates', () => { | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |       const ngUrl = `${componentPath}.AppComponent.html`; | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       function templateDecorator(template: string) { | 
					
						
							|  |  |  |         return `template: \`${template}\`,`; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 17:33:17 -07:00
										 |  |  |       declareTests({ngUrl, templateDecorator}); | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('external templates', () => { | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |       const ngUrl = '/app/app.component.html'; | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |       const templateUrl = '/app/app.component.html'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function templateDecorator(template: string) { | 
					
						
							|  |  |  |         appDir['app.component.html'] = template; | 
					
						
							|  |  |  |         return `templateUrl: 'app.component.html',`; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 17:33:17 -07:00
										 |  |  |       declareTests({ngUrl, templateDecorator}); | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 17:33:17 -07:00
										 |  |  |     function declareTests({ngUrl, templateDecorator}: | 
					
						
							|  |  |  |                               {ngUrl: string, templateDecorator: (template: string) => string}) { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should use the right source url in html parse errors', () => { | 
					
						
							|  |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator('<div>\n  </error>')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => compileApp()) | 
					
						
							|  |  |  |             .toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should use the right source url in template parse errors', () => { | 
					
						
							|  |  |  |         appDir['app.component.ts'] = | 
					
						
							|  |  |  |             createComponentSource(templateDecorator('<div>\n  <div unknown="{{ctxProp}}"></div>')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => compileApp()) | 
					
						
							|  |  |  |             .toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should create a sourceMap for the template', () => { | 
					
						
							|  |  |  |         const template = 'Hello World!'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator(template)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const genFile = compileApp(); | 
					
						
							|  |  |  |         const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const sourceMap = extractSourceMap(genSource)!; | 
					
						
							| 
									
										
										
										
											2017-10-12 16:09:49 -07:00
										 |  |  |         expect(sourceMap.file).toEqual(genFile.genFileUrl); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |         // Note: the generated file also contains code that is not mapped to
 | 
					
						
							|  |  |  |         // the template (e.g. import statements, ...)
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         const templateIndex = sourceMap.sources.indexOf(ngUrl); | 
					
						
							|  |  |  |         expect(sourceMap.sourcesContent[templateIndex]).toEqual(template); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // for the mapping to the original source file we don't store the source code
 | 
					
						
							|  |  |  |         // as we want to keep whatever TypeScript / ... produced for them.
 | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |         const sourceIndex = sourceMap.sources.indexOf(ngFactoryPath); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         expect(sourceMap.sourcesContent[sourceIndex]).toBe(' '); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should map elements correctly to the source', () => { | 
					
						
							|  |  |  |         const template = '<div>\n   <span></span></div>'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator(template)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const genFile = compileApp(); | 
					
						
							|  |  |  |         const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const sourceMap = extractSourceMap(genSource)!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`))) | 
					
						
							|  |  |  |             .toEqual({line: 2, column: 3, source: ngUrl}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should map bindings correctly to the source', () => { | 
					
						
							|  |  |  |         const template = `<div>\n   <span [title]="someMethod()"></span></div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator(template)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const genFile = compileApp(); | 
					
						
							|  |  |  |         const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const sourceMap = extractSourceMap(genSource)!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`))) | 
					
						
							|  |  |  |             .toEqual({line: 2, column: 9, source: ngUrl}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should map events correctly to the source', () => { | 
					
						
							|  |  |  |         const template = `<div>\n   <span (click)="someMethod()"></span></div>`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator(template)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const genFile = compileApp(); | 
					
						
							|  |  |  |         const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const sourceMap = extractSourceMap(genSource)!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`))) | 
					
						
							|  |  |  |             .toEqual({line: 2, column: 9, source: ngUrl}); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |       it('should map non template parts to the factory file', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const genFile = compileApp(); | 
					
						
							|  |  |  |         const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const sourceMap = extractSourceMap(genSource)!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         expect(originalPositionFor(sourceMap, {line: 1, column: 0})) | 
					
						
							| 
									
										
										
										
											2017-10-04 13:37:27 -07:00
										 |  |  |             .toEqual({line: 1, column: 0, source: ngFactoryPath}); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-14 09:16:15 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-13 14:39:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   describe('errors', () => { | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |     it('should not error or warn if an unprovided @Injectable with DI-incompatible ' + | 
					
						
							|  |  |  |            'constructor is discovered', | 
					
						
							|  |  |  |        () => { | 
					
						
							|  |  |  |          const FILES: MockDirectory = { | 
					
						
							|  |  |  |            app: { | 
					
						
							|  |  |  |              'app.ts': `
 | 
					
						
							|  |  |  |             import {Injectable, NgModule} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |             // This injectable is not provided. It is used as a base class for another
 | 
					
						
							|  |  |  |             // service but is not directly provided. It's allowed for such classes to
 | 
					
						
							|  |  |  |             // have a decorator applied as they use Angular features.
 | 
					
						
							|  |  |  |             @Injectable() | 
					
						
							|  |  |  |             export class ServiceBase { | 
					
						
							|  |  |  |               constructor(a: boolean) {} | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |               ngOnDestroy() {} | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |             @Injectable() | 
					
						
							|  |  |  |             export class MyService extends ServiceBase { | 
					
						
							|  |  |  |               constructor() { | 
					
						
							|  |  |  |                 super(true); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |             @NgModule({providers: [MyService]}) | 
					
						
							|  |  |  |             export class AppModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          spyOn(console, 'error'); | 
					
						
							|  |  |  |          spyOn(console, 'warn'); | 
					
						
							|  |  |  |          expect(() => compile([FILES, angularFiles])).not.toThrowError(); | 
					
						
							|  |  |  |          expect(console.warn).toHaveBeenCalledTimes(0); | 
					
						
							|  |  |  |          expect(console.error).toHaveBeenCalledTimes(0); | 
					
						
							|  |  |  |        }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should error if parameters of a provided @Injectable class cannot be resolved', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       const FILES: MockDirectory = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'app.ts': `
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |             import {Injectable, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @Injectable() | 
					
						
							|  |  |  |             export class MyService { | 
					
						
							|  |  |  |               constructor(a: boolean) {} | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |             @NgModule({ | 
					
						
							|  |  |  |               providers: [MyService], | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             export class MyModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2020-05-07 21:35:54 +02:00
										 |  |  |       expect(() => compile([FILES, angularFiles])) | 
					
						
							|  |  |  |           .toThrowError(`Can't resolve all parameters for MyService in /app/app.ts: (?).`); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-10 17:14:58 +00:00
										 |  |  |     it('should error if not all arguments of an @Injectable class can be resolved if strictInjectionParameters is true', | 
					
						
							| 
									
										
										
										
											2017-09-26 13:40:47 -07:00
										 |  |  |        () => { | 
					
						
							|  |  |  |          const FILES: MockDirectory = { | 
					
						
							|  |  |  |            app: { | 
					
						
							|  |  |  |              'app.ts': `
 | 
					
						
							|  |  |  |                 import {Injectable} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @Injectable() | 
					
						
							|  |  |  |                 export class MyService { | 
					
						
							|  |  |  |                   constructor(a: boolean) {} | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               `
 | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }; | 
					
						
							|  |  |  |          const warnSpy = spyOn(console, 'warn'); | 
					
						
							|  |  |  |          expect(() => compile([FILES, angularFiles], {strictInjectionParameters: true})) | 
					
						
							|  |  |  |              .toThrowError(`Can't resolve all parameters for MyService in /app/app.ts: (?).`); | 
					
						
							|  |  |  |          expect(warnSpy).not.toHaveBeenCalled(); | 
					
						
							|  |  |  |        }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 15:57:09 -07:00
										 |  |  |     it('should be able to suppress a null access', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       const FILES: MockDirectory = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'app.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-05-11 10:15:54 -07:00
										 |  |  |                 import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 interface Person { name: string; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @Component({ | 
					
						
							|  |  |  |                   selector: 'my-comp', | 
					
						
							|  |  |  |                   template: '{{maybe_person!.name}}' | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyComp { | 
					
						
							|  |  |  |                   maybe_person?: Person; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @NgModule({ | 
					
						
							|  |  |  |                   declarations: [MyComp] | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyModule {} | 
					
						
							|  |  |  |               `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       compile([FILES, angularFiles], {postCompile: expectNoDiagnostics}); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-06-21 15:05:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should not contain a self import in factory', () => { | 
					
						
							|  |  |  |       const FILES: MockDirectory = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'app.ts': `
 | 
					
						
							|  |  |  |                 import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 interface Person { name: string; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @Component({ | 
					
						
							|  |  |  |                   selector: 'my-comp', | 
					
						
							|  |  |  |                   template: '{{person.name}}' | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyComp { | 
					
						
							|  |  |  |                   person: Person; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @NgModule({ | 
					
						
							|  |  |  |                   declarations: [MyComp] | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyModule {} | 
					
						
							|  |  |  |               `
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       compile([FILES, angularFiles], { | 
					
						
							|  |  |  |         postCompile: program => { | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           const factorySource = program.getSourceFile('/app/app.ngfactory.ts')!; | 
					
						
							| 
									
										
										
										
											2017-06-21 15:05:11 -07:00
										 |  |  |           expect(factorySource.text).not.toContain('\'/app/app.ngfactory\''); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-13 14:39:34 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-15 15:50:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 15:57:09 -07:00
										 |  |  |   it('should report when a component is declared in any module', () => { | 
					
						
							|  |  |  |     const FILES: MockDirectory = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'app.ts': `
 | 
					
						
							|  |  |  |           import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({selector: 'my-comp', template: ''}) | 
					
						
							|  |  |  |           export class MyComp {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @NgModule({}) | 
					
						
							|  |  |  |           export class MyModule {} | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     expect(() => compile([FILES, angularFiles])) | 
					
						
							|  |  |  |         .toThrowError(/Cannot determine the module for class MyComp/); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |   it('should add the preamble to generated files', () => { | 
					
						
							|  |  |  |     const FILES: MockDirectory = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'app.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-03-15 15:50:30 -07:00
										 |  |  |               import { NgModule, Component } from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               @Component({ template: '' }) | 
					
						
							|  |  |  |               export class AppComponent {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               @NgModule({ declarations: [ AppComponent ] }) | 
					
						
							|  |  |  |               export class AppModule { } | 
					
						
							|  |  |  |             `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const genFilePreamble = '/* Hello world! */'; | 
					
						
							|  |  |  |     const {genFiles} = compile([FILES, angularFiles]); | 
					
						
							|  |  |  |     const genFile = | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'))!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     const genSource = toTypeScript(genFile, genFilePreamble); | 
					
						
							|  |  |  |     expect(genSource.startsWith(genFilePreamble)).toBe(true); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:54:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-13 17:16:56 -06:00
										 |  |  |   it('should be able to use animation macro methods', () => { | 
					
						
							|  |  |  |     const FILES = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'app.ts': `
 | 
					
						
							|  |  |  |       import {Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  |       import {trigger, state, style, transition, animate} from '@angular/animations'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,1)'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @Component({ | 
					
						
							|  |  |  |         selector: 'app-component', | 
					
						
							|  |  |  |         template: '<div></div>', | 
					
						
							|  |  |  |         animations: [ | 
					
						
							|  |  |  |           trigger('bodyExpansion', [ | 
					
						
							|  |  |  |             state('collapsed', style({height: '0px'})), | 
					
						
							|  |  |  |             state('expanded', style({height: '*'})), | 
					
						
							|  |  |  |             transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)), | 
					
						
							|  |  |  |           ]), | 
					
						
							|  |  |  |           trigger('displayMode', [ | 
					
						
							|  |  |  |             state('collapsed', style({margin: '0'})), | 
					
						
							|  |  |  |             state('default', style({margin: '16px 0'})), | 
					
						
							|  |  |  |             state('flat', style({margin: '0'})), | 
					
						
							|  |  |  |             transition('flat <=> collapsed, default <=> collapsed, flat <=> default', | 
					
						
							|  |  |  |                       animate(EXPANSION_PANEL_ANIMATION_TIMING)), | 
					
						
							|  |  |  |           ]), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       export class AppComponent { } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       @NgModule({ declarations: [ AppComponent ] }) | 
					
						
							|  |  |  |       export class AppModule { } | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     compile([FILES, angularFiles]); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should detect an entry component via an indirection', () => { | 
					
						
							|  |  |  |     const FILES = { | 
					
						
							|  |  |  |       app: { | 
					
						
							|  |  |  |         'app.ts': `
 | 
					
						
							|  |  |  |           import {NgModule, ANALYZE_FOR_ENTRY_COMPONENTS} from '@angular/core'; | 
					
						
							|  |  |  |           import {AppComponent} from './app.component'; | 
					
						
							|  |  |  |           import {COMPONENT_VALUE, MyComponent} from './my-component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @NgModule({ | 
					
						
							|  |  |  |             declarations: [ AppComponent, MyComponent ], | 
					
						
							|  |  |  |             bootstrap: [ AppComponent ], | 
					
						
							|  |  |  |             providers: [{ | 
					
						
							|  |  |  |               provide: ANALYZE_FOR_ENTRY_COMPONENTS, | 
					
						
							|  |  |  |               multi: true, | 
					
						
							|  |  |  |               useValue: COMPONENT_VALUE | 
					
						
							|  |  |  |             }], | 
					
						
							|  |  |  |           }) | 
					
						
							|  |  |  |           export class AppModule { } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'app.component.ts': `
 | 
					
						
							|  |  |  |           import {Component} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({ | 
					
						
							|  |  |  |             selector: 'app-component', | 
					
						
							|  |  |  |             template: '<div></div>', | 
					
						
							|  |  |  |           }) | 
					
						
							|  |  |  |           export class AppComponent { } | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'my-component.ts': `
 | 
					
						
							|  |  |  |           import {Component} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           @Component({ | 
					
						
							|  |  |  |             selector: 'my-component', | 
					
						
							|  |  |  |             template: '<div></div>', | 
					
						
							|  |  |  |           }) | 
					
						
							|  |  |  |           export class MyComponent {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           export const COMPONENT_VALUE = [{a: 'b', component: MyComponent}]; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const result = compile([FILES, angularFiles]); | 
					
						
							|  |  |  |     const appModuleFactory = | 
					
						
							| 
									
										
										
										
											2017-10-12 16:09:49 -07:00
										 |  |  |         result.genFiles.find(f => /my-component\.ngfactory/.test(f.genFileUrl)); | 
					
						
							| 
									
										
										
										
											2017-07-13 17:16:56 -06:00
										 |  |  |     expect(appModuleFactory).toBeDefined(); | 
					
						
							|  |  |  |     if (appModuleFactory) { | 
					
						
							|  |  |  |       expect(toTypeScript(appModuleFactory)).toContain('MyComponentNgFactory'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 14:54:36 -07:00
										 |  |  |   describe('ComponentFactories', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should include inputs, outputs and ng-content selectors in the component factory', () => { | 
					
						
							|  |  |  |       const FILES: MockDirectory = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'app.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-03-14 14:54:36 -07:00
										 |  |  |                 import {Component, NgModule, Input, Output} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @Component({ | 
					
						
							|  |  |  |                   selector: 'my-comp', | 
					
						
							| 
									
										
										
										
											2018-12-21 10:05:31 +00:00
										 |  |  |                   template: | 
					
						
							|  |  |  |                   '<ng-content select="child1"></ng-content>' + | 
					
						
							|  |  |  |                   '<ng-content></ng-content>' + | 
					
						
							|  |  |  |                   '<ng-template><ng-content select="child2"></ng-content></ng-template>' + | 
					
						
							|  |  |  |                   '<ng-content select="child3"></ng-content>' + | 
					
						
							|  |  |  |                   '<ng-content select="child1"></ng-content>' | 
					
						
							| 
									
										
										
										
											2017-03-14 14:54:36 -07:00
										 |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyComp { | 
					
						
							|  |  |  |                   @Input('aInputName') | 
					
						
							|  |  |  |                   aInputProp: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   @Output('aOutputName') | 
					
						
							|  |  |  |                   aOutputProp: any; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @NgModule({ | 
					
						
							|  |  |  |                   declarations: [MyComp] | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 export class MyModule {} | 
					
						
							|  |  |  |               `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const {genFiles} = compile([FILES, angularFiles]); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts')!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       const genSource = toTypeScript(genFile); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource)![0].replace(/\s*/g, ''); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       // selector
 | 
					
						
							|  |  |  |       expect(createComponentFactoryCall).toContain('my-comp'); | 
					
						
							|  |  |  |       // inputs
 | 
					
						
							|  |  |  |       expect(createComponentFactoryCall).toContain(`{aInputProp:'aInputName'}`); | 
					
						
							|  |  |  |       // outputs
 | 
					
						
							|  |  |  |       expect(createComponentFactoryCall).toContain(`{aOutputProp:'aOutputName'}`); | 
					
						
							| 
									
										
										
										
											2018-12-21 10:05:31 +00:00
										 |  |  |       // ngContentSelectors - note that the catch-all doesn't have to appear at the start
 | 
					
						
							|  |  |  |       expect(createComponentFactoryCall).toContain(`['child1','*','child2','child3','child1']`); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:54:36 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-20 15:26:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   describe('generated templates', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit', () => { | 
					
						
							|  |  |  |       const FILES: MockDirectory = { | 
					
						
							|  |  |  |         app: { | 
					
						
							|  |  |  |           'app.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-03-20 15:26:06 -07:00
										 |  |  |                 import { NgModule, Component } from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @Component({ template: '' }) | 
					
						
							|  |  |  |                 export class AppComponent {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 @NgModule({ declarations: [ AppComponent ] }) | 
					
						
							|  |  |  |                 export class AppModule { } | 
					
						
							|  |  |  |               `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const {genFiles} = compile([FILES, angularFiles]); | 
					
						
							|  |  |  |       const genFile = | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'))!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       const genSource = toTypeScript(genFile); | 
					
						
							|  |  |  |       expect(genSource).not.toContain('check('); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-20 15:26:06 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |   describe('summaries', () => { | 
					
						
							| 
									
										
										
										
											2017-08-16 09:00:03 -07:00
										 |  |  |     let angularSummaryFiles: MockDirectory; | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |     beforeAll(() => { | 
					
						
							| 
									
										
										
										
											2017-08-16 09:00:03 -07:00
										 |  |  |       angularSummaryFiles = compile(angularFiles, {useSummaries: false, emit: true}).outDir; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |     inheritanceWithSummariesSpecs(() => angularSummaryFiles); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |     describe('external symbol re-exports enabled', () => { | 
					
						
							|  |  |  |       it('should not reexport type symbols mentioned in constructors', () => { | 
					
						
							|  |  |  |         const libInput: MockDirectory = { | 
					
						
							|  |  |  |           'lib': { | 
					
						
							|  |  |  |             'base.ts': `
 | 
					
						
							|  |  |  |               export class AValue {} | 
					
						
							|  |  |  |               export type AType = {}; | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export class AClass { | 
					
						
							|  |  |  |                 constructor(a: AType, b: AValue) {} | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         const appInput: MockDirectory = { | 
					
						
							|  |  |  |           'app': { | 
					
						
							|  |  |  |             'main.ts': `
 | 
					
						
							|  |  |  |               export {AClass} from '../lib/base'; | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const {outDir: libOutDir} = compile( | 
					
						
							|  |  |  |             [libInput, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							|  |  |  |         const {genFiles: appGenFiles} = compile( | 
					
						
							|  |  |  |             [appInput, libOutDir, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |         const appNgFactoryTs = toTypeScript(appNgFactory); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).not.toContain('AType'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('AValue'); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-23 17:51:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |       it('should not reexport complex function calls', () => { | 
					
						
							|  |  |  |         const libInput: MockDirectory = { | 
					
						
							|  |  |  |           'lib': { | 
					
						
							|  |  |  |             'base.ts': `
 | 
					
						
							|  |  |  |               export class AClass { | 
					
						
							|  |  |  |                 constructor(arg: any) {} | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |                 static create(arg: any = null): AClass { return new AClass(arg); } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |                 call(arg: any) {} | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export function simple(arg: any) { return [arg]; } | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export const ctor_arg = {}; | 
					
						
							|  |  |  |               export const ctor_call = new AClass(ctor_arg); | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export const static_arg = {}; | 
					
						
							|  |  |  |               export const static_call = AClass.create(static_arg); | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export const complex_arg = {}; | 
					
						
							|  |  |  |               export const complex_call = AClass.create().call(complex_arg); | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export const simple_arg = {}; | 
					
						
							|  |  |  |               export const simple_call = simple(simple_arg); | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         const appInput: MockDirectory = { | 
					
						
							|  |  |  |           'app': { | 
					
						
							|  |  |  |             'main.ts': `
 | 
					
						
							|  |  |  |               import {ctor_call, static_call, complex_call, simple_call} from '../lib/base'; | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               export const calls = [ctor_call, static_call, complex_call, simple_call]; | 
					
						
							|  |  |  |             `,
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const {outDir: libOutDir} = compile( | 
					
						
							|  |  |  |             [libInput, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							|  |  |  |         const {genFiles: appGenFiles} = compile( | 
					
						
							|  |  |  |             [appInput, libOutDir, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |         const appNgFactoryTs = toTypeScript(appNgFactory); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // metadata of ctor calls is preserved, so we reexport the argument
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('ctor_arg'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('ctor_call'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // metadata of static calls is preserved, so we reexport the argument
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('static_arg'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('AClass'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('static_call'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // metadata of complex calls is elided, so we don't reexport the argument
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).not.toContain('complex_arg'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('complex_call'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // metadata of simple calls is preserved, so we reexport the argument
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('simple_arg'); | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('simple_call'); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-23 17:51:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |       it('should not reexport already exported symbols except for lowered symbols', () => { | 
					
						
							|  |  |  |         const libInput: MockDirectory = { | 
					
						
							|  |  |  |           'lib': { | 
					
						
							|  |  |  |             'base.ts': `
 | 
					
						
							|  |  |  |               export const exportedVar = 1; | 
					
						
							| 
									
										
										
										
											2017-10-23 17:51:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |               // A symbol introduced by lowering expressions
 | 
					
						
							|  |  |  |               export const ɵ1 = 'lowered symbol'; | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         const appInput: MockDirectory = { | 
					
						
							|  |  |  |           'app': { | 
					
						
							|  |  |  |             'main.ts': `export * from '../lib/base';`, | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const {outDir: libOutDir} = compile( | 
					
						
							|  |  |  |             [libInput, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							|  |  |  |         const {genFiles: appGenFiles} = compile( | 
					
						
							|  |  |  |             [appInput, libOutDir, angularSummaryFiles], | 
					
						
							|  |  |  |             {useSummaries: true, createExternalSymbolFactoryReexports: true}); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts')!; | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |         const appNgFactoryTs = toTypeScript(appNgFactory); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // we don't need to reexport exported symbols via the .ngfactory
 | 
					
						
							|  |  |  |         // as we can refer to them via the reexport.
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).not.toContain('exportedVar'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // although ɵ1 is reexported via `export *`, we still need to reexport it
 | 
					
						
							|  |  |  |         // via the .ngfactory as tsickle expands `export *` into named exports,
 | 
					
						
							|  |  |  |         // and doesn't know about our lowered symbols as we introduce them
 | 
					
						
							|  |  |  |         // after the typecheck phase.
 | 
					
						
							|  |  |  |         expect(appNgFactoryTs).toContain('ɵ1'); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function inheritanceWithSummariesSpecs(getAngularSummaryFiles: () => MockDirectory) { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     function compileParentAndChild( | 
					
						
							|  |  |  |         {parentClassDecorator, parentModuleDecorator, childClassDecorator, childModuleDecorator}: { | 
					
						
							|  |  |  |           parentClassDecorator: string, | 
					
						
							|  |  |  |           parentModuleDecorator: string, | 
					
						
							|  |  |  |           childClassDecorator: string, | 
					
						
							|  |  |  |           childModuleDecorator: string | 
					
						
							|  |  |  |         }) { | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |       const libInput: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |         'lib': { | 
					
						
							|  |  |  |           'base.ts': `
 | 
					
						
							|  |  |  |               import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               ${parentClassDecorator} | 
					
						
							|  |  |  |               export class Base {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               ${parentModuleDecorator} | 
					
						
							|  |  |  |               export class BaseModule {} | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |       const appInput: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |         'app': { | 
					
						
							|  |  |  |           'main.ts': `
 | 
					
						
							|  |  |  |               import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core'; | 
					
						
							|  |  |  |               import {Base} from '../lib/base'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               ${childClassDecorator} | 
					
						
							|  |  |  |               export class Extends extends Base {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               ${childModuleDecorator} | 
					
						
							|  |  |  |               export class MyModule {} | 
					
						
							|  |  |  |             `
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |       const {outDir: libOutDir} = | 
					
						
							|  |  |  |           compile([libInput, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							|  |  |  |       const {genFiles} = | 
					
						
							|  |  |  |           compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							| 
									
										
										
										
											2017-10-12 16:09:49 -07:00
										 |  |  |       return genFiles.find(gf => gf.srcFileUrl === '/app/main.ts'); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should inherit ctor and lifecycle hooks from classes in other compilation units', () => { | 
					
						
							|  |  |  |       const libInput: MockDirectory = { | 
					
						
							|  |  |  |         'lib': { | 
					
						
							|  |  |  |           'base.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |             export class AParam {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             export class Base { | 
					
						
							|  |  |  |               constructor(a: AParam) {} | 
					
						
							|  |  |  |               ngOnDestroy() {} | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const appInput: MockDirectory = { | 
					
						
							|  |  |  |         'app': { | 
					
						
							|  |  |  |           'main.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |             import {NgModule, Component} from '@angular/core'; | 
					
						
							|  |  |  |             import {Base} from '../lib/base'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @Component({template: ''}) | 
					
						
							|  |  |  |             export class Extends extends Base {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @NgModule({ | 
					
						
							|  |  |  |               declarations: [Extends] | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             export class MyModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |       const {outDir: libOutDir} = | 
					
						
							|  |  |  |           compile([libInput, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							|  |  |  |       const {genFiles} = | 
					
						
							|  |  |  |           compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts')!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy; | 
					
						
							|  |  |  |       expect(toTypeScript(mainNgFactory)) | 
					
						
							|  |  |  |           .toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels', | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |        () => { | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |          const lib1Input: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |            'lib1': { | 
					
						
							|  |  |  |              'base.ts': `
 | 
					
						
							|  |  |  |             export class AParam {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             export class Base { | 
					
						
							|  |  |  |               constructor(a: AParam) {} | 
					
						
							|  |  |  |               ngOnDestroy() {} | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |          const lib2Input: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |            'lib2': { | 
					
						
							|  |  |  |              'middle.ts': `
 | 
					
						
							|  |  |  |             import {Base} from '../lib1/base'; | 
					
						
							|  |  |  |             export class Middle extends Base {} | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |          const appInput: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |            'app': { | 
					
						
							|  |  |  |              'main.ts': `
 | 
					
						
							|  |  |  |             import {NgModule, Component} from '@angular/core'; | 
					
						
							|  |  |  |             import {Middle} from '../lib2/middle'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @Component({template: ''}) | 
					
						
							|  |  |  |             export class Extends extends Middle {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             @NgModule({ | 
					
						
							|  |  |  |               declarations: [Extends] | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             export class MyModule {} | 
					
						
							|  |  |  |           `
 | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }; | 
					
						
							| 
									
										
										
										
											2017-08-16 09:00:03 -07:00
										 |  |  |          const {outDir: lib1OutDir} = | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |              compile([lib1Input, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |          const {outDir: lib2OutDir} = | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |              compile([lib1OutDir, lib2Input, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |          const {genFiles} = compile( | 
					
						
							|  |  |  |              [lib1OutDir, lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |          const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts')!; | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |          const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy; | 
					
						
							| 
									
										
										
										
											2019-02-12 23:29:28 +01:00
										 |  |  |          const mainNgFactorySource = toTypeScript(mainNgFactory); | 
					
						
							|  |  |  |          expect(mainNgFactorySource).toContain(`import * as i2 from '/lib1/base';`); | 
					
						
							|  |  |  |          expect(mainNgFactorySource).toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |        }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     describe('Injectable', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should allow to inherit', () => { | 
					
						
							|  |  |  |         const mainNgFactory = compileParentAndChild({ | 
					
						
							|  |  |  |           parentClassDecorator: '@Injectable()', | 
					
						
							|  |  |  |           parentModuleDecorator: '@NgModule({providers: [Base]})', | 
					
						
							|  |  |  |           childClassDecorator: '@Injectable()', | 
					
						
							|  |  |  |           childModuleDecorator: '@NgModule({providers: [Extends]})', | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         expect(mainNgFactory).toBeTruthy(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should error if the child class has no matching decorator', () => { | 
					
						
							|  |  |  |         expect(() => compileParentAndChild({ | 
					
						
							|  |  |  |                  parentClassDecorator: '@Injectable()', | 
					
						
							|  |  |  |                  parentModuleDecorator: '@NgModule({providers: [Base]})', | 
					
						
							|  |  |  |                  childClassDecorator: '', | 
					
						
							|  |  |  |                  childModuleDecorator: '@NgModule({providers: [Extends]})', | 
					
						
							|  |  |  |                })) | 
					
						
							| 
									
										
										
										
											2017-11-14 17:49:47 -08:00
										 |  |  |             .toThrowError(`Error during template compile of 'Extends'
 | 
					
						
							|  |  |  |   Class Extends in /app/main.ts extends from a Injectable in another compilation unit without duplicating the decorator | 
					
						
							|  |  |  |     Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.`);
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('Component', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should allow to inherit', () => { | 
					
						
							|  |  |  |         const mainNgFactory = compileParentAndChild({ | 
					
						
							|  |  |  |           parentClassDecorator: `@Component({template: ''})`, | 
					
						
							|  |  |  |           parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |           childClassDecorator: `@Component({template: ''})`, | 
					
						
							|  |  |  |           childModuleDecorator: '@NgModule({declarations: [Extends]})' | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         expect(mainNgFactory).toBeTruthy(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should error if the child class has no matching decorator', () => { | 
					
						
							|  |  |  |         expect(() => compileParentAndChild({ | 
					
						
							|  |  |  |                  parentClassDecorator: `@Component({template: ''})`, | 
					
						
							|  |  |  |                  parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |                  childClassDecorator: '', | 
					
						
							|  |  |  |                  childModuleDecorator: '@NgModule({declarations: [Extends]})', | 
					
						
							|  |  |  |                })) | 
					
						
							| 
									
										
										
										
											2017-11-14 17:49:47 -08:00
										 |  |  |             .toThrowError(`Error during template compile of 'Extends'
 | 
					
						
							|  |  |  |   Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator | 
					
						
							|  |  |  |     Please add a Directive or Component decorator to the class.`);
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('Directive', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should allow to inherit', () => { | 
					
						
							|  |  |  |         const mainNgFactory = compileParentAndChild({ | 
					
						
							|  |  |  |           parentClassDecorator: `@Directive({selector: '[someDir]'})`, | 
					
						
							|  |  |  |           parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |           childClassDecorator: `@Directive({selector: '[someDir]'})`, | 
					
						
							|  |  |  |           childModuleDecorator: '@NgModule({declarations: [Extends]})', | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         expect(mainNgFactory).toBeTruthy(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should error if the child class has no matching decorator', () => { | 
					
						
							|  |  |  |         expect(() => compileParentAndChild({ | 
					
						
							|  |  |  |                  parentClassDecorator: `@Directive({selector: '[someDir]'})`, | 
					
						
							|  |  |  |                  parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |                  childClassDecorator: '', | 
					
						
							|  |  |  |                  childModuleDecorator: '@NgModule({declarations: [Extends]})', | 
					
						
							|  |  |  |                })) | 
					
						
							| 
									
										
										
										
											2017-11-14 17:49:47 -08:00
										 |  |  |             .toThrowError(`Error during template compile of 'Extends'
 | 
					
						
							|  |  |  |   Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator | 
					
						
							|  |  |  |     Please add a Directive or Component decorator to the class.`);
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('Pipe', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should allow to inherit', () => { | 
					
						
							|  |  |  |         const mainNgFactory = compileParentAndChild({ | 
					
						
							|  |  |  |           parentClassDecorator: `@Pipe({name: 'somePipe'})`, | 
					
						
							|  |  |  |           parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |           childClassDecorator: `@Pipe({name: 'somePipe'})`, | 
					
						
							|  |  |  |           childModuleDecorator: '@NgModule({declarations: [Extends]})', | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         expect(mainNgFactory).toBeTruthy(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should error if the child class has no matching decorator', () => { | 
					
						
							|  |  |  |         expect(() => compileParentAndChild({ | 
					
						
							|  |  |  |                  parentClassDecorator: `@Pipe({name: 'somePipe'})`, | 
					
						
							|  |  |  |                  parentModuleDecorator: '@NgModule({declarations: [Base]})', | 
					
						
							|  |  |  |                  childClassDecorator: '', | 
					
						
							|  |  |  |                  childModuleDecorator: '@NgModule({declarations: [Extends]})', | 
					
						
							|  |  |  |                })) | 
					
						
							| 
									
										
										
										
											2017-11-14 17:49:47 -08:00
										 |  |  |             .toThrowError(`Error during template compile of 'Extends'
 | 
					
						
							|  |  |  |   Class Extends in /app/main.ts extends from a Pipe in another compilation unit without duplicating the decorator | 
					
						
							|  |  |  |     Please add a Pipe decorator to the class.`);
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('NgModule', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       it('should allow to inherit', () => { | 
					
						
							|  |  |  |         const mainNgFactory = compileParentAndChild({ | 
					
						
							|  |  |  |           parentClassDecorator: `@NgModule()`, | 
					
						
							|  |  |  |           parentModuleDecorator: '', | 
					
						
							|  |  |  |           childClassDecorator: `@NgModule()`, | 
					
						
							|  |  |  |           childModuleDecorator: '', | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         expect(mainNgFactory).toBeTruthy(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should error if the child class has no matching decorator', () => { | 
					
						
							|  |  |  |         expect(() => compileParentAndChild({ | 
					
						
							|  |  |  |                  parentClassDecorator: `@NgModule()`, | 
					
						
							|  |  |  |                  parentModuleDecorator: '', | 
					
						
							|  |  |  |                  childClassDecorator: '', | 
					
						
							|  |  |  |                  childModuleDecorator: '', | 
					
						
							|  |  |  |                })) | 
					
						
							| 
									
										
										
										
											2017-11-14 17:49:47 -08:00
										 |  |  |             .toThrowError(`Error during template compile of 'Extends'
 | 
					
						
							|  |  |  |   Class Extends in /app/main.ts extends from a NgModule in another compilation unit without duplicating the decorator | 
					
						
							|  |  |  |     Please add a NgModule decorator to the class.`);
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-03-30 14:51:29 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-08-22 17:07:55 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-02-15 13:30:40 -08:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('compiler (bundled Angular)', () => { | 
					
						
							| 
									
										
										
										
											2018-03-20 17:02:50 -07:00
										 |  |  |   let angularFiles: Map<string, string> = setup(); | 
					
						
							| 
									
										
										
										
											2017-02-15 13:30:40 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   beforeAll(() => { | 
					
						
							| 
									
										
										
										
											2018-03-20 17:02:50 -07:00
										 |  |  |     if (!isInBazel()) { | 
					
						
							|  |  |  |       // If we are not using Bazel then we need to build these files explicitly
 | 
					
						
							|  |  |  |       const emittingHost = new EmittingCompilerHost(['@angular/core/index'], {emitMetadata: false}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Create the metadata bundled
 | 
					
						
							|  |  |  |       const indexModule = emittingHost.effectiveName('@angular/core/index'); | 
					
						
							|  |  |  |       const bundler = new MetadataBundler( | 
					
						
							|  |  |  |           indexModule, '@angular/core', new MockMetadataBundlerHost(emittingHost)); | 
					
						
							|  |  |  |       const bundle = bundler.getMetadataBundle(); | 
					
						
							|  |  |  |       const metadata = JSON.stringify(bundle.metadata, null, ' '); | 
					
						
							|  |  |  |       const bundleIndexSource = privateEntriesToIndex('./index', bundle.privates); | 
					
						
							|  |  |  |       emittingHost.override('@angular/core/bundle_index.ts', bundleIndexSource); | 
					
						
							|  |  |  |       emittingHost.addWrittenFile( | 
					
						
							|  |  |  |           '@angular/core/package.json', JSON.stringify({typings: 'bundle_index.d.ts'})); | 
					
						
							|  |  |  |       emittingHost.addWrittenFile('@angular/core/bundle_index.metadata.json', metadata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Emit the sources
 | 
					
						
							|  |  |  |       const bundleIndexName = emittingHost.effectiveName('@angular/core/bundle_index.ts'); | 
					
						
							|  |  |  |       const emittingProgram = ts.createProgram([bundleIndexName], settings, emittingHost); | 
					
						
							|  |  |  |       emittingProgram.emit(); | 
					
						
							|  |  |  |       angularFiles = emittingHost.writtenAngularFiles(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-02-15 13:30:40 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('Quickstart', () => { | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should compile', () => { | 
					
						
							|  |  |  |       const {genFiles} = compile([QUICKSTART, angularFiles]); | 
					
						
							| 
									
										
										
										
											2017-10-12 16:09:49 -07:00
										 |  |  |       expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							|  |  |  |       expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-11-29 15:27:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it('should support tsx', () => { | 
					
						
							|  |  |  |       const tsOptions = {jsx: ts.JsxEmit.React}; | 
					
						
							|  |  |  |       const {genFiles} = | 
					
						
							|  |  |  |           compile([QUICKSTART_TSX, angularFiles], /* options */ undefined, tsOptions); | 
					
						
							|  |  |  |       expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							|  |  |  |       expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-23 14:24:34 +11:00
										 |  |  |   describe('Bundled library', () => { | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |     let libraryFiles: MockDirectory; | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     beforeAll(() => { | 
					
						
							|  |  |  |       // Emit the library bundle
 | 
					
						
							|  |  |  |       const emittingHost = | 
					
						
							|  |  |  |           new EmittingCompilerHost(['/bolder/index.ts'], {emitMetadata: false, mockData: LIBRARY}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 17:02:50 -07:00
										 |  |  |       if (isInBazel()) { | 
					
						
							|  |  |  |         // In bazel we can just add the angular files from the ones read during setup.
 | 
					
						
							|  |  |  |         emittingHost.addFiles(angularFiles); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |       // Create the metadata bundled
 | 
					
						
							|  |  |  |       const indexModule = '/bolder/public-api'; | 
					
						
							|  |  |  |       const bundler = | 
					
						
							|  |  |  |           new MetadataBundler(indexModule, 'bolder', new MockMetadataBundlerHost(emittingHost)); | 
					
						
							|  |  |  |       const bundle = bundler.getMetadataBundle(); | 
					
						
							|  |  |  |       const metadata = JSON.stringify(bundle.metadata, null, ' '); | 
					
						
							|  |  |  |       const bundleIndexSource = privateEntriesToIndex('./public-api', bundle.privates); | 
					
						
							|  |  |  |       emittingHost.override('/bolder/index.ts', bundleIndexSource); | 
					
						
							|  |  |  |       emittingHost.addWrittenFile('/bolder/index.metadata.json', metadata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // Emit the sources
 | 
					
						
							|  |  |  |       const emittingProgram = ts.createProgram(['/bolder/index.ts'], settings, emittingHost); | 
					
						
							|  |  |  |       emittingProgram.emit(); | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |       const libFiles = emittingHost.written; | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Copy the .html file
 | 
					
						
							|  |  |  |       const htmlFileName = '/bolder/src/bolder.component.html'; | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |       libFiles.set(htmlFileName, emittingHost.readFile(htmlFileName)); | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  |       libraryFiles = arrayToMockDir(toMockFileArray(libFiles).map( | 
					
						
							|  |  |  |           ({fileName, content}) => ({fileName: `/node_modules${fileName}`, content}))); | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-17 15:39:08 -07:00
										 |  |  |     it('should compile', () => compile([LIBRARY_USING_APP, libraryFiles, angularFiles])); | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  | const QUICKSTART: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  |   quickstart: { | 
					
						
							|  |  |  |     app: { | 
					
						
							|  |  |  |       'app.component.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-02-14 13:33:06 -08:00
										 |  |  |         import {Component} from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           template: '<h1>Hello {{name}}</h1>' | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppComponent { | 
					
						
							|  |  |  |           name = 'Angular'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |       'app.module.ts': `
 | 
					
						
							| 
									
										
										
										
											2017-02-14 13:33:06 -08:00
										 |  |  |         import { NgModule }      from '@angular/core'; | 
					
						
							| 
									
										
										
										
											2017-03-23 13:38:01 -07:00
										 |  |  |         import { toString }      from './utils'; | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         import { AppComponent }  from './app.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @NgModule({ | 
					
						
							|  |  |  |           declarations: [ AppComponent ], | 
					
						
							|  |  |  |           bootstrap:    [ AppComponent ] | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppModule { } | 
					
						
							| 
									
										
										
										
											2017-03-23 13:38:01 -07:00
										 |  |  |       `,
 | 
					
						
							|  |  |  |       // #15420
 | 
					
						
							|  |  |  |       'utils.ts': `
 | 
					
						
							|  |  |  |         export function toString(value: any): string { | 
					
						
							|  |  |  |           return  ''; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-01-26 09:35:49 -08:00
										 |  |  |       `
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-03-15 15:50:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 15:27:16 +08:00
										 |  |  | const QUICKSTART_TSX: MockDirectory = { | 
					
						
							|  |  |  |   quickstart: { | 
					
						
							|  |  |  |     app: { | 
					
						
							|  |  |  |       // #20555
 | 
					
						
							|  |  |  |       'app.component.tsx': `
 | 
					
						
							|  |  |  |         import {Component} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           template: '<h1>Hello {{name}}</h1>' | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppComponent { | 
					
						
							|  |  |  |           name = 'Angular'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |       'app.module.ts': `
 | 
					
						
							|  |  |  |         import { NgModule }      from '@angular/core'; | 
					
						
							|  |  |  |         import { AppComponent }  from './app.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @NgModule({ | 
					
						
							|  |  |  |           declarations: [ AppComponent ], | 
					
						
							|  |  |  |           bootstrap:    [ AppComponent ] | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppModule { } | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  | const LIBRARY: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |   bolder: { | 
					
						
							|  |  |  |     'public-api.ts': `
 | 
					
						
							|  |  |  |       export * from './src/bolder.component'; | 
					
						
							|  |  |  |       export * from './src/bolder.module'; | 
					
						
							| 
									
										
										
										
											2017-09-07 14:43:29 +02:00
										 |  |  |       export {BolderModule as ReExportedModule} from './src/bolder.module'; | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |     `,
 | 
					
						
							|  |  |  |     src: { | 
					
						
							|  |  |  |       'bolder.component.ts': `
 | 
					
						
							|  |  |  |         import {Component, Input} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           selector: 'bolder', | 
					
						
							|  |  |  |           templateUrl: './bolder.component.html' | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class BolderComponent { | 
					
						
							|  |  |  |           @Input() data: string; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |       'bolder.component.html': `
 | 
					
						
							|  |  |  |         <b>{{data}}</b> | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |       'bolder.module.ts': `
 | 
					
						
							|  |  |  |         import {NgModule} from '@angular/core'; | 
					
						
							|  |  |  |         import {BolderComponent} from './bolder.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @NgModule({ | 
					
						
							|  |  |  |           declarations: [BolderComponent], | 
					
						
							|  |  |  |           exports: [BolderComponent] | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class BolderModule {} | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 09:24:42 -07:00
										 |  |  | const LIBRARY_USING_APP: MockDirectory = { | 
					
						
							| 
									
										
										
										
											2017-03-20 16:31:11 -07:00
										 |  |  |   'lib-user': { | 
					
						
							|  |  |  |     app: { | 
					
						
							|  |  |  |       'app.component.ts': `
 | 
					
						
							|  |  |  |         import {Component} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           template: '<h1>Hello <bolder [data]="name"></bolder></h1>' | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppComponent { | 
					
						
							|  |  |  |           name = 'Angular'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |       'app.module.ts': `
 | 
					
						
							|  |  |  |         import { NgModule }      from '@angular/core'; | 
					
						
							|  |  |  |         import { BolderModule }  from 'bolder'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         import { AppComponent }  from './app.component'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @NgModule({ | 
					
						
							|  |  |  |           declarations: [ AppComponent ], | 
					
						
							|  |  |  |           bootstrap:    [ AppComponent ], | 
					
						
							|  |  |  |           imports:      [ BolderModule ] | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class AppModule { } | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; |