Permalink
Browse files

refactor(testing): introduce new testing api to support ng modules

BREAKING CHANGE:
- deprecations:
  * `withProviders`, use `TestBed.withModule` instead
  * `addProviders`, use `TestBed.configureTestingModule` instead
  * `TestComponentBuilder`, use `TestBed.configureTestModule` / `TestBed.override...` / `TestBed.createComponent` instead.

Closes #10354
  • Loading branch information...
tbosch committed Jul 28, 2016
1 parent acc6c8d commit d0a95e35af943258c360486ccb2f60d11a36e97d
Showing with 1,077 additions and 488 deletions.
  1. +2 −2 modules/@angular/common/test/forms-deprecated/integration_spec.ts
  2. +15 −0 modules/@angular/compiler/core_private_testing.ts
  3. +51 −4 modules/@angular/compiler/src/runtime_compiler.ts
  4. +4 −3 modules/@angular/compiler/test/directive_normalizer_spec.ts
  5. +1 −1 modules/@angular/compiler/test/directive_resolver_mock_spec.ts
  6. +116 −0 modules/@angular/compiler/test/metadata_overrider_spec.ts
  7. +2 −2 modules/@angular/compiler/test/metadata_resolver_spec.ts
  8. +3 −2 modules/@angular/compiler/test/runtime_compiler_spec.ts
  9. +7 −7 modules/@angular/compiler/test/template_parser_spec.ts
  10. +0 −21 modules/@angular/compiler/test/test_component_builder_spec.ts
  11. +90 −12 modules/@angular/compiler/testing.ts
  12. +2 −1 modules/@angular/compiler/testing/directive_resolver_mock.ts
  13. +131 −0 modules/@angular/compiler/testing/metadata_overrider.ts
  14. +1 −1 modules/@angular/compiler/testing/pipe_resolver_mock.ts
  15. +8 −7 modules/@angular/compiler/testing/test_component_builder.ts
  16. +21 −0 modules/@angular/core/private_export_testing.ts
  17. +0 −5 modules/@angular/core/src/application_module.ts
  18. +1 −1 modules/@angular/core/src/linker.ts
  19. +44 −23 modules/@angular/core/src/linker/compiler.ts
  20. +4 −3 modules/@angular/core/test/animation/animation_integration_spec.ts
  21. +3 −3 modules/@angular/core/test/linker/change_detection_integration_spec.ts
  22. +4 −3 modules/@angular/core/test/linker/entry_components_integration_spec.ts
  23. +4 −3 modules/@angular/core/test/linker/integration_spec.ts
  24. +2 −2 modules/@angular/core/test/linker/ng_container_integration_spec.ts
  25. +3 −2 modules/@angular/core/test/linker/ng_module_integration_spec.ts
  26. +3 −3 modules/@angular/core/test/linker/regression_integration_spec.ts
  27. +2 −2 modules/@angular/core/test/linker/security_integration_spec.ts
  28. +3 −0 modules/@angular/core/testing.ts
  29. +18 −0 modules/@angular/core/testing/metadata_override.ts
  30. +297 −172 modules/@angular/core/testing/test_bed.ts
  31. +45 −0 modules/@angular/core/testing/test_compiler.ts
  32. +10 −31 modules/@angular/core/testing/test_component_builder.ts
  33. +5 −55 modules/@angular/core/testing/testing.ts
  34. +3 −3 modules/@angular/core/testing/testing_internal.ts
  35. +4 −2 modules/@angular/forms/test/reactive_integration_spec.ts
  36. +3 −2 modules/@angular/forms/test/template_integration_spec.ts
  37. +2 −2 modules/@angular/platform-browser-dynamic/test/xhr/xhr_cache_spec.ts
  38. +2 −2 modules/@angular/platform-browser-dynamic/testing.ts
  39. +151 −95 modules/@angular/platform-browser/test/testing_public_spec.ts
  40. +3 −4 modules/@angular/platform-browser/test/web_workers/worker/renderer_integration_spec.ts
  41. +2 −2 modules/@angular/platform-server/testing/server.ts
  42. +1 −1 modules/@angular/router/karma-test-shim.js
  43. +2 −2 modules/@angular/router/test/router.spec.ts
  44. +1 −1 test-main.js
  45. +1 −1 tools/cjs-jasmine/test-cjs-main.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(
@@ -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;
@@ -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<Type, NgModuleFactory<any>>();
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<T>(moduleType: ConcreteType<T>): NgModuleFactory<T> {
return this._compileModuleAndComponents(moduleType, true).syncResult;
@@ -60,6 +60,16 @@ export class RuntimeCompiler implements Compiler {
return this._compileModuleAndComponents(moduleType, false).asyncResult;
}
+ compileModuleAndAllComponentsSync<T>(moduleType: ConcreteType<T>):
+ ModuleWithComponentFactories<T> {
+ return this._compileModuleAndAllComponents(moduleType, true).syncResult;
+ }
+
+ compileModuleAndAllComponentsAsync<T>(moduleType: ConcreteType<T>):
+ Promise<ModuleWithComponentFactories<T>> {
+ return this._compileModuleAndAllComponents(moduleType, false).asyncResult;
+ }
+
compileComponentAsync<T>(compType: ConcreteType<T>, ngModule: ConcreteType<any> = null):
Promise<ComponentFactory<T>> {
if (!ngModule) {
@@ -85,6 +95,34 @@ export class RuntimeCompiler implements Compiler {
return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory));
}
+ private _compileModuleAndAllComponents<T>(moduleType: ConcreteType<T>, isSync: boolean):
+ SyncAsyncResult<ModuleWithComponentFactories<T>> {
+ const componentPromise = this._compileComponents(moduleType, isSync);
+ const ngModuleFactory = this._compileModule(moduleType);
+ const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
+ const componentFactories: ComponentFactory<any>[] = [];
+ const templates = new Set<CompiledTemplate>();
+ 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<T>(moduleType: ConcreteType<T>): NgModuleFactory<T> {
let ngModuleFactory = this._compiledNgModuleCache.get(moduleType);
if (!ngModuleFactory) {
@@ -381,7 +419,7 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver {
private _delegate: RuntimeCompiler, private _ngModule: ConcreteType<any>,
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<ComponentFactory<any>> {
if (isString(component)) {
@@ -416,6 +454,15 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver {
compileModuleAsync<T>(moduleType: ConcreteType<T>): Promise<NgModuleFactory<T>> {
return this._delegate.compileModuleAsync(moduleType);
}
+ compileModuleAndAllComponentsSync<T>(moduleType: ConcreteType<T>):
+ ModuleWithComponentFactories<T> {
+ return this._delegate.compileModuleAndAllComponentsSync(moduleType);
+ }
+
+ compileModuleAndAllComponentsAsync<T>(moduleType: ConcreteType<T>):
+ Promise<ModuleWithComponentFactories<T>> {
+ return this._delegate.compileModuleAndAllComponentsAsync(moduleType);
+ }
/**
* Clears all caches
@@ -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(
@@ -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);
@@ -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]}));
+
+ });
+ });
+ });
+}
@@ -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',
@@ -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],
@@ -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();
Oops, something went wrong.

0 comments on commit d0a95e3

Please sign in to comment.