refactor(compiler): allow sync AOT compilation (#16832).

AOT compilation can be executed synchronously now,
if the `ReosurceLoader` returns a string directly
(and no `Promise`).
This commit is contained in:
Tobias Bosch
2017-05-17 15:39:08 -07:00
committed by Chuck Jazdzewski
parent 255d7226d1
commit 5af143e8e4
15 changed files with 577 additions and 643 deletions

View File

@ -8,7 +8,6 @@
import {GeneratedFile, toTypeScript} from '@angular/compiler';
import {NodeFlags} from '@angular/core/src/view/index';
import {async} from '@angular/core/testing';
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
import * as ts from 'typescript';
@ -20,11 +19,11 @@ describe('compiler (unbundled Angular)', () => {
let angularFiles = setup();
describe('Quickstart', () => {
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
.toBeDefined();
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
})));
it('should compile', () => {
const {genFiles} = compile([QUICKSTART, angularFiles]);
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
});
});
describe('aot source mapping', () => {
@ -51,12 +50,10 @@ describe('compiler (unbundled Angular)', () => {
rootDir = {'app': appDir};
});
function compileApp(): Promise<GeneratedFile> {
return compile([rootDir, angularFiles])
.then(
({genFiles}) => {return genFiles.find(
genFile =>
genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'))});
function compileApp(): GeneratedFile {
const {genFiles} = compile([rootDir, angularFiles]);
return genFiles.find(
genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'));
}
function findLineAndColumn(
@ -106,102 +103,95 @@ describe('compiler (unbundled Angular)', () => {
function declareTests({ngUrl, templateDecorator}:
{ngUrl: string, templateDecorator: (template: string) => string}) {
it('should use the right source url in html parse errors', async(() => {
appDir['app.component.ts'] =
createComponentSource(templateDecorator('<div>\n </error>'));
it('should use the right source url in html parse errors', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator('<div>\n </error>'));
expectPromiseToThrow(
compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`));
}));
expect(() => compileApp())
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:2`));
});
it('should use the right source url in template parse errors', async(() => {
appDir['app.component.ts'] = createComponentSource(
templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
it('should use the right source url in template parse errors', () => {
appDir['app.component.ts'] =
createComponentSource(templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>'));
expectPromiseToThrow(
compileApp(), new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`));
}));
expect(() => compileApp())
.toThrowError(new RegExp(`Template parse errors[\\s\\S]*${ngUrl}@1:7`));
});
it('should create a sourceMap for the template', async(() => {
const template = 'Hello World!';
it('should create a sourceMap for the template', () => {
const template = 'Hello World!';
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(sourceMap.file).toEqual(genFile.genFileUrl);
const genFile = compileApp();
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(sourceMap.file).toEqual(genFile.genFileUrl);
// the generated file contains code that is not mapped to
// the template but rather to the original source file (e.g. import statements, ...)
const templateIndex = sourceMap.sources.indexOf(ngUrl);
expect(sourceMap.sourcesContent[templateIndex]).toEqual(template);
// the generated file contains code that is not mapped to
// the template but rather to the original source file (e.g. import statements, ...)
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.
const sourceIndex = sourceMap.sources.indexOf(ngComponentPath);
expect(sourceMap.sourcesContent[sourceIndex]).toBe(' ');
});
}));
// 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.
const sourceIndex = sourceMap.sources.indexOf(ngComponentPath);
expect(sourceMap.sourcesContent[sourceIndex]).toBe(' ');
});
it('should map elements correctly to the source', async(() => {
const template = '<div>\n <span></span></div>';
it('should map elements correctly to the source', () => {
const template = '<div>\n <span></span></div>';
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`)))
.toEqual({line: 2, column: 3, source: ngUrl});
});
}));
const genFile = compileApp();
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`)))
.toEqual({line: 2, column: 3, source: ngUrl});
});
it('should map bindings correctly to the source', async(() => {
const template = `<div>\n <span [title]="someMethod()"></span></div>`;
it('should map bindings correctly to the source', () => {
const template = `<div>\n <span [title]="someMethod()"></span></div>`;
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
}));
const genFile = compileApp();
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
it('should map events correctly to the source', async(() => {
const template = `<div>\n <span (click)="someMethod()"></span></div>`;
it('should map events correctly to the source', () => {
const template = `<div>\n <span (click)="someMethod()"></span></div>`;
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
}));
const genFile = compileApp();
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
it('should map non template parts to the source file', async(() => {
appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!'));
it('should map non template parts to the source file', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!'));
compileApp().then((genFile) => {
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
.toEqual({line: 1, column: 0, source: ngComponentPath});
});
}));
const genFile = compileApp();
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
.toEqual({line: 1, column: 0, source: ngComponentPath});
});
}
});
describe('errors', () => {
it('should only warn if not all arguments of an @Injectable class can be resolved',
async(() => {
const FILES: MockDirectory = {
app: {
'app.ts': `
it('should only warn if not all arguments of an @Injectable class can be resolved', () => {
const FILES: MockDirectory = {
app: {
'app.ts': `
import {Injectable} from '@angular/core';
@Injectable()
@ -209,20 +199,19 @@ describe('compiler (unbundled Angular)', () => {
constructor(a: boolean) {}
}
`
}
};
const warnSpy = spyOn(console, 'warn');
compile([FILES, angularFiles]).then(() => {
expect(warnSpy).toHaveBeenCalledWith(
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
});
}
};
const warnSpy = spyOn(console, 'warn');
compile([FILES, angularFiles]);
expect(warnSpy).toHaveBeenCalledWith(
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
}));
});
it('should be able to supress a null access', async(() => {
const FILES: MockDirectory = {
app: {
'app.ts': `
it('should be able to supress a null access', () => {
const FILES: MockDirectory = {
app: {
'app.ts': `
import {Component, NgModule} from '@angular/core';
interface Person { name: string; }
@ -240,16 +229,16 @@ describe('compiler (unbundled Angular)', () => {
})
export class MyModule {}
`
}
};
compile([FILES, angularFiles], {postCompile: expectNoDiagnostics});
}));
}
};
compile([FILES, angularFiles], {postCompile: expectNoDiagnostics});
});
});
it('should add the preamble to generated files', async(() => {
const FILES: MockDirectory = {
app: {
'app.ts': `
it('should add the preamble to generated files', () => {
const FILES: MockDirectory = {
app: {
'app.ts': `
import { NgModule, Component } from '@angular/core';
@Component({ template: '' })
@ -258,24 +247,21 @@ describe('compiler (unbundled Angular)', () => {
@NgModule({ declarations: [ AppComponent ] })
export class AppModule { }
`
}
};
const genFilePreamble = '/* Hello world! */';
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile =
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
const genSource = toTypeScript(genFile, genFilePreamble);
expect(genSource.startsWith(genFilePreamble)).toBe(true);
});
}));
}
};
const genFilePreamble = '/* Hello world! */';
const {genFiles} = compile([FILES, angularFiles]);
const genFile =
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
const genSource = toTypeScript(genFile, genFilePreamble);
expect(genSource.startsWith(genFilePreamble)).toBe(true);
});
describe('ComponentFactories', () => {
it('should include inputs, outputs and ng-content selectors in the component factory',
async(() => {
const FILES: MockDirectory = {
app: {
'app.ts': `
it('should include inputs, outputs and ng-content selectors in the component factory', () => {
const FILES: MockDirectory = {
app: {
'app.ts': `
import {Component, NgModule, Input, Output} from '@angular/core';
@Component({
@ -295,31 +281,28 @@ describe('compiler (unbundled Angular)', () => {
})
export class MyModule {}
`
}
};
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
const genSource = toTypeScript(genFile);
const createComponentFactoryCall =
/ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
// selector
expect(createComponentFactoryCall).toContain('my-comp');
// inputs
expect(createComponentFactoryCall).toContain(`{aInputProp:'aInputName'}`);
// outputs
expect(createComponentFactoryCall).toContain(`{aOutputProp:'aOutputName'}`);
// ngContentSelectors
expect(createComponentFactoryCall).toContain(`['*','child']`);
});
}));
}
};
const {genFiles} = compile([FILES, angularFiles]);
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
const genSource = toTypeScript(genFile);
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
// selector
expect(createComponentFactoryCall).toContain('my-comp');
// inputs
expect(createComponentFactoryCall).toContain(`{aInputProp:'aInputName'}`);
// outputs
expect(createComponentFactoryCall).toContain(`{aOutputProp:'aOutputName'}`);
// ngContentSelectors
expect(createComponentFactoryCall).toContain(`['*','child']`);
});
});
describe('generated templates', () => {
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit',
async(() => {
const FILES: MockDirectory = {
app: {
'app.ts': `
it('should not call `check` for directives without bindings nor ngDoCheck/ngOnInit', () => {
const FILES: MockDirectory = {
app: {
'app.ts': `
import { NgModule, Component } from '@angular/core';
@Component({ template: '' })
@ -328,16 +311,15 @@ describe('compiler (unbundled Angular)', () => {
@NgModule({ declarations: [ AppComponent ] })
export class AppModule { }
`
}
};
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile = genFiles.find(
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
const genSource = toTypeScript(genFile);
expect(genSource).not.toContain('check(');
});
}
};
const {genFiles} = compile([FILES, angularFiles]);
const genFile =
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
const genSource = toTypeScript(genFile);
expect(genSource).not.toContain('check(');
}));
});
});
describe('inheritance with summaries', () => {
@ -376,16 +358,15 @@ describe('compiler (unbundled Angular)', () => {
}
};
return compile([libInput, angularFiles], {useSummaries: true})
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
.then(({genFiles}) => genFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
const {outDir: libOutDir} = compile([libInput, angularFiles], {useSummaries: true});
const {genFiles} = compile([libOutDir, appInput, angularFiles], {useSummaries: true});
return genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
}
it('should inherit ctor and lifecycle hooks from classes in other compilation units',
async(() => {
const libInput: MockDirectory = {
'lib': {
'base.ts': `
it('should inherit ctor and lifecycle hooks from classes in other compilation units', () => {
const libInput: MockDirectory = {
'lib': {
'base.ts': `
export class AParam {}
export class Base {
@ -393,11 +374,11 @@ describe('compiler (unbundled Angular)', () => {
ngOnDestroy() {}
}
`
}
};
const appInput: MockDirectory = {
'app': {
'main.ts': `
}
};
const appInput: MockDirectory = {
'app': {
'main.ts': `
import {NgModule, Component} from '@angular/core';
import {Base} from '../lib/base';
@ -409,21 +390,19 @@ describe('compiler (unbundled Angular)', () => {
})
export class MyModule {}
`
}
};
}
};
compile([libInput, angularFiles], {useSummaries: true})
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
.then(({genFiles}) => {
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
});
}));
const {outDir: libOutDir} = compile([libInput, angularFiles], {useSummaries: true});
const {genFiles} = compile([libOutDir, appInput, angularFiles], {useSummaries: true});
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
});
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
async(() => {
() => {
const lib1Input: MockDirectory = {
'lib1': {
'base.ts': `
@ -463,135 +442,134 @@ describe('compiler (unbundled Angular)', () => {
`
}
};
compile([lib1Input, angularFiles], {useSummaries: true})
.then(({outDir}) => compile([outDir, lib2Input, angularFiles], {useSummaries: true}))
.then(({outDir}) => compile([outDir, appInput, angularFiles], {useSummaries: true}))
.then(({genFiles}) => {
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
});
}));
const {outDir: lib1OutDir} = compile([lib1Input, angularFiles], {useSummaries: true});
const {outDir: lib2OutDir} =
compile([lib1OutDir, lib2Input, angularFiles], {useSummaries: true});
const {genFiles} = compile([lib2OutDir, appInput, angularFiles], {useSummaries: true});
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
});
describe('Injectable', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: '@Injectable()',
parentModuleDecorator: '@NgModule({providers: [Base]})',
childClassDecorator: '@Injectable()',
childModuleDecorator: '@NgModule({providers: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
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', async(() => {
compileParentAndChild({
parentClassDecorator: '@Injectable()',
parentModuleDecorator: '@NgModule({providers: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({providers: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'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.');
});
}));
it('should error if the child class has no matching decorator', () => {
expect(() => compileParentAndChild({
parentClassDecorator: '@Injectable()',
parentModuleDecorator: '@NgModule({providers: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({providers: [Extends]})',
}))
.toThrowError(
'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.');
});
});
describe('Component', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Component({template: ''})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Component({template: ''})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
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', async(() => {
compileParentAndChild({
parentClassDecorator: `@Component({template: ''})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'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.');
});
}));
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]})',
}))
.toThrowError(
'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.');
});
});
describe('Directive', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Directive({selector: '[someDir]'})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
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', async(() => {
compileParentAndChild({
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'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.');
});
}));
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]})',
}))
.toThrowError(
'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.');
});
});
describe('Pipe', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Pipe({name: 'somePipe'})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
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', async(() => {
compileParentAndChild({
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'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.');
});
}));
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]})',
}))
.toThrowError(
'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.');
});
});
describe('NgModule', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@NgModule()`,
parentModuleDecorator: '',
childClassDecorator: `@NgModule()`,
childModuleDecorator: '',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
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', async(() => {
compileParentAndChild({
parentClassDecorator: `@NgModule()`,
parentModuleDecorator: '',
childClassDecorator: '',
childModuleDecorator: '',
}).then(fail, (e) => {
expect(e.message).toContain(
'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.');
});
}));
it('should error if the child class has no matching decorator', () => {
expect(() => compileParentAndChild({
parentClassDecorator: `@NgModule()`,
parentModuleDecorator: '',
childClassDecorator: '',
childModuleDecorator: '',
}))
.toThrowError(
'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.');
});
});
});
});
@ -624,11 +602,11 @@ describe('compiler (bundled Angular)', () => {
});
describe('Quickstart', () => {
it('should compile', async(() => compile([QUICKSTART, angularFiles]).then(({genFiles}) => {
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl)))
.toBeDefined();
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
})));
it('should compile', () => {
const {genFiles} = compile([QUICKSTART, angularFiles]);
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
});
});
describe('Bundled library', () => {
@ -662,7 +640,7 @@ describe('compiler (bundled Angular)', () => {
({fileName, content}) => ({fileName: `/node_modules${fileName}`, content})));
});
it('should compile', async(() => compile([LIBRARY_USING_APP, libraryFiles, angularFiles])));
it('should compile', () => compile([LIBRARY_USING_APP, libraryFiles, angularFiles]));
});
});
@ -767,8 +745,3 @@ const LIBRARY_USING_APP: MockDirectory = {
}
}
};
function expectPromiseToThrow(p: Promise<any>, msg: RegExp) {
p.then(
() => { throw new Error('Expected to throw'); }, (e) => { expect(e.message).toMatch(msg); });
}

