From 8d746e3f6795c8840116f4649a37a1efb4243eb9 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Mon, 4 Jul 2016 09:37:30 -0700 Subject: [PATCH] feat(testing): add implicit test module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every test now has an implicit module. It can be configured via `configureModule` (from @angular/core/testing) to add providers, directives, pipes, ... The compiler now has to be configured separately via `configureCompiler` (from @angular/core/testing) to add providers or define whether to use jit. BREAKING CHANGE: - Application providers can no longer inject compiler internals (i.e. everything from `@angular/compiler). Inject `Compiler` instead. This reflects the changes to `bootstrap` for module support (3f55aa609f60f130f1d69188ed057214b1267cb3). - Compiler providers can no longer be added via `addProviders` / `withProviders`. Use the new method `configureCompiler` instead. - Platform directives / pipes need to be provided via `configureModule` and can no longer be provided via the `PLATFORM_PIPES` / `PLATFORM_DIRECTIVES` tokens. - `setBaseTestProviders()` was renamed into `initTestEnvironment` and now takes a `PlatformRef` and a factory for a `Compiler`. - E.g. for the browser platform: BEFORE: ``` import {setBaseTestProviders} from ‘@angular/core/testing’; import {TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from ‘@angular/platform-browser-dynamic/testing’; setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS); ``` AFTER: ``` import {setBaseTestProviders} from ‘@angular/core/testing’; import {browserTestCompiler, browserDynamicTestPlatform, BrowserDynamicTestModule} from ‘@angular/platform-browser-dynamic/testing’; initTestEnvironment( browserTestCompiler, browserDynamicTestPlatform(), BrowserDynamicTestModule); ``` - E.g. for the server platform: BEFORE: ``` import {setBaseTestProviders} from ‘@angular/core/testing’; import {TEST_SERVER_PLATFORM_PROVIDERS, TEST_SERVER_APPLICATION_PROVIDERS} from ‘@angular/platform-server/testing/server’; setBaseTestProviders(TEST_SERVER_PLATFORM_PROVIDERS, TEST_SERVER_APPLICATION_PROVIDERS); ``` AFTER: ``` import {setBaseTestProviders} from ‘@angular/core/testing’; import {serverTestCompiler, serverTestPlatform, ServerTestModule} from ‘@angular/platform-browser-dynamic/testing’; initTestEnvironment( serverTestCompiler, serverTestPlatform(), ServerTestModule); ``` Related to #9726 Closes #9846 --- .../@angular/compiler/src/runtime_compiler.ts | 42 ++- .../test/directive_normalizer_spec.ts | 7 +- .../compiler/test/metadata_resolver_spec.ts | 19 +- .../compiler/test/runtime_compiler_spec.ts | 4 +- .../compiler/test/template_parser_spec.ts | 40 ++- .../@angular/compiler/test/test_bindings.ts | 2 +- .../testing/test_component_builder.ts | 8 +- modules/@angular/core/src/linker.ts | 2 +- modules/@angular/core/src/linker/compiler.ts | 24 +- .../animation/animation_integration_spec.ts | 13 +- .../linker/app_module_integration_spec.ts | 28 +- .../change_detection_integration_spec.ts | 18 +- .../core/test/linker/integration_spec.ts | 27 +- .../linker/ng_container_integration_spec.ts | 12 +- .../reflector_component_resolver_spec.ts | 40 +-- .../linker/regression_integration_spec.ts | 11 +- .../test/linker/security_integration_spec.ts | 11 +- .../@angular/core/testing/test_injector.ts | 239 +++++++++--- modules/@angular/core/testing/testing.ts | 44 ++- .../@angular/core/testing/testing_internal.ts | 5 +- .../test/xhr/xhr_cache_spec.ts | 19 +- .../platform-browser-dynamic/testing.ts | 68 ++-- .../test/testing_public_spec.ts | 191 +++++++++- .../worker/renderer_integration_spec.ts | 340 +++++++++--------- .../platform-browser/testing/browser.ts | 59 +-- .../platform-server/test/integration_spec.ts | 1 + .../platform-server/testing/server.ts | 84 ++--- .../test/integration/bootstrap_spec.ts | 4 + .../test/route_config/route_config_spec.ts | 5 +- modules/@angular/router/karma-test-shim.js | 6 +- modules/@angular/upgrade/test/upgrade_spec.ts | 7 +- test-main.js | 10 +- tools/cjs-jasmine/test-cjs-main.ts | 6 +- tools/public_api_guard/core/index.d.ts | 7 + tools/public_api_guard/core/testing.d.ts | 66 +++- .../platform-browser-dynamic/testing.d.ts | 11 +- .../platform-browser/testing.d.ts | 7 +- .../platform-server/testing.d.ts | 10 +- 38 files changed, 1000 insertions(+), 497 deletions(-) diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index 2eedc118e0..5ff7f3f308 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 {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, Injectable, Provider} from '@angular/core'; +import {AppModuleFactory, AppModuleMetadata, Compiler, ComponentFactory, ComponentResolver, ComponentStillLoadingError, Injectable, Injector, OptionalMetadata, Provider, SkipSelfMetadata} from '@angular/core'; import {BaseException} from '../src/facade/exceptions'; import {ConcreteType, IS_DART, Type, isBlank, isString, stringify} from '../src/facade/lang'; @@ -43,11 +43,13 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { private _compiledAppModuleCache = new Map>(); constructor( - private _metadataResolver: CompileMetadataResolver, + private _injector: Injector, private _metadataResolver: CompileMetadataResolver, private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _appModuleCompiler: AppModuleCompiler, private _genConfig: CompilerConfig) {} + get injector(): Injector { return this._injector; } + resolveComponent(component: Type|string): Promise> { if (isString(component)) { return PromiseWrapper.reject( @@ -76,12 +78,15 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { let componentCompilePromises: Promise[] = []; if (!appModuleFactory || !useCache) { var compileModuleMeta = this._metadataResolver.getAppModuleMetadata(moduleType, metadata); - let boundCompiler = new BoundCompiler( + let boundCompilerFactory = (parentResolver: ComponentResolver) => new BoundCompiler( this, compileModuleMeta.directives.map(dir => dir.type.runtime), - compileModuleMeta.pipes.map((pipe) => pipe.type.runtime)); - // Always provide a bound Compiler / ComponentResolver - compileModuleMeta.providers.push(this._metadataResolver.getProviderMetadata( - new Provider(Compiler, {useValue: boundCompiler}))); + compileModuleMeta.pipes.map((pipe) => pipe.type.runtime), parentResolver); + // Always provide a bound Compiler and ComponentResolver + compileModuleMeta.providers.push( + this._metadataResolver.getProviderMetadata(new Provider(Compiler, { + useFactory: boundCompilerFactory, + deps: [[new OptionalMetadata(), new SkipSelfMetadata(), ComponentResolver]] + }))); compileModuleMeta.providers.push(this._metadataResolver.getProviderMetadata( new Provider(ComponentResolver, {useExisting: Compiler}))); var compileResult = this._appModuleCompiler.compile(compileModuleMeta); @@ -130,8 +135,7 @@ export class RuntimeCompiler implements ComponentResolver, Compiler { templates.forEach((template) => { if (template.loading) { if (isSync) { - throw new BaseException( - `Can't compile synchronously as ${template.compType.name} is still being loaded!`); + throw new ComponentStillLoadingError(template.compType.runtime); } else { loadingPromises.push(template.loading); } @@ -355,12 +359,19 @@ function assertComponent(meta: CompileDirectiveMetadata) { */ class BoundCompiler implements Compiler, ComponentResolver { constructor( - private _delegate: RuntimeCompiler, private _directives: any[], private _pipes: any[]) {} + private _delegate: RuntimeCompiler, private _directives: any[], private _pipes: any[], + private _parentComponentResolver: ComponentResolver) {} + + get injector(): Injector { return this._delegate.injector; } resolveComponent(component: Type|string): Promise> { if (isString(component)) { - return PromiseWrapper.reject( - new BaseException(`Cannot resolve component using '${component}'.`), null); + if (this._parentComponentResolver) { + return this._parentComponentResolver.resolveComponent(component); + } else { + return PromiseWrapper.reject( + new BaseException(`Cannot resolve component using '${component}'.`), null); + } } return this.compileComponentAsync(>component); } @@ -388,7 +399,12 @@ class BoundCompiler implements Compiler, ComponentResolver { /** * Clears all caches */ - clearCache(): void { this._delegate.clearCache(); } + clearCache(): void { + this._delegate.clearCache(); + if (this._parentComponentResolver) { + this._parentComponentResolver.clearCache(); + } + } /** * Clears the cache for the given component/appModule. diff --git a/modules/@angular/compiler/test/directive_normalizer_spec.ts b/modules/@angular/compiler/test/directive_normalizer_spec.ts index a1cc00ca70..1e1c41fa9c 100644 --- a/modules/@angular/compiler/test/directive_normalizer_spec.ts +++ b/modules/@angular/compiler/test/directive_normalizer_spec.ts @@ -12,18 +12,19 @@ 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 {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; import {SpyXHR} from './spies'; -import {TEST_PROVIDERS} from './test_bindings'; +import {TEST_COMPILER_PROVIDERS} from './test_bindings'; export function main() { describe('DirectiveNormalizer', () => { var dirType: CompileTypeMetadata; var dirTypeWithHttpUrl: CompileTypeMetadata; - beforeEachProviders(() => TEST_PROVIDERS); + beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); beforeEach(() => { dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'}); @@ -179,7 +180,7 @@ export function main() { describe('normalizeExternalStylesheets', () => { - beforeEachProviders(() => [{provide: XHR, useClass: SpyXHR}]); + beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); it('should load an external stylesheet', inject( diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index a3f0bf91c0..e7eaf97f39 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -9,17 +9,18 @@ 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 {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {IS_DART, stringify} from '../src/facade/lang'; import {CompileMetadataResolver} from '../src/metadata_resolver'; import {MalformedStylesComponent} from './metadata_resolver_fixture'; -import {TEST_PROVIDERS} from './test_bindings'; +import {TEST_COMPILER_PROVIDERS} from './test_bindings'; export function main() { describe('CompileMetadataResolver', () => { - beforeEachProviders(() => TEST_PROVIDERS); + beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); describe('getMetadata', () => { it('should read metadata', @@ -110,11 +111,15 @@ export function main() { })); describe('platform directives', () => { - beforeEachProviders(() => [{ - provide: CompilerConfig, - useValue: new CompilerConfig( - {genDebugInfo: true, platformDirectives: [ADirective]}) - }]); + beforeEach(() => { + configureCompiler({ + providers: [{ + provide: CompilerConfig, + useValue: + new CompilerConfig({genDebugInfo: true, platformDirectives: [ADirective]}) + }] + }); + }); it('should include platform directives when available', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { diff --git a/modules/@angular/compiler/test/runtime_compiler_spec.ts b/modules/@angular/compiler/test/runtime_compiler_spec.ts index af58f6b3de..39fb831094 100644 --- a/modules/@angular/compiler/test/runtime_compiler_spec.ts +++ b/modules/@angular/compiler/test/runtime_compiler_spec.ts @@ -1,7 +1,7 @@ import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector, AppModule, AppModuleMetadata, AppModuleFactory} from '@angular/core'; import {ConcreteType, stringify} from '../src/facade/lang'; -import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing'; +import {fakeAsync, tick, TestComponentBuilder, ComponentFixture, configureCompiler} from '@angular/core/testing'; import {XHR, ViewResolver} from '@angular/compiler'; import {MockViewResolver} from '@angular/compiler/testing'; @@ -31,7 +31,7 @@ export function main() { let viewResolver: MockViewResolver; let injector: Injector; - beforeEachProviders(() => [{provide: XHR, useValue: new SpyXHR()}]); + beforeEach(() => { configureCompiler({providers: [{provide: XHR, useClass: SpyXHR}]}); }); beforeEach(inject( [Compiler, TestComponentBuilder, XHR, ViewResolver, Injector], diff --git a/modules/@angular/compiler/test/template_parser_spec.ts b/modules/@angular/compiler/test/template_parser_spec.ts index 313c173830..cc585adb9c 100644 --- a/modules/@angular/compiler/test/template_parser_spec.ts +++ b/modules/@angular/compiler/test/template_parser_spec.ts @@ -14,13 +14,14 @@ import {TEMPLATE_TRANSFORMS, TemplateParser, splitClasses} from '@angular/compil import {MockSchemaRegistry} from '@angular/compiler/testing'; import {SecurityContext} from '@angular/core'; import {Console} from '@angular/core/src/console'; +import {configureCompiler} 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'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../src/interpolation_config'; import {unparse} from './expression_parser/unparser'; -import {TEST_PROVIDERS} from './test_bindings'; +import {TEST_COMPILER_PROVIDERS} from './test_bindings'; var someModuleUrl = 'package:someModule'; @@ -39,9 +40,9 @@ export function main() { var console: ArrayConsole; function commonBeforeEach() { - beforeEachProviders(() => { + beforeEach(() => { console = new ArrayConsole(); - return [{provide: Console, useValue: console}]; + configureCompiler({providers: [{provide: Console, useValue: console}]}); }); beforeEach(inject([TemplateParser], (parser: TemplateParser) => { var component = CompileDirectiveMetadata.create({ @@ -67,10 +68,14 @@ export function main() { } describe('TemplateParser template transform', () => { - beforeEachProviders(() => [TEST_PROVIDERS, MOCK_SCHEMA_REGISTRY]); + beforeEach(() => { configureCompiler({providers: TEST_COMPILER_PROVIDERS}); }); - beforeEachProviders( - () => [{provide: TEMPLATE_TRANSFORMS, useValue: new FooAstTransformer(), multi: true}]); + beforeEach(() => { + configureCompiler({ + providers: + [{provide: TEMPLATE_TRANSFORMS, useValue: new FooAstTransformer(), multi: true}] + }); + }); describe('single', () => { commonBeforeEach(); @@ -80,8 +85,12 @@ export function main() { }); describe('multiple', () => { - beforeEachProviders( - () => [{provide: TEMPLATE_TRANSFORMS, useValue: new BarAstTransformer(), multi: true}]); + beforeEach(() => { + configureCompiler({ + providers: + [{provide: TEMPLATE_TRANSFORMS, useValue: new BarAstTransformer(), multi: true}] + }); + }); commonBeforeEach(); it('should compose transformers', () => { @@ -93,9 +102,14 @@ export function main() { describe('TemplateParser Security', () => { // Semi-integration test to make sure TemplateParser properly sets the security context. // Uses the actual DomElementSchemaRegistry. - beforeEachProviders( - () => - [TEST_PROVIDERS, {provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry}]); + beforeEach(() => { + configureCompiler({ + providers: [ + TEST_COMPILER_PROVIDERS, + {provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry} + ] + }); + }); commonBeforeEach(); @@ -125,7 +139,9 @@ export function main() { }); describe('TemplateParser', () => { - beforeEachProviders(() => [TEST_PROVIDERS, MOCK_SCHEMA_REGISTRY]); + beforeEach(() => { + configureCompiler({providers: [TEST_COMPILER_PROVIDERS, MOCK_SCHEMA_REGISTRY]}); + }); commonBeforeEach(); diff --git a/modules/@angular/compiler/test/test_bindings.ts b/modules/@angular/compiler/test/test_bindings.ts index 8f300c399f..28b03ddd5a 100644 --- a/modules/@angular/compiler/test/test_bindings.ts +++ b/modules/@angular/compiler/test/test_bindings.ts @@ -11,7 +11,7 @@ import {createUrlResolverWithoutPackagePrefix} from '@angular/compiler/src/url_r import {MockSchemaRegistry} from '@angular/compiler/testing'; import {MockXHR} from '@angular/compiler/testing/xhr_mock'; -export var TEST_PROVIDERS: any[] = [ +export var TEST_COMPILER_PROVIDERS: any[] = [ {provide: ElementSchemaRegistry, useValue: new MockSchemaRegistry({}, {})}, {provide: XHR, useClass: MockXHR}, {provide: UrlResolver, useFactory: createUrlResolverWithoutPackagePrefix} diff --git a/modules/@angular/compiler/testing/test_component_builder.ts b/modules/@angular/compiler/testing/test_component_builder.ts index 72a470af7c..14549f8f58 100644 --- a/modules/@angular/compiler/testing/test_component_builder.ts +++ b/modules/@angular/compiler/testing/test_component_builder.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationEntryMetadata, Compiler, ComponentFactory, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core'; -import {ComponentFixture, ComponentFixtureNoNgZone, TestComponentBuilder} from '@angular/core/testing'; +import {AnimationEntryMetadata, Compiler, ComponentFactory, Inject, Injectable, Injector, NgZone, ViewMetadata} from '@angular/core'; +import {ComponentFixture, ComponentFixtureNoNgZone, TestComponentBuilder, TestInjector} from '@angular/core/testing'; import {DirectiveResolver, ViewResolver} from '../index'; import {MapWrapper} from '../src/facade/collection'; @@ -54,9 +54,7 @@ export class OverridingTestComponentBuilder extends TestComponentBuilder { /** @internal */ _viewOverrides = new Map(); - - - constructor(injector: Injector) { super(injector); } + constructor(@Inject(TestInjector) injector: Injector) { super(injector); } /** @internal */ _clone(): OverridingTestComponentBuilder { diff --git a/modules/@angular/core/src/linker.ts b/modules/@angular/core/src/linker.ts index 0adbbcd9a6..a290320b0a 100644 --- a/modules/@angular/core/src/linker.ts +++ b/modules/@angular/core/src/linker.ts @@ -9,7 +9,7 @@ // Public API for compiler export {AppModuleFactory, AppModuleRef} from './linker/app_module_factory'; export {AppModuleFactoryLoader} from './linker/app_module_factory_loader'; -export {Compiler} from './linker/compiler'; +export {Compiler, ComponentStillLoadingError} 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 ee014d8e1f..a9f59f140e 100644 --- a/modules/@angular/core/src/linker/compiler.ts +++ b/modules/@angular/core/src/linker/compiler.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {Injector} from '../di'; import {BaseException} from '../facade/exceptions'; import {ConcreteType, Type, stringify} from '../facade/lang'; import {AppModuleMetadata} from '../metadata/app_module'; @@ -14,6 +15,17 @@ import {AppModuleFactory} from './app_module_factory'; import {ComponentFactory} from './component_factory'; +/** + * Indicates that a component is still being loaded in a synchronous compile. + * + * @stable + */ +export class ComponentStillLoadingError extends BaseException { + constructor(public compType: Type) { + super(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`); + } +} + /** * Low-level service for running the angular compiler duirng runtime * to create {@link ComponentFactory}s, which @@ -25,6 +37,13 @@ import {ComponentFactory} from './component_factory'; * @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`. */ @@ -34,7 +53,8 @@ export class Compiler { } /** * Compiles the given component. All templates have to be either inline or compiled via - * `compileComponentAsync` before. + * `compileComponentAsync` before. Otherwise throws a {@link + * CompileSyncComponentStillLoadingError}. */ compileComponentSync(component: ConcreteType): ComponentFactory { throw new BaseException( @@ -43,7 +63,7 @@ export class Compiler { /** * Compiles the given App Module. All templates of the components listed in `precompile` * have to be either inline or compiled before via `compileComponentAsync` / - * `compileAppModuleAsync`. + * `compileAppModuleAsync`. Otherwise throws a {@link CompileSyncComponentStillLoadingError}. */ compileAppModuleSync(moduleType: ConcreteType, metadata: AppModuleMetadata = null): AppModuleFactory { diff --git a/modules/@angular/core/test/animation/animation_integration_spec.ts b/modules/@angular/core/test/animation/animation_integration_spec.ts index b973afb43d..2ffc43df30 100644 --- a/modules/@angular/core/test/animation/animation_integration_spec.ts +++ b/modules/@angular/core/test/animation/animation_integration_spec.ts @@ -18,7 +18,7 @@ import {DEFAULT_STATE} from '../../src/animation/animation_constants'; import {AnimationEntryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '../../src/animation/metadata'; import {AUTO_STYLE} from '../../src/animation/metadata'; import {IS_DART, isArray, isPresent} from '../../src/facade/lang'; -import {fakeAsync, flushMicrotasks, tick} from '../../testing'; +import {configureCompiler, configureModule, fakeAsync, flushMicrotasks, tick} from '../../testing'; import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../../testing/testing_internal'; export function main() { @@ -33,13 +33,10 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('animation tests', function() { - beforeEachProviders( - () => - [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }, - {provide: AnimationDriver, useClass: MockAnimationDriver}]); + beforeEachProviders(() => { + configureCompiler({useJit: useJit}); + configureModule({providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}]}); + }); var makeAnimationCmp = (tcb: TestComponentBuilder, tpl: string, diff --git a/modules/@angular/core/test/linker/app_module_integration_spec.ts b/modules/@angular/core/test/linker/app_module_integration_spec.ts index e0db19f0e6..fa64a8b021 100644 --- a/modules/@angular/core/test/linker/app_module_integration_spec.ts +++ b/modules/@angular/core/test/linker/app_module_integration_spec.ts @@ -1,7 +1,7 @@ import {LowerCasePipe, NgIf} from '@angular/common'; import {CompilerConfig} from '@angular/compiler'; -import {AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Host, Inject, Injectable, Injector, OpaqueToken, Optional, Provider, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; -import {ComponentFixture} from '@angular/core/testing'; +import {AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Host, Inject, Injectable, Injector, OpaqueToken, Optional, Provider, ReflectiveInjector, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core'; +import {ComponentFixture, configureCompiler} from '@angular/core/testing'; import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {BaseException} from '../../src/facade/exceptions'; @@ -119,16 +119,13 @@ function declareTests({useJit}: {useJit: boolean}) { var compiler: Compiler; var injector: Injector; + beforeEach(() => { configureCompiler({useJit: useJit}); }); + beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => { compiler = _compiler; injector = _injector; })); - beforeEachProviders(() => [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }]); - describe('precompile', function() { it('should resolve ComponentFactories', () => { let appModule = compiler.compileAppModuleSync(ModuleWithPrecompile).create(); @@ -206,6 +203,7 @@ function declareTests({useJit}: {useJit: boolean}) { it('should provide a ComponentResolver instance that uses the directives/pipes of the module', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create(); let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver); boundCompiler.resolveComponent(CompUsingModuleDirectiveAndPipe).then((cf) => { @@ -215,6 +213,22 @@ function declareTests({useJit}: {useJit: boolean}) { }); })); + it('should provide a ComponentResolver instance that delegates to the parent ComponentResolver for strings', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + let parentResolver: any = + jasmine.createSpyObj('resolver', ['resolveComponent', 'clearCache']); + let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes) + .create(ReflectiveInjector.resolveAndCreate( + [{provide: ComponentResolver, useValue: parentResolver}])); + parentResolver.resolveComponent.and.returnValue( + Promise.resolve('someFactoryFromParent')); + let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver); + boundCompiler.resolveComponent('someString').then((result) => { + expect(parentResolver.resolveComponent).toHaveBeenCalledWith('someString'); + expect(result).toBe('someFactoryFromParent'); + async.done(); + }); + })); }); describe('providers', function() { 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 1d26fa7a09..ad7203beeb 100644 --- a/modules/@angular/core/test/linker/change_detection_integration_spec.ts +++ b/modules/@angular/core/test/linker/change_detection_integration_spec.ts @@ -7,7 +7,7 @@ */ import {TestComponentBuilder} from '@angular/compiler/testing'; -import {ComponentFixture, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {ComponentFixture, configureCompiler, configureModule, 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 {isBlank, NumberWrapper, ConcreteType,} from '../../src/facade/lang'; @@ -23,14 +23,14 @@ import {IS_DART, Type} from '../../src/facade/lang'; import {EventEmitter, ObservableWrapper} from '../../src/facade/async'; -import {Component, DebugElement, Directive, TemplateRef, ChangeDetectorRef, ViewContainerRef, Input, Output, forwardRef, ViewMetadata, Pipe, RootRenderer, Renderer, RenderComponentType, Injectable, provide, OnInit, DoCheck, OnChanges, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked} from '@angular/core'; +import {Component, DebugElement, Directive, TemplateRef, ChangeDetectorRef, ViewContainerRef, Input, Output, forwardRef, ViewMetadata, Pipe, RootRenderer, Renderer, RenderComponentType, Injectable, provide, OnInit, DoCheck, OnChanges, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, Injector} from '@angular/core'; import {NgFor, NgIf} from '@angular/common'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {AsyncPipe} from '@angular/common'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {MockSchemaRegistry} from '@angular/compiler/testing'; -import {TEST_PROVIDERS} from '@angular/compiler/test/test_bindings'; +import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/test/test_bindings'; import {DebugDomRenderer} from '@angular/core/src/debug/debug_renderer'; import {DomRootRenderer} from '@angular/platform-browser/src/dom/dom_renderer'; @@ -88,11 +88,13 @@ export function main() { // On CJS fakeAsync is not supported... if (!getDOM().supportsDOMEvents()) return; - beforeEachProviders( - () => - [RenderLog, DirectiveLog, {provide: RootRenderer, useClass: LoggingRootRenderer}, - TEST_PROVIDERS, - ]); + beforeEach(() => { + configureCompiler({providers: TEST_COMPILER_PROVIDERS}); + configureModule({ + providers: + [RenderLog, DirectiveLog, {provide: RootRenderer, useClass: LoggingRootRenderer}] + }); + }); beforeEach(inject( [TestComponentBuilder, ElementSchemaRegistry, RenderLog, DirectiveLog], diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index 3956f90c10..cdc3b31eb0 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -7,7 +7,7 @@ */ import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; -import {fakeAsync, tick, ComponentFixture} from '@angular/core/testing'; +import {fakeAsync, tick, ComponentFixture, configureCompiler, configureModule} from '@angular/core/testing'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; @@ -53,13 +53,10 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('integration tests', function() { - beforeEachProviders( - () => - [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }, - {provide: ANCHOR_ELEMENT, useValue: el('
')}]); + beforeEach(() => { + configureCompiler({useJit: useJit}); + configureModule({providers: [{provide: ANCHOR_ELEMENT, useValue: el('
')}]}); + }); describe('react to record changes', function() { it('should consume text node changes', @@ -1802,10 +1799,16 @@ function declareTests({useJit}: {useJit: boolean}) { }); describe('logging property updates', () => { - beforeEachProviders(() => [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }]); + beforeEach(() => { + configureCompiler({ + providers: [{ + provide: CompilerConfig, + // Note: we are testing the `genDebugInfo` flag here, so we + // need to set it explicitely! + useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) + }] + }); + }); it('should reflect property values as attributes', inject( 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 c6c6589b21..d5a99fda35 100644 --- a/modules/@angular/core/test/linker/ng_container_integration_spec.ts +++ b/modules/@angular/core/test/linker/ng_container_integration_spec.ts @@ -9,14 +9,13 @@ import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; +import {configureCompiler} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {OpaqueToken, ViewMetadata, Component, Directive, AfterContentInit, AfterViewInit, QueryList, ContentChildren, ViewChildren, Input} from '@angular/core'; import {NgIf} from '@angular/common'; import {CompilerConfig} from '@angular/compiler'; import {el} from '@angular/platform-browser/testing/browser_util'; -const ANCHOR_ELEMENT = new OpaqueToken('AnchorElement'); - export function main() { describe('jit', () => { declareTests({useJit: true}); }); describe('no jit', () => { declareTests({useJit: false}); }); @@ -25,14 +24,7 @@ export function main() { function declareTests({useJit}: {useJit: boolean}) { describe('', function() { - beforeEachProviders( - () => - [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }, - {provide: ANCHOR_ELEMENT, useValue: el('
')}, - ]); + beforeEach(() => { configureCompiler({useJit: useJit}); }); it('should be rendered as comment with children as siblings', inject( diff --git a/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts b/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts index 457a9277e7..f48c178d9b 100644 --- a/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts +++ b/modules/@angular/core/test/linker/reflector_component_resolver_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {provide} from '@angular/core'; +import {Component, provide} from '@angular/core'; import {ComponentFactory} from '@angular/core/src/linker/component_factory'; import {ComponentResolver, ReflectorComponentResolver} from '@angular/core/src/linker/component_resolver'; import {ReflectionInfo, reflector} from '@angular/core/src/reflection/reflection'; @@ -16,34 +16,30 @@ import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; export function main() { describe('Compiler', () => { var someCompFactory: any /** TODO #9100 */; + var compiler: ComponentResolver; - beforeEachProviders(() => [{provide: ComponentResolver, useClass: ReflectorComponentResolver}]); - - beforeEach(inject([ComponentResolver], (_compiler: ComponentResolver) => { + beforeEach(() => { someCompFactory = new ComponentFactory(null, null, null); reflector.registerType(SomeComponent, new ReflectionInfo([someCompFactory])); - })); + compiler = new ReflectorComponentResolver(); + }); it('should read the template from an annotation', - inject( - [AsyncTestCompleter, ComponentResolver], - (async: AsyncTestCompleter, compiler: ComponentResolver) => { - compiler.resolveComponent(SomeComponent).then((compFactory: ComponentFactory) => { - expect(compFactory).toBe(someCompFactory); - async.done(); - return null; - }); - })); + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + compiler.resolveComponent(SomeComponent).then((compFactory: ComponentFactory) => { + expect(compFactory).toBe(someCompFactory); + async.done(); + return null; + }); + })); it('should throw when given a string', - inject( - [AsyncTestCompleter, ComponentResolver], - (async: AsyncTestCompleter, compiler: ComponentResolver) => { - compiler.resolveComponent('someString').catch((e) => { - expect(e.message).toContain('Cannot resolve component using \'someString\'.') - async.done(); - }); - })); + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + compiler.resolveComponent('someString').catch((e) => { + expect(e.message).toContain('Cannot resolve component using \'someString\'.') + async.done(); + }); + })); }); } diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 5799aa5269..17deed5dbe 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -7,6 +7,7 @@ */ import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; +import {configureCompiler, configureModule} from '@angular/core/testing'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; @@ -31,12 +32,10 @@ function declareTests({useJit}: {useJit: boolean}) { describe('regressions', () => { describe('platform pipes', () => { - beforeEachProviders( - () => [{ - provide: CompilerConfig, - useValue: new CompilerConfig( - {genDebugInfo: true, useJit: useJit, platformPipes: [PlatformPipe]}) - }]); + beforeEach(() => { + configureCompiler({useJit: useJit}); + configureModule({pipes: [PlatformPipe]}); + }); it('should overwrite them by custom pipes', inject( diff --git a/modules/@angular/core/test/linker/security_integration_spec.ts b/modules/@angular/core/test/linker/security_integration_spec.ts index e8cf109250..9c29cf5c42 100644 --- a/modules/@angular/core/test/linker/security_integration_spec.ts +++ b/modules/@angular/core/test/linker/security_integration_spec.ts @@ -9,6 +9,7 @@ import {ddescribe, describe, expect, inject, beforeEachProviders, beforeEach, afterEach, it,} from '@angular/core/testing/testing_internal'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; +import {configureCompiler} from '@angular/core/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {provide, Injectable, OpaqueToken} from '@angular/core'; import {CompilerConfig} from '@angular/compiler'; @@ -18,8 +19,6 @@ import {el} from '@angular/platform-browser/testing/browser_util'; import {DomSanitizationService} from '@angular/platform-browser/src/security/dom_sanitization_service'; -const ANCHOR_ELEMENT = /*@ts2dart_const*/ new OpaqueToken('AnchorElement'); - export function main() { if (IS_DART) { declareTests({useJit: false}); @@ -53,13 +52,7 @@ function itAsync( function declareTests({useJit}: {useJit: boolean}) { describe('security integration tests', function() { - beforeEachProviders( - () => - [{ - provide: CompilerConfig, - useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit}) - }, - {provide: ANCHOR_ELEMENT, useValue: el('
')}]); + beforeEach(() => { configureCompiler({useJit: useJit}); }); let originalLog: (msg: any) => any; beforeEach(() => { diff --git a/modules/@angular/core/testing/test_injector.ts b/modules/@angular/core/testing/test_injector.ts index 52b26ad602..e944c4b430 100644 --- a/modules/@angular/core/testing/test_injector.ts +++ b/modules/@angular/core/testing/test_injector.ts @@ -6,61 +6,157 @@ * found in the LICENSE file at https://angular.io/license */ -import {PLATFORM_INITIALIZER, Provider, ReflectiveInjector, Type} from '../index'; -import {lockRunMode} from '../src/application_ref'; +import {AppModule, AppModuleFactory, AppModuleMetadata, AppModuleRef, Compiler, ComponentStillLoadingError, Injector, PlatformRef, Provider, Type} from '../index'; import {ListWrapper} from '../src/facade/collection'; import {BaseException} from '../src/facade/exceptions'; -import {FunctionWrapper, isPresent} from '../src/facade/lang'; +import {FunctionWrapper, isPresent, stringify} from '../src/facade/lang'; + import {AsyncTestCompleter} from './async_test_completer'; +const UNDEFINED = new Object(); + +/** + * Signature of the compiler factory passed to `initTestEnvironment`. + * + * @experimental + */ +export type TestCompilerFactory = + (config: {providers?: Array, useJit?: boolean}) => Compiler; + /** * @experimental */ -export class TestInjector { +export class TestInjector implements Injector { private _instantiated: boolean = false; - private _injector: ReflectiveInjector = null; + private _compiler: Compiler = null; + private _moduleRef: AppModuleRef = null; + + private _compilerProviders: Array = []; + private _compilerUseJit: boolean = true; private _providers: Array = []; + private _directives: Array = []; + private _pipes: Array = []; + private _modules: Array = []; + private _precompile: Array = []; reset() { - this._injector = null; + this._compiler = null; + this._moduleRef = null; + this._compilerProviders = []; + this._compilerUseJit = true; this._providers = []; + this._directives = []; + this._pipes = []; + this._modules = []; + this._precompile = []; this._instantiated = false; } - platformProviders: Array = []; + compilerFactory: TestCompilerFactory = null; - applicationProviders: Array = []; + platform: PlatformRef = null; - addProviders(providers: Array) { + appModule: Type = null; + + configureCompiler(config: {providers?: any[], useJit?: boolean}) { if (this._instantiated) { - throw new BaseException('Cannot add providers after test injector is instantiated'); + throw new BaseException('Cannot add configuration after test injector is instantiated'); + } + if (config.providers) { + this._compilerProviders = ListWrapper.concat(this._compilerProviders, config.providers); + } + if (config.useJit !== undefined) { + this._compilerUseJit = config.useJit; } - this._providers = ListWrapper.concat(this._providers, providers); } - createInjector() { - lockRunMode(); - var rootInjector = ReflectiveInjector.resolveAndCreate(this.platformProviders); - this._injector = rootInjector.resolveAndCreateChild( - ListWrapper.concat(this.applicationProviders, this._providers)); + configureModule(moduleDef: { + providers?: any[], + directives?: any[], + pipes?: any[], + precompile?: any[], + modules?: any[] + }) { + if (this._instantiated) { + throw new BaseException('Cannot add configuration after test injector is instantiated'); + } + if (moduleDef.providers) { + this._providers = ListWrapper.concat(this._providers, moduleDef.providers); + } + if (moduleDef.directives) { + this._directives = ListWrapper.concat(this._directives, moduleDef.directives); + } + if (moduleDef.pipes) { + this._pipes = ListWrapper.concat(this._pipes, moduleDef.pipes); + } + if (moduleDef.precompile) { + this._precompile = ListWrapper.concat(this._precompile, moduleDef.precompile); + } + if (moduleDef.modules) { + this._modules = ListWrapper.concat(this._modules, moduleDef.modules); + } + } + + createInjectorSync(): Injector { + if (this._instantiated) { + return this; + } + let moduleMeta = this._createCompilerAndModuleMeta(); + return this._createFromModuleFactory( + this._compiler.compileAppModuleSync(_NoopModule, moduleMeta)); + } + + createInjectorAsync(): Promise { + if (this._instantiated) { + return Promise.resolve(this); + } + let moduleMeta = this._createCompilerAndModuleMeta(); + return this._compiler.compileAppModuleAsync(_NoopModule, moduleMeta) + .then((appModuleFactory) => this._createFromModuleFactory(appModuleFactory)); + } + + private _createCompilerAndModuleMeta(): AppModuleMetadata { + this._compiler = + this.compilerFactory({providers: this._compilerProviders, useJit: this._compilerUseJit}); + const moduleMeta = new AppModuleMetadata({ + providers: this._providers.concat([{provide: TestInjector, useValue: this}]), + modules: this._modules.concat([this.appModule]), + directives: this._directives, + pipes: this._pipes, + precompile: this._precompile + }); + + return moduleMeta; + } + + private _createFromModuleFactory(appModuleFactory: AppModuleFactory): Injector { + this._moduleRef = appModuleFactory.create(this.platform.injector); this._instantiated = true; - return this._injector; + return this; } - get(token: any) { + get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) { if (!this._instantiated) { - this.createInjector(); + throw new BaseException( + 'Illegal state: The TestInjector has not yet been created. Call createInjectorSync/Async first!'); } - return this._injector.get(token); + if (token === TestInjector) { + return this; + } + // Tests can inject things from the app module and from the compiler, + // but the app 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; } execute(tokens: any[], fn: Function): any { if (!this._instantiated) { - this.createInjector(); + throw new BaseException( + 'Illegal state: The TestInjector has not yet been created. Call createInjectorSync/Async first!'); } - var params = tokens.map(t => this._injector.get(t)); + var params = tokens.map(t => this.get(t)); return FunctionWrapper.apply(fn, params); } } @@ -83,28 +179,22 @@ export function getTestInjector() { * * 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 `resetBaseTestProviders`. + * first use `resetTestEnvironment`. * * Test Providers for individual platforms are available from * 'angular2/platform/testing/'. * * @experimental */ -export function setBaseTestProviders( - platformProviders: Array, - applicationProviders: Array) { +export function initTestEnvironment( + compilerFactory: TestCompilerFactory, platform: PlatformRef, appModule: Type) { var testInjector = getTestInjector(); - if (testInjector.platformProviders.length > 0 || testInjector.applicationProviders.length > 0) { + if (testInjector.compilerFactory || testInjector.platform || testInjector.appModule) { throw new BaseException('Cannot set base providers because it has already been called'); } - testInjector.platformProviders = platformProviders; - testInjector.applicationProviders = applicationProviders; - var injector = testInjector.createInjector(); - let inits: Function[] = injector.get(PLATFORM_INITIALIZER, null); - if (isPresent(inits)) { - inits.forEach(init => init()); - } - testInjector.reset(); + testInjector.compilerFactory = compilerFactory; + testInjector.platform = platform; + testInjector.appModule = appModule; } /** @@ -112,10 +202,11 @@ export function setBaseTestProviders( * * @experimental */ -export function resetBaseTestProviders() { +export function resetTestEnvironment() { var testInjector = getTestInjector(); - testInjector.platformProviders = []; - testInjector.applicationProviders = []; + testInjector.compilerFactory = null; + testInjector.platform = null; + testInjector.appModule = null; testInjector.reset(); } @@ -146,16 +237,38 @@ export function resetBaseTestProviders() { export function inject(tokens: any[], fn: Function): () => any { let testInjector = getTestInjector(); if (tokens.indexOf(AsyncTestCompleter) >= 0) { - // Return an async test method that returns a Promise if AsyncTestCompleter is one of the - // injected tokens. return () => { - let completer: AsyncTestCompleter = testInjector.get(AsyncTestCompleter); - testInjector.execute(tokens, fn); - return completer.promise; + // Return an async test method that returns a Promise if AsyncTestCompleter is one of the + // injected tokens. + return testInjector.createInjectorAsync().then(() => { + let completer: AsyncTestCompleter = testInjector.get(AsyncTestCompleter); + testInjector.execute(tokens, fn); + return completer.promise; + }); }; } else { - // Return a synchronous test method with the injected tokens. - return () => { return getTestInjector().execute(tokens, fn); }; + return () => { + // Return a asynchronous test method with the injected tokens. + // TODO(tbosch): Right now, we can only detect the AsyncTestZoneSpec via its name. + // (see https://github.com/angular/zone.js/issues/370) + if (Zone.current.name.toLowerCase().indexOf('asynctestzone') >= 0) { + return testInjector.createInjectorAsync().then(() => testInjector.execute(tokens, fn)); + } else { + // Return a synchronous test method with the injected tokens. + try { + testInjector.createInjectorSync(); + } catch (e) { + if (e instanceof ComponentStillLoadingError) { + throw new Error( + `This test module precompiles the component ${stringify(e.compType)} which is using a "templateUrl", but the test is synchronous. ` + + `Please use the "async(...)" or "fakeAsync(...)" helper functions to make the test asynchronous.`); + } else { + throw e; + } + } + return testInjector.execute(tokens, fn); + } + }; } } @@ -163,18 +276,24 @@ export function inject(tokens: any[], fn: Function): () => any { * @experimental */ export class InjectSetupWrapper { - constructor(private _providers: () => any) {} + constructor(private _moduleDef: () => { + providers?: any[], + directives?: any[], + pipes?: any[], + precompile?: any[], + modules?: any[] + }) {} - private _addProviders() { - var additionalProviders = this._providers(); - if (additionalProviders.length > 0) { - getTestInjector().addProviders(additionalProviders); + private _addModule() { + var moduleDef = this._moduleDef(); + if (moduleDef) { + getTestInjector().configureModule(moduleDef); } } inject(tokens: any[], fn: Function): () => any { return () => { - this._addProviders(); + this._addModule(); return inject_impl(tokens, fn)(); }; } @@ -184,9 +303,25 @@ export class InjectSetupWrapper { * @experimental */ export function withProviders(providers: () => any) { - return new InjectSetupWrapper(providers); + return new InjectSetupWrapper(() => {{return {providers: providers()};}}); } +/** + * @experimental + */ +export function withModule(moduleDef: () => { + providers?: any[], + directives?: any[], + pipes?: any[], + precompile?: any[], + modules?: any[] +}) { + return new InjectSetupWrapper(moduleDef); +} + + // This is to ensure inject(Async) within InjectSetupWrapper doesn't call itself // when transpiled to Dart. var inject_impl = inject; + +class _NoopModule {} diff --git a/modules/@angular/core/testing/testing.ts b/modules/@angular/core/testing/testing.ts index da692aa1ec..fd53381a2f 100644 --- a/modules/@angular/core/testing/testing.ts +++ b/modules/@angular/core/testing/testing.ts @@ -123,7 +123,7 @@ if (_global.beforeEach) { export function addProviders(providers: Array): void { if (!providers) return; try { - testInjector.addProviders(providers); + testInjector.configureModule({providers: providers}); } catch (e) { throw new Error( 'addProviders can\'t be called after the injector has been already created for this test. ' + @@ -132,6 +132,48 @@ export function addProviders(providers: Array): void { } } +/** + * 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[], + directives?: any[], + pipes?: any[], + precompile?: any[], + modules?: any[] +}): void { + if (!moduleDef) return; + try { + testInjector.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 { + testInjector.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.'); + } +} + /** * @deprecated Use beforeEach(() => addProviders()) */ diff --git a/modules/@angular/core/testing/testing_internal.ts b/modules/@angular/core/testing/testing_internal.ts index cc6180831c..57dbb5e9ea 100644 --- a/modules/@angular/core/testing/testing_internal.ts +++ b/modules/@angular/core/testing/testing_internal.ts @@ -110,7 +110,7 @@ export function beforeEachProviders(fn: any /** TODO #9100 */): void { jsmBeforeEach(() => { var providers = fn(); if (!providers) return; - testInjector.addProviders(providers); + testInjector.configureModule({providers: providers}); }); } @@ -135,11 +135,10 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe provide: AsyncTestCompleter, useFactory: () => { // Mark the test as async when an AsyncTestCompleter is injected in an it() - if (!inIt) throw new Error('AsyncTestCompleter can only be injected in an "it()"'); return new AsyncTestCompleter(); } }; - testInjector.addProviders([completerProvider]); + testInjector.configureModule({providers: [completerProvider]}); runner.run(); inIt = true; 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 ff2ec0e30f..5017b29009 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 @@ -7,15 +7,16 @@ */ import {UrlResolver, XHR} from '@angular/compiler'; +import {TestComponentBuilder} from '@angular/compiler/testing'; import {Component, provide} from '@angular/core'; +import {configureCompiler, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; - -import {fakeAsync, flushMicrotasks, tick,} from '@angular/core/testing'; -import {TestComponentBuilder} from '@angular/compiler/testing'; import {expect} from '@angular/platform-browser/testing/matchers'; + import {BaseException} from '../../src/facade/exceptions'; import {CachedXHR} from '../../src/xhr/xhr_cache'; + import {setTemplateCache} from './xhr_cache_setter'; export function main() { @@ -26,10 +27,14 @@ export function main() { setTemplateCache({'test.html': '
Hello
'}); return new CachedXHR(); } - beforeEachProviders(() => [{provide: UrlResolver, useClass: TestUrlResolver}, { - provide: XHR, - useFactory: createCachedXHR - }]); + beforeEach(() => { + configureCompiler({ + providers: [ + {provide: UrlResolver, useClass: TestUrlResolver}, + {provide: XHR, useFactory: createCachedXHR} + ] + }); + }); it('should throw exception if $templateCache is not found', () => { setTemplateCache(null); diff --git a/modules/@angular/platform-browser-dynamic/testing.ts b/modules/@angular/platform-browser-dynamic/testing.ts index 04fcae1961..4ce34daa79 100644 --- a/modules/@angular/platform-browser-dynamic/testing.ts +++ b/modules/@angular/platform-browser-dynamic/testing.ts @@ -6,35 +6,61 @@ * found in the LICENSE file at https://angular.io/license */ -import {DirectiveResolver, ViewResolver} from '@angular/compiler'; +import {CompilerConfig, DirectiveResolver, ViewResolver} from '@angular/compiler'; import {MockDirectiveResolver, MockViewResolver, OverridingTestComponentBuilder} from '@angular/compiler/testing'; +import {AppModule, Compiler, Provider, ReflectiveInjector, Type} from '@angular/core'; import {TestComponentBuilder, TestComponentRenderer} from '@angular/core/testing'; -import {TEST_BROWSER_APPLICATION_PROVIDERS, TEST_BROWSER_PLATFORM_PROVIDERS} from '@angular/platform-browser/testing'; +import {BrowserTestModule, browserTestPlatform} from '@angular/platform-browser/testing'; import {BROWSER_APP_COMPILER_PROVIDERS} from './index'; import {DOMTestComponentRenderer} from './testing/dom_test_component_renderer'; export * from './private_export_testing' -/** - * Default platform providers for testing. - * - * @stable - */ -export const TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array = - [TEST_BROWSER_PLATFORM_PROVIDERS]; - -/** - * Default application providers for testing. - * - * @stable - */ -export const TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS: Array = [ - TEST_BROWSER_APPLICATION_PROVIDERS, BROWSER_APP_COMPILER_PROVIDERS, +const TEST_BROWSER_DYNAMIC_COMPILER_PROVIDERS: Array = [ + BROWSER_APP_COMPILER_PROVIDERS, [ - {provide: TestComponentBuilder, useClass: OverridingTestComponentBuilder}, - {provide: DirectiveResolver, useClass: MockDirectiveResolver}, - {provide: ViewResolver, useClass: MockViewResolver}, - {provide: TestComponentRenderer, useClass: DOMTestComponentRenderer}, + { provide: DirectiveResolver, + useClass: MockDirectiveResolver }, + { provide: ViewResolver, + useClass: MockViewResolver } ] ]; + +/** + * Creates a compiler for testing + * + * @stable + */ +export function browserTestCompiler( + {providers = [], useJit = true}: {providers?: Array, + useJit?: boolean} = {}): Compiler { + const injector = ReflectiveInjector.resolveAndCreate([ + TEST_BROWSER_DYNAMIC_COMPILER_PROVIDERS, + {provide: CompilerConfig, useValue: new CompilerConfig({genDebugInfo: true, useJit: useJit})}, + providers ? providers : [] + ]); + return injector.get(Compiler); +} + +/** + * Platform for testing. + * + * @experimental API related to bootstrapping are still under review. + */ +export const browserDynamicTestPlatform = browserTestPlatform; + +/** + * AppModule for testing. + * + * @stable + */ +@AppModule({ + modules: [BrowserTestModule], + providers: [ + {provide: TestComponentBuilder, useClass: OverridingTestComponentBuilder}, + {provide: TestComponentRenderer, useClass: DOMTestComponentRenderer}, + ] +}) +export class BrowserDynamicTestModule { +} \ No newline at end of file diff --git a/modules/@angular/platform-browser/test/testing_public_spec.ts b/modules/@angular/platform-browser/test/testing_public_spec.ts index c60331bdae..7f43a2f9a5 100644 --- a/modules/@angular/platform-browser/test/testing_public_spec.ts +++ b/modules/@angular/platform-browser/test/testing_public_spec.ts @@ -6,11 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {addProviders, inject, fakeAsync, async, withProviders, tick,} from '@angular/core/testing'; -import {TestComponentBuilder} from '@angular/compiler/testing'; -import {expect} from '@angular/platform-browser/testing/matchers'; -import {Injectable, provide, Component, ViewMetadata} from '@angular/core'; import {NgIf} from '@angular/common'; +import {CompilerConfig, XHR} from '@angular/compiler'; +import {TestComponentBuilder} from '@angular/compiler/testing'; +import {AppModule, Component, ComponentFactoryResolver, Directive, Injectable, Input, Pipe, ViewMetadata, provide} from '@angular/core'; +import {addProviders, async, configureCompiler, configureModule, fakeAsync, inject, tick, withModule, withProviders} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/matchers'; + +import {stringify} from '../../http/src/facade/lang'; import {PromiseWrapper} from '../../http/src/facade/promise'; // Services, and components for the tests. @@ -98,6 +101,29 @@ class TestViewProvidersComp { constructor(private fancyService: FancyService) {} } +@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) +class SomeDirective { + @Input() + someDir: string; +} + +@Pipe({name: 'somePipe'}) +class SomePipe { + transform(value: string): any { return `transformed ${value}`; } +} + +@Component({selector: 'comp', template: `
`}) +class CompUsingModuleDirectiveAndPipe { +} + +@AppModule({}) +class SomeNestedModule { +} + +@Component({selector: 'comp', templateUrl: 'someTemplate.html'}) +class CompWithUrlTemplate { +} + export function main() { describe('using the async helper', () => { var actuallyDone: boolean; @@ -203,6 +229,133 @@ export function main() { }); }); + describe('using the test injector with modules', () => { + let moduleConfig: any; + beforeEach(() => { + moduleConfig = { + providers: [FancyService], + directives: [SomeDirective], + pipes: [SomePipe], + precompile: [CompUsingModuleDirectiveAndPipe], + modules: [SomeNestedModule] + }; + }); + + describe('setting up a module', () => { + beforeEach(() => configureModule(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); + let el = compFixture.debugElement; + + compFixture.detectChanges(); + expect(el.children[0].properties['title']).toBe('transformed someValue'); + })); + + it('should use set up nested modules', + inject([SomeNestedModule], (nestedModule: SomeNestedModule) => { + expect(nestedModule).toBeAnInstanceOf(SomeNestedModule); + })); + + it('should use set up precompile components', + inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { + expect(resolver.resolveComponentFactory(CompUsingModuleDirectiveAndPipe).componentType) + .toBe(CompUsingModuleDirectiveAndPipe); + })); + }); + + 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 nested modules', + withModule(() => moduleConfig) + .inject([SomeNestedModule], (nestedModule: SomeNestedModule) => { + expect(nestedModule).toBeAnInstanceOf(SomeNestedModule); + })); + + it('should use set up precompile components', + withModule(() => moduleConfig) + .inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { + expect( + resolver.resolveComponentFactory(CompUsingModuleDirectiveAndPipe).componentType) + .toBe(CompUsingModuleDirectiveAndPipe); + })); + }); + + describe('precompile components with template url', () => { + let xhrGet: jasmine.Spy; + beforeEach(() => { + xhrGet = jasmine.createSpy('xhrGet').and.returnValue(Promise.resolve('Hello world!')); + configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); + }); + + it('should allow to precompile components with templateUrl using the async helper', + async(withModule(() => { + return {precompile: [CompWithUrlTemplate]}; + }).inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { + expect(resolver.resolveComponentFactory(CompWithUrlTemplate).componentType) + .toBe(CompWithUrlTemplate); + }))); + + it('should allow to precompile components with templateUrl using the fakeAsync helper', + fakeAsync(withModule(() => { + return {precompile: [CompWithUrlTemplate]}; + }).inject([ComponentFactoryResolver], (resolver: ComponentFactoryResolver) => { + expect(resolver.resolveComponentFactory(CompWithUrlTemplate).componentType) + .toBe(CompWithUrlTemplate); + }))); + }); + + describe('setting up the compiler', () => { + + describe('providers', () => { + beforeEach(() => { + let xhrGet = jasmine.createSpy('xhrGet').and.returnValue(Promise.resolve('Hello world!')); + configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); + }); + + it('should use set up providers', + fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { + let compFixture = tcb.createFakeAsync(CompWithUrlTemplate); + expect(compFixture.nativeElement).toHaveText('Hello world!'); + }))); + }); + + describe('useJit true', () => { + beforeEach(() => { 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}); }); + it('should set the value into CompilerConfig', + inject([CompilerConfig], (config: CompilerConfig) => { + expect(config.useJit).toBe(false); + })); + }); + }); + }); + describe('errors', () => { var originalJasmineIt: any; var originalJasmineBeforeEach: any; @@ -294,6 +447,36 @@ export function main() { }); }); }); + + describe('precompile', () => { + let xhrGet: jasmine.Spy; + beforeEach(() => { + xhrGet = jasmine.createSpy('xhrGet').and.returnValue(Promise.resolve('Hello world!')); + configureCompiler({providers: [{provide: XHR, useValue: {get: xhrGet}}]}); + }); + + it('should report an error for precompile components with templateUrl and sync tests', () => { + var itPromise = patchJasmineIt(); + + expect( + () => it( + 'should fail', + withModule(() => { return {precompile: [CompWithUrlTemplate]}; }) + .inject( + [ComponentFactoryResolver], + (resolver: ComponentFactoryResolver) => { + expect( + resolver.resolveComponentFactory(CompWithUrlTemplate).componentType) + .toBe(CompWithUrlTemplate); + }))) + .toThrowError( + `This test module precompiles the component ${stringify(CompWithUrlTemplate)} which is using a "templateUrl", but the test is synchronous. ` + + 'Please use the "async(...)" or "fakeAsync(...)" helper functions to make the test asynchronous.'); + + 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 bec950ee97..72b93f6fd3 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,10 +8,10 @@ import {inject, ddescribe, describe, it, iit, expect, beforeEach, beforeEachProviders,} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; -import {TestInjector} from '@angular/core/testing'; +import {TestInjector, configureModule} from '@angular/core/testing'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; -import {provide, Injector, ViewMetadata, Component, Injectable, ComponentRef} from '@angular/core'; +import {provide, Injector, ViewMetadata, Component, Injectable, ComponentRef, ReflectiveInjector, getPlatform} from '@angular/core'; import {NgIf} from '@angular/common'; import {WebWorkerRootRenderer} from '@angular/platform-browser/src/web_workers/worker/renderer'; import {ClientMessageBrokerFactory, ClientMessageBrokerFactory_} from '@angular/platform-browser/src/web_workers/shared/client_message_broker'; @@ -25,7 +25,8 @@ import {createPairedMessageBuses, PairedMessageBuses} from '../shared/web_worker import {ServiceMessageBrokerFactory_} from '@angular/platform-browser/src/web_workers/shared/service_message_broker'; import {CompilerConfig} from '@angular/compiler'; import {dispatchEvent} from '../../../../platform-browser/testing/browser_util'; -import {TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS} from '@angular/platform-browser/testing'; +import {BrowserTestModule} from '@angular/platform-browser/testing'; +import {browserTestCompiler, browserDynamicTestPlatform} from '@angular/platform-browser-dynamic/testing' export function main() { function createWebWorkerBrokerFactory( @@ -58,199 +59,206 @@ export function main() { return new DebugDomRootRenderer(workerRootRenderer); } - describe('Web Worker Renderer', () => { - var uiInjector: Injector; - var uiRenderStore: RenderStore; - var workerRenderStore: RenderStore; + describe( + 'Web Worker Renderer', + () => { + var uiInjector: Injector; + var uiRenderStore: RenderStore; + var workerRenderStore: RenderStore; - beforeEachProviders(() => { - uiRenderStore = new RenderStore(); - var testUiInjector = new TestInjector(); - testUiInjector.platformProviders = TEST_BROWSER_PLATFORM_PROVIDERS; - testUiInjector.applicationProviders = TEST_BROWSER_APPLICATION_PROVIDERS; - testUiInjector.addProviders([ - Serializer, {provide: RenderStore, useValue: uiRenderStore}, - {provide: DomRootRenderer, useClass: DomRootRenderer_}, - {provide: RootRenderer, useExisting: DomRootRenderer} - ]); - uiInjector = testUiInjector.createInjector(); - var uiSerializer = uiInjector.get(Serializer); - var domRootRenderer = uiInjector.get(DomRootRenderer); - workerRenderStore = new RenderStore(); - return [ - Serializer, {provide: CompilerConfig, useValue: new CompilerConfig({genDebugInfo: true})}, - {provide: RenderStore, useValue: workerRenderStore}, { - provide: RootRenderer, - useFactory: (workerSerializer: Serializer) => { - return createWorkerRenderer( - workerSerializer, uiSerializer, domRootRenderer, uiRenderStore, workerRenderStore); - }, - deps: [Serializer] + beforeEach(() => { + uiRenderStore = new RenderStore(); + var testUiInjector = new TestInjector(); + testUiInjector.platform = browserDynamicTestPlatform(); + testUiInjector.compilerFactory = browserTestCompiler; + testUiInjector.appModule = BrowserTestModule; + testUiInjector.configureModule({ + providers: [ + Serializer, {provide: RenderStore, useValue: uiRenderStore}, + {provide: DomRootRenderer, useClass: DomRootRenderer_}, + {provide: RootRenderer, useExisting: DomRootRenderer} + ] + }); + uiInjector = testUiInjector.createInjectorSync(); + var uiSerializer = uiInjector.get(Serializer); + var domRootRenderer = uiInjector.get(DomRootRenderer); + workerRenderStore = new RenderStore(); + + configureModule({ + providers: [ + Serializer, {provide: RenderStore, useValue: workerRenderStore}, { + provide: RootRenderer, + useFactory: (workerSerializer: Serializer) => { + return createWorkerRenderer( + workerSerializer, uiSerializer, domRootRenderer, uiRenderStore, + workerRenderStore); + }, + deps: [Serializer] + } + ] + }); + }); + + function getRenderElement(workerEl: any) { + var id = workerRenderStore.serialize(workerEl); + return uiRenderStore.deserialize(id); } - ]; - }); - function getRenderElement(workerEl: any) { - var id = workerRenderStore.serialize(workerEl); - return uiRenderStore.deserialize(id); - } + function getRenderer(componentRef: ComponentRef) { + return (componentRef.hostView).internalView.renderer; + } - function getRenderer(componentRef: ComponentRef) { - return (componentRef.hostView).internalView.renderer; - } + it('should update text nodes', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + tcb.overrideView(MyComp2, new ViewMetadata({template: '
{{ctxProp}}
'})) + .createAsync(MyComp2) + .then((fixture) => { + var renderEl = getRenderElement(fixture.debugElement.nativeElement); + expect(renderEl).toHaveText(''); - it('should update text nodes', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView(MyComp2, new ViewMetadata({template: '
{{ctxProp}}
'})) - .createAsync(MyComp2) - .then((fixture) => { - var renderEl = getRenderElement(fixture.debugElement.nativeElement); - expect(renderEl).toHaveText(''); + fixture.debugElement.componentInstance.ctxProp = 'Hello World!'; + fixture.detectChanges(); + expect(renderEl).toHaveText('Hello World!'); + async.done(); - fixture.debugElement.componentInstance.ctxProp = 'Hello World!'; - fixture.detectChanges(); - expect(renderEl).toHaveText('Hello World!'); - async.done(); + }); + })); - }); - })); + it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + tcb.overrideView( + MyComp2, new ViewMetadata( + {template: ''})) + .createAsync(MyComp2) + .then((fixture) => { + var checkSetters = + (componentRef: any /** TODO #9100 */, + workerEl: any /** TODO #9100 */) => { + var renderer = getRenderer(componentRef); + var el = getRenderElement(workerEl); + renderer.setElementProperty(workerEl, 'tabIndex', 1); + expect((el).tabIndex).toEqual(1); - it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp2, - new ViewMetadata({template: ''})) - .createAsync(MyComp2) - .then((fixture) => { - var checkSetters = - (componentRef: any /** TODO #9100 */, workerEl: any /** TODO #9100 */) => { - var renderer = getRenderer(componentRef); - var el = getRenderElement(workerEl); - renderer.setElementProperty(workerEl, 'tabIndex', 1); - expect((el).tabIndex).toEqual(1); + renderer.setElementClass(workerEl, 'a', true); + expect(getDOM().hasClass(el, 'a')).toBe(true); + renderer.setElementClass(workerEl, 'a', false); + expect(getDOM().hasClass(el, 'a')).toBe(false); - renderer.setElementClass(workerEl, 'a', true); - expect(getDOM().hasClass(el, 'a')).toBe(true); - renderer.setElementClass(workerEl, 'a', false); - expect(getDOM().hasClass(el, 'a')).toBe(false); + renderer.setElementStyle(workerEl, 'width', '10px'); + expect(getDOM().getStyle(el, 'width')).toEqual('10px'); + renderer.setElementStyle(workerEl, 'width', null); + expect(getDOM().getStyle(el, 'width')).toEqual(''); - renderer.setElementStyle(workerEl, 'width', '10px'); - expect(getDOM().getStyle(el, 'width')).toEqual('10px'); - renderer.setElementStyle(workerEl, 'width', null); - expect(getDOM().getStyle(el, 'width')).toEqual(''); + renderer.setElementAttribute(workerEl, 'someattr', 'someValue'); + expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue'); + }; - renderer.setElementAttribute(workerEl, 'someattr', 'someValue'); - expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue'); - }; + // root element + checkSetters(fixture.componentRef, fixture.debugElement.nativeElement); + // nested elements + checkSetters( + fixture.componentRef, fixture.debugElement.children[0].nativeElement); - // root element - checkSetters(fixture.componentRef, fixture.debugElement.nativeElement); - // nested elements - checkSetters( - fixture.componentRef, fixture.debugElement.children[0].nativeElement); + async.done(); + }); + })); - async.done(); - }); - })); + it('should update any template comment property/attributes', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + var tpl = ''; + tcb.overrideView(MyComp2, new ViewMetadata({template: tpl, directives: [NgIf]})) - it('should update any template comment property/attributes', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var tpl = ''; - tcb.overrideView(MyComp2, new ViewMetadata({template: tpl, directives: [NgIf]})) + .createAsync(MyComp2) + .then((fixture) => { + (fixture.debugElement.componentInstance).ctxBoolProp = true; + fixture.detectChanges(); + var el = getRenderElement(fixture.debugElement.nativeElement); + expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"'); + async.done(); + }); + })); - .createAsync(MyComp2) - .then((fixture) => { - (fixture.debugElement.componentInstance).ctxBoolProp = true; - fixture.detectChanges(); - var el = getRenderElement(fixture.debugElement.nativeElement); - expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"'); - async.done(); - }); - })); + it('should add and remove fragments', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + tcb.overrideView(MyComp2, new ViewMetadata({ + template: '', + directives: [NgIf] + })) + .createAsync(MyComp2) + .then((fixture) => { - it('should add and remove fragments', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView(MyComp2, new ViewMetadata({ - template: '', - directives: [NgIf] - })) - .createAsync(MyComp2) - .then((fixture) => { + var rootEl = getRenderElement(fixture.debugElement.nativeElement); + expect(rootEl).toHaveText(''); - var rootEl = getRenderElement(fixture.debugElement.nativeElement); - expect(rootEl).toHaveText(''); + fixture.debugElement.componentInstance.ctxBoolProp = true; + fixture.detectChanges(); + expect(rootEl).toHaveText('hello'); - fixture.debugElement.componentInstance.ctxBoolProp = true; - fixture.detectChanges(); - expect(rootEl).toHaveText('hello'); + fixture.debugElement.componentInstance.ctxBoolProp = false; + fixture.detectChanges(); + expect(rootEl).toHaveText(''); - fixture.debugElement.componentInstance.ctxBoolProp = false; - fixture.detectChanges(); - expect(rootEl).toHaveText(''); + async.done(); + }); + })); - async.done(); - }); - })); + if (getDOM().supportsDOMEvents()) { + it('should call actions on the element', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + tcb.overrideView(MyComp2, new ViewMetadata({template: ''})) + .createAsync(MyComp2) + .then((fixture) => { + var el = fixture.debugElement.children[0]; + getRenderer(fixture.componentRef) + .invokeElementMethod(el.nativeElement, 'setAttribute', ['a', 'b']); - if (getDOM().supportsDOMEvents()) { - it('should call actions on the element', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView(MyComp2, new ViewMetadata({template: ''})) - .createAsync(MyComp2) - .then((fixture) => { - var el = fixture.debugElement.children[0]; - getRenderer(fixture.componentRef) - .invokeElementMethod(el.nativeElement, 'setAttribute', ['a', 'b']); + expect(getDOM().getAttribute(getRenderElement(el.nativeElement), 'a')) + .toEqual('b'); + async.done(); + }); + })); - expect(getDOM().getAttribute(getRenderElement(el.nativeElement), 'a')) - .toEqual('b'); - async.done(); - }); - })); + it('should listen to events', + inject( + [TestComponentBuilder, AsyncTestCompleter], + (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { + tcb.overrideView( + MyComp2, + new ViewMetadata({template: ''})) + .createAsync(MyComp2) + .then((fixture) => { + var el = fixture.debugElement.children[0]; + dispatchEvent(getRenderElement(el.nativeElement), 'change'); + expect(fixture.componentInstance.ctxNumProp).toBe(1); - it('should listen to events', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp2, new ViewMetadata({template: ''})) - .createAsync(MyComp2) - .then((fixture) => { - var el = fixture.debugElement.children[0]; - dispatchEvent(getRenderElement(el.nativeElement), 'change'); - expect(fixture.componentInstance.ctxNumProp).toBe(1); + fixture.destroy(); - fixture.destroy(); - - async.done(); - }); - })); - } - }); + async.done(); + }); + })); + } + }); } -@Component({selector: 'my-comp', directives: []}) -@Injectable() -class MyComp2 { - ctxProp: string; - ctxNumProp: any /** TODO #9100 */; - ctxBoolProp: boolean; - constructor() { +@Component( + {selector: 'my-comp', + directives: []}) @Injectable() class MyComp2 { + ctxProp: string; ctxNumProp: any /** TODO #9100 */; ctxBoolProp: boolean; constructor() { this.ctxProp = 'initial value'; this.ctxNumProp = 0; this.ctxBoolProp = false; } - throwError() { throw 'boom'; } + throwError() { throw 'boom';} } diff --git a/modules/@angular/platform-browser/testing/browser.ts b/modules/@angular/platform-browser/testing/browser.ts index 8fe8de5855..ba3bc7b9e1 100644 --- a/modules/@angular/platform-browser/testing/browser.ts +++ b/modules/@angular/platform-browser/testing/browser.ts @@ -7,31 +7,16 @@ */ import {LocationStrategy} from '@angular/common'; -import {APP_ID, NgZone, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER} from '@angular/core'; +import {APP_ID, AppModule, NgZone, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, getPlatform} from '@angular/core'; -import {BROWSER_APP_PROVIDERS} from '../src/browser'; +import {BROWSER_APP_PROVIDERS, BrowserModule} from '../src/browser'; import {BrowserDomAdapter} from '../src/browser/browser_adapter'; import {AnimationDriver} from '../src/dom/animation_driver'; import {ELEMENT_PROBE_PROVIDERS} from '../src/dom/debug/ng_probe'; import {BrowserDetection} from './browser_util'; - - -/** - * Default platform providers for testing without a compiler. - */ -const TEST_BROWSER_STATIC_PLATFORM_PROVIDERS: Array = [ - PLATFORM_COMMON_PROVIDERS, - {provide: PLATFORM_INITIALIZER, useValue: initBrowserTests, multi: true} -]; - -const ADDITIONAL_TEST_BROWSER_STATIC_PROVIDERS: Array = [ - {provide: APP_ID, useValue: 'a'}, ELEMENT_PROBE_PROVIDERS, - {provide: NgZone, useFactory: createNgZone}, - {provide: AnimationDriver, useValue: AnimationDriver.NOOP} -]; - +const BROWSER_TEST_PLATFORM_MARKER = new OpaqueToken('BrowserTestPlatformMarker'); function initBrowserTests() { BrowserDomAdapter.makeCurrent(); @@ -42,19 +27,35 @@ function createNgZone(): NgZone { return new NgZone({enableLongStackTrace: true}); } +const TEST_BROWSER_PLATFORM_PROVIDERS: Array = [ + PLATFORM_COMMON_PROVIDERS, {provide: BROWSER_TEST_PLATFORM_MARKER, useValue: true}, + {provide: PLATFORM_INITIALIZER, useValue: initBrowserTests, multi: true} +]; /** - * Default platform providers for testing. + * Platform for testing + * + * @experimental API related to bootstrapping are still under review. + */ +export function browserTestPlatform(): PlatformRef { + if (!getPlatform()) { + createPlatform(ReflectiveInjector.resolveAndCreate(TEST_BROWSER_PLATFORM_PROVIDERS)); + } + return assertPlatform(BROWSER_TEST_PLATFORM_MARKER); +} + +/** + * AppModule for testing. * * @stable */ -export const TEST_BROWSER_PLATFORM_PROVIDERS: Array = - TEST_BROWSER_STATIC_PLATFORM_PROVIDERS; - -/** - * Default application providers for testing without a compiler. - * - * @stable - */ -export const TEST_BROWSER_APPLICATION_PROVIDERS: Array = - [BROWSER_APP_PROVIDERS, ADDITIONAL_TEST_BROWSER_STATIC_PROVIDERS]; +@AppModule({ + modules: [BrowserModule], + providers: [ + {provide: APP_ID, useValue: 'a'}, ELEMENT_PROBE_PROVIDERS, + {provide: NgZone, useFactory: createNgZone}, + {provide: AnimationDriver, useValue: AnimationDriver.NOOP} + ] +}) +export class BrowserTestModule { +} diff --git a/modules/@angular/platform-server/test/integration_spec.ts b/modules/@angular/platform-server/test/integration_spec.ts index 7e4721cfac..2d0c406b81 100644 --- a/modules/@angular/platform-server/test/integration_spec.ts +++ b/modules/@angular/platform-server/test/integration_spec.ts @@ -26,6 +26,7 @@ export function main() { describe('platform-server integration', () => { + beforeEach(() => disposePlatform()); afterEach(() => disposePlatform()); it('should bootstrap', async(() => { diff --git a/modules/@angular/platform-server/testing/server.ts b/modules/@angular/platform-server/testing/server.ts index 6a2dc8d3d4..022be046fc 100644 --- a/modules/@angular/platform-server/testing/server.ts +++ b/modules/@angular/platform-server/testing/server.ts @@ -6,76 +6,48 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_PROVIDERS, DirectiveResolver, ViewResolver, XHR} from '@angular/compiler'; -import {MockDirectiveResolver, MockViewResolver, OverridingTestComponentBuilder} from '@angular/compiler/testing'; -import {APPLICATION_COMMON_PROVIDERS, APP_ID, NgZone, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, RootRenderer} from '@angular/core'; -import {TestComponentBuilder, TestComponentRenderer} from '@angular/core/testing'; -import {AnimationDriver, BROWSER_SANITIZATION_PROVIDERS, DOCUMENT, EVENT_MANAGER_PLUGINS, EventManager} from '@angular/platform-browser'; +import {AppModule, OpaqueToken, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, PlatformRef, ReflectiveInjector, assertPlatform, createPlatform, getPlatform} from '@angular/core'; +import {BrowserDynamicTestModule, browserTestCompiler} from '@angular/platform-browser-dynamic/testing'; -import {DOMTestComponentRenderer} from '../platform_browser_dynamic_testing_private'; -import {DomEventsPlugin, DomRootRenderer, DomRootRenderer_, DomSharedStylesHost, ELEMENT_PROBE_PROVIDERS, SharedStylesHost, getDOM} from '../platform_browser_private'; import {Parse5DomAdapter} from '../src/parse5_adapter'; +const SERVER_TEST_PLATFORM_MARKER = new OpaqueToken('ServerTestPlatformMarker'); + function initServerTests() { Parse5DomAdapter.makeCurrent(); } /** - * Default platform providers for testing. + * Creates a compiler for testing * - * @experimental + * @stable */ -export const TEST_SERVER_PLATFORM_PROVIDERS: Array = +export const serverTestCompiler = browserTestCompiler; + +const TEST_SERVER_PLATFORM_PROVIDERS: Array = /*@ts2dart_const*/[ PLATFORM_COMMON_PROVIDERS, - /*@ts2dart_Provider*/ { - provide: PLATFORM_INITIALIZER, - useValue: initServerTests, - multi: true - } + /*@ts2dart_Provider*/ {provide: PLATFORM_INITIALIZER, useValue: initServerTests, multi: true}, + {provide: SERVER_TEST_PLATFORM_MARKER, useValue: true} ]; -function appDoc() { - try { - return getDOM().defaultDoc(); - } catch (e) { - return null; - } -} - - -function createNgZone(): NgZone { - return new NgZone({enableLongStackTrace: true}); -} - - /** - * Default application providers for testing. + * Platform for testing * - * @experimental + * @experimental API related to bootstrapping are still under review. */ -export const TEST_SERVER_APPLICATION_PROVIDERS: Array = - /*@ts2dart_const*/[ - // TODO(julie: when angular2/platform/server is available, use that instead of making our own - // list here. - APPLICATION_COMMON_PROVIDERS, COMPILER_PROVIDERS, BROWSER_SANITIZATION_PROVIDERS, - /* @ts2dart_Provider */ {provide: DOCUMENT, useFactory: appDoc}, - /* @ts2dart_Provider */ {provide: DomRootRenderer, useClass: DomRootRenderer_}, - /* @ts2dart_Provider */ {provide: RootRenderer, useExisting: DomRootRenderer}, - /* @ts2dart_Provider */ {provide: AnimationDriver, useValue: AnimationDriver.NOOP}, - EventManager, - /* @ts2dart_Provider */ { - provide: EVENT_MANAGER_PLUGINS, - useClass: DomEventsPlugin, - multi: true - }, - /* @ts2dart_Provider */ {provide: XHR, useClass: XHR}, - /* @ts2dart_Provider */ {provide: APP_ID, useValue: 'a'}, - /* @ts2dart_Provider */ {provide: SharedStylesHost, useExisting: DomSharedStylesHost}, - DomSharedStylesHost, ELEMENT_PROBE_PROVIDERS, - {provide: TestComponentBuilder, useClass: OverridingTestComponentBuilder}, - /* @ts2dart_Provider */ {provide: DirectiveResolver, useClass: MockDirectiveResolver}, - /* @ts2dart_Provider */ {provide: ViewResolver, useClass: MockViewResolver}, - /* @ts2dart_Provider */ {provide: TestComponentRenderer, useClass: DOMTestComponentRenderer}, - /* @ts2dart_Provider */ {provide: NgZone, useFactory: createNgZone} - ]; +export function serverTestPlatform(): PlatformRef { + if (!getPlatform()) { + createPlatform(ReflectiveInjector.resolveAndCreate(TEST_SERVER_PLATFORM_PROVIDERS)); + } + return assertPlatform(SERVER_TEST_PLATFORM_MARKER); +} + +/** + * AppModule for testing. + * + * @stable + */ +@AppModule({modules: [BrowserDynamicTestModule]}) +export class ServerTestModule { +} diff --git a/modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts b/modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts index d753251fde..8458ff1f69 100644 --- a/modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts +++ b/modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts @@ -9,6 +9,7 @@ import {APP_BASE_HREF, LocationStrategy} from '@angular/common'; import {MockLocationStrategy} from '@angular/common/testing/mock_location_strategy'; import {TestComponentBuilder} from '@angular/compiler/testing'; +import {disposePlatform} from '@angular/core'; import {ApplicationRef} from '@angular/core/src/application_ref'; import {Console} from '@angular/core/src/console'; import {Component} from '@angular/core/src/metadata'; @@ -39,6 +40,9 @@ export function main() { useClass: MockApplicationRef }]); + beforeEach(() => disposePlatform()); + afterEach(() => disposePlatform()); + // do not refactor out the `bootstrap` functionality. We still want to // keep this test around so we can ensure that bootstrap a router works it('should bootstrap a simple app', diff --git a/modules/@angular/router-deprecated/test/route_config/route_config_spec.ts b/modules/@angular/router-deprecated/test/route_config/route_config_spec.ts index 6b1006f2c2..ef9e58eabd 100644 --- a/modules/@angular/router-deprecated/test/route_config/route_config_spec.ts +++ b/modules/@angular/router-deprecated/test/route_config/route_config_spec.ts @@ -13,7 +13,7 @@ import {APP_BASE_HREF, LocationStrategy} from '@angular/common'; import {Component, Directive} from '@angular/core/src/metadata'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {Console} from '@angular/core/src/console'; -import {provide} from '@angular/core'; +import {provide, disposePlatform} from '@angular/core'; import {DOCUMENT} from '@angular/platform-browser/src/dom/dom_tokens'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; import {ROUTER_PROVIDERS, Router, RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated'; @@ -38,6 +38,7 @@ export function main() { var fakeDoc: any /** TODO #9100 */, el: any /** TODO #9100 */, testBindings: any /** TODO #9100 */; beforeEach(() => { + disposePlatform(); fakeDoc = getDOM().createHtmlDocument(); el = getDOM().createElement('app-cmp', fakeDoc); getDOM().appendChild(fakeDoc.body, el); @@ -51,6 +52,8 @@ export function main() { ]; }); + afterEach(() => disposePlatform()); + it('should bootstrap an app with a hierarchy', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { bootstrap(HierarchyAppCmp, testBindings).then((applicationRef) => { diff --git a/modules/@angular/router/karma-test-shim.js b/modules/@angular/router/karma-test-shim.js index 089d9c1ae9..6768c2e80c 100644 --- a/modules/@angular/router/karma-test-shim.js +++ b/modules/@angular/router/karma-test-shim.js @@ -72,8 +72,10 @@ Promise.all([ var testing = providers[0]; var testingBrowser = providers[1]; - testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, - testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS); + testing.initTestEnvironment( + testingBrowser.browserTestCompiler, + testingBrowser.browserDynamicTestPlatform(), + testingBrowser.BrowserDynamicTestModule); }).then(function() { // Finally, load all spec files. diff --git a/modules/@angular/upgrade/test/upgrade_spec.ts b/modules/@angular/upgrade/test/upgrade_spec.ts index 72e647393e..a2a27e025e 100644 --- a/modules/@angular/upgrade/test/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/upgrade_spec.ts @@ -6,13 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {Class, Component, EventEmitter, Testability, provide} from '@angular/core'; -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; +import {Class, Component, EventEmitter, Testability, disposePlatform, provide} from '@angular/core'; +import {AsyncTestCompleter, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; import {UpgradeAdapter} from '@angular/upgrade'; import * as angular from '@angular/upgrade/src/angular_js'; export function main() { describe('adapter: ng1 to ng2', () => { + beforeEach(() => disposePlatform()); + afterEach(() => disposePlatform()); + it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1)); it('should instantiate ng2 in ng1 template and project content', diff --git a/test-main.js b/test-main.js index a0118f8aff..a00efba040 100644 --- a/test-main.js +++ b/test-main.js @@ -74,11 +74,11 @@ System.config({ System.import('@angular/core/testing') .then(function(coreTesting){ return System.import('@angular/platform-browser-dynamic/testing') - .then(function(browserTesting){ - coreTesting.setBaseTestProviders( - browserTesting.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, - browserTesting.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS - ); + .then(function(browserTesting) { + coreTesting.initTestEnvironment( + browserTesting.browserTestCompiler, + browserTesting.browserDynamicTestPlatform(), + browserTesting.BrowserDynamicTestModule); }); }) .then(function() { diff --git a/tools/cjs-jasmine/test-cjs-main.ts b/tools/cjs-jasmine/test-cjs-main.ts index f201293084..9b223fe62a 100644 --- a/tools/cjs-jasmine/test-cjs-main.ts +++ b/tools/cjs-jasmine/test-cjs-main.ts @@ -1,6 +1,6 @@ var testingPlatformServer = require('../../all/@angular/platform-server/testing/server.js'); var testing = require('../../all/@angular/core/testing'); -testing.setBaseTestProviders( - testingPlatformServer.TEST_SERVER_PLATFORM_PROVIDERS, - testingPlatformServer.TEST_SERVER_APPLICATION_PROVIDERS); +testing.initTestEnvironment( + testingPlatformServer.serverTestCompiler, testingPlatformServer.serverTestPlatform(), + testingPlatformServer.ServerTestModule); diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index 3e2978f537..453f3be29d 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -290,6 +290,7 @@ export declare class CollectionChangeRecord { /** @stable */ export declare class Compiler { + injector: Injector; clearCache(): void; clearCacheFor(type: Type): void; compileAppModuleAsync(moduleType: ConcreteType, metadata?: AppModuleMetadata): Promise>; @@ -451,6 +452,12 @@ export declare abstract class ComponentResolver { abstract resolveComponent(component: Type | string): Promise>; } +/** @stable */ +export declare class ComponentStillLoadingError extends BaseException { + compType: Type; + constructor(compType: Type); +} + /** @stable */ export declare var ContentChild: ContentChildMetadataFactory; diff --git a/tools/public_api_guard/core/testing.d.ts b/tools/public_api_guard/core/testing.d.ts index 908a6bf594..555374fff1 100644 --- a/tools/public_api_guard/core/testing.d.ts +++ b/tools/public_api_guard/core/testing.d.ts @@ -37,6 +37,21 @@ export declare var ComponentFixtureAutoDetect: OpaqueToken; /** @experimental */ export declare var ComponentFixtureNoNgZone: OpaqueToken; +/** @stable */ +export declare function configureCompiler(config: { + providers?: any[]; + useJit?: boolean; +}): void; + +/** @stable */ +export declare function configureModule(moduleDef: { + providers?: any[]; + directives?: any[]; + pipes?: any[]; + precompile?: any[]; + modules?: any[]; +}): void; + /** @deprecated */ export declare var ddescribe: any; @@ -67,12 +82,21 @@ export declare function getTestInjector(): TestInjector; /** @deprecated */ export declare var iit: any; +/** @experimental */ +export declare function initTestEnvironment(compilerFactory: TestCompilerFactory, platform: PlatformRef, appModule: Type): void; + /** @stable */ export declare function inject(tokens: any[], fn: Function): () => any; /** @experimental */ export declare class InjectSetupWrapper { - constructor(_providers: () => any); + constructor(_moduleDef: () => { + providers?: any[]; + directives?: any[]; + pipes?: any[]; + precompile?: any[]; + modules?: any[]; + }); inject(tokens: any[], fn: Function): () => any; } @@ -80,10 +104,13 @@ export declare class InjectSetupWrapper { export declare var it: any; /** @experimental */ -export declare function resetBaseTestProviders(): void; +export declare function resetTestEnvironment(): void; /** @experimental */ -export declare function setBaseTestProviders(platformProviders: Array, applicationProviders: Array): void; +export declare type TestCompilerFactory = (config: { + providers?: Array; + useJit?: boolean; +}) => Compiler; /** @stable */ export declare class TestComponentBuilder { @@ -107,19 +134,40 @@ export declare class TestComponentRenderer { } /** @experimental */ -export declare class TestInjector { - applicationProviders: Array; - platformProviders: Array; - addProviders(providers: Array): void; - createInjector(): ReflectiveInjector; +export declare class TestInjector implements Injector { + appModule: Type; + compilerFactory: TestCompilerFactory; + platform: PlatformRef; + configureCompiler(config: { + providers?: any[]; + useJit?: boolean; + }): void; + configureModule(moduleDef: { + providers?: any[]; + directives?: any[]; + pipes?: any[]; + precompile?: any[]; + modules?: any[]; + }): void; + createInjectorAsync(): Promise; + createInjectorSync(): Injector; execute(tokens: any[], fn: Function): any; - get(token: any): any; + get(token: any, notFoundValue?: any): any; reset(): void; } /** @experimental */ export declare function tick(millis?: number): void; +/** @experimental */ +export declare function withModule(moduleDef: () => { + providers?: any[]; + directives?: any[]; + pipes?: any[]; + precompile?: any[]; + modules?: any[]; +}): InjectSetupWrapper; + /** @experimental */ export declare function withProviders(providers: () => any): InjectSetupWrapper; diff --git a/tools/public_api_guard/platform-browser-dynamic/testing.d.ts b/tools/public_api_guard/platform-browser-dynamic/testing.d.ts index c43fe0b985..f83d623436 100644 --- a/tools/public_api_guard/platform-browser-dynamic/testing.d.ts +++ b/tools/public_api_guard/platform-browser-dynamic/testing.d.ts @@ -1,5 +1,12 @@ /** @stable */ -export declare const TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS: Array; +export declare class BrowserDynamicTestModule { +} + +/** @experimental */ +export declare const browserDynamicTestPlatform: typeof browserTestPlatform; /** @stable */ -export declare const TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS: Array; +export declare function browserTestCompiler({providers, useJit}?: { + providers?: Array; + useJit?: boolean; +}): Compiler; diff --git a/tools/public_api_guard/platform-browser/testing.d.ts b/tools/public_api_guard/platform-browser/testing.d.ts index 7671383028..19248cb7c7 100644 --- a/tools/public_api_guard/platform-browser/testing.d.ts +++ b/tools/public_api_guard/platform-browser/testing.d.ts @@ -1,5 +1,6 @@ /** @stable */ -export declare const TEST_BROWSER_APPLICATION_PROVIDERS: Array; +export declare class BrowserTestModule { +} -/** @stable */ -export declare const TEST_BROWSER_PLATFORM_PROVIDERS: Array; +/** @experimental */ +export declare function browserTestPlatform(): PlatformRef; diff --git a/tools/public_api_guard/platform-server/testing.d.ts b/tools/public_api_guard/platform-server/testing.d.ts index 97bba15f09..d3dd91345a 100644 --- a/tools/public_api_guard/platform-server/testing.d.ts +++ b/tools/public_api_guard/platform-server/testing.d.ts @@ -1,5 +1,9 @@ -/** @experimental */ -export declare const TEST_SERVER_APPLICATION_PROVIDERS: Array; +/** @stable */ +export declare const serverTestCompiler: typeof browserTestCompiler; + +/** @stable */ +export declare class ServerTestModule { +} /** @experimental */ -export declare const TEST_SERVER_PLATFORM_PROVIDERS: Array; +export declare function serverTestPlatform(): PlatformRef;