diff --git a/modules/@angular/common/test/forms-deprecated/integration_spec.ts b/modules/@angular/common/test/forms-deprecated/integration_spec.ts index 7b5f2755c7..59e2b5f083 100644 --- a/modules/@angular/common/test/forms-deprecated/integration_spec.ts +++ b/modules/@angular/common/test/forms-deprecated/integration_spec.ts @@ -9,7 +9,7 @@ import {NgFor, NgIf} from '@angular/common'; import {Control, ControlGroup, ControlValueAccessor, DeprecatedFormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, RadioButtonState, Validator, Validators} from '@angular/common/src/forms-deprecated'; import {Component, Directive, EventEmitter, Input, Output, Provider, forwardRef} from '@angular/core'; -import {ComponentFixture, TestComponentBuilder, configureModule, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -22,7 +22,7 @@ import {PromiseWrapper} from '../../src/facade/promise'; export function main() { describe('integration tests', () => { - beforeEach(() => configureModule({imports: [DeprecatedFormsModule]})); + beforeEach(() => TestBed.configureTestingModule({imports: [DeprecatedFormsModule]})); it('should initialize DOM elements with the given form object', inject( diff --git a/modules/@angular/compiler/core_private_testing.ts b/modules/@angular/compiler/core_private_testing.ts new file mode 100644 index 0000000000..75fa5a1b6c --- /dev/null +++ b/modules/@angular/compiler/core_private_testing.ts @@ -0,0 +1,15 @@ +/** + * @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 {__core_private_testing__ as r, __core_private_testing_types__ as t} from '@angular/core/testing'; + +export type TestingCompiler = t.TestingCompiler; +export var TestingCompiler: typeof t.TestingCompiler = r.TestingCompiler; + +export type TestingCompilerFactory = t.TestingCompilerFactory; +export var TestingCompilerFactory: typeof t.TestingCompilerFactory = r.TestingCompilerFactory; diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index b06e0a30e9..09bf8fc01f 100644 --- a/modules/@angular/compiler/src/runtime_compiler.ts +++ b/modules/@angular/compiler/src/runtime_compiler.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, NgModule, NgModuleFactory, NgModuleMetadata, OptionalMetadata, Provider, SchemaMetadata, SkipSelfMetadata} from '@angular/core'; +import {Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleMetadata, OptionalMetadata, Provider, SchemaMetadata, SkipSelfMetadata} from '@angular/core'; import {Console} from '../core_private'; import {BaseException} from '../src/facade/exceptions'; @@ -44,13 +44,13 @@ export class RuntimeCompiler implements Compiler { private _compiledNgModuleCache = new Map>(); constructor( - private __injector: Injector, private _metadataResolver: CompileMetadataResolver, + private _injector: Injector, private _metadataResolver: CompileMetadataResolver, private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig, private _console: Console) {} - get _injector(): Injector { return this.__injector; } + get injector(): Injector { return this._injector; } compileModuleSync(moduleType: ConcreteType): NgModuleFactory { return this._compileModuleAndComponents(moduleType, true).syncResult; @@ -60,6 +60,16 @@ export class RuntimeCompiler implements Compiler { return this._compileModuleAndComponents(moduleType, false).asyncResult; } + compileModuleAndAllComponentsSync(moduleType: ConcreteType): + ModuleWithComponentFactories { + return this._compileModuleAndAllComponents(moduleType, true).syncResult; + } + + compileModuleAndAllComponentsAsync(moduleType: ConcreteType): + Promise> { + return this._compileModuleAndAllComponents(moduleType, false).asyncResult; + } + compileComponentAsync(compType: ConcreteType, ngModule: ConcreteType = null): Promise> { if (!ngModule) { @@ -85,6 +95,34 @@ export class RuntimeCompiler implements Compiler { return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory)); } + private _compileModuleAndAllComponents(moduleType: ConcreteType, isSync: boolean): + SyncAsyncResult> { + const componentPromise = this._compileComponents(moduleType, isSync); + const ngModuleFactory = this._compileModule(moduleType); + const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType); + const componentFactories: ComponentFactory[] = []; + const templates = new Set(); + moduleMeta.transitiveModule.modules.forEach((moduleMeta) => { + moduleMeta.declaredDirectives.forEach((dirMeta) => { + if (dirMeta.isComponent) { + const template = this._createCompiledHostTemplate(dirMeta.type.runtime); + templates.add(template); + componentFactories.push(template.proxyComponentFactory); + } + }); + }); + const syncResult = new ModuleWithComponentFactories(ngModuleFactory, componentFactories); + // Note: host components themselves can always be compiled synchronously as they have an + // inline template. However, we still need to wait for the components that they + // reference to be loaded / compiled. + const compile = () => { + templates.forEach((template) => { this._compileTemplate(template); }); + return syncResult; + }; + const asyncResult = isSync ? Promise.resolve(compile()) : componentPromise.then(compile); + return new SyncAsyncResult(syncResult, asyncResult); + } + private _compileModule(moduleType: ConcreteType): NgModuleFactory { let ngModuleFactory = this._compiledNgModuleCache.get(moduleType); if (!ngModuleFactory) { @@ -381,7 +419,7 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver { private _delegate: RuntimeCompiler, private _ngModule: ConcreteType, private _parentComponentResolver: ComponentResolver, private _console: Console) {} - get _injector(): Injector { return this._delegate._injector; } + get _injector(): Injector { return this._delegate.injector; } resolveComponent(component: Type|string): Promise> { if (isString(component)) { @@ -416,6 +454,15 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver { compileModuleAsync(moduleType: ConcreteType): Promise> { return this._delegate.compileModuleAsync(moduleType); } + compileModuleAndAllComponentsSync(moduleType: ConcreteType): + ModuleWithComponentFactories { + return this._delegate.compileModuleAndAllComponentsSync(moduleType); + } + + compileModuleAndAllComponentsAsync(moduleType: ConcreteType): + Promise> { + return this._delegate.compileModuleAndAllComponentsAsync(moduleType); + } /** * Clears all caches diff --git a/modules/@angular/compiler/test/directive_normalizer_spec.ts b/modules/@angular/compiler/test/directive_normalizer_spec.ts index 50c771f10c..980e1974b0 100644 --- a/modules/@angular/compiler/test/directive_normalizer_spec.ts +++ b/modules/@angular/compiler/test/directive_normalizer_spec.ts @@ -12,7 +12,7 @@ import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer'; import {XHR} from '@angular/compiler/src/xhr'; import {MockXHR} from '@angular/compiler/testing/xhr_mock'; import {ViewEncapsulation} from '@angular/core/src/metadata/view'; -import {configureCompiler} from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {SpyXHR} from './spies'; @@ -23,7 +23,7 @@ export function main() { var dirType: CompileTypeMetadata; var dirTypeWithHttpUrl: CompileTypeMetadata; - beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); + beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); beforeEach(() => { dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'}); @@ -179,7 +179,8 @@ export function main() { describe('normalizeExternalStylesheets', () => { - beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); + beforeEach( + () => { TestBed.configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); it('should load an external stylesheet', inject( diff --git a/modules/@angular/compiler/test/directive_resolver_mock_spec.ts b/modules/@angular/compiler/test/directive_resolver_mock_spec.ts index 5aa3e5a599..7f14f05dbb 100644 --- a/modules/@angular/compiler/test/directive_resolver_mock_spec.ts +++ b/modules/@angular/compiler/test/directive_resolver_mock_spec.ts @@ -27,7 +27,7 @@ export function main() { expect(ngModule.selector).toEqual('cmp'); }); - it('should allow overriding the @NgModule', () => { + it('should allow overriding the @Directive', () => { dirResolver.setDirective( SomeComponent, new ComponentMetadata({selector: 'someOtherSelector'})); var metadata = dirResolver.resolve(SomeComponent); diff --git a/modules/@angular/compiler/test/metadata_overrider_spec.ts b/modules/@angular/compiler/test/metadata_overrider_spec.ts new file mode 100644 index 0000000000..501329d173 --- /dev/null +++ b/modules/@angular/compiler/test/metadata_overrider_spec.ts @@ -0,0 +1,116 @@ +/** + * @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 {expect} from '@angular/platform-browser/testing/matchers'; +import {MetadataOverrider} from '../testing/metadata_overrider'; + +interface SomeMetadataType { + plainProp?: string; + getterProp?: string; + arrayProp?: any[]; +} + +class SomeMetadata implements SomeMetadataType { + plainProp: string; + private _getterProp: string; + get getterProp(): string { return this._getterProp; } + arrayProp: any[]; + + constructor(options: SomeMetadataType) { + this.plainProp = options.plainProp; + this._getterProp = options.getterProp; + this.arrayProp = options.arrayProp; + } +} + +export function main() { + describe('metadata overrider', () => { + let overrider: MetadataOverrider; + + beforeEach(() => { overrider = new MetadataOverrider(); }); + + it('should return a new instance with the same values', () => { + const oldInstance = new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someInput'}); + const newInstance = overrider.overrideMetadata(SomeMetadata, oldInstance, {}); + expect(newInstance).not.toBe(oldInstance); + expect(newInstance).toBeAnInstanceOf(SomeMetadata); + expect(newInstance).toEqual(oldInstance); + }); + + it('should set individual properties and keep others', () => { + const oldInstance = + new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'}); + const newInstance = + overrider.overrideMetadata(SomeMetadata, oldInstance, {set: {plainProp: 'newPlainProp'}}); + expect(newInstance) + .toEqual(new SomeMetadata({plainProp: 'newPlainProp', getterProp: 'someGetterProp'})); + }); + + describe('add properties', () => { + it('should replace non array values', () => { + const oldInstance = + new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'}); + const newInstance = overrider.overrideMetadata( + SomeMetadata, oldInstance, {add: {plainProp: 'newPlainProp'}}); + expect(newInstance) + .toEqual(new SomeMetadata({plainProp: 'newPlainProp', getterProp: 'someGetterProp'})); + }); + + it('should add to array values', () => { + const oldInstance = new SomeMetadata({arrayProp: ['a']}); + const newInstance = + overrider.overrideMetadata(SomeMetadata, oldInstance, {add: {arrayProp: ['b']}}); + expect(newInstance).toEqual(new SomeMetadata({arrayProp: ['a', 'b']})); + }); + }); + + describe('remove', () => { + it('should set values to undefined if their value matches', () => { + const oldInstance = + new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'}); + const newInstance = overrider.overrideMetadata( + SomeMetadata, oldInstance, {remove: {plainProp: 'somePlainProp'}}); + expect(newInstance) + .toEqual(new SomeMetadata({plainProp: undefined, getterProp: 'someGetterProp'})); + }); + + it('should leave values if their value does not match', () => { + const oldInstance = + new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'}); + const newInstance = overrider.overrideMetadata( + SomeMetadata, oldInstance, {remove: {plainProp: 'newPlainProp'}}); + expect(newInstance) + .toEqual(new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'})); + }); + + it('should remove a value from an array', () => { + const oldInstance = + new SomeMetadata({arrayProp: ['a', 'b', 'c'], getterProp: 'someGetterProp'}); + const newInstance = overrider.overrideMetadata( + SomeMetadata, oldInstance, {remove: {arrayProp: ['a', 'c']}}); + expect(newInstance) + .toEqual(new SomeMetadata({arrayProp: ['b'], getterProp: 'someGetterProp'})); + }); + + it('should support types as values', () => { + class Class1 {} + class Class2 {} + class Class3 {} + + const instance1 = new SomeMetadata({arrayProp: [Class1, Class2, Class3]}); + const instance2 = + overrider.overrideMetadata(SomeMetadata, instance1, {remove: {arrayProp: [Class1]}}); + expect(instance2).toEqual(new SomeMetadata({arrayProp: [Class2, Class3]})); + const instance3 = + overrider.overrideMetadata(SomeMetadata, instance2, {remove: {arrayProp: [Class3]}}); + expect(instance3).toEqual(new SomeMetadata({arrayProp: [Class2]})); + + }); + }); + }); +} diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index 9a3e53e97e..8ef18abe00 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -9,7 +9,7 @@ import {CompilerConfig} from '@angular/compiler/src/config'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation} from '@angular/core'; import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks'; -import {configureCompiler} from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {CompileNgModuleMetadata} from '../src/compile_metadata'; @@ -21,7 +21,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings'; export function main() { describe('CompileMetadataResolver', () => { - beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); + beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); describe('getDirectiveMetadata', () => { it('should read metadata', diff --git a/modules/@angular/compiler/test/runtime_compiler_spec.ts b/modules/@angular/compiler/test/runtime_compiler_spec.ts index 06c5680969..3ffefafa62 100644 --- a/modules/@angular/compiler/test/runtime_compiler_spec.ts +++ b/modules/@angular/compiler/test/runtime_compiler_spec.ts @@ -10,7 +10,7 @@ import {beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProvi import {expect} from '@angular/platform-browser/testing/matchers'; import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, NgModule, NgModuleFactory} from '@angular/core'; import {ConcreteType, stringify} from '../src/facade/lang'; -import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing'; +import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, TestBed} from '@angular/core/testing'; import {XHR, DirectiveResolver} from '@angular/compiler'; import {MockDirectiveResolver} from '@angular/compiler/testing'; @@ -36,7 +36,8 @@ export function main() { let dirResolver: MockDirectiveResolver; let injector: Injector; - beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); + beforeEach( + () => { TestBed.configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); beforeEach(inject( [Compiler, TestComponentBuilder, XHR, DirectiveResolver, Injector], diff --git a/modules/@angular/compiler/test/template_parser_spec.ts b/modules/@angular/compiler/test/template_parser_spec.ts index 480207af0e..e720bc1d14 100644 --- a/modules/@angular/compiler/test/template_parser_spec.ts +++ b/modules/@angular/compiler/test/template_parser_spec.ts @@ -14,7 +14,7 @@ import {TEMPLATE_TRANSFORMS, TemplateParser, splitClasses} from '@angular/compil import {MockSchemaRegistry} from '@angular/compiler/testing'; import {SchemaMetadata, SecurityContext} from '@angular/core'; import {Console} from '@angular/core/src/console'; -import {configureCompiler} from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {Identifiers, identifierToken} from '../src/identifiers'; @@ -40,7 +40,7 @@ export function main() { function commonBeforeEach() { beforeEach(() => { console = new ArrayConsole(); - configureCompiler({providers: [{provide: Console, useValue: console}]}); + TestBed.configureCompiler({providers: [{provide: Console, useValue: console}]}); }); beforeEach(inject([TemplateParser], (parser: TemplateParser) => { var component = CompileDirectiveMetadata.create({ @@ -66,10 +66,10 @@ export function main() { } describe('TemplateParser template transform', () => { - beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); + beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); beforeEach(() => { - configureCompiler({ + TestBed.configureCompiler({ providers: [{provide: TEMPLATE_TRANSFORMS, useValue: new FooAstTransformer(), multi: true}] }); @@ -84,7 +84,7 @@ export function main() { describe('multiple', () => { beforeEach(() => { - configureCompiler({ + TestBed.configureCompiler({ providers: [{provide: TEMPLATE_TRANSFORMS, useValue: new BarAstTransformer(), multi: true}] }); @@ -101,7 +101,7 @@ export function main() { // Semi-integration test to make sure TemplateParser properly sets the security context. // Uses the actual DomElementSchemaRegistry. beforeEach(() => { - configureCompiler({ + TestBed.configureCompiler({ providers: [ TEST_COMPILER_PROVIDERS, {provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry} @@ -138,7 +138,7 @@ export function main() { describe('TemplateParser', () => { beforeEach(() => { - configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); }); commonBeforeEach(); diff --git a/modules/@angular/compiler/test/test_component_builder_spec.ts b/modules/@angular/compiler/test/test_component_builder_spec.ts index e9d469b3ea..04bb0bd6a7 100644 --- a/modules/@angular/compiler/test/test_component_builder_spec.ts +++ b/modules/@angular/compiler/test/test_component_builder_spec.ts @@ -327,27 +327,6 @@ export function main() { expect(componentFixture.nativeElement).toHaveText('Mock'); })); - it('should create components synchronously with a custom module', - inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - @Pipe({name: 'somePipe'}) - class SomePipe { - transform(value: any) { return `transformed ${value}`; } - } - - @NgModule({declarations: [SomePipe]}) - class SomeModule { - } - - @Component({selector: 'comp', template: `{{'hello' | somePipe}}`}) - class SomeComponent { - } - - - let componentFixture = tcb.createSync(SomeComponent, SomeModule); - componentFixture.detectChanges(); - expect(componentFixture.nativeElement).toHaveText('transformed hello'); - })); - describe('ComponentFixture', () => { it('should auto detect changes if autoDetectChanges is called', inject( diff --git a/modules/@angular/compiler/testing.ts b/modules/@angular/compiler/testing.ts index cd6908962b..52a3fa52ff 100644 --- a/modules/@angular/compiler/testing.ts +++ b/modules/@angular/compiler/testing.ts @@ -10,12 +10,86 @@ export * from './testing/schema_registry_mock'; export * from './testing/test_component_builder'; export * from './testing/directive_resolver_mock'; export * from './testing/ng_module_resolver_mock'; +export * from './testing/pipe_resolver_mock'; -import {createPlatformFactory, CompilerOptions, PlatformRef} from '@angular/core'; -import {platformCoreDynamic, DirectiveResolver, NgModuleResolver} from './index'; +import {ConcreteType, Type} from './src/facade/lang'; +import {createPlatformFactory, ModuleWithComponentFactories, Injectable, CompilerOptions, PlatformRef, CompilerFactory, ComponentFactory, NgModuleFactory, Injector, NgModuleMetadata, NgModuleMetadataType, ComponentMetadata, ComponentMetadataType, DirectiveMetadata, DirectiveMetadataType, PipeMetadata, PipeMetadataType} from '@angular/core'; +import {MetadataOverride} from '@angular/core/testing'; +import {TestingCompilerFactory, TestingCompiler} from './core_private_testing'; +import {platformCoreDynamic, RuntimeCompiler, DirectiveResolver, NgModuleResolver, PipeResolver} from './index'; import {MockDirectiveResolver} from './testing/directive_resolver_mock'; import {MockNgModuleResolver} from './testing/ng_module_resolver_mock'; +import {MockPipeResolver} from './testing/pipe_resolver_mock'; +import {MetadataOverrider} from './testing/metadata_overrider'; +@Injectable() +export class TestingCompilerFactoryImpl implements TestingCompilerFactory { + constructor(private _compilerFactory: CompilerFactory) {} + + createTestingCompiler(options: CompilerOptions[]): TestingCompiler { + const compiler = this._compilerFactory.createCompiler(options); + return new TestingCompilerImpl( + compiler, compiler.injector.get(MockDirectiveResolver), + compiler.injector.get(MockPipeResolver), compiler.injector.get(MockNgModuleResolver)); + } +} + +export class TestingCompilerImpl implements TestingCompiler { + private _overrider = new MetadataOverrider(); + constructor( + private _compiler: RuntimeCompiler, private _directiveResolver: MockDirectiveResolver, + private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver) {} + get injector(): Injector { return this._compiler.injector; } + compileComponentAsync(component: ConcreteType, ngModule: Type = null): + Promise> { + return this._compiler.compileComponentAsync(component, ngModule); + } + compileComponentSync(component: ConcreteType, ngModule: Type = null): ComponentFactory { + return this._compiler.compileComponentSync(component, ngModule); + } + compileModuleSync(moduleType: ConcreteType): NgModuleFactory { + return this._compiler.compileModuleSync(moduleType); + } + + compileModuleAsync(moduleType: ConcreteType): Promise> { + return this._compiler.compileModuleAsync(moduleType); + } + compileModuleAndAllComponentsSync(moduleType: ConcreteType): + ModuleWithComponentFactories { + return this._compiler.compileModuleAndAllComponentsSync(moduleType); + } + + compileModuleAndAllComponentsAsync(moduleType: ConcreteType): + Promise> { + return this._compiler.compileModuleAndAllComponentsAsync(moduleType); + } + + overrideModule(ngModule: ConcreteType, override: MetadataOverride): + void { + const oldMetadata = this._moduleResolver.resolve(ngModule, false); + this._moduleResolver.setNgModule( + ngModule, this._overrider.overrideMetadata(NgModuleMetadata, oldMetadata, override)); + } + overrideDirective( + directive: ConcreteType, override: MetadataOverride): void { + const oldMetadata = this._directiveResolver.resolve(directive, false); + this._directiveResolver.setDirective( + directive, this._overrider.overrideMetadata(DirectiveMetadata, oldMetadata, override)); + } + overrideComponent( + component: ConcreteType, override: MetadataOverride): void { + const oldMetadata = this._directiveResolver.resolve(component, false); + this._directiveResolver.setDirective( + component, this._overrider.overrideMetadata(ComponentMetadata, oldMetadata, override)); + } + overridePipe(pipe: ConcreteType, override: MetadataOverride): void { + const oldMetadata = this._pipeResolver.resolve(pipe, false); + this._pipeResolver.setPipe( + pipe, this._overrider.overrideMetadata(PipeMetadata, oldMetadata, override)); + } + clearCache(): void { this._compiler.clearCache(); } + clearCacheFor(type: Type) { this._compiler.clearCacheFor(type); } +} /** * Platform for dynamic tests @@ -23,13 +97,17 @@ import {MockNgModuleResolver} from './testing/ng_module_resolver_mock'; * @experimental */ export const platformCoreDynamicTesting = - createPlatformFactory(platformCoreDynamic, 'coreDynamicTesting', [{ - provide: CompilerOptions, - useValue: { - providers: [ - {provide: DirectiveResolver, useClass: MockDirectiveResolver}, - {provide: NgModuleResolver, useClass: MockNgModuleResolver} - ] - }, - multi: true - }]); + createPlatformFactory(platformCoreDynamic, 'coreDynamicTesting', [ + { + provide: CompilerOptions, + useValue: { + providers: [ + MockPipeResolver, {provide: PipeResolver, useExisting: MockPipeResolver}, + MockDirectiveResolver, {provide: DirectiveResolver, useExisting: MockDirectiveResolver}, + MockNgModuleResolver, {provide: NgModuleResolver, useExisting: MockNgModuleResolver} + ] + }, + multi: true + }, + {provide: TestingCompilerFactory, useClass: TestingCompilerFactoryImpl} + ]); diff --git a/modules/@angular/compiler/testing/directive_resolver_mock.ts b/modules/@angular/compiler/testing/directive_resolver_mock.ts index 8679205398..30af94b97f 100644 --- a/modules/@angular/compiler/testing/directive_resolver_mock.ts +++ b/modules/@angular/compiler/testing/directive_resolver_mock.ts @@ -55,7 +55,8 @@ export class MockDirectiveResolver extends DirectiveResolver { if (metadata instanceof ComponentMetadata) { let viewProviders = metadata.viewProviders; if (isPresent(viewProviderOverrides)) { - const originalViewProviders: any[] = isPresent(metadata.viewProviders) ? metadata.viewProviders : []; + const originalViewProviders: any[] = + isPresent(metadata.viewProviders) ? metadata.viewProviders : []; viewProviders = originalViewProviders.concat(viewProviderOverrides); } diff --git a/modules/@angular/compiler/testing/metadata_overrider.ts b/modules/@angular/compiler/testing/metadata_overrider.ts new file mode 100644 index 0000000000..6f4d2260a8 --- /dev/null +++ b/modules/@angular/compiler/testing/metadata_overrider.ts @@ -0,0 +1,131 @@ +/** + * @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 {MetadataOverride} from '@angular/core/testing'; + +import {BaseException} from '../src/facade/exceptions'; +import {ConcreteType, stringify} from '../src/facade/lang'; + +type StringMap = { + [key: string]: any +}; + +let _nextReferenceId = 0; + +export class MetadataOverrider { + private _references = new Map(); + /** + * Creates a new instance for the given metadata class + * based on an old instance and overrides. + */ + overrideMetadata( + metadataClass: {new (options: T): C;}, oldMetadata: C, override: MetadataOverride): C { + const props: StringMap = {}; + if (oldMetadata) { + _valueProps(oldMetadata).forEach((prop) => props[prop] = (oldMetadata)[prop]); + } + + if (override.set) { + if (override.remove || override.add) { + throw new BaseException( + `Cannot set and add/remove ${stringify(metadataClass)} at the same time!`); + } + setMetadata(props, override.set); + } + if (override.remove) { + removeMetadata(props, override.remove, this._references); + } + if (override.add) { + addMetadata(props, override.add); + } + return new metadataClass(props); + } +} + +function removeMetadata(metadata: StringMap, remove: any, references: Map) { + const removeObjects = new Set(); + for (let prop in remove) { + const removeValue = remove[prop]; + if (removeValue instanceof Array) { + removeValue.forEach( + (value: any) => { removeObjects.add(_propHashKey(prop, value, references)); }); + } else { + removeObjects.add(_propHashKey(prop, removeValue, references)); + } + } + + for (let prop in metadata) { + const propValue = metadata[prop]; + if (propValue instanceof Array) { + metadata[prop] = propValue.filter( + (value: any) => { return !removeObjects.has(_propHashKey(prop, value, references)); }); + } else { + if (removeObjects.has(_propHashKey(prop, propValue, references))) { + metadata[prop] = undefined; + } + } + } +} + +function addMetadata(metadata: StringMap, add: any) { + for (let prop in add) { + const addValue = add[prop]; + const propValue = metadata[prop]; + if (propValue != null && propValue instanceof Array) { + metadata[prop] = propValue.concat(addValue); + } else { + metadata[prop] = addValue; + } + } +} + +function setMetadata(metadata: StringMap, set: any) { + for (let prop in set) { + metadata[prop] = set[prop]; + } +} + +function _propHashKey(propName: any, propValue: any, references: Map): string { + const replacer = (key: any, value: any) => { + if (typeof value === 'function') { + value = _serializeReference(value, references); + } + return value; + }; + + return `${propName}:${JSON.stringify(propValue, replacer)}`; +} + +function _serializeReference(ref: any, references: Map): string { + let id = references.get(ref); + if (!id) { + id = `${stringify(ref)}${_nextReferenceId++}`; + references.set(ref, id); + } + return id; +} + + +function _valueProps(obj: any): string[] { + const props: string[] = []; + // regular public props + Object.getOwnPropertyNames(obj).forEach((prop) => { + if (!prop.startsWith('_')) { + props.push(prop); + } + }); + // getters + const proto = Object.getPrototypeOf(obj); + Object.getOwnPropertyNames(proto).forEach((protoProp) => { + var desc = Object.getOwnPropertyDescriptor(proto, protoProp); + if (!protoProp.startsWith('_') && desc && 'get' in desc) { + props.push(protoProp); + } + }); + return props; +} diff --git a/modules/@angular/compiler/testing/pipe_resolver_mock.ts b/modules/@angular/compiler/testing/pipe_resolver_mock.ts index a0ef928ef2..e302bf386a 100644 --- a/modules/@angular/compiler/testing/pipe_resolver_mock.ts +++ b/modules/@angular/compiler/testing/pipe_resolver_mock.ts @@ -19,7 +19,7 @@ export class MockPipeResolver extends PipeResolver { private get _compiler(): Compiler { return this._injector.get(Compiler); } - private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); } + private _clearCacheFor(pipe: Type) { this._compiler.clearCacheFor(pipe); } /** * Overrides the {@link PipeMetadata} for a pipe. diff --git a/modules/@angular/compiler/testing/test_component_builder.ts b/modules/@angular/compiler/testing/test_component_builder.ts index d0a2647b3e..f5ababf9dd 100644 --- a/modules/@angular/compiler/testing/test_component_builder.ts +++ b/modules/@angular/compiler/testing/test_component_builder.ts @@ -16,7 +16,10 @@ import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang'; /** * A TestComponentBuilder that allows overriding based on the compiler. - */ + * + * @deprecated Use `TestBed.configureTestModule` / `TestBed.override...` / `TestBed.createComponent` + * instead. +*/ @Injectable() export class OverridingTestComponentBuilder extends TestComponentBuilder { /** @internal */ @@ -87,16 +90,14 @@ export class OverridingTestComponentBuilder extends TestComponentBuilder { return clone; } - createAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): - Promise> { + createAsync(rootComponentType: ConcreteType): Promise> { this._applyMetadataOverrides(); - return super.createAsync(rootComponentType, ngModule); + return super.createAsync(rootComponentType); } - createSync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): - ComponentFixture { + createSync(rootComponentType: ConcreteType): ComponentFixture { this._applyMetadataOverrides(); - return super.createSync(rootComponentType, ngModule); + return super.createSync(rootComponentType); } private _applyMetadataOverrides() { diff --git a/modules/@angular/core/private_export_testing.ts b/modules/@angular/core/private_export_testing.ts new file mode 100644 index 0000000000..3d1c07a061 --- /dev/null +++ b/modules/@angular/core/private_export_testing.ts @@ -0,0 +1,21 @@ +/** + * @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 * as test_compiler from './testing/test_compiler'; + +export declare namespace __core_private_testing_types__ { + export type TestingCompiler = test_compiler.TestingCompiler; + export var TestingCompiler: typeof test_compiler.TestingCompiler; + export type TestingCompilerFactory = test_compiler.TestingCompilerFactory; + export var TestingCompilerFactory: typeof test_compiler.TestingCompilerFactory; +} + +export var __core_private_testing__ = { + TestingCompiler: test_compiler.TestingCompiler, + TestingCompilerFactory: test_compiler.TestingCompilerFactory +}; diff --git a/modules/@angular/core/src/application_module.ts b/modules/@angular/core/src/application_module.ts index 91caaa9d75..6c9be7b0c8 100644 --- a/modules/@angular/core/src/application_module.ts +++ b/modules/@angular/core/src/application_module.ts @@ -22,10 +22,6 @@ import {NgZone} from './zone/ng_zone'; let __unused: Type; // avoid unused import when Type union types are erased -export function _componentFactoryResolverFactory() { - return ComponentFactoryResolver.NULL; -} - export function _iterableDiffersFactory() { return defaultIterableDiffers; } @@ -72,7 +68,6 @@ export const APPLICATION_COMMON_PROVIDERS: Array {provide: ApplicationRef, useExisting: ApplicationRef_}, Compiler, {provide: ComponentResolver, useExisting: Compiler}, - {provide: ComponentFactoryResolver, useFactory: _componentFactoryResolverFactory}, APP_ID_RANDOM_PROVIDER, ViewUtils, {provide: IterableDiffers, useFactory: _iterableDiffersFactory}, diff --git a/modules/@angular/core/src/linker.ts b/modules/@angular/core/src/linker.ts index 0fb5b6b971..6cff67fb12 100644 --- a/modules/@angular/core/src/linker.ts +++ b/modules/@angular/core/src/linker.ts @@ -7,7 +7,7 @@ */ // Public API for compiler -export {Compiler, CompilerFactory, CompilerOptions, ComponentStillLoadingError} from './linker/compiler'; +export {Compiler, CompilerFactory, CompilerOptions, ComponentStillLoadingError, ModuleWithComponentFactories} from './linker/compiler'; export {ComponentFactory, ComponentRef} from './linker/component_factory'; export {ComponentFactoryResolver, NoComponentFactoryError} from './linker/component_factory_resolver'; export {ComponentResolver} from './linker/component_resolver'; diff --git a/modules/@angular/core/src/linker/compiler.ts b/modules/@angular/core/src/linker/compiler.ts index 59df4df5a6..a848458a97 100644 --- a/modules/@angular/core/src/linker/compiler.ts +++ b/modules/@angular/core/src/linker/compiler.ts @@ -7,10 +7,9 @@ */ import {Injector, OpaqueToken} from '../di'; -import {BaseException} from '../facade/exceptions'; +import {BaseException, unimplemented} from '../facade/exceptions'; import {ConcreteType, Type, stringify} from '../facade/lang'; -import {ViewEncapsulation} from '../metadata'; -import {NgModuleMetadata} from '../metadata/ng_module'; +import {NgModuleMetadata, ViewEncapsulation} from '../metadata'; import {ComponentFactory} from './component_factory'; import {ComponentResolver} from './component_resolver'; @@ -28,6 +27,22 @@ export class ComponentStillLoadingError extends BaseException { } } +/** + * Combination of NgModuleFactory and ComponentFactorys. + * + * @experimental + */ +export class ModuleWithComponentFactories { + constructor( + public ngModuleFactory: NgModuleFactory, + public componentFactories: ComponentFactory[]) {} +} + + +function _throwError() { + throw new BaseException(`Runtime compiler is not loaded`); +} + /** * Low-level service for running the angular compiler duirng runtime * to create {@link ComponentFactory}s, which @@ -39,42 +54,48 @@ export class ComponentStillLoadingError extends BaseException { * @stable */ export class Compiler { - /** - * Returns the injector with which the compiler has been created. - */ - get _injector(): Injector { - throw new BaseException(`Runtime compiler is not loaded. Tried to read the injector.`); - } - /** * Loads the template and styles of a component and returns the associated `ComponentFactory`. */ compileComponentAsync(component: ConcreteType, ngModule: Type = null): Promise> { - throw new BaseException( - `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); + throw _throwError(); } /** * Compiles the given component. All templates have to be either inline or compiled via * `compileComponentAsync` before. Otherwise throws a {@link ComponentStillLoadingError}. */ compileComponentSync(component: ConcreteType, ngModule: Type = null): ComponentFactory { - throw new BaseException( - `Runtime compiler is not loaded. Tried to compile ${stringify(component)}`); + throw _throwError(); } /** - * Compiles the given NgModule. All templates of the components listed in `entryComponents` - * have to be either inline or compiled before via `compileComponentAsync` / - * `compileModuleAsync`. Otherwise throws a {@link ComponentStillLoadingError}. + * Compiles the given NgModule and all of its components. All templates of the components listed + * in `entryComponents` + * have to be inlined. Otherwise throws a {@link ComponentStillLoadingError}. */ - compileModuleSync(moduleType: ConcreteType): NgModuleFactory { - throw new BaseException( - `Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`); + compileModuleSync(moduleType: ConcreteType): NgModuleFactory { throw _throwError(); } + + /** + * Compiles the given NgModule and all of its components + */ + compileModuleAsync(moduleType: ConcreteType): Promise> { + throw _throwError(); } - compileModuleAsync(moduleType: ConcreteType): Promise> { - throw new BaseException( - `Runtime compiler is not loaded. Tried to compile ${stringify(moduleType)}`); + /** + * Same as {@link compileModuleSync} put also creates ComponentFactories for all components. + */ + compileModuleAndAllComponentsSync(moduleType: ConcreteType): + ModuleWithComponentFactories { + throw _throwError(); + } + + /** + * Same as {@link compileModuleAsync} put also creates ComponentFactories for all components. + */ + compileModuleAndAllComponentsAsync(moduleType: ConcreteType): + Promise> { + throw _throwError(); } /** diff --git a/modules/@angular/core/test/animation/animation_integration_spec.ts b/modules/@angular/core/test/animation/animation_integration_spec.ts index 66a2cb4737..d6bf14a58a 100644 --- a/modules/@angular/core/test/animation/animation_integration_spec.ts +++ b/modules/@angular/core/test/animation/animation_integration_spec.ts @@ -20,7 +20,7 @@ import {AnimationPlayer} from '../../src/animation/animation_player'; import {AnimationStyles} from '../../src/animation/animation_styles'; import {AUTO_STYLE, AnimationEntryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata'; import {isArray, isPresent} from '../../src/facade/lang'; -import {configureCompiler, configureModule, fakeAsync, flushMicrotasks, tick} from '../../testing'; +import {TestBed, fakeAsync, flushMicrotasks, tick} from '../../testing'; import {MockAnimationPlayer} from '../../testing/mock_animation_player'; import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal'; @@ -32,8 +32,9 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('animation tests', function() { beforeEachProviders(() => { - configureCompiler({useJit: useJit}); - configureModule({providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}]}); + TestBed.configureCompiler({useJit: useJit}); + TestBed.configureTestingModule( + {providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}]}); }); var makeAnimationCmp = diff --git a/modules/@angular/core/test/linker/change_detection_integration_spec.ts b/modules/@angular/core/test/linker/change_detection_integration_spec.ts index 22bad2cdb3..ded38f79f0 100644 --- a/modules/@angular/core/test/linker/change_detection_integration_spec.ts +++ b/modules/@angular/core/test/linker/change_detection_integration_spec.ts @@ -12,7 +12,7 @@ import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/test/test_bindings'; import {MockSchemaRegistry} from '@angular/compiler/testing'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef, ViewMetadata, WrappedValue, forwardRef} from '@angular/core'; import {DebugDomRenderer} from '@angular/core/src/debug/debug_renderer'; -import {ComponentFixture, TestComponentBuilder, configureCompiler, configureModule, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -78,8 +78,8 @@ export function main() { if (!getDOM().supportsDOMEvents()) return; beforeEach(() => { - configureCompiler({providers: TEST_COMPILER_PROVIDERS}); - configureModule({ + TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); + TestBed.configureTestingModule({ providers: [RenderLog, DirectiveLog, {provide: RootRenderer, useClass: LoggingRootRenderer}] }); diff --git a/modules/@angular/core/test/linker/entry_components_integration_spec.ts b/modules/@angular/core/test/linker/entry_components_integration_spec.ts index 50b24d1e04..b0d44b8e69 100644 --- a/modules/@angular/core/test/linker/entry_components_integration_spec.ts +++ b/modules/@angular/core/test/linker/entry_components_integration_spec.ts @@ -7,7 +7,7 @@ */ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; -import {TestComponentBuilder, configureModule, configureCompiler} from '@angular/core/testing'; +import {TestComponentBuilder, TestBed} from '@angular/core/testing'; import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_ENTRY_COMPONENTS, ViewMetadata} from '@angular/core'; import {stringify} from '../../src/facade/lang'; import {Console} from '../../src/console'; @@ -29,8 +29,9 @@ function declareTests({useJit}: {useJit: boolean}) { var console: DummyConsole; beforeEach(() => { console = new DummyConsole(); - configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]}); - configureModule({declarations: [MainComp, ChildComp, NestedChildComp]}); + TestBed.configureCompiler( + {useJit: useJit, providers: [{provide: Console, useValue: console}]}); + TestBed.configureTestingModule({declarations: [MainComp, ChildComp, NestedChildComp]}); }); it('should warn and auto declare if the component was not declared nor imported by the module', diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index 737f7ea460..b485094cd3 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {fakeAsync, tick, ComponentFixture, configureCompiler, configureModule, TestComponentBuilder} from '@angular/core/testing'; +import {fakeAsync, tick, ComponentFixture, TestBed, TestComponentBuilder} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {isPresent, stringify, isBlank,} from '../../src/facade/lang'; import {BaseException} from '../../src/facade/exceptions'; @@ -48,8 +48,9 @@ function declareTests({useJit}: {useJit: boolean}) { describe('integration tests', function() { beforeEach(() => { - configureCompiler({useJit: useJit}); - configureModule({providers: [{provide: ANCHOR_ELEMENT, useValue: el('
')}]}); + TestBed.configureCompiler({useJit: useJit}); + TestBed.configureTestingModule( + {providers: [{provide: ANCHOR_ELEMENT, useValue: el('
')}]}); }); describe('react to record changes', function() { diff --git a/modules/@angular/core/test/linker/ng_container_integration_spec.ts b/modules/@angular/core/test/linker/ng_container_integration_spec.ts index 11d89cf7ad..b4c0dec2f5 100644 --- a/modules/@angular/core/test/linker/ng_container_integration_spec.ts +++ b/modules/@angular/core/test/linker/ng_container_integration_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {configureCompiler, TestComponentBuilder} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {Component, Directive, AfterContentInit, AfterViewInit, QueryList, ContentChildren, ViewChildren, Input} from '@angular/core'; import {NgIf} from '@angular/common'; @@ -21,7 +21,7 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('', function() { - beforeEach(() => { configureCompiler({useJit: useJit}); }); + beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); it('should be rendered as comment with children as siblings', inject( diff --git a/modules/@angular/core/test/linker/ng_module_integration_spec.ts b/modules/@angular/core/test/linker/ng_module_integration_spec.ts index ac47d5921c..1eb731c59f 100644 --- a/modules/@angular/core/test/linker/ng_module_integration_spec.ts +++ b/modules/@angular/core/test/linker/ng_module_integration_spec.ts @@ -11,7 +11,7 @@ import {CompilerConfig, NgModuleResolver} from '@angular/compiler'; import {MockNgModuleResolver} from '@angular/compiler/testing'; import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Directive, Host, HostBinding, Inject, Injectable, Injector, Input, ModuleWithProviders, NgModule, NgModuleMetadata, NgModuleRef, OpaqueToken, Optional, Pipe, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, ViewMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; import {Console} from '@angular/core/src/console'; -import {ComponentFixture, configureCompiler} from '@angular/core/testing'; +import {ComponentFixture, TestBed} 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'; @@ -122,7 +122,8 @@ function declareTests({useJit}: {useJit: boolean}) { beforeEach(() => { console = new DummyConsole(); - configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]}); + TestBed.configureCompiler( + {useJit: useJit, providers: [{provide: Console, useValue: console}]}); }); beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => { diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 4670a3f698..fcdc48c892 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {configureCompiler, configureModule, TestComponentBuilder} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder} from '@angular/core/testing'; import {Component, Pipe, PipeTransform, ViewMetadata, OpaqueToken, Injector, forwardRef} from '@angular/core'; import {NgIf, NgClass} from '@angular/common'; @@ -25,8 +25,8 @@ function declareTests({useJit}: {useJit: boolean}) { describe('platform pipes', () => { beforeEach(() => { - configureCompiler({useJit: useJit}); - configureModule({declarations: [PlatformPipe]}); + TestBed.configureCompiler({useJit: useJit}); + TestBed.configureTestingModule({declarations: [PlatformPipe]}); }); it('should overwrite them by custom pipes', diff --git a/modules/@angular/core/test/linker/security_integration_spec.ts b/modules/@angular/core/test/linker/security_integration_spec.ts index e89edc4147..8fb7395b11 100644 --- a/modules/@angular/core/test/linker/security_integration_spec.ts +++ b/modules/@angular/core/test/linker/security_integration_spec.ts @@ -7,7 +7,7 @@ */ import {AsyncTestCompleter, ddescribe, describe, expect, inject, beforeEachProviders, beforeEach, afterEach, it,} from '@angular/core/testing/testing_internal'; -import {configureCompiler, TestComponentBuilder} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {Component} from '@angular/core/src/metadata'; import {DomSanitizationService} from '@angular/platform-browser/src/security/dom_sanitization_service'; @@ -42,7 +42,7 @@ function itAsync( function declareTests({useJit}: {useJit: boolean}) { describe('security integration tests', function() { - beforeEach(() => { configureCompiler({useJit: useJit}); }); + beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); let originalLog: (msg: any) => any; beforeEach(() => { diff --git a/modules/@angular/core/testing.ts b/modules/@angular/core/testing.ts index ad2ca6dac1..decb6df225 100644 --- a/modules/@angular/core/testing.ts +++ b/modules/@angular/core/testing.ts @@ -12,3 +12,6 @@ export * from './testing/fake_async'; export * from './testing/test_component_builder'; export * from './testing/test_bed'; export * from './testing/testing'; +export * from './testing/metadata_override'; + +export * from './private_export_testing'; diff --git a/modules/@angular/core/testing/metadata_override.ts b/modules/@angular/core/testing/metadata_override.ts new file mode 100644 index 0000000000..7e2d1d6467 --- /dev/null +++ b/modules/@angular/core/testing/metadata_override.ts @@ -0,0 +1,18 @@ +/** + * @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 + */ + +/** + * Type used for modifications to metadata + * + * @experimental + */ +export type MetadataOverride = { + add?: T, + remove?: T, + set?: T +}; diff --git a/modules/@angular/core/testing/test_bed.ts b/modules/@angular/core/testing/test_bed.ts index 10e485f4a8..6339868ff0 100644 --- a/modules/@angular/core/testing/test_bed.ts +++ b/modules/@angular/core/testing/test_bed.ts @@ -6,42 +6,205 @@ * found in the LICENSE file at https://angular.io/license */ -import {Compiler, CompilerFactory, CompilerOptions, ComponentStillLoadingError, Injector, NgModule, NgModuleFactory, NgModuleMetadata, NgModuleRef, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, assertPlatform, createPlatform, getPlatform} from '../index'; +import {CompilerOptions, ComponentFactory, ComponentMetadataType, ComponentStillLoadingError, DirectiveMetadataType, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleMetadataType, NgModuleRef, NgZone, OpaqueToken, PipeMetadataType, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, assertPlatform, createPlatform, getPlatform} from '../index'; import {ListWrapper} from '../src/facade/collection'; import {BaseException} from '../src/facade/exceptions'; import {ConcreteType, FunctionWrapper, isPresent, stringify} from '../src/facade/lang'; import {AsyncTestCompleter} from './async_test_completer'; +import {ComponentFixture} from './component_fixture'; +import {MetadataOverride} from './metadata_override'; +import {TestingCompiler, TestingCompilerFactory} from './test_compiler'; const UNDEFINED = new Object(); +/** + * An abstract class for inserting the root test component element in a platform independent way. + * + * @experimental + */ +export class TestComponentRenderer { + insertRootElement(rootElementId: string) {} +} + +var _nextRootElementId = 0; + +/** + * @experimental + */ +export var ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect'); + +/** + * @experimental + */ +export var ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone'); + +/** + * @experimental + */ +export type TestModuleMetadata = { + providers?: any[]; declarations?: any[]; imports?: any[]; schemas?: Array; +} + /** * @experimental */ export class TestBed implements Injector { + /** + * Initialize the environment for testing with a compiler factory, a PlatformRef, and an + * angular module. These are common to every test in the suite. + * + * This may only be called once, to set up the common providers for the current test + * suite on the current platform. If you absolutely need to change the providers, + * first use `resetTestEnvironment`. + * + * Test modules and platforms for individual platforms are available from + * 'angular2/platform/testing/'. + * + * @experimental + */ + static initTestEnvironment(ngModule: Type, platform: PlatformRef): TestBed { + const testBed = getTestBed(); + getTestBed().initTestEnvironment(ngModule, platform); + return testBed; + } + + /** + * Reset the providers for the test injector. + * + * @experimental + */ + static resetTestEnvironment() { getTestBed().resetTestEnvironment(); } + + static resetTestingModule(): typeof TestBed { + getTestBed().resetTestingModule(); + return TestBed; + } + + /** + * Allows overriding default compiler providers and settings + * which are defined in test_injector.js + */ + static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): typeof TestBed { + getTestBed().configureCompiler(config); + return TestBed; + } + + /** + * Allows overriding default providers, directives, pipes, modules of the test injector, + * which are defined in test_injector.js + */ + static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed { + getTestBed().configureTestingModule(moduleDef); + return TestBed; + } + + /** + * Compile components with a `templateUrl` for the test's NgModule. + * It is necessary to call this function + * as fetching urls is asynchronous. + */ + static compileComponents(): Promise { return getTestBed().compileComponents(); } + + static overrideModule( + ngModule: ConcreteType, + override: MetadataOverride): typeof TestBed { + getTestBed().overrideModule(ngModule, override); + return TestBed; + } + + static overrideComponent( + component: ConcreteType, + override: MetadataOverride): typeof TestBed { + getTestBed().overrideComponent(component, override); + return TestBed; + } + + static overrideDirective( + directive: ConcreteType, + override: MetadataOverride): typeof TestBed { + getTestBed().overrideDirective(directive, override); + return TestBed; + } + + static overridePipe(pipe: ConcreteType, override: MetadataOverride): + typeof TestBed { + getTestBed().overridePipe(pipe, override); + return TestBed; + } + + static createComponent(component: ConcreteType): ComponentFixture { + return getTestBed().createComponent(component); + } + private _instantiated: boolean = false; - private _compiler: Compiler = null; + private _compiler: TestingCompiler = null; private _moduleRef: NgModuleRef = null; - private _ngModuleFactory: NgModuleFactory = null; + private _moduleWithComponentFactories: ModuleWithComponentFactories = null; private _compilerOptions: CompilerOptions[] = []; + private _moduleOverrides: [ConcreteType, MetadataOverride][] = []; + private _componentOverrides: [ConcreteType, MetadataOverride][] = []; + private _directiveOverrides: [ConcreteType, MetadataOverride][] = []; + private _pipeOverrides: [ConcreteType, MetadataOverride][] = []; + private _providers: Array = []; private _declarations: Array = []; private _imports: Array = []; - private _entryComponents: Array = []; private _schemas: Array = []; - reset() { + /** + * Initialize the environment for testing with a compiler factory, a PlatformRef, and an + * angular module. These are common to every test in the suite. + * + * This may only be called once, to set up the common providers for the current test + * suite on the current platform. If you absolutely need to change the providers, + * first use `resetTestEnvironment`. + * + * Test modules and platforms for individual platforms are available from + * 'angular2/platform/testing/'. + * + * @experimental + */ + initTestEnvironment(ngModule: Type, platform: PlatformRef) { + if (this.platform || this.ngModule) { + throw new BaseException('Cannot set base providers because it has already been called'); + } + this.platform = platform; + this.ngModule = ngModule; + } + + /** + * Reset the providers for the test injector. + * + * @experimental + */ + resetTestEnvironment() { + this.resetTestingModule(); + this.platform = null; + this.ngModule = null; + } + + /** + * @deprecated use `resetTestingModule` instead + */ + reset() { this.resetTestingModule(); } + + resetTestingModule() { this._compiler = null; + this._moduleOverrides = []; + this._componentOverrides = []; + this._directiveOverrides = []; + this._pipeOverrides = []; + this._moduleRef = null; - this._ngModuleFactory = null; + this._moduleWithComponentFactories = null; this._compilerOptions = []; this._providers = []; this._declarations = []; this._imports = []; - this._entryComponents = []; this._schemas = []; this._instantiated = false; } @@ -51,22 +214,12 @@ export class TestBed implements Injector { ngModule: Type = null; configureCompiler(config: {providers?: any[], useJit?: boolean}) { - if (this._instantiated) { - throw new BaseException('Cannot add configuration after test injector is instantiated'); - } + this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler'); this._compilerOptions.push(config); } - configureModule(moduleDef: { - providers?: any[], - declarations?: any[], - imports?: any[], - entryComponents?: any[], - schemas?: Array - }) { - if (this._instantiated) { - throw new BaseException('Cannot add configuration after test injector is instantiated'); - } + configureTestingModule(moduleDef: TestModuleMetadata) { + this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module'); if (moduleDef.providers) { this._providers = ListWrapper.concat(this._providers, moduleDef.providers); } @@ -76,109 +229,141 @@ export class TestBed implements Injector { if (moduleDef.imports) { this._imports = ListWrapper.concat(this._imports, moduleDef.imports); } - if (moduleDef.entryComponents) { - this._entryComponents = ListWrapper.concat(this._entryComponents, moduleDef.entryComponents); - } if (moduleDef.schemas) { this._schemas = ListWrapper.concat(this._schemas, moduleDef.schemas); } } - createModuleFactory(): Promise> { - if (this._instantiated) { - throw new BaseException( - 'Cannot compile entryComponents when the test NgModule has already been instantiated. ' + - 'Make sure you are not using `inject` before `doAsyncEntryPointCompilation`.'); - } - - if (this._ngModuleFactory) { - return Promise.resolve(this._ngModuleFactory); + compileComponents(): Promise { + if (this._moduleWithComponentFactories || this._instantiated) { + return Promise.resolve(null); } const moduleType = this._createCompilerAndModule(); - - return this._compiler.compileModuleAsync(moduleType).then((ngModuleFactory) => { - this._ngModuleFactory = ngModuleFactory; - return ngModuleFactory; - }); + return this._compiler.compileModuleAndAllComponentsAsync(moduleType) + .then((moduleAndComponentFactories) => { + this._moduleWithComponentFactories = moduleAndComponentFactories; + }); } - initTestModule() { + private _initIfNeeded() { if (this._instantiated) { return; } - - if (this._ngModuleFactory) { - this._createFromModuleFactory(this._ngModuleFactory); - } else { - let moduleType = this._createCompilerAndModule(); - this._createFromModuleFactory(this._compiler.compileModuleSync(moduleType)); + if (!this._moduleWithComponentFactories) { + try { + let moduleType = this._createCompilerAndModule(); + this._moduleWithComponentFactories = + this._compiler.compileModuleAndAllComponentsSync(moduleType); + } catch (e) { + if (e instanceof ComponentStillLoadingError) { + throw new Error( + `This test module uses the component ${stringify(e.compType)} which is using a "templateUrl", but they were never compiled. ` + + `Please call "TestBed.compileComponents" before your test.`); + } else { + throw e; + } + } } - } - - /** - * @internal - */ - _createInjectorAsync(): Promise { - if (this._instantiated) { - return Promise.resolve(this); - } - let ngModule = this._createCompilerAndModule(); - return this._compiler.compileModuleAsync(ngModule).then( - (ngModuleFactory) => this._createFromModuleFactory(ngModuleFactory)); + this._moduleRef = + this._moduleWithComponentFactories.ngModuleFactory.create(this.platform.injector); + this._instantiated = true; } private _createCompilerAndModule(): ConcreteType { const providers = this._providers.concat([{provide: TestBed, useValue: this}]); const declarations = this._declarations; const imports = [this.ngModule, this._imports]; - const entryComponents = this._entryComponents; const schemas = this._schemas; - @NgModule({ - providers: providers, - declarations: declarations, - imports: imports, - entryComponents: entryComponents, - schemas: schemas - }) + @NgModule( + {providers: providers, declarations: declarations, imports: imports, schemas: schemas}) class DynamicTestModule { } - const compilerFactory: CompilerFactory = this.platform.injector.get(CompilerFactory); + const compilerFactory: TestingCompilerFactory = + this.platform.injector.get(TestingCompilerFactory); this._compiler = - compilerFactory.createCompiler(this._compilerOptions.concat([{useDebug: true}])); + compilerFactory.createTestingCompiler(this._compilerOptions.concat([{useDebug: true}])); + this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1])); + this._componentOverrides.forEach( + (entry) => this._compiler.overrideComponent(entry[0], entry[1])); + this._directiveOverrides.forEach( + (entry) => this._compiler.overrideDirective(entry[0], entry[1])); + this._pipeOverrides.forEach((entry) => this._compiler.overridePipe(entry[0], entry[1])); return DynamicTestModule; } - private _createFromModuleFactory(ngModuleFactory: NgModuleFactory): Injector { - this._moduleRef = ngModuleFactory.create(this.platform.injector); - this._instantiated = true; - return this; + private _assertNotInstantiated(methodName: string, methodDescription: string) { + if (this._instantiated) { + throw new BaseException( + `Cannot ${methodDescription} when the test module has already been instantiated. ` + + `Make sure you are not using \`inject\` before \`${methodName}\`.`); + } } get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) { - if (!this._instantiated) { - throw new BaseException( - 'Illegal state: The test bed\'s injector has not yet been created. Call initTestModule first!'); - } + this._initIfNeeded(); if (token === TestBed) { return this; } // Tests can inject things from the ng module and from the compiler, // but the ng module can't inject things from the compiler and vice versa. let result = this._moduleRef.injector.get(token, UNDEFINED); - return result === UNDEFINED ? this._compiler._injector.get(token, notFoundValue) : result; + return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue) : result; } execute(tokens: any[], fn: Function): any { - if (!this._instantiated) { - throw new BaseException( - 'Illegal state: The test bed\'s injector has not yet been created. Call initTestModule first!'); - } + this._initIfNeeded(); var params = tokens.map(t => this.get(t)); return FunctionWrapper.apply(fn, params); } + + overrideModule(ngModule: ConcreteType, override: MetadataOverride): + void { + this._assertNotInstantiated('overrideModule', 'override module metadata'); + this._moduleOverrides.push([ngModule, override]); + } + + overrideComponent( + component: ConcreteType, override: MetadataOverride): void { + this._assertNotInstantiated('overrideComponent', 'override component metadata'); + this._componentOverrides.push([component, override]); + } + + overrideDirective( + directive: ConcreteType, override: MetadataOverride): void { + this._assertNotInstantiated('overrideDirective', 'override directive metadata'); + this._directiveOverrides.push([directive, override]); + } + + overridePipe(pipe: ConcreteType, override: MetadataOverride): void { + this._assertNotInstantiated('overridePipe', 'override pipe metadata'); + this._pipeOverrides.push([pipe, override]); + } + + createComponent(component: ConcreteType): ComponentFixture { + this._initIfNeeded(); + const componentFactory = this._moduleWithComponentFactories.componentFactories.find( + (compFactory) => compFactory.componentType === component); + if (!componentFactory) { + throw new BaseException( + `Cannot create the component ${stringify(component)} as it was not imported into the testing module!`); + } + const noNgZone = this.get(ComponentFixtureNoNgZone, false); + const autoDetect: boolean = this.get(ComponentFixtureAutoDetect, false); + const ngZone: NgZone = noNgZone ? null : this.get(NgZone, null); + const testComponentRenderer: TestComponentRenderer = this.get(TestComponentRenderer); + const rootElId = `root${_nextRootElementId++}`; + testComponentRenderer.insertRootElement(rootElId); + + const initComponent = () => { + var componentRef = componentFactory.create(this, [], `#${rootElId}`); + return new ComponentFixture(componentRef, ngZone, autoDetect); + }; + + return ngZone == null ? initComponent() : ngZone.run(initComponent); + } } var _testBed: TestBed = null; @@ -211,7 +396,7 @@ export function getTestInjector() { * Test modules and platforms for individual platforms are available from * 'angular2/platform/testing/'. * - * @deprecated Use initTestEnvironment instead + * @deprecated Use TestBed.initTestEnvironment instead */ export function setBaseTestProviders( platformProviders: Array, @@ -220,65 +405,17 @@ export function setBaseTestProviders( (platformProviders[0])(applicationProviders); } else { throw new Error( - `setBaseTestProviders is deprecated and only supports platformProviders that are predefined by Angular. Use 'initTestEnvironment' instead.`); + `setBaseTestProviders is deprecated and only supports platformProviders that are predefined by Angular. Use 'TestBed.initTestEnvironment' instead.`); } } -/** - * Initialize the environment for testing with a compiler factory, a PlatformRef, and an - * angular module. These are common to every test in the suite. - * - * This may only be called once, to set up the common providers for the current test - * suite on the current platform. If you absolutely need to change the providers, - * first use `resetTestEnvironment`. - * - * Test modules and platforms for individual platforms are available from - * 'angular2/platform/testing/'. - * - * @experimental - */ -export function initTestEnvironment(ngModule: Type, platform: PlatformRef): Injector { - var testBed = getTestBed(); - if (testBed.platform || testBed.ngModule) { - throw new BaseException('Cannot set base providers because it has already been called'); - } - testBed.platform = platform; - testBed.ngModule = ngModule; - - return testBed; -} - /** * Reset the providers for the test injector. * - * @deprecated Use resetTestEnvironment instead. + * @deprecated Use TestBed.resetTestEnvironment instead. */ export function resetBaseTestProviders() { - resetTestEnvironment(); -} - -/** - * Reset the providers for the test injector. - * - * @experimental - */ -export function resetTestEnvironment() { - var testBed = getTestBed(); - testBed.platform = null; - testBed.ngModule = null; - testBed.reset(); -} - -/** - * Compile entryComponents with a `templateUrl` for the test's NgModule. - * It is necessary to call this function - * as fetching urls is asynchronous. - * - * @experimental - */ -export function doAsyncEntryPointCompilation(): Promise { - let testBed = getTestBed(); - return testBed.createModuleFactory(); + TestBed.resetTestEnvironment(); } /** @@ -308,30 +445,17 @@ export function doAsyncEntryPointCompilation(): Promise { export function inject(tokens: any[], fn: Function): () => any { let testBed = getTestBed(); if (tokens.indexOf(AsyncTestCompleter) >= 0) { - return () => { - // Return an async test method that returns a Promise if AsyncTestCompleter is one of the - // injected tokens. - return testBed._createInjectorAsync().then(() => { - let completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter); - testBed.execute(tokens, fn); - return completer.promise; - }); - }; + return () => + // Return an async test method that returns a Promise if AsyncTestCompleter is one of + // the + // injected tokens. + testBed.compileComponents().then(() => { + let completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter); + testBed.execute(tokens, fn); + return completer.promise; + }); } else { - return () => { - try { - testBed.initTestModule(); - } catch (e) { - if (e instanceof ComponentStillLoadingError) { - throw new Error( - `This test module uses the entryComponents ${stringify(e.compType)} which is using a "templateUrl", but they were never compiled. ` + - `Please call "doAsyncEntryPointCompilation" before "inject".`); - } else { - throw e; - } - } - return testBed.execute(tokens, fn); - }; + return () => testBed.execute(tokens, fn); } } @@ -339,17 +463,12 @@ export function inject(tokens: any[], fn: Function): () => any { * @experimental */ export class InjectSetupWrapper { - constructor(private _moduleDef: () => { - providers?: any[], - declarations?: any[], - imports?: any[], - entryComponents?: any[] - }) {} + constructor(private _moduleDef: () => TestModuleMetadata) {} private _addModule() { - var moduleDef = this._moduleDef(); + const moduleDef = this._moduleDef(); if (moduleDef) { - getTestBed().configureModule(moduleDef); + getTestBed().configureTestingModule(moduleDef); } } @@ -362,21 +481,27 @@ export class InjectSetupWrapper { } /** - * @experimental + * @deprecated Use `TestBed.configureTestingModule instead. */ export function withProviders(providers: () => any) { - return new InjectSetupWrapper(() => {{return {providers: providers()};}}); + return new InjectSetupWrapper(() => { return {providers: providers()}; }); } /** * @experimental */ -export function withModule(moduleDef: () => { - providers?: any[], - declarations?: any[], - imports?: any[], - entryComponents?: any[], - schemas?: Array -}) { - return new InjectSetupWrapper(moduleDef); +export function withModule(moduleDef: TestModuleMetadata): InjectSetupWrapper; +export function withModule(moduleDef: TestModuleMetadata, fn: Function): () => any; +export function withModule(moduleDef: TestModuleMetadata, fn: Function = null): (() => any)| + InjectSetupWrapper { + if (fn) { + return () => { + const testBed = getTestBed(); + if (moduleDef) { + testBed.configureTestingModule(moduleDef); + } + return fn(); + }; + } + return new InjectSetupWrapper(() => moduleDef); } diff --git a/modules/@angular/core/testing/test_compiler.ts b/modules/@angular/core/testing/test_compiler.ts new file mode 100644 index 0000000000..dbfb420e64 --- /dev/null +++ b/modules/@angular/core/testing/test_compiler.ts @@ -0,0 +1,45 @@ +/** + * @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 {Compiler, CompilerOptions, ComponentMetadataType, DirectiveMetadataType, Injector, NgModuleFactory, NgModuleMetadataType, PipeMetadataType} from '../index'; +import {unimplemented} from '../src/facade/exceptions'; +import {ConcreteType} from '../src/facade/lang'; +import {MetadataOverride} from './metadata_override'; + +/** + * Special interface to the compiler only used by testing + * + * @experimental + */ +export class TestingCompiler extends Compiler { + get injector(): Injector { throw unimplemented(); } + overrideModule(module: ConcreteType, overrides: MetadataOverride): + void { + throw unimplemented(); + } + overrideDirective( + directive: ConcreteType, overrides: MetadataOverride): void { + throw unimplemented(); + } + overrideComponent( + component: ConcreteType, overrides: MetadataOverride): void { + throw unimplemented(); + } + overridePipe(directive: ConcreteType, overrides: MetadataOverride): void { + throw unimplemented(); + } +} + +/** + * A factory for creating a Compiler + * + * @experimental + */ +export abstract class TestingCompilerFactory { + abstract createTestingCompiler(options?: CompilerOptions[]): TestingCompiler; +} diff --git a/modules/@angular/core/testing/test_component_builder.ts b/modules/@angular/core/testing/test_component_builder.ts index b715ea245c..8e0ef84be9 100644 --- a/modules/@angular/core/testing/test_component_builder.ts +++ b/modules/@angular/core/testing/test_component_builder.ts @@ -13,32 +13,15 @@ import {ConcreteType, IS_DART, Type, isPresent} from '../src/facade/lang'; import {ComponentFixture} from './component_fixture'; import {tick} from './fake_async'; - - -/** - * An abstract class for inserting the root test component element in a platform independent way. - * - * @experimental - */ -export class TestComponentRenderer { - insertRootElement(rootElementId: string) {} -} - -/** - * @experimental - */ -export var ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect'); - -/** - * @experimental - */ -export var ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone'); +import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestComponentRenderer} from './test_bed'; var _nextRootElementId = 0; /** * Builds a ComponentFixture for use in component level tests. - * @stable + * + * @deprecated Use `TestBed.configureTestModule` / `TestBed.override...` / `TestBed.createComponent` + * instead. */ @Injectable() export class TestComponentBuilder { @@ -119,27 +102,25 @@ export class TestComponentBuilder { /** * Builds and returns a ComponentFixture. */ - createAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): - Promise> { + createAsync(rootComponentType: ConcreteType): Promise> { let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false); let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null); let compiler: Compiler = this._injector.get(Compiler); let initComponent = () => { let promise: Promise> = - compiler.compileComponentAsync(rootComponentType, ngModule); + compiler.compileComponentAsync(rootComponentType); return promise.then(componentFactory => this.createFromFactory(ngZone, componentFactory)); }; return ngZone == null ? initComponent() : ngZone.run(initComponent); } - createFakeAsync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): - ComponentFixture { + createFakeAsync(rootComponentType: ConcreteType): ComponentFixture { let result: any /** TODO #9100 */; let error: any /** TODO #9100 */; PromiseWrapper.then( - this.createAsync(rootComponentType, ngModule), (_result) => { result = _result; }, + this.createAsync(rootComponentType), (_result) => { result = _result; }, (_error) => { error = _error; }); tick(); if (isPresent(error)) { @@ -148,15 +129,13 @@ export class TestComponentBuilder { return result; } - createSync(rootComponentType: ConcreteType, ngModule: ConcreteType = null): - ComponentFixture { + createSync(rootComponentType: ConcreteType): ComponentFixture { let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false); let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null); let compiler: Compiler = this._injector.get(Compiler); let initComponent = () => { - return this.createFromFactory( - ngZone, compiler.compileComponentSync(rootComponentType, ngModule)); + return this.createFromFactory(ngZone, compiler.compileComponentSync(rootComponentType)); }; return ngZone == null ? initComponent() : ngZone.run(initComponent); diff --git a/modules/@angular/core/testing/testing.ts b/modules/@angular/core/testing/testing.ts index d5f55ca573..3c3cf0631a 100644 --- a/modules/@angular/core/testing/testing.ts +++ b/modules/@angular/core/testing/testing.ts @@ -13,75 +13,25 @@ */ import {SchemaMetadata} from '../index'; -import {TestBed, getTestBed} from './test_bed'; + +import {TestBed, TestModuleMetadata, getTestBed} from './test_bed'; declare var global: any; var _global = (typeof window === 'undefined' ? global : window); -var testBed: TestBed = getTestBed(); - // Reset the test providers before each test. if (_global.beforeEach) { - _global.beforeEach(() => { testBed.reset(); }); + _global.beforeEach(() => { TestBed.resetTestingModule(); }); } /** * Allows overriding default providers of the test injector, * which are defined in test_injector.js * - * @stable + * @deprecated Use `TestBed.configureTestingModule instead. */ export function addProviders(providers: Array): void { if (!providers) return; - try { - testBed.configureModule({providers: providers}); - } catch (e) { - throw new Error( - 'addProviders can\'t be called after the injector has been already created for this test. ' + - 'This is most likely because you\'ve already used the injector to inject a beforeEach or the ' + - 'current `it` function.'); - } -} - -/** - * Allows overriding default providers, directives, pipes, modules of the test injector, - * which are defined in test_injector.js - * - * @stable - */ -export function configureModule(moduleDef: { - providers?: any[], - declarations?: any[], - imports?: any[], - entryComponents?: any[], - schemas?: Array -}): void { - if (!moduleDef) return; - try { - testBed.configureModule(moduleDef); - } catch (e) { - throw new Error( - 'configureModule can\'t be called after the injector has been already created for this test. ' + - 'This is most likely because you\'ve already used the injector to inject a beforeEach or the ' + - 'current `it` function.'); - } -} - -/** - * Allows overriding default compiler providers and settings - * which are defined in test_injector.js - * - * @stable - */ -export function configureCompiler(config: {providers?: any[], useJit?: boolean}): void { - if (!config) return; - try { - testBed.configureCompiler(config); - } catch (e) { - throw new Error( - 'configureCompiler can\'t be called after the injector has been already created for this test. ' + - 'This is most likely because you\'ve already used the injector to inject a beforeEach or the ' + - 'current `it` function.'); - } + TestBed.configureTestingModule({providers: providers}); } diff --git a/modules/@angular/core/testing/testing_internal.ts b/modules/@angular/core/testing/testing_internal.ts index 9e4a3a8e1d..7c16a27d9a 100644 --- a/modules/@angular/core/testing/testing_internal.ts +++ b/modules/@angular/core/testing/testing_internal.ts @@ -61,7 +61,7 @@ class BeforeEachRunner { } // Reset the test providers before each test -jsmBeforeEach(() => { testBed.reset(); }); +jsmBeforeEach(() => { testBed.resetTestingModule(); }); function _describe(jsmFn: any /** TODO #9100 */, ...args: any[] /** TODO #9100 */) { var parentRunner = runnerStack.length === 0 ? null : runnerStack[runnerStack.length - 1]; @@ -110,7 +110,7 @@ export function beforeEachProviders(fn: any /** TODO #9100 */): void { jsmBeforeEach(() => { var providers = fn(); if (!providers) return; - testBed.configureModule({providers: providers}); + testBed.configureTestingModule({providers: providers}); }); } @@ -138,7 +138,7 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe return new AsyncTestCompleter(); } }; - testBed.configureModule({providers: [completerProvider]}); + testBed.configureTestingModule({providers: [completerProvider]}); runner.run(); inIt = true; diff --git a/modules/@angular/forms/test/reactive_integration_spec.ts b/modules/@angular/forms/test/reactive_integration_spec.ts index 0316084a40..fc6fc123b4 100644 --- a/modules/@angular/forms/test/reactive_integration_spec.ts +++ b/modules/@angular/forms/test/reactive_integration_spec.ts @@ -8,7 +8,7 @@ import {NgFor, NgIf} from '@angular/common'; import {Component, Directive, EventEmitter, Input, Output, forwardRef} from '@angular/core'; -import {ComponentFixture, TestComponentBuilder, configureModule, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {ControlValueAccessor, FormArray, FormControl, FormGroup, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, ReactiveFormsModule, Validator, Validators} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; @@ -22,7 +22,9 @@ import {PromiseWrapper} from '../src/facade/promise'; export function main() { describe('reactive forms integration tests', () => { - beforeEach(() => { configureModule({imports: [FormsModule, ReactiveFormsModule]}); }); + beforeEach(() => { + TestBed.configureTestingModule({imports: [FormsModule, ReactiveFormsModule]}); + }); it('should initialize DOM elements with the given form object', inject( diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index 0f712099e3..67cad045bc 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -8,19 +8,20 @@ import {NgFor, NgIf} from '@angular/common'; import {Component} from '@angular/core'; -import {ComponentFixture, TestComponentBuilder, configureModule, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {FormsModule, NgForm} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {dispatchEvent} from '@angular/platform-browser/testing/browser_util'; + import {ObservableWrapper} from '../src/facade/async'; import {ListWrapper} from '../src/facade/collection'; export function main() { describe('template-driven forms integration tests', () => { - beforeEach(() => { configureModule({imports: [FormsModule]}); }); + beforeEach(() => { TestBed.configureTestingModule({imports: [FormsModule]}); }); it('should support ngModel for single fields', fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { diff --git a/modules/@angular/platform-browser-dynamic/test/xhr/xhr_cache_spec.ts b/modules/@angular/platform-browser-dynamic/test/xhr/xhr_cache_spec.ts index a46ea27610..53c9a825ae 100644 --- a/modules/@angular/platform-browser-dynamic/test/xhr/xhr_cache_spec.ts +++ b/modules/@angular/platform-browser-dynamic/test/xhr/xhr_cache_spec.ts @@ -8,7 +8,7 @@ import {UrlResolver, XHR} from '@angular/compiler'; import {Component} from '@angular/core'; -import {TestComponentBuilder, configureCompiler, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -26,7 +26,7 @@ export function main() { return new CachedXHR(); } beforeEach(() => { - configureCompiler({ + TestBed.configureCompiler({ providers: [ {provide: UrlResolver, useClass: TestUrlResolver}, {provide: XHR, useFactory: createCachedXHR} diff --git a/modules/@angular/platform-browser-dynamic/testing.ts b/modules/@angular/platform-browser-dynamic/testing.ts index f2fc1ede47..3be69571c4 100644 --- a/modules/@angular/platform-browser-dynamic/testing.ts +++ b/modules/@angular/platform-browser-dynamic/testing.ts @@ -9,7 +9,7 @@ import {CompilerConfig, DirectiveResolver, NgModuleResolver, analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler'; import {OverridingTestComponentBuilder, platformCoreDynamicTesting} from '@angular/compiler/testing'; import {Compiler, CompilerFactory, CompilerOptions, NgModule, PlatformRef, Provider, ReflectiveInjector, Type, createPlatform, createPlatformFactory} from '@angular/core'; -import {TestComponentBuilder, TestComponentRenderer, initTestEnvironment} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder, TestComponentRenderer} from '@angular/core/testing'; import {BrowserTestingModule, platformBrowserTesting} from '@angular/platform-browser/testing'; import {Console} from './core_private'; @@ -62,7 +62,7 @@ export const TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array console.warn(msg)); }]; diff --git a/modules/@angular/platform-browser/test/testing_public_spec.ts b/modules/@angular/platform-browser/test/testing_public_spec.ts index 88ebe22abb..18c15279b0 100644 --- a/modules/@angular/platform-browser/test/testing_public_spec.ts +++ b/modules/@angular/platform-browser/test/testing_public_spec.ts @@ -8,8 +8,8 @@ import {NgIf} from '@angular/common'; import {CompilerConfig, XHR} from '@angular/compiler'; -import {CUSTOM_ELEMENTS_SCHEMA, Component, ComponentFactoryResolver, Directive, Injectable, Input, NgModule, Pipe, ViewMetadata, provide} from '@angular/core'; -import {TestComponentBuilder, addProviders, async, configureCompiler, configureModule, doAsyncEntryPointCompilation, fakeAsync, inject, tick, withModule, withProviders} from '@angular/core/testing'; +import {CUSTOM_ELEMENTS_SCHEMA, Component, ComponentFactoryResolver, ComponentMetadata, Directive, DirectiveMetadata, HostBinding, Injectable, Input, NgModule, NgModuleMetadata, Pipe, PipeMetadata, ViewMetadata, provide} from '@angular/core'; +import {TestBed, TestComponentBuilder, addProviders, async, fakeAsync, inject, tick, withModule, withProviders} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; import {stringify} from '../../http/src/facade/lang'; @@ -229,26 +229,63 @@ export function main() { }); describe('using the test injector with modules', () => { - let moduleConfig: any; - beforeEach(() => { - moduleConfig = { - providers: [FancyService], - imports: [SomeLibModule], - declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe], - entryComponents: [CompUsingModuleDirectiveAndPipe] - }; - }); + let moduleConfig = { + providers: [FancyService], + imports: [SomeLibModule], + declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe], + }; describe('setting up a module', () => { - beforeEach(() => configureModule(moduleConfig)); + beforeEach(() => TestBed.configureTestingModule(moduleConfig)); it('should use set up providers', inject([FancyService], (service: FancyService) => { expect(service.value).toEqual('real value'); })); - it('should use set up directives and pipes', - inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - let compFixture = tcb.createSync(CompUsingModuleDirectiveAndPipe); + it('should be able to create any declared components', () => { + const compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe); + expect(compFixture.componentInstance).toBeAnInstanceOf(CompUsingModuleDirectiveAndPipe); + }); + + it('should use set up directives and pipes', () => { + const compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe); + let el = compFixture.debugElement; + + compFixture.detectChanges(); + expect(el.children[0].properties['title']).toBe('transformed someValue'); + }); + + it('should use set up imported modules', + inject([SomeLibModule], (libModule: SomeLibModule) => { + expect(libModule).toBeAnInstanceOf(SomeLibModule); + })); + + describe('provided schemas', () => { + @Component({template: ''}) + class ComponentUsingInvalidProperty { + } + + beforeEach(() => { + TestBed.configureTestingModule( + {schemas: [CUSTOM_ELEMENTS_SCHEMA], declarations: [ComponentUsingInvalidProperty]}); + }); + + it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', + () => { + expect(TestBed.createComponent(ComponentUsingInvalidProperty).componentInstance) + .toBeAnInstanceOf(ComponentUsingInvalidProperty); + }); + }); + }); + + describe('per test modules', () => { + it('should use set up providers', + withModule(moduleConfig).inject([FancyService], (service: FancyService) => { + expect(service.value).toEqual('real value'); + })); + + it('should use set up directives and pipes', withModule(moduleConfig, () => { + let compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe); let el = compFixture.debugElement; compFixture.detectChanges(); @@ -256,83 +293,96 @@ export function main() { })); it('should use set up library modules', - inject([SomeLibModule], (libModule: SomeLibModule) => { + withModule(moduleConfig).inject([SomeLibModule], (libModule: SomeLibModule) => { expect(libModule).toBeAnInstanceOf(SomeLibModule); })); - - it('should use set up entryComponents components', - inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { - expect(resolver.resolveComponentFactory(CompUsingModuleDirectiveAndPipe).componentType) - .toBe(CompUsingModuleDirectiveAndPipe); - })); - - it('should error on unknown bound properties on custom elements by default', - inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - @Component({template: ''}) - class ComponentUsingInvalidProperty { - } - - expect(() => tcb.createSync(ComponentUsingInvalidProperty)) - .toThrowError(/Can't bind to 'someUnknownProp'/); - })); - - describe('provided schemas', () => { - beforeEach(() => { configureModule({schemas: [CUSTOM_ELEMENTS_SCHEMA]}); }); - - it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', - inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - @Component({template: ''}) - class ComponentUsingInvalidProperty { - } - - tcb.createSync(ComponentUsingInvalidProperty); - expect(() => tcb.createSync(ComponentUsingInvalidProperty)).not.toThrow(); - })); - }); }); - describe('per test modules', () => { - it('should use set up providers', - withModule(() => moduleConfig).inject([FancyService], (service: FancyService) => { - expect(service.value).toEqual('real value'); - })); - - it('should use set up directives and pipes', - withModule(() => moduleConfig) - .inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - let compFixture = tcb.createSync(CompUsingModuleDirectiveAndPipe); - let el = compFixture.debugElement; - - compFixture.detectChanges(); - expect(el.children[0].properties['title']).toBe('transformed someValue'); - })); - - it('should use set up library modules', - withModule(() => moduleConfig).inject([SomeLibModule], (libModule: SomeLibModule) => { - expect(libModule).toBeAnInstanceOf(SomeLibModule); - })); - - it('should use set up entryComponents components', - withModule(() => moduleConfig) - .inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { - expect( - resolver.resolveComponentFactory(CompUsingModuleDirectiveAndPipe).componentType) - .toBe(CompUsingModuleDirectiveAndPipe); - })); - }); - - describe('entryComponents components with template url', () => { + describe('components with template url', () => { beforeEach(async(() => { - configureModule( - {declarations: [CompWithUrlTemplate], entryComponents: [CompWithUrlTemplate]}); - doAsyncEntryPointCompilation(); + TestBed.configureTestingModule({declarations: [CompWithUrlTemplate]}); + TestBed.compileComponents(); })); it('should allow to createSync components with templateUrl after explicit async compilation', - inject([TestComponentBuilder], (builder: TestComponentBuilder) => { - let fixture = builder.createSync(CompWithUrlTemplate); + () => { + let fixture = TestBed.createComponent(CompWithUrlTemplate); expect(fixture.nativeElement).toHaveText('from external template\n'); - })); + }); + }); + + describe('overwrite metadata', () => { + @Pipe({name: 'undefined'}) + class SomePipe { + transform(value: string): string { return `transformed ${value}`; } + } + + @Directive({selector: '[undefined]'}) + class SomeDirective { + someProp = 'hello'; + } + + @Component({selector: 'comp', template: 'someText'}) + class SomeComponent { + } + + @Component({selector: 'othercomp', template: 'someOtherText'}) + class SomeOtherComponent { + } + + @NgModule({declarations: [SomeComponent, SomeDirective, SomePipe]}) + class SomeModule { + } + + beforeEach(() => { TestBed.configureTestingModule({imports: [SomeModule]}); }); + + describe('module', () => { + beforeEach(() => { + TestBed.overrideModule(SomeModule, {set: {declarations: [SomeOtherComponent]}}); + }); + it('should work', () => { + expect(TestBed.createComponent(SomeOtherComponent).componentInstance) + .toBeAnInstanceOf(SomeOtherComponent); + }); + }); + + describe('component', () => { + beforeEach(() => { + TestBed.overrideComponent(SomeComponent, {set: {selector: 'comp', template: 'newText'}}); + }); + it('should work', () => { + expect(TestBed.createComponent(SomeComponent).nativeElement).toHaveText('newText'); + }); + }); + + describe('directive', () => { + beforeEach(() => { + TestBed + .overrideComponent( + SomeComponent, {set: {selector: 'comp', template: `
`}}) + .overrideDirective( + SomeDirective, {set: {selector: '[someDir]', host: {'[title]': 'someProp'}}}); + }); + it('should work', () => { + const compFixture = TestBed.createComponent(SomeComponent); + compFixture.detectChanges(); + expect(compFixture.debugElement.children[0].properties['title']).toEqual('hello'); + }); + }); + + describe('pipe', () => { + beforeEach(() => { + TestBed + .overrideComponent( + SomeComponent, {set: {selector: 'comp', template: `{{'hello' | somePipe}}`}}) + .overridePipe(SomePipe, {set: {name: 'somePipe'}}); + }); + it('should work', () => { + const compFixture = TestBed.createComponent(SomeComponent); + compFixture.detectChanges(); + expect(compFixture.nativeElement).toHaveText('transformed hello'); + }); + }); }); describe('setting up the compiler', () => { @@ -340,7 +390,7 @@ export function main() { describe('providers', () => { beforeEach(() => { let xhrGet = jasmine.createSpy('xhrGet').and.returnValue(Promise.resolve('Hello world!')); - configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); + TestBed.configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); }); it('should use set up providers', @@ -351,14 +401,14 @@ export function main() { }); describe('useJit true', () => { - beforeEach(() => { configureCompiler({useJit: true}); }); + beforeEach(() => { TestBed.configureCompiler({useJit: true}); }); it('should set the value into CompilerConfig', inject([CompilerConfig], (config: CompilerConfig) => { expect(config.useJit).toBe(true); })); }); describe('useJit false', () => { - beforeEach(() => { configureCompiler({useJit: false}); }); + beforeEach(() => { TestBed.configureCompiler({useJit: false}); }); it('should set the value into CompilerConfig', inject([CompilerConfig], (config: CompilerConfig) => { expect(config.useJit).toBe(false); @@ -451,49 +501,55 @@ export function main() { beforeEach(() => addProviders([{provide: FancyService, useValue: new FancyService()}])); }) .toThrowError( - 'addProviders can\'t be called after the injector has been already created for this test. ' + - 'This is most likely because you\'ve already used the injector to inject a beforeEach or the ' + - 'current `it` function.'); + `Cannot configure the test module when the test module has already been instantiated. ` + + `Make sure you are not using \`inject\` before \`TestBed.configureTestingModule\`.`); restoreJasmineBeforeEach(); }); }); }); - describe('entryComponents', () => { + describe('components', () => { let xhrGet: jasmine.Spy; beforeEach(() => { xhrGet = jasmine.createSpy('xhrGet').and.returnValue(Promise.resolve('Hello world!')); - configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); + TestBed.configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); }); - it('should report an error for entryComponents components with templateUrl which never call doAsyncEntryComponents', + it('should report an error for declared components with templateUrl which never call TestBed.compileComponents', () => { var itPromise = patchJasmineIt(); expect( () => - it('should fail', - withModule(() => { - return { - declarations: [CompWithUrlTemplate], - entryComponents: [CompWithUrlTemplate] - }; - }) - .inject( - [ComponentFactoryResolver], - (resolver: ComponentFactoryResolver) => { - expect(resolver.resolveComponentFactory(CompWithUrlTemplate) - .componentType) - .toBe(CompWithUrlTemplate); - }))) + it('should fail', withModule( + {declarations: [CompWithUrlTemplate]}, + () => { TestBed.createComponent(CompWithUrlTemplate); }))) .toThrowError( - `This test module uses the entryComponents ${stringify(CompWithUrlTemplate)} which is using a "templateUrl", but they were never compiled. ` + - `Please call "doAsyncEntryPointCompilation" before "inject".`); + `This test module uses the component ${stringify(CompWithUrlTemplate)} which is using a "templateUrl", but they were never compiled. ` + + `Please call "TestBed.compileComponents" before your test.`); restoreJasmineIt(); }); }); + + it('should error on unknown bound properties on custom elements by default', () => { + @Component({template: ''}) + class ComponentUsingInvalidProperty { + } + + var itPromise = patchJasmineIt(); + + expect( + () => + it('should fail', + withModule( + {declarations: [ComponentUsingInvalidProperty]}, + () => { TestBed.createComponent(ComponentUsingInvalidProperty); }))) + .toThrowError(/Can't bind to 'someUnknownProp'/); + + restoreJasmineIt(); + }); }); describe('test component builder', function() { diff --git a/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts b/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts index d967f5354d..32d7396e46 100644 --- a/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts +++ b/modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts @@ -8,7 +8,7 @@ import {AsyncTestCompleter, inject, ddescribe, describe, it, iit, beforeEach, beforeEachProviders,} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; -import {TestBed, TestComponentBuilder, configureModule} from '@angular/core/testing'; +import {TestBed, TestComponentBuilder} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {Injector, ViewMetadata, Component, Injectable, ComponentRef, ReflectiveInjector, getPlatform} from '@angular/core'; import {NgIf} from '@angular/common'; @@ -67,19 +67,18 @@ export function main() { var testUiInjector = new TestBed(); testUiInjector.platform = platformBrowserDynamicTesting(); testUiInjector.ngModule = BrowserTestingModule; - testUiInjector.configureModule({ + testUiInjector.configureTestingModule({ providers: [ Serializer, {provide: RenderStore, useValue: uiRenderStore}, {provide: DomRootRenderer, useClass: DomRootRenderer_}, {provide: RootRenderer, useExisting: DomRootRenderer} ] }); - testUiInjector.initTestModule(); var uiSerializer = testUiInjector.get(Serializer); var domRootRenderer = testUiInjector.get(DomRootRenderer); workerRenderStore = new RenderStore(); - configureModule({ + TestBed.configureTestingModule({ providers: [ Serializer, {provide: RenderStore, useValue: workerRenderStore}, { provide: RootRenderer, diff --git a/modules/@angular/platform-server/testing/server.ts b/modules/@angular/platform-server/testing/server.ts index 79cad85720..9526e3aa4c 100644 --- a/modules/@angular/platform-server/testing/server.ts +++ b/modules/@angular/platform-server/testing/server.ts @@ -9,7 +9,7 @@ import {analyzeAppProvidersForDeprecatedConfiguration} from '@angular/compiler'; import {platformCoreDynamicTesting} from '@angular/compiler/testing'; import {CompilerFactory, CompilerOptions, NgModule, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, createPlatformFactory, getPlatform} from '@angular/core'; -import {initTestEnvironment} from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; import {BrowserDynamicTestingModule, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; import {Console} from '../core_private'; @@ -63,7 +63,7 @@ export const TEST_SERVER_PLATFORM_PROVIDERS: Array console.warn(msg)); }]; diff --git a/modules/@angular/router/karma-test-shim.js b/modules/@angular/router/karma-test-shim.js index e4b03e2137..75a2e5ddff 100644 --- a/modules/@angular/router/karma-test-shim.js +++ b/modules/@angular/router/karma-test-shim.js @@ -72,7 +72,7 @@ Promise.all([ var testing = providers[0]; var testingBrowser = providers[1]; - testing.initTestEnvironment( + testing.TestBed.initTestEnvironment( testingBrowser.BrowserDynamicTestingModule, testingBrowser.platformBrowserDynamicTesting()); diff --git a/modules/@angular/router/test/router.spec.ts b/modules/@angular/router/test/router.spec.ts index 073a560c66..7de6bd870a 100644 --- a/modules/@angular/router/test/router.spec.ts +++ b/modules/@angular/router/test/router.spec.ts @@ -10,7 +10,7 @@ import 'rxjs/add/operator/map'; import {Location} from '@angular/common'; import {Component, NgModule, NgModuleFactoryLoader} from '@angular/core'; -import {ComponentFixture, TestComponentBuilder, addProviders, configureModule, fakeAsync, inject, tick} from '@angular/core/testing'; +import {ComponentFixture, TestBed, TestComponentBuilder, addProviders, fakeAsync, inject, tick} from '@angular/core/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; import {Observable} from 'rxjs/Observable'; import {of } from 'rxjs/observable/of'; @@ -20,7 +20,7 @@ import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing'; describe('Integration', () => { beforeEach(() => { - configureModule({ + TestBed.configureTestingModule({ imports: [RouterTestingModule], providers: [provideRoutes( [{path: '', component: BlankCmp}, {path: 'simple', component: SimpleCmp}])], diff --git a/test-main.js b/test-main.js index 896db2c67c..592f7711cd 100644 --- a/test-main.js +++ b/test-main.js @@ -75,7 +75,7 @@ System.import('@angular/core/testing') .then(function(coreTesting){ return System.import('@angular/platform-browser-dynamic/testing') .then(function(browserTesting) { - coreTesting.initTestEnvironment( + coreTesting.TestBed.initTestEnvironment( browserTesting.BrowserDynamicTestingModule, browserTesting.platformBrowserDynamicTesting()); }); diff --git a/tools/cjs-jasmine/test-cjs-main.ts b/tools/cjs-jasmine/test-cjs-main.ts index a710b4955b..60cab6b52d 100644 --- a/tools/cjs-jasmine/test-cjs-main.ts +++ b/tools/cjs-jasmine/test-cjs-main.ts @@ -1,5 +1,5 @@ var testingPlatformServer = require('../../all/@angular/platform-server/testing/server.js'); var testing = require('../../all/@angular/core/testing'); -testing.initTestEnvironment( +testing.TestBed.initTestEnvironment( testingPlatformServer.ServerTestingModule, testingPlatformServer.serverTestingPlatform());