refactor(core): change module semantics
This contains major changes to the compiler, bootstrap of the platforms and test environment initialization. Main part of #10043 Closes #10164 BREAKING CHANGE: - Semantics and name of `@AppModule` (now `@NgModule`) changed quite a bit. This is actually not breaking as `@AppModules` were not part of rc.4. We will have detailed docs on `@NgModule` separately. - `coreLoadAndBootstrap` and `coreBootstrap` can't be used any more (without migration support). Use `bootstrapModule` / `bootstrapModuleFactory` instead. - All Components listed in routes have to be part of the `declarations` of an NgModule. Either directly on the bootstrap module / lazy loaded module, or in an NgModule imported by them.
This commit is contained in:
@ -1,557 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ANALYZE_FOR_PRECOMPILE, AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, Inject, Injectable, Injector, Input, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core';
|
||||
import {ComponentFixture, configureCompiler} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {BaseException} from '../../src/facade/exceptions';
|
||||
import {ConcreteType, IS_DART, Type, stringify} from '../../src/facade/lang';
|
||||
|
||||
class Engine {}
|
||||
|
||||
class BrokenEngine {
|
||||
constructor() { throw new BaseException('Broken Engine'); }
|
||||
}
|
||||
|
||||
class DashboardSoftware {}
|
||||
|
||||
@Injectable()
|
||||
class Dashboard {
|
||||
constructor(software: DashboardSoftware) {}
|
||||
}
|
||||
|
||||
class TurboEngine extends Engine {}
|
||||
|
||||
@Injectable()
|
||||
class Car {
|
||||
engine: Engine;
|
||||
constructor(engine: Engine) { this.engine = engine; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarWithOptionalEngine {
|
||||
engine: Engine;
|
||||
constructor(@Optional() engine: Engine) { this.engine = engine; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarWithDashboard {
|
||||
engine: Engine;
|
||||
dashboard: Dashboard;
|
||||
constructor(engine: Engine, dashboard: Dashboard) {
|
||||
this.engine = engine;
|
||||
this.dashboard = dashboard;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class SportsCar extends Car {
|
||||
engine: Engine;
|
||||
constructor(engine: Engine) { super(engine); }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarWithInject {
|
||||
engine: Engine;
|
||||
constructor(@Inject(TurboEngine) engine: Engine) { this.engine = engine; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CyclicEngine {
|
||||
constructor(car: Car) {}
|
||||
}
|
||||
|
||||
class NoAnnotations {
|
||||
constructor(secretDependency: any) {}
|
||||
}
|
||||
|
||||
function factoryFn(a: any) {}
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
}
|
||||
|
||||
@AppModule({})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
@AppModule({providers: [{provide: 'someToken', useValue: 'someValue'}]})
|
||||
class ModuleWithProvider {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp', template: ''})
|
||||
class SomeComp {
|
||||
}
|
||||
|
||||
@AppModule({precompile: [SomeComp]})
|
||||
class ModuleWithPrecompile {
|
||||
}
|
||||
|
||||
@AppModule({
|
||||
providers:
|
||||
[{provide: ANALYZE_FOR_PRECOMPILE, multi: true, useValue: [{a: 'b', component: SomeComp}]}]
|
||||
})
|
||||
class ModuleWithAnalyzePrecompileProvider {
|
||||
}
|
||||
|
||||
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
|
||||
class SomeDirective {
|
||||
@Input()
|
||||
someDir: string;
|
||||
}
|
||||
|
||||
@Pipe({name: 'somePipe'})
|
||||
class SomePipe {
|
||||
transform(value: string): any { return `transformed ${value}`; }
|
||||
}
|
||||
|
||||
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
|
||||
class CompUsingModuleDirectiveAndPipe {
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'parent', template: `<comp></comp>`, directives: [CompUsingModuleDirectiveAndPipe]})
|
||||
class ParentCompUsingModuleDirectiveAndPipe {
|
||||
}
|
||||
|
||||
@AppModule(
|
||||
{directives: [SomeDirective], pipes: [SomePipe], precompile: [CompUsingModuleDirectiveAndPipe]})
|
||||
class ModuleWithDirectivesAndPipes {
|
||||
}
|
||||
|
||||
export function main() {
|
||||
if (IS_DART) {
|
||||
declareTests({useJit: false});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
}
|
||||
}
|
||||
|
||||
function declareTests({useJit}: {useJit: boolean}) {
|
||||
describe('AppModule', () => {
|
||||
var compiler: Compiler;
|
||||
var injector: Injector;
|
||||
|
||||
beforeEach(() => { configureCompiler({useJit: useJit}); });
|
||||
|
||||
beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => {
|
||||
compiler = _compiler;
|
||||
injector = _injector;
|
||||
}));
|
||||
|
||||
describe('precompile', function() {
|
||||
it('should resolve ComponentFactories', () => {
|
||||
let appModule = compiler.compileAppModuleSync(ModuleWithPrecompile).create();
|
||||
expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(appModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
.componentType)
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
it('should resolve ComponentFactories via ANALYZE_FOR_PRECOMPILE', () => {
|
||||
let appModule = compiler.compileAppModuleSync(ModuleWithAnalyzePrecompileProvider).create();
|
||||
expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(appModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
.componentType)
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
it('should resolve ComponentFactories for nested modules', () => {
|
||||
let appModule =
|
||||
compiler
|
||||
.compileAppModuleSync(
|
||||
SomeModule, new AppModuleMetadata({modules: [ModuleWithPrecompile]}))
|
||||
.create();
|
||||
expect(appModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(appModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
.componentType)
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('directives and pipes', () => {
|
||||
function createComp<T>(
|
||||
compType: ConcreteType<T>, moduleType: ConcreteType<any>,
|
||||
moduleMeta: AppModuleMetadata = null): ComponentFixture<T> {
|
||||
let appModule = compiler.compileAppModuleSync(moduleType, moduleMeta).create();
|
||||
var cf = appModule.componentFactoryResolver.resolveComponentFactory(compType);
|
||||
return new ComponentFixture(cf.create(injector), null, false);
|
||||
}
|
||||
|
||||
it('should support module directives and pipes', () => {
|
||||
let compFixture = createComp(CompUsingModuleDirectiveAndPipe, ModuleWithDirectivesAndPipes);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should support module directives and pipes for nested modules', () => {
|
||||
let compFixture = createComp(
|
||||
CompUsingModuleDirectiveAndPipe, SomeModule,
|
||||
new AppModuleMetadata({modules: [ModuleWithDirectivesAndPipes]}));
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should support module directives and pipes in nested components', () => {
|
||||
let compFixture =
|
||||
createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule, new AppModuleMetadata({
|
||||
directives: [SomeDirective],
|
||||
pipes: [SomePipe],
|
||||
precompile: [ParentCompUsingModuleDirectiveAndPipe]
|
||||
}));
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should provide a Compiler instance that uses the directives/pipes of the module', () => {
|
||||
let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create();
|
||||
let boundCompiler: Compiler = appModule.injector.get(Compiler);
|
||||
var cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe);
|
||||
let compFixture = new ComponentFixture(cf.create(injector), null, false);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
it('should provide a ComponentResolver instance that uses the directives/pipes of the module',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
|
||||
let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create();
|
||||
let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver);
|
||||
boundCompiler.resolveComponent(CompUsingModuleDirectiveAndPipe).then((cf) => {
|
||||
let compFixture = new ComponentFixture(cf.create(injector), null, false);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should provide a ComponentResolver instance that delegates to the parent ComponentResolver for strings',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
let parentResolver: any =
|
||||
jasmine.createSpyObj('resolver', ['resolveComponent', 'clearCache']);
|
||||
let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes)
|
||||
.create(ReflectiveInjector.resolveAndCreate(
|
||||
[{provide: ComponentResolver, useValue: parentResolver}]));
|
||||
parentResolver.resolveComponent.and.returnValue(
|
||||
Promise.resolve('someFactoryFromParent'));
|
||||
let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver);
|
||||
boundCompiler.resolveComponent('someString').then((result) => {
|
||||
expect(parentResolver.resolveComponent).toHaveBeenCalledWith('someString');
|
||||
expect(result).toBe('someFactoryFromParent');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('providers', function() {
|
||||
function createInjector(providers: any[], parent: Injector = null): Injector {
|
||||
return compiler
|
||||
.compileAppModuleSync(SomeModule, new AppModuleMetadata({providers: providers}))
|
||||
.create(parent)
|
||||
.injector;
|
||||
}
|
||||
|
||||
it('should provide the module',
|
||||
() => { expect(createInjector([]).get(SomeModule)).toBeAnInstanceOf(SomeModule); });
|
||||
|
||||
it('should instantiate a class without dependencies', () => {
|
||||
var injector = createInjector([Engine]);
|
||||
var engine = injector.get(Engine);
|
||||
|
||||
expect(engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should resolve dependencies based on type information', () => {
|
||||
var injector = createInjector([Engine, Car]);
|
||||
var car = injector.get(Car);
|
||||
|
||||
expect(car).toBeAnInstanceOf(Car);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should resolve dependencies based on @Inject annotation', () => {
|
||||
var injector = createInjector([TurboEngine, Engine, CarWithInject]);
|
||||
var car = injector.get(CarWithInject);
|
||||
|
||||
expect(car).toBeAnInstanceOf(CarWithInject);
|
||||
expect(car.engine).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
it('should throw when no type and not @Inject (class case)', () => {
|
||||
expect(() => createInjector([NoAnnotations]))
|
||||
.toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).');
|
||||
});
|
||||
|
||||
it('should throw when no type and not @Inject (factory case)', () => {
|
||||
expect(() => createInjector([provide('someToken', {useFactory: factoryFn})]))
|
||||
.toThrowError('Can\'t resolve all parameters for factoryFn: (?).');
|
||||
});
|
||||
|
||||
it('should cache instances', () => {
|
||||
var injector = createInjector([Engine]);
|
||||
|
||||
var e1 = injector.get(Engine);
|
||||
var e2 = injector.get(Engine);
|
||||
|
||||
expect(e1).toBe(e2);
|
||||
});
|
||||
|
||||
it('should provide to a value', () => {
|
||||
var injector = createInjector([provide(Engine, {useValue: 'fake engine'})]);
|
||||
|
||||
var engine = injector.get(Engine);
|
||||
expect(engine).toEqual('fake engine');
|
||||
});
|
||||
|
||||
it('should provide to a factory', () => {
|
||||
function sportsCarFactory(e: Engine) { return new SportsCar(e); }
|
||||
|
||||
var injector =
|
||||
createInjector([Engine, provide(Car, {useFactory: sportsCarFactory, deps: [Engine]})]);
|
||||
|
||||
var car = injector.get(Car);
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should supporting provider to null', () => {
|
||||
var injector = createInjector([provide(Engine, {useValue: null})]);
|
||||
var engine = injector.get(Engine);
|
||||
expect(engine).toBeNull();
|
||||
});
|
||||
|
||||
it('should provide to an alias', () => {
|
||||
var injector = createInjector([
|
||||
Engine, provide(SportsCar, {useClass: SportsCar}),
|
||||
provide(Car, {useExisting: SportsCar})
|
||||
]);
|
||||
|
||||
var car = injector.get(Car);
|
||||
var sportsCar = injector.get(SportsCar);
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
expect(car).toBe(sportsCar);
|
||||
});
|
||||
|
||||
it('should support multiProviders', () => {
|
||||
var injector = createInjector([
|
||||
Engine, provide(Car, {useClass: SportsCar, multi: true}),
|
||||
provide(Car, {useClass: CarWithOptionalEngine, multi: true})
|
||||
]);
|
||||
|
||||
var cars = injector.get(Car);
|
||||
expect(cars.length).toEqual(2);
|
||||
expect(cars[0]).toBeAnInstanceOf(SportsCar);
|
||||
expect(cars[1]).toBeAnInstanceOf(CarWithOptionalEngine);
|
||||
});
|
||||
|
||||
it('should support multiProviders that are created using useExisting', () => {
|
||||
var injector = createInjector(
|
||||
[Engine, SportsCar, provide(Car, {useExisting: SportsCar, multi: true})]);
|
||||
|
||||
var cars = injector.get(Car);
|
||||
expect(cars.length).toEqual(1);
|
||||
expect(cars[0]).toBe(injector.get(SportsCar));
|
||||
});
|
||||
|
||||
it('should throw when the aliased provider does not exist', () => {
|
||||
var injector = createInjector([provide('car', {useExisting: SportsCar})]);
|
||||
var e = `No provider for ${stringify(SportsCar)}!`;
|
||||
expect(() => injector.get('car')).toThrowError(e);
|
||||
});
|
||||
|
||||
it('should handle forwardRef in useExisting', () => {
|
||||
var injector = createInjector([
|
||||
provide('originalEngine', {useClass: forwardRef(() => Engine)}),
|
||||
provide('aliasedEngine', {useExisting: <any>forwardRef(() => 'originalEngine')})
|
||||
]);
|
||||
expect(injector.get('aliasedEngine')).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should support overriding factory dependencies', () => {
|
||||
var injector = createInjector(
|
||||
[Engine, provide(Car, {useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]})]);
|
||||
|
||||
var car = injector.get(Car);
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should support optional dependencies', () => {
|
||||
var injector = createInjector([CarWithOptionalEngine]);
|
||||
|
||||
var car = injector.get(CarWithOptionalEngine);
|
||||
expect(car.engine).toEqual(null);
|
||||
});
|
||||
|
||||
it('should flatten passed-in providers', () => {
|
||||
var injector = createInjector([[[Engine, Car]]]);
|
||||
|
||||
var car = injector.get(Car);
|
||||
expect(car).toBeAnInstanceOf(Car);
|
||||
});
|
||||
|
||||
it('should use the last provider when there are multiple providers for same token', () => {
|
||||
var injector = createInjector(
|
||||
[provide(Engine, {useClass: Engine}), provide(Engine, {useClass: TurboEngine})]);
|
||||
|
||||
expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
it('should use non-type tokens', () => {
|
||||
var injector = createInjector([provide('token', {useValue: 'value'})]);
|
||||
|
||||
expect(injector.get('token')).toEqual('value');
|
||||
});
|
||||
|
||||
it('should throw when given invalid providers', () => {
|
||||
expect(() => createInjector(<any>['blah']))
|
||||
.toThrowError(
|
||||
'Invalid provider - only instances of Provider and Type are allowed, got: blah');
|
||||
});
|
||||
|
||||
it('should provide itself', () => {
|
||||
var parent = createInjector([]);
|
||||
var child = createInjector([], parent);
|
||||
|
||||
expect(child.get(Injector)).toBe(child);
|
||||
});
|
||||
|
||||
it('should throw when no provider defined', () => {
|
||||
var injector = createInjector([]);
|
||||
expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!');
|
||||
});
|
||||
|
||||
it('should throw when trying to instantiate a cyclic dependency', () => {
|
||||
expect(() => createInjector([Car, provide(Engine, {useClass: CyclicEngine})]))
|
||||
.toThrowError(/Cannot instantiate cyclic dependency! Car/g);
|
||||
});
|
||||
|
||||
it('should support null values', () => {
|
||||
var injector = createInjector([provide('null', {useValue: null})]);
|
||||
expect(injector.get('null')).toBe(null);
|
||||
});
|
||||
|
||||
|
||||
describe('child', () => {
|
||||
it('should load instances from parent injector', () => {
|
||||
var parent = createInjector([Engine]);
|
||||
var child = createInjector([], parent);
|
||||
|
||||
var engineFromParent = parent.get(Engine);
|
||||
var engineFromChild = child.get(Engine);
|
||||
|
||||
expect(engineFromChild).toBe(engineFromParent);
|
||||
});
|
||||
|
||||
it('should not use the child providers when resolving the dependencies of a parent provider',
|
||||
() => {
|
||||
var parent = createInjector([Car, Engine]);
|
||||
var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent);
|
||||
|
||||
var carFromChild = child.get(Car);
|
||||
expect(carFromChild.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it('should create new instance in a child injector', () => {
|
||||
var parent = createInjector([Engine]);
|
||||
var child = createInjector([provide(Engine, {useClass: TurboEngine})], parent);
|
||||
|
||||
var engineFromParent = parent.get(Engine);
|
||||
var engineFromChild = child.get(Engine);
|
||||
|
||||
expect(engineFromParent).not.toBe(engineFromChild);
|
||||
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('depedency resolution', () => {
|
||||
describe('@Self()', () => {
|
||||
it('should return a dependency from self', () => {
|
||||
var inj = createInjector([
|
||||
Engine,
|
||||
provide(
|
||||
Car,
|
||||
{useFactory: (e: Engine) => new Car(e), deps: [[Engine, new SelfMetadata()]]})
|
||||
]);
|
||||
|
||||
expect(inj.get(Car)).toBeAnInstanceOf(Car);
|
||||
});
|
||||
|
||||
it('should throw when not requested provider on self', () => {
|
||||
expect(() => createInjector([provide(Car, {
|
||||
useFactory: (e: Engine) => new Car(e),
|
||||
deps: [[Engine, new SelfMetadata()]]
|
||||
})]))
|
||||
.toThrowError(/No provider for Engine/g);
|
||||
});
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
it('should not skip self', () => {
|
||||
var parent = createInjector([Engine]);
|
||||
var child = createInjector(
|
||||
[
|
||||
provide(Engine, {useClass: TurboEngine}),
|
||||
provide(Car, {useFactory: (e: Engine) => new Car(e), deps: [Engine]})
|
||||
],
|
||||
parent);
|
||||
|
||||
expect(child.get(Car).engine).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested modules', () => {
|
||||
it('should merge the providers of nested modules', () => {
|
||||
var injector =
|
||||
compiler
|
||||
.compileAppModuleSync(SomeModule, new AppModuleMetadata({
|
||||
providers: [{provide: 'a', useValue: 'aValue'}],
|
||||
modules: [ModuleWithProvider]
|
||||
}))
|
||||
.create()
|
||||
.injector;
|
||||
expect(injector.get(SomeModule)).toBeAnInstanceOf(SomeModule);
|
||||
expect(injector.get(ModuleWithProvider)).toBeAnInstanceOf(ModuleWithProvider);
|
||||
expect(injector.get('a')).toBe('aValue');
|
||||
expect(injector.get('someToken')).toBe('someValue');
|
||||
});
|
||||
|
||||
it('should override the providers of nested modules', () => {
|
||||
var injector = compiler
|
||||
.compileAppModuleSync(
|
||||
SomeModule, new AppModuleMetadata({
|
||||
providers: [{provide: 'someToken', useValue: 'someNewValue'}],
|
||||
modules: [ModuleWithProvider]
|
||||
}))
|
||||
.create()
|
||||
.injector;
|
||||
expect(injector.get('someToken')).toBe('someNewValue');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
@ -1775,17 +1775,6 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('logging property updates', () => {
|
||||
beforeEach(() => {
|
||||
configureCompiler({
|
||||
providers: [{
|
||||
provide: CompilerConfig,
|
||||
// Note: we are testing the `genDebugInfo` flag here, so we
|
||||
// need to set it explicitely!
|
||||
useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit})
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should reflect property values as attributes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
|
1038
modules/@angular/core/test/linker/ng_module_integration_spec.ts
Normal file
1038
modules/@angular/core/test/linker/ng_module_integration_spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_PRECOMPILE} from '@angular/core';
|
||||
import {TestComponentBuilder, configureModule} from '@angular/core/testing';
|
||||
import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_PRECOMPILE, ViewMetadata} from '@angular/core';
|
||||
import {stringify} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
@ -17,19 +18,33 @@ export function main() {
|
||||
|
||||
function declareTests({useJit}: {useJit: boolean}) {
|
||||
describe('@Component.precompile', function() {
|
||||
beforeEach(() => { configureModule({declarations: [MainComp, ChildComp, NestedChildComp]}); });
|
||||
|
||||
it('should error if the component was not declared nor imported by the module',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
|
||||
@Component({selector: 'child', template: ''})
|
||||
class ChildComp {
|
||||
}
|
||||
|
||||
@Component({template: 'comp', precompile: [ChildComp]})
|
||||
class SomeComp {
|
||||
}
|
||||
|
||||
expect(() => tcb.createSync(SomeComp))
|
||||
.toThrowError(
|
||||
`Component ${stringify(SomeComp)} in NgModule DynamicTestModule uses ${stringify(ChildComp)} via "precompile" but it was neither declared nor imported into the module!`);
|
||||
}));
|
||||
|
||||
|
||||
it('should resolve ComponentFactories from the same component',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.createAsync(MainComp).then((compFixture) => {
|
||||
let mainComp: MainComp = compFixture.componentInstance;
|
||||
expect(compFixture.debugElement.injector.get(ComponentFactoryResolver))
|
||||
.toBe(mainComp.cfr);
|
||||
var cf = mainComp.cfr.resolveComponentFactory(ChildComp);
|
||||
expect(cf.componentType).toBe(ChildComp);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
const compFixture = tcb.createSync(MainComp);
|
||||
let mainComp: MainComp = compFixture.componentInstance;
|
||||
expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr);
|
||||
var cf = mainComp.cfr.resolveComponentFactory(ChildComp);
|
||||
expect(cf.componentType).toBe(ChildComp);
|
||||
}));
|
||||
|
||||
|
||||
it('should resolve ComponentFactories via ANALYZE_FOR_PRECOMPILE',
|
||||
@ -37,63 +52,52 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
let compFixture = tcb.createSync(CompWithAnalyzePrecompileProvider);
|
||||
let mainComp: CompWithAnalyzePrecompileProvider = compFixture.componentInstance;
|
||||
let cfr: ComponentFactoryResolver =
|
||||
compFixture.debugElement.injector.get(ComponentFactoryResolver);
|
||||
compFixture.componentRef.injector.get(ComponentFactoryResolver);
|
||||
expect(cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp);
|
||||
expect(cfr.resolveComponentFactory(NestedChildComp).componentType).toBe(NestedChildComp);
|
||||
}));
|
||||
|
||||
it('should be able to get a component form a parent component (view hiearchy)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(MainComp, '<child></child>')
|
||||
.createAsync(MainComp)
|
||||
.then((compFixture) => {
|
||||
let childCompEl = compFixture.debugElement.children[0];
|
||||
let childComp: ChildComp = childCompEl.componentInstance;
|
||||
// declared on ChildComp directly
|
||||
expect(childComp.cfr.resolveComponentFactory(NestedChildComp).componentType)
|
||||
.toBe(NestedChildComp);
|
||||
// inherited from MainComp
|
||||
expect(childComp.cfr.resolveComponentFactory(ChildComp).componentType)
|
||||
.toBe(ChildComp);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
const compFixture =
|
||||
tcb.overrideView(
|
||||
MainComp,
|
||||
new ViewMetadata({template: '<child></child>', directives: [ChildComp]}))
|
||||
.createSync(MainComp);
|
||||
let childCompEl = compFixture.debugElement.children[0];
|
||||
let childComp: ChildComp = childCompEl.componentInstance;
|
||||
// declared on ChildComp directly
|
||||
expect(childComp.cfr.resolveComponentFactory(NestedChildComp).componentType)
|
||||
.toBe(NestedChildComp);
|
||||
// inherited from MainComp
|
||||
expect(childComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp);
|
||||
}));
|
||||
|
||||
it('should not be able to get components from a parent component (content hierarchy)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(MainComp, '<child><nested></nested></child>')
|
||||
.overrideTemplate(ChildComp, '<ng-content></ng-content>')
|
||||
.createAsync(MainComp)
|
||||
.then((compFixture) => {
|
||||
let nestedChildCompEl = compFixture.debugElement.children[0].children[0];
|
||||
let nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType)
|
||||
.toBe(ChildComp);
|
||||
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
|
||||
.toThrow(new NoComponentFactoryError(NestedChildComp));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
const compFixture = tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<child><nested></nested></child>',
|
||||
directives: [ChildComp, NestedChildComp]
|
||||
}))
|
||||
.overrideTemplate(ChildComp, '<ng-content></ng-content>')
|
||||
.createSync(MainComp);
|
||||
let nestedChildCompEl = compFixture.debugElement.children[0].children[0];
|
||||
let nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType)
|
||||
.toBe(ChildComp);
|
||||
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
|
||||
.toThrow(new NoComponentFactoryError(NestedChildComp));
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var DIRECTIVES: any[] = [
|
||||
forwardRef(() => NestedChildComp),
|
||||
forwardRef(() => ChildComp),
|
||||
forwardRef(() => MainComp),
|
||||
];
|
||||
|
||||
@Component({selector: 'nested', directives: DIRECTIVES, template: ''})
|
||||
@Component({selector: 'nested', template: ''})
|
||||
class NestedChildComp {
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'child', precompile: [NestedChildComp], directives: DIRECTIVES, template: ''})
|
||||
@Component({selector: 'child', precompile: [NestedChildComp], template: ''})
|
||||
class ChildComp {
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
}
|
||||
@ -101,7 +105,6 @@ class ChildComp {
|
||||
@Component({
|
||||
selector: 'main',
|
||||
precompile: [ChildComp],
|
||||
directives: DIRECTIVES,
|
||||
template: '',
|
||||
})
|
||||
class MainComp {
|
||||
|
@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ComponentFactory} from '@angular/core/src/linker/component_factory';
|
||||
import {ComponentResolver, ReflectorComponentResolver} from '@angular/core/src/linker/component_resolver';
|
||||
import {ReflectionInfo, reflector} from '@angular/core/src/reflection/reflection';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {Console} from '../../src/console';
|
||||
|
||||
class DummyConsole implements Console {
|
||||
log(message: string) {}
|
||||
warn(message: string) {}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('Compiler', () => {
|
||||
var someCompFactory: any /** TODO #9100 */;
|
||||
var compiler: ComponentResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
someCompFactory = new ComponentFactory(null, null, null);
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([someCompFactory]));
|
||||
compiler = new ReflectorComponentResolver(new DummyConsole());
|
||||
});
|
||||
|
||||
it('should read the template from an annotation',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
compiler.resolveComponent(SomeComponent).then((compFactory: ComponentFactory<any>) => {
|
||||
expect(compFactory).toBe(someCompFactory);
|
||||
async.done();
|
||||
return null;
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw when given a string',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
compiler.resolveComponent('someString').catch((e) => {
|
||||
expect(e.message).toContain('Cannot resolve component using \'someString\'.');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
class SomeComponent {}
|
@ -26,7 +26,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
describe('platform pipes', () => {
|
||||
beforeEach(() => {
|
||||
configureCompiler({useJit: useJit});
|
||||
configureModule({pipes: [PlatformPipe]});
|
||||
configureModule({declarations: [PlatformPipe]});
|
||||
});
|
||||
|
||||
it('should overwrite them by custom pipes',
|
||||
|
Reference in New Issue
Block a user