View File

@ -7,7 +7,6 @@
*/
import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler';
import {fakeAsync, tick} from '@angular/core/testing';
import {MockDirectory, compile, setup} from './test_util';
@ -16,19 +15,12 @@ describe('aot summaries for jit', () => {
function compileApp(rootDir: MockDirectory, options: {useSummaries?: boolean} = {}):
{genFiles: GeneratedFile[], outDir: MockDirectory} {
let result: {genFiles: GeneratedFile[], outDir: MockDirectory} = null !;
let error: Error|null = null;
compile([rootDir, angularFiles], options).then((r) => result = r, (e) => error = e);
tick();
if (error) {
throw error;
}
return result;
return compile([rootDir, angularFiles], options);
}
it('should create @Injectable summaries', fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should create @Injectable summaries', () => {
const appDir = {
'app.module.ts': `
import { Injectable } from '@angular/core';
export class Dep {}
@ -38,23 +30,23 @@ describe('aot summaries for jit', () => {
constructor(d: Dep) {}
}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyServiceNgSummary()');
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyServiceNgSummary()');
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
});
it('should create @Pipe summaries', fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should create @Pipe summaries', () => {
const appDir = {
'app.module.ts': `
import { Pipe, NgModule } from '@angular/core';
export class Dep {}
@ -67,23 +59,23 @@ describe('aot summaries for jit', () => {
@NgModule({declarations: [MyPipe]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyPipeNgSummary()');
// Note: CompileSummaryKind.Pipe = 1
expect(genSource).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyPipeNgSummary()');
// Note: CompileSummaryKind.Pipe = 1
expect(genSource).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
});
it('should create @Directive summaries', fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should create @Directive summaries', () => {
const appDir = {
'app.module.ts': `
import { Directive, NgModule } from '@angular/core';
export class Dep {}
@ -96,23 +88,23 @@ describe('aot summaries for jit', () => {
@NgModule({declarations: [MyDirective]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyDirectiveNgSummary()');
// Note: CompileSummaryKind.Directive = 1
expect(genSource).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyDirectiveNgSummary()');
// Note: CompileSummaryKind.Directive = 1
expect(genSource).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
});
it('should create @NgModule summaries', fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should create @NgModule summaries', () => {
const appDir = {
'app.module.ts': `
import { NgModule } from '@angular/core';
export class Dep {}
@ -122,23 +114,23 @@ describe('aot summaries for jit', () => {
constructor(d: Dep) {}
}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyModuleNgSummary()');
// Note: CompileSummaryKind.NgModule = 2
expect(genSource).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyModuleNgSummary()');
// Note: CompileSummaryKind.NgModule = 2
expect(genSource).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
});
it('should embed useClass provider summaries in @Directive summaries', fakeAsync(() => {
const appDir = {
'app.service.ts': `
it('should embed useClass provider summaries in @Directive summaries', () => {
const appDir = {
'app.service.ts': `
import { Injectable } from '@angular/core';
export class Dep {}
@ -148,7 +140,7 @@ describe('aot summaries for jit', () => {
constructor(d: Dep) {}
}
`,
'app.module.ts': `
'app.module.ts': `
import { Directive, NgModule } from '@angular/core';
import { MyService } from './app.service';
@ -161,22 +153,22 @@ describe('aot summaries for jit', () => {
@NgModule({declarations: [MyDirective]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
}));
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
});
it('should embed useClass provider summaries into @NgModule summaries', fakeAsync(() => {
const appDir = {
'app.service.ts': `
it('should embed useClass provider summaries into @NgModule summaries', () => {
const appDir = {
'app.service.ts': `
import { Injectable } from '@angular/core';
export class Dep {}
@ -186,7 +178,7 @@ describe('aot summaries for jit', () => {
constructor(d: Dep) {}
}
`,
'app.module.ts': `
'app.module.ts': `
import { NgModule } from '@angular/core';
import { MyService } from './app.service';
@ -195,23 +187,22 @@ describe('aot summaries for jit', () => {
})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
}));
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
});
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries',
fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries', () => {
const appDir = {
'app.module.ts': `
import { Directive, Pipe, NgModule } from '@angular/core';
@Directive({selector: '[myDir]'})
@ -223,20 +214,20 @@ describe('aot summaries for jit', () => {
@NgModule({declarations: [MyDirective, MyPipe]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
}));
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
});
it('should reference imported @NgModule summaries in @NgModule summaries', fakeAsync(() => {
const appDir = {
'app.module.ts': `
it('should reference imported @NgModule summaries in @NgModule summaries', () => {
const appDir = {
'app.module.ts': `
import { NgModule } from '@angular/core';
@NgModule()
@ -245,20 +236,20 @@ describe('aot summaries for jit', () => {
@NgModule({imports: [MyImportedModule]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
};
const rootDir = {'app': appDir};
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
const genSource = toTypeScript(genFile);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
}));
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
});
it('should create and use reexports for imported NgModules ' +
'accross compilation units',
fakeAsync(() => {
() => {
const lib1In = {
'lib1': {
'module.ts': `
@ -348,5 +339,5 @@ describe('aot summaries for jit', () => {
expect(toTypeScript(lib3ReexportNgSummary))
.toContain(
`export {ReexportModule_2NgSummary as ReexportModule_3NgSummary} from '/lib2/reexport.ngsummary'`);
}));
});
});

View File

@ -5,16 +5,15 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {async} from '@angular/core/testing';
import {MockDirectory, compile, expectNoDiagnostics, setup} from './test_util';
describe('regressions', () => {
let angularFiles = setup();
it('should compile components with empty templates', async(() => {
const appDir = {
'app.module.ts': `
it('should compile components with empty templates', () => {
const appDir = {
'app.module.ts': `
import { Component, NgModule } from '@angular/core';
@Component({template: ''})
@ -23,14 +22,11 @@ describe('regressions', () => {
@NgModule({declarations: [EmptyComp]})
export class MyModule {}
`
};
const rootDir = {'app': appDir};
compile([rootDir, angularFiles], {postCompile: expectNoDiagnostics}, {
noUnusedLocals: true,
noUnusedParameters: true
}).then((result) => {
expect(result.genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts'))
.toBeTruthy();
});
}));
};
const rootDir = {'app': appDir};
const {genFiles} = compile(
[rootDir, angularFiles], {postCompile: expectNoDiagnostics},
{noUnusedLocals: true, noUnusedParameters: true});
expect(genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts')).toBeTruthy();
});
});

View File

@ -399,11 +399,11 @@ export class MockAotCompilerHost implements AotCompilerHost {
return importedFile.replace(EXT, '');
}
loadResource(path: string): Promise<string> {
loadResource(path: string): string {
if (this.tsHost.fileExists(path)) {
return Promise.resolve(this.tsHost.readFile(path));
return this.tsHost.readFile(path);
} else {
return Promise.reject(new Error(`Resource ${path} not found.`))
throw new Error(`Resource ${path} not found.`);
}
}
}
@ -604,47 +604,43 @@ export function compile(
preCompile?: (program: ts.Program) => void,
postCompile?: (program: ts.Program) => void,
}& AotCompilerOptions = {},
tsOptions: ts.CompilerOptions = {}):
Promise<{genFiles: GeneratedFile[], outDir: MockDirectory}> {
// Make sure we always return errors via the promise...
return Promise.resolve(null).then(() => {
// when using summaries, always emit so the next step can use the results.
const emit = options.emit || options.useSummaries;
const preCompile = options.preCompile || expectNoDiagnostics;
const postCompile = options.postCompile || expectNoDiagnostics;
const rootDirArr = toMockFileArray(rootDirs);
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
tsOptions: ts.CompilerOptions = {}): {genFiles: GeneratedFile[], outDir: MockDirectory} {
// when using summaries, always emit so the next step can use the results.
const emit = options.emit || options.useSummaries;
const preCompile = options.preCompile || expectNoDiagnostics;
const postCompile = options.postCompile || expectNoDiagnostics;
const rootDirArr = toMockFileArray(rootDirs);
const scriptNames = rootDirArr.map(entry => entry.fileName).filter(isSource);
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
const aotHost = new MockAotCompilerHost(host);
if (options.useSummaries) {
aotHost.hideMetadata();
aotHost.tsFilesOnly();
const host = new MockCompilerHost(scriptNames, arrayToMockDir(rootDirArr));
const aotHost = new MockAotCompilerHost(host);
if (options.useSummaries) {
aotHost.hideMetadata();
aotHost.tsFilesOnly();
}
const tsSettings = {...settings, ...tsOptions};
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
if (preCompile) preCompile(program);
const {compiler, reflector} = createAotCompiler(aotHost, options);
const genFiles = compiler.compileAllSync(program.getSourceFiles().map(sf => sf.fileName));
genFiles.forEach((file) => {
const source = file.source || toTypeScript(file);
if (isSource(file.genFileUrl)) {
host.addScript(file.genFileUrl, source);
} else {
host.override(file.genFileUrl, source);
}
const tsSettings = {...settings, ...tsOptions};
const scripts = host.scriptNames.slice(0);
const program = ts.createProgram(scripts, tsSettings, host);
if (preCompile) preCompile(program);
const {compiler, reflector} = createAotCompiler(aotHost, options);
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
genFiles.forEach((file) => {
const source = file.source || toTypeScript(file);
isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, source) :
host.override(file.genFileUrl, source);
});
const scripts = host.scriptNames.slice(0);
const newProgram = ts.createProgram(scripts, tsSettings, host);
if (postCompile) postCompile(newProgram);
if (emit) {
newProgram.emit();
}
let outDir: MockDirectory = {};
if (emit) {
outDir = arrayToMockDir(toMockFileArray([
host.writtenFiles, host.overrides
]).filter((entry) => !isSource(entry.fileName)));
}
return {genFiles, outDir};
});
});
const newProgram = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
if (postCompile) postCompile(newProgram);
if (emit) {
newProgram.emit();
}
let outDir: MockDirectory = {};
if (emit) {
outDir = arrayToMockDir(toMockFileArray([
host.writtenFiles, host.overrides
]).filter((entry) => !isSource(entry.fileName)));
}
return {genFiles, outDir};
}

View File

@ -16,7 +16,7 @@ import {ViewEncapsulation} from '@angular/core/src/metadata/view';
import {TestBed} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
import {SyncAsyncResult, noUndefined} from '../src/util';
import {noUndefined} from '../src/util';
import {SpyResourceLoader} from './spies';
@ -46,7 +46,7 @@ function normalizeTemplate(normalizer: DirectiveNormalizer, o: {
});
}
function normalizeTemplateAsync(normalizer: DirectiveNormalizer, o: {
function normalizeTemplateOnly(normalizer: DirectiveNormalizer, o: {
ngModuleType?: any; componentType?: any; moduleUrl?: string; template?: string | null;
templateUrl?: string | null;
styles?: string[];
@ -55,30 +55,7 @@ function normalizeTemplateAsync(normalizer: DirectiveNormalizer, o: {
encapsulation?: ViewEncapsulation | null;
animations?: CompileAnimationEntryMetadata[];
}) {
return normalizer.normalizeTemplateAsync({
ngModuleType: noUndefined(o.ngModuleType),
componentType: noUndefined(o.componentType),
moduleUrl: noUndefined(o.moduleUrl),
template: noUndefined(o.template),
templateUrl: noUndefined(o.templateUrl),
styles: noUndefined(o.styles),
styleUrls: noUndefined(o.styleUrls),
interpolation: noUndefined(o.interpolation),
encapsulation: noUndefined(o.encapsulation),
animations: noUndefined(o.animations)
});
}
function normalizeTemplateSync(normalizer: DirectiveNormalizer, o: {
ngModuleType?: any; componentType?: any; moduleUrl?: string; template?: string | null;
templateUrl?: string | null;
styles?: string[];
styleUrls?: string[];
interpolation?: [string, string] | null;
encapsulation?: ViewEncapsulation | null;
animations?: CompileAnimationEntryMetadata[];
}): CompileTemplateMetadata {
return normalizer.normalizeTemplateSync({
return normalizer.normalizeTemplateOnly({
ngModuleType: noUndefined(o.ngModuleType),
componentType: noUndefined(o.componentType),
moduleUrl: noUndefined(o.moduleUrl),
@ -194,10 +171,10 @@ export function main() {
}));
});
describe('normalizeTemplateSync', () => {
describe('normalizeTemplateOnly sync', () => {
it('should store the template',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizeTemplateSync(normalizer, {
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -214,7 +191,7 @@ export function main() {
it('should resolve styles on the annotation against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizeTemplateSync(normalizer, {
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -229,7 +206,7 @@ export function main() {
it('should resolve styles in the template against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizeTemplateSync(normalizer, {
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -244,7 +221,7 @@ export function main() {
it('should use ViewEncapsulation.Emulated by default',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
const template = normalizeTemplateSync(normalizer, {
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -262,7 +239,7 @@ export function main() {
[CompilerConfig, DirectiveNormalizer],
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
config.defaultEncapsulation = ViewEncapsulation.None;
const template = normalizeTemplateSync(normalizer, {
const template = <CompileTemplateMetadata>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -284,7 +261,7 @@ export function main() {
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
resourceLoader: MockResourceLoader) => {
resourceLoader.expect('package:some/module/sometplurl.html', 'a');
normalizeTemplateAsync(normalizer, {
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -293,7 +270,7 @@ export function main() {
templateUrl: 'sometplurl.html',
styles: [],
styleUrls: ['test.css']
}).then((template: CompileTemplateMetadata) => {
})).then((template) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
expect(template.isInline).toBe(false);
@ -308,7 +285,7 @@ export function main() {
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
resourceLoader: MockResourceLoader) => {
resourceLoader.expect('package:some/module/tpl/sometplurl.html', '');
normalizeTemplateAsync(normalizer, {
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -317,7 +294,7 @@ export function main() {
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: ['test.css']
}).then((template: CompileTemplateMetadata) => {
})).then((template) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
@ -331,7 +308,7 @@ export function main() {
resourceLoader: MockResourceLoader) => {
resourceLoader.expect(
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
normalizeTemplateAsync(normalizer, {
(<Promise<CompileTemplateMetadata>>normalizeTemplateOnly(normalizer, {
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
@ -340,7 +317,7 @@ export function main() {
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: []
}).then((template: CompileTemplateMetadata) => {
})).then((template) => {
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
async.done();
});
@ -362,13 +339,13 @@ export function main() {
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer,
resourceLoader: SpyResourceLoader) => {
programResourceLoaderSpy(resourceLoader, {'package:some/module/test.css': 'a'});
normalizer
.normalizeExternalStylesheets(compileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
}))
.then((template: CompileTemplateMetadata) => {
(<Promise<CompileTemplateMetadata>>normalizer.normalizeExternalStylesheets(
compileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
})))
.then((template) => {
expect(template.externalStylesheets.length).toBe(1);
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
moduleUrl: 'package:some/module/test.css',
@ -388,13 +365,13 @@ export function main() {
'package:some/module/test.css': 'a@import "test2.css"',
'package:some/module/test2.css': 'b'
});
normalizer
.normalizeExternalStylesheets(compileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
}))
.then((template: CompileTemplateMetadata) => {
(<Promise<CompileTemplateMetadata>>normalizer.normalizeExternalStylesheets(
compileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
})))
.then((template) => {
expect(template.externalStylesheets.length).toBe(2);
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
moduleUrl: 'package:some/module/test.css',
@ -426,8 +403,8 @@ export function main() {
};
Promise
.all([
normalizeTemplateAsync(normalizer, prenormMeta),
normalizeTemplateAsync(normalizer, prenormMeta)
normalizeTemplateOnly(normalizer, prenormMeta),
normalizeTemplateOnly(normalizer, prenormMeta)
])
.then((templates: CompileTemplateMetadata[]) => {
expect(templates[0].template).toEqual('a');

View File

@ -7,18 +7,10 @@
*/
import {fakeAsync} from '@angular/core/testing/src/fake_async';
import {SyncAsyncResult, escapeRegExp, splitAtColon, utf8Encode} from '../src/util';
import {SyncAsync, escapeRegExp, splitAtColon, utf8Encode} from '../src/util';
export function main() {
describe('util', () => {
describe('SyncAsyncResult', () => {
it('async value should default to Promise.resolve(syncValue)', fakeAsync(() => {
const syncValue = {};
const sar = new SyncAsyncResult(syncValue);
sar.asyncResult !.then((v: any) => expect(v).toBe(syncValue));
}));
});
describe('splitAtColon', () => {
it('should split when a single ":" is present', () => {
expect(splitAtColon('a:b', [])).toEqual(['a', 'b']);