Skip to content

Commit d0a95e3

Browse files
committed
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 angular#10354
1 parent acc6c8d commit d0a95e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1077
-488
lines changed

modules/@angular/common/test/forms-deprecated/integration_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {NgFor, NgIf} from '@angular/common';
1010
import {Control, ControlGroup, ControlValueAccessor, DeprecatedFormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, RadioButtonState, Validator, Validators} from '@angular/common/src/forms-deprecated';
1111
import {Component, Directive, EventEmitter, Input, Output, Provider, forwardRef} from '@angular/core';
12-
import {ComponentFixture, TestComponentBuilder, configureModule, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
12+
import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
1313
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
1414
import {By} from '@angular/platform-browser/src/dom/debug/by';
1515
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@@ -22,7 +22,7 @@ import {PromiseWrapper} from '../../src/facade/promise';
2222
export function main() {
2323
describe('integration tests', () => {
2424

25-
beforeEach(() => configureModule({imports: [DeprecatedFormsModule]}));
25+
beforeEach(() => TestBed.configureTestingModule({imports: [DeprecatedFormsModule]}));
2626

2727
it('should initialize DOM elements with the given form object',
2828
inject(
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {__core_private_testing__ as r, __core_private_testing_types__ as t} from '@angular/core/testing';
10+
11+
export type TestingCompiler = t.TestingCompiler;
12+
export var TestingCompiler: typeof t.TestingCompiler = r.TestingCompiler;
13+
14+
export type TestingCompilerFactory = t.TestingCompilerFactory;
15+
export var TestingCompilerFactory: typeof t.TestingCompilerFactory = r.TestingCompilerFactory;

modules/@angular/compiler/src/runtime_compiler.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, NgModule, NgModuleFactory, NgModuleMetadata, OptionalMetadata, Provider, SchemaMetadata, SkipSelfMetadata} from '@angular/core';
9+
import {Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleMetadata, OptionalMetadata, Provider, SchemaMetadata, SkipSelfMetadata} from '@angular/core';
1010

1111
import {Console} from '../core_private';
1212
import {BaseException} from '../src/facade/exceptions';
@@ -44,13 +44,13 @@ export class RuntimeCompiler implements Compiler {
4444
private _compiledNgModuleCache = new Map<Type, NgModuleFactory<any>>();
4545

4646
constructor(
47-
private __injector: Injector, private _metadataResolver: CompileMetadataResolver,
47+
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
4848
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
4949
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
5050
private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig,
5151
private _console: Console) {}
5252

53-
get _injector(): Injector { return this.__injector; }
53+
get injector(): Injector { return this._injector; }
5454

5555
compileModuleSync<T>(moduleType: ConcreteType<T>): NgModuleFactory<T> {
5656
return this._compileModuleAndComponents(moduleType, true).syncResult;
@@ -60,6 +60,16 @@ export class RuntimeCompiler implements Compiler {
6060
return this._compileModuleAndComponents(moduleType, false).asyncResult;
6161
}
6262

63+
compileModuleAndAllComponentsSync<T>(moduleType: ConcreteType<T>):
64+
ModuleWithComponentFactories<T> {
65+
return this._compileModuleAndAllComponents(moduleType, true).syncResult;
66+
}
67+
68+
compileModuleAndAllComponentsAsync<T>(moduleType: ConcreteType<T>):
69+
Promise<ModuleWithComponentFactories<T>> {
70+
return this._compileModuleAndAllComponents(moduleType, false).asyncResult;
71+
}
72+
6373
compileComponentAsync<T>(compType: ConcreteType<T>, ngModule: ConcreteType<any> = null):
6474
Promise<ComponentFactory<T>> {
6575
if (!ngModule) {
@@ -85,6 +95,34 @@ export class RuntimeCompiler implements Compiler {
8595
return new SyncAsyncResult(ngModuleFactory, componentPromise.then(() => ngModuleFactory));
8696
}
8797

98+
private _compileModuleAndAllComponents<T>(moduleType: ConcreteType<T>, isSync: boolean):
99+
SyncAsyncResult<ModuleWithComponentFactories<T>> {
100+
const componentPromise = this._compileComponents(moduleType, isSync);
101+
const ngModuleFactory = this._compileModule(moduleType);
102+
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
103+
const componentFactories: ComponentFactory<any>[] = [];
104+
const templates = new Set<CompiledTemplate>();
105+
moduleMeta.transitiveModule.modules.forEach((moduleMeta) => {
106+
moduleMeta.declaredDirectives.forEach((dirMeta) => {
107+
if (dirMeta.isComponent) {
108+
const template = this._createCompiledHostTemplate(dirMeta.type.runtime);
109+
templates.add(template);
110+
componentFactories.push(template.proxyComponentFactory);
111+
}
112+
});
113+
});
114+
const syncResult = new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
115+
// Note: host components themselves can always be compiled synchronously as they have an
116+
// inline template. However, we still need to wait for the components that they
117+
// reference to be loaded / compiled.
118+
const compile = () => {
119+
templates.forEach((template) => { this._compileTemplate(template); });
120+
return syncResult;
121+
};
122+
const asyncResult = isSync ? Promise.resolve(compile()) : componentPromise.then(compile);
123+
return new SyncAsyncResult(syncResult, asyncResult);
124+
}
125+
88126
private _compileModule<T>(moduleType: ConcreteType<T>): NgModuleFactory<T> {
89127
let ngModuleFactory = this._compiledNgModuleCache.get(moduleType);
90128
if (!ngModuleFactory) {
@@ -381,7 +419,7 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver {
381419
private _delegate: RuntimeCompiler, private _ngModule: ConcreteType<any>,
382420
private _parentComponentResolver: ComponentResolver, private _console: Console) {}
383421

384-
get _injector(): Injector { return this._delegate._injector; }
422+
get _injector(): Injector { return this._delegate.injector; }
385423

386424
resolveComponent(component: Type|string): Promise<ComponentFactory<any>> {
387425
if (isString(component)) {
@@ -416,6 +454,15 @@ class ModuleBoundCompiler implements Compiler, ComponentResolver {
416454
compileModuleAsync<T>(moduleType: ConcreteType<T>): Promise<NgModuleFactory<T>> {
417455
return this._delegate.compileModuleAsync(moduleType);
418456
}
457+
compileModuleAndAllComponentsSync<T>(moduleType: ConcreteType<T>):
458+
ModuleWithComponentFactories<T> {
459+
return this._delegate.compileModuleAndAllComponentsSync(moduleType);
460+
}
461+
462+
compileModuleAndAllComponentsAsync<T>(moduleType: ConcreteType<T>):
463+
Promise<ModuleWithComponentFactories<T>> {
464+
return this._delegate.compileModuleAndAllComponentsAsync(moduleType);
465+
}
419466

420467
/**
421468
* Clears all caches

modules/@angular/compiler/test/directive_normalizer_spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
1212
import {XHR} from '@angular/compiler/src/xhr';
1313
import {MockXHR} from '@angular/compiler/testing/xhr_mock';
1414
import {ViewEncapsulation} from '@angular/core/src/metadata/view';
15-
import {configureCompiler} from '@angular/core/testing';
15+
import {TestBed} from '@angular/core/testing';
1616
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
1717

1818
import {SpyXHR} from './spies';
@@ -23,7 +23,7 @@ export function main() {
2323
var dirType: CompileTypeMetadata;
2424
var dirTypeWithHttpUrl: CompileTypeMetadata;
2525

26-
beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
26+
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
2727

2828
beforeEach(() => {
2929
dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'});
@@ -179,7 +179,8 @@ export function main() {
179179

180180
describe('normalizeExternalStylesheets', () => {
181181

182-
beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); });
182+
beforeEach(
183+
() => { TestBed.configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); });
183184

184185
it('should load an external stylesheet',
185186
inject(

modules/@angular/compiler/test/directive_resolver_mock_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function main() {
2727
expect(ngModule.selector).toEqual('cmp');
2828
});
2929

30-
it('should allow overriding the @NgModule', () => {
30+
it('should allow overriding the @Directive', () => {
3131
dirResolver.setDirective(
3232
SomeComponent, new ComponentMetadata({selector: 'someOtherSelector'}));
3333
var metadata = dirResolver.resolve(SomeComponent);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {expect} from '@angular/platform-browser/testing/matchers';
10+
import {MetadataOverrider} from '../testing/metadata_overrider';
11+
12+
interface SomeMetadataType {
13+
plainProp?: string;
14+
getterProp?: string;
15+
arrayProp?: any[];
16+
}
17+
18+
class SomeMetadata implements SomeMetadataType {
19+
plainProp: string;
20+
private _getterProp: string;
21+
get getterProp(): string { return this._getterProp; }
22+
arrayProp: any[];
23+
24+
constructor(options: SomeMetadataType) {
25+
this.plainProp = options.plainProp;
26+
this._getterProp = options.getterProp;
27+
this.arrayProp = options.arrayProp;
28+
}
29+
}
30+
31+
export function main() {
32+
describe('metadata overrider', () => {
33+
let overrider: MetadataOverrider;
34+
35+
beforeEach(() => { overrider = new MetadataOverrider(); });
36+
37+
it('should return a new instance with the same values', () => {
38+
const oldInstance = new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someInput'});
39+
const newInstance = overrider.overrideMetadata(SomeMetadata, oldInstance, {});
40+
expect(newInstance).not.toBe(oldInstance);
41+
expect(newInstance).toBeAnInstanceOf(SomeMetadata);
42+
expect(newInstance).toEqual(oldInstance);
43+
});
44+
45+
it('should set individual properties and keep others', () => {
46+
const oldInstance =
47+
new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'});
48+
const newInstance =
49+
overrider.overrideMetadata(SomeMetadata, oldInstance, {set: {plainProp: 'newPlainProp'}});
50+
expect(newInstance)
51+
.toEqual(new SomeMetadata({plainProp: 'newPlainProp', getterProp: 'someGetterProp'}));
52+
});
53+
54+
describe('add properties', () => {
55+
it('should replace non array values', () => {
56+
const oldInstance =
57+
new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'});
58+
const newInstance = overrider.overrideMetadata(
59+
SomeMetadata, oldInstance, {add: {plainProp: 'newPlainProp'}});
60+
expect(newInstance)
61+
.toEqual(new SomeMetadata({plainProp: 'newPlainProp', getterProp: 'someGetterProp'}));
62+
});
63+
64+
it('should add to array values', () => {
65+
const oldInstance = new SomeMetadata({arrayProp: ['a']});
66+
const newInstance =
67+
overrider.overrideMetadata(SomeMetadata, oldInstance, {add: {arrayProp: ['b']}});
68+
expect(newInstance).toEqual(new SomeMetadata({arrayProp: ['a', 'b']}));
69+
});
70+
});
71+
72+
describe('remove', () => {
73+
it('should set values to undefined if their value matches', () => {
74+
const oldInstance =
75+
new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'});
76+
const newInstance = overrider.overrideMetadata(
77+
SomeMetadata, oldInstance, {remove: {plainProp: 'somePlainProp'}});
78+
expect(newInstance)
79+
.toEqual(new SomeMetadata({plainProp: undefined, getterProp: 'someGetterProp'}));
80+
});
81+
82+
it('should leave values if their value does not match', () => {
83+
const oldInstance =
84+
new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'});
85+
const newInstance = overrider.overrideMetadata(
86+
SomeMetadata, oldInstance, {remove: {plainProp: 'newPlainProp'}});
87+
expect(newInstance)
88+
.toEqual(new SomeMetadata({plainProp: 'somePlainProp', getterProp: 'someGetterProp'}));
89+
});
90+
91+
it('should remove a value from an array', () => {
92+
const oldInstance =
93+
new SomeMetadata({arrayProp: ['a', 'b', 'c'], getterProp: 'someGetterProp'});
94+
const newInstance = overrider.overrideMetadata(
95+
SomeMetadata, oldInstance, {remove: {arrayProp: ['a', 'c']}});
96+
expect(newInstance)
97+
.toEqual(new SomeMetadata({arrayProp: ['b'], getterProp: 'someGetterProp'}));
98+
});
99+
100+
it('should support types as values', () => {
101+
class Class1 {}
102+
class Class2 {}
103+
class Class3 {}
104+
105+
const instance1 = new SomeMetadata({arrayProp: [Class1, Class2, Class3]});
106+
const instance2 =
107+
overrider.overrideMetadata(SomeMetadata, instance1, {remove: {arrayProp: [Class1]}});
108+
expect(instance2).toEqual(new SomeMetadata({arrayProp: [Class2, Class3]}));
109+
const instance3 =
110+
overrider.overrideMetadata(SomeMetadata, instance2, {remove: {arrayProp: [Class3]}});
111+
expect(instance3).toEqual(new SomeMetadata({arrayProp: [Class2]}));
112+
113+
});
114+
});
115+
});
116+
}

modules/@angular/compiler/test/metadata_resolver_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {CompilerConfig} from '@angular/compiler/src/config';
1010
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation} from '@angular/core';
1111
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
12-
import {configureCompiler} from '@angular/core/testing';
12+
import {TestBed} from '@angular/core/testing';
1313
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
1414

1515
import {CompileNgModuleMetadata} from '../src/compile_metadata';
@@ -21,7 +21,7 @@ import {TEST_COMPILER_PROVIDERS} from './test_bindings';
2121

2222
export function main() {
2323
describe('CompileMetadataResolver', () => {
24-
beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
24+
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
2525

2626
describe('getDirectiveMetadata', () => {
2727
it('should read metadata',

modules/@angular/compiler/test/runtime_compiler_spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {beforeEach, ddescribe, xdescribe, describe, iit, inject, beforeEachProvi
1010
import {expect} from '@angular/platform-browser/testing/matchers';
1111
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, NgModule, NgModuleFactory} from '@angular/core';
1212
import {ConcreteType, stringify} from '../src/facade/lang';
13-
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing';
13+
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, TestBed} from '@angular/core/testing';
1414
import {XHR, DirectiveResolver} from '@angular/compiler';
1515
import {MockDirectiveResolver} from '@angular/compiler/testing';
1616

@@ -36,7 +36,8 @@ export function main() {
3636
let dirResolver: MockDirectiveResolver;
3737
let injector: Injector;
3838

39-
beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); });
39+
beforeEach(
40+
() => { TestBed.configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); });
4041

4142
beforeEach(inject(
4243
[Compiler, TestComponentBuilder, XHR, DirectiveResolver, Injector],

modules/@angular/compiler/test/template_parser_spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {TEMPLATE_TRANSFORMS, TemplateParser, splitClasses} from '@angular/compil
1414
import {MockSchemaRegistry} from '@angular/compiler/testing';
1515
import {SchemaMetadata, SecurityContext} from '@angular/core';
1616
import {Console} from '@angular/core/src/console';
17-
import {configureCompiler} from '@angular/core/testing';
17+
import {TestBed} from '@angular/core/testing';
1818
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
1919

2020
import {Identifiers, identifierToken} from '../src/identifiers';
@@ -40,7 +40,7 @@ export function main() {
4040
function commonBeforeEach() {
4141
beforeEach(() => {
4242
console = new ArrayConsole();
43-
configureCompiler({providers: [{provide: Console, useValue: console}]});
43+
TestBed.configureCompiler({providers: [{provide: Console, useValue: console}]});
4444
});
4545
beforeEach(inject([TemplateParser], (parser: TemplateParser) => {
4646
var component = CompileDirectiveMetadata.create({
@@ -66,10 +66,10 @@ export function main() {
6666
}
6767

6868
describe('TemplateParser template transform', () => {
69-
beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
69+
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
7070

7171
beforeEach(() => {
72-
configureCompiler({
72+
TestBed.configureCompiler({
7373
providers:
7474
[{provide: TEMPLATE_TRANSFORMS, useValue: new FooAstTransformer(), multi: true}]
7575
});
@@ -84,7 +84,7 @@ export function main() {
8484

8585
describe('multiple', () => {
8686
beforeEach(() => {
87-
configureCompiler({
87+
TestBed.configureCompiler({
8888
providers:
8989
[{provide: TEMPLATE_TRANSFORMS, useValue: new BarAstTransformer(), multi: true}]
9090
});
@@ -101,7 +101,7 @@ export function main() {
101101
// Semi-integration test to make sure TemplateParser properly sets the security context.
102102
// Uses the actual DomElementSchemaRegistry.
103103
beforeEach(() => {
104-
configureCompiler({
104+
TestBed.configureCompiler({
105105
providers: [
106106
TEST_COMPILER_PROVIDERS,
107107
{provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry}
@@ -138,7 +138,7 @@ export function main() {
138138

139139
describe('TemplateParser', () => {
140140
beforeEach(() => {
141-
configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]});
141+
TestBed.configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]});
142142
});
143143

144144
commonBeforeEach();

0 commit comments

Comments
 (0)