There is an inconsistency in overrideProvider behaviour. Testing documentation says (https://angular.io/guide/testing-components-basics#createcomponent) that all override... methods throw error if TestBed is already instantiated. However overrideProvider doesn't throw any error, but (same as other override... methods) doesn't replace providers if TestBed is instantiated. Add TestBed instantiation check to overrideProvider method to make it consistent. BREAKING CHANGE: If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function. Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an error, thus there is a chance that some tests (where `TestBed.overrideProvider` is called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls before TestBed initialization is completed. Issue mentioned here: https://github.com/angular/angular/issues/13460#issuecomment-636005966 Documentation: https://angular.io/guide/testing-components-basics#createcomponent PR Close #38717
746 lines
26 KiB
TypeScript
746 lines
26 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import {AbstractType, ApplicationInitStatus, CompilerOptions, Component, Directive, InjectFlags, InjectionToken, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵclearOverrides as clearOverrides, ɵDepFlags as DepFlags, ɵgetInjectableDef as getInjectableDef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵivyEnabled as ivyEnabled, ɵNodeFlags as NodeFlags, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify, ɵɵInjectableDef} from '@angular/core';
|
|
|
|
import {AsyncTestCompleter} from './async_test_completer';
|
|
import {ComponentFixture} from './component_fixture';
|
|
import {MetadataOverride} from './metadata_override';
|
|
import {_getTestBedRender3, TestBedRender3} from './r3_test_bed';
|
|
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
|
|
import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
|
|
|
|
|
|
let _nextRootElementId = 0;
|
|
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
export interface TestBed {
|
|
platform: PlatformRef;
|
|
|
|
ngModule: Type<any>|Type<any>[];
|
|
|
|
/**
|
|
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
|
* angular module. These are common to every test in the suite.
|
|
*
|
|
* This may only be called once, to set up the common providers for the current test
|
|
* suite on the current platform. If you absolutely need to change the providers,
|
|
* first use `resetTestEnvironment`.
|
|
*
|
|
* Test modules and platforms for individual platforms are available from
|
|
* '@angular/<platform_name>/testing'.
|
|
*/
|
|
initTestEnvironment(
|
|
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void;
|
|
|
|
/**
|
|
* Reset the providers for the test injector.
|
|
*/
|
|
resetTestEnvironment(): void;
|
|
|
|
resetTestingModule(): void;
|
|
|
|
configureCompiler(config: {providers?: any[], useJit?: boolean}): void;
|
|
|
|
configureTestingModule(moduleDef: TestModuleMetadata): void;
|
|
|
|
compileComponents(): Promise<any>;
|
|
|
|
inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
|
inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue: null, flags?: InjectFlags): T
|
|
|null;
|
|
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): any;
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
get(token: any, notFoundValue?: any): any;
|
|
|
|
execute(tokens: any[], fn: Function, context?: any): any;
|
|
|
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
|
|
|
|
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
|
|
|
|
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
|
|
|
|
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
|
|
|
|
/**
|
|
* Overwrites all providers for the given token with the given provider definition.
|
|
*/
|
|
overrideProvider(token: any, provider: {
|
|
useFactory: Function,
|
|
deps: any[],
|
|
}): void;
|
|
overrideProvider(token: any, provider: {useValue: any;}): void;
|
|
overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}):
|
|
void;
|
|
|
|
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void;
|
|
|
|
createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
|
}
|
|
|
|
/**
|
|
* @description
|
|
* Configures and initializes environment for unit testing and provides methods for
|
|
* creating components and services in unit tests.
|
|
*
|
|
* `TestBed` is the primary api for writing unit tests for Angular applications and libraries.
|
|
*
|
|
* Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3`
|
|
* according to the compiler used.
|
|
*/
|
|
export class TestBedViewEngine implements TestBed {
|
|
/**
|
|
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
|
* angular module. These are common to every test in the suite.
|
|
*
|
|
* This may only be called once, to set up the common providers for the current test
|
|
* suite on the current platform. If you absolutely need to change the providers,
|
|
* first use `resetTestEnvironment`.
|
|
*
|
|
* Test modules and platforms for individual platforms are available from
|
|
* '@angular/<platform_name>/testing'.
|
|
*/
|
|
static initTestEnvironment(
|
|
ngModule: Type<any>|Type<any>[], platform: PlatformRef,
|
|
aotSummaries?: () => any[]): TestBedViewEngine {
|
|
const testBed = _getTestBedViewEngine();
|
|
testBed.initTestEnvironment(ngModule, platform, aotSummaries);
|
|
return testBed;
|
|
}
|
|
|
|
/**
|
|
* Reset the providers for the test injector.
|
|
*/
|
|
static resetTestEnvironment(): void {
|
|
_getTestBedViewEngine().resetTestEnvironment();
|
|
}
|
|
|
|
static resetTestingModule(): TestBedStatic {
|
|
_getTestBedViewEngine().resetTestingModule();
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
/**
|
|
* Allows overriding default compiler providers and settings
|
|
* which are defined in test_injector.js
|
|
*/
|
|
static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic {
|
|
_getTestBedViewEngine().configureCompiler(config);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
/**
|
|
* Allows overriding default providers, directives, pipes, modules of the test injector,
|
|
* which are defined in test_injector.js
|
|
*/
|
|
static configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic {
|
|
_getTestBedViewEngine().configureTestingModule(moduleDef);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
/**
|
|
* Compile components with a `templateUrl` for the test's NgModule.
|
|
* It is necessary to call this function
|
|
* as fetching urls is asynchronous.
|
|
*/
|
|
static compileComponents(): Promise<any> {
|
|
return getTestBed().compileComponents();
|
|
}
|
|
|
|
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic {
|
|
_getTestBedViewEngine().overrideModule(ngModule, override);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
|
|
TestBedStatic {
|
|
_getTestBedViewEngine().overrideComponent(component, override);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
|
|
TestBedStatic {
|
|
_getTestBedViewEngine().overrideDirective(directive, override);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic {
|
|
_getTestBedViewEngine().overridePipe(pipe, override);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
static overrideTemplate(component: Type<any>, template: string): TestBedStatic {
|
|
_getTestBedViewEngine().overrideComponent(component, {set: {template, templateUrl: null!}});
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
/**
|
|
* Overrides the template of the given component, compiling the template
|
|
* in the context of the TestingModule.
|
|
*
|
|
* Note: This works for JIT and AOTed components as well.
|
|
*/
|
|
static overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic {
|
|
_getTestBedViewEngine().overrideTemplateUsingTestingModule(component, template);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
/**
|
|
* Overwrites all providers for the given token with the given provider definition.
|
|
*
|
|
* Note: This works for JIT and AOTed components as well.
|
|
*/
|
|
static overrideProvider(token: any, provider: {
|
|
useFactory: Function,
|
|
deps: any[],
|
|
}): TestBedStatic;
|
|
static overrideProvider(token: any, provider: {useValue: any;}): TestBedStatic;
|
|
static overrideProvider(token: any, provider: {
|
|
useFactory?: Function,
|
|
useValue?: any,
|
|
deps?: any[],
|
|
}): TestBedStatic {
|
|
_getTestBedViewEngine().overrideProvider(token, provider as any);
|
|
return TestBedViewEngine as any as TestBedStatic;
|
|
}
|
|
|
|
static inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
|
static inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue: null, flags?: InjectFlags): T
|
|
|null;
|
|
static inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T|null,
|
|
flags?: InjectFlags): T|null {
|
|
return _getTestBedViewEngine().inject(token, notFoundValue, flags);
|
|
}
|
|
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
static get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): any;
|
|
/**
|
|
* @deprecated from v9.0.0 use TestBed.inject
|
|
* @suppress {duplicate}
|
|
*/
|
|
static get(token: any, notFoundValue?: any): any;
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
static get(
|
|
token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND,
|
|
flags: InjectFlags = InjectFlags.Default): any {
|
|
return _getTestBedViewEngine().inject(token, notFoundValue, flags);
|
|
}
|
|
|
|
static createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
|
return _getTestBedViewEngine().createComponent(component);
|
|
}
|
|
|
|
private _instantiated: boolean = false;
|
|
|
|
private _compiler: TestingCompiler = null!;
|
|
private _moduleRef: NgModuleRef<any> = null!;
|
|
private _moduleFactory: NgModuleFactory<any> = null!;
|
|
|
|
private _compilerOptions: CompilerOptions[] = [];
|
|
|
|
private _moduleOverrides: [Type<any>, MetadataOverride<NgModule>][] = [];
|
|
private _componentOverrides: [Type<any>, MetadataOverride<Component>][] = [];
|
|
private _directiveOverrides: [Type<any>, MetadataOverride<Directive>][] = [];
|
|
private _pipeOverrides: [Type<any>, MetadataOverride<Pipe>][] = [];
|
|
|
|
private _providers: Provider[] = [];
|
|
private _declarations: Array<Type<any>|any[]|any> = [];
|
|
private _imports: Array<Type<any>|any[]|any> = [];
|
|
private _schemas: Array<SchemaMetadata|any[]> = [];
|
|
private _activeFixtures: ComponentFixture<any>[] = [];
|
|
|
|
private _testEnvAotSummaries: () => any[] = () => [];
|
|
private _aotSummaries: Array<() => any[]> = [];
|
|
private _templateOverrides: Array<{component: Type<any>, templateOf: Type<any>}> = [];
|
|
|
|
private _isRoot: boolean = true;
|
|
private _rootProviderOverrides: Provider[] = [];
|
|
|
|
platform: PlatformRef = null!;
|
|
|
|
ngModule: Type<any>|Type<any>[] = null!;
|
|
|
|
/**
|
|
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
|
|
* angular module. These are common to every test in the suite.
|
|
*
|
|
* This may only be called once, to set up the common providers for the current test
|
|
* suite on the current platform. If you absolutely need to change the providers,
|
|
* first use `resetTestEnvironment`.
|
|
*
|
|
* Test modules and platforms for individual platforms are available from
|
|
* '@angular/<platform_name>/testing'.
|
|
*/
|
|
initTestEnvironment(
|
|
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void {
|
|
if (this.platform || this.ngModule) {
|
|
throw new Error('Cannot set base providers because it has already been called');
|
|
}
|
|
this.platform = platform;
|
|
this.ngModule = ngModule;
|
|
if (aotSummaries) {
|
|
this._testEnvAotSummaries = aotSummaries;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the providers for the test injector.
|
|
*/
|
|
resetTestEnvironment(): void {
|
|
this.resetTestingModule();
|
|
this.platform = null!;
|
|
this.ngModule = null!;
|
|
this._testEnvAotSummaries = () => [];
|
|
}
|
|
|
|
resetTestingModule(): void {
|
|
clearOverrides();
|
|
this._aotSummaries = [];
|
|
this._templateOverrides = [];
|
|
this._compiler = null!;
|
|
this._moduleOverrides = [];
|
|
this._componentOverrides = [];
|
|
this._directiveOverrides = [];
|
|
this._pipeOverrides = [];
|
|
|
|
this._isRoot = true;
|
|
this._rootProviderOverrides = [];
|
|
|
|
this._moduleRef = null!;
|
|
this._moduleFactory = null!;
|
|
this._compilerOptions = [];
|
|
this._providers = [];
|
|
this._declarations = [];
|
|
this._imports = [];
|
|
this._schemas = [];
|
|
this._instantiated = false;
|
|
this._activeFixtures.forEach((fixture) => {
|
|
try {
|
|
fixture.destroy();
|
|
} catch (e) {
|
|
console.error('Error during cleanup of component', {
|
|
component: fixture.componentInstance,
|
|
stacktrace: e,
|
|
});
|
|
}
|
|
});
|
|
this._activeFixtures = [];
|
|
}
|
|
|
|
configureCompiler(config: {providers?: any[], useJit?: boolean}): void {
|
|
this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler');
|
|
this._compilerOptions.push(config);
|
|
}
|
|
|
|
configureTestingModule(moduleDef: TestModuleMetadata): void {
|
|
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
|
|
if (moduleDef.providers) {
|
|
this._providers.push(...moduleDef.providers);
|
|
}
|
|
if (moduleDef.declarations) {
|
|
this._declarations.push(...moduleDef.declarations);
|
|
}
|
|
if (moduleDef.imports) {
|
|
this._imports.push(...moduleDef.imports);
|
|
}
|
|
if (moduleDef.schemas) {
|
|
this._schemas.push(...moduleDef.schemas);
|
|
}
|
|
if (moduleDef.aotSummaries) {
|
|
this._aotSummaries.push(moduleDef.aotSummaries);
|
|
}
|
|
}
|
|
|
|
compileComponents(): Promise<any> {
|
|
if (this._moduleFactory || this._instantiated) {
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
const moduleType = this._createCompilerAndModule();
|
|
return this._compiler.compileModuleAndAllComponentsAsync(moduleType)
|
|
.then((moduleAndComponentFactories) => {
|
|
this._moduleFactory = moduleAndComponentFactories.ngModuleFactory;
|
|
});
|
|
}
|
|
|
|
private _initIfNeeded(): void {
|
|
if (this._instantiated) {
|
|
return;
|
|
}
|
|
if (!this._moduleFactory) {
|
|
try {
|
|
const moduleType = this._createCompilerAndModule();
|
|
this._moduleFactory =
|
|
this._compiler.compileModuleAndAllComponentsSync(moduleType).ngModuleFactory;
|
|
} catch (e) {
|
|
const errorCompType = this._compiler.getComponentFromError(e);
|
|
if (errorCompType) {
|
|
throw new Error(
|
|
`This test module uses the component ${
|
|
stringify(
|
|
errorCompType)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
|
|
`Please call "TestBed.compileComponents" before your test.`);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
for (const {component, templateOf} of this._templateOverrides) {
|
|
const compFactory = this._compiler.getComponentFactory(templateOf);
|
|
overrideComponentView(component, compFactory);
|
|
}
|
|
|
|
const ngZone =
|
|
new NgZone({enableLongStackTrace: true, shouldCoalesceEventChangeDetection: false});
|
|
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
|
|
const ngZoneInjector = Injector.create({
|
|
providers: providers,
|
|
parent: this.platform.injector,
|
|
name: this._moduleFactory.moduleType.name
|
|
});
|
|
this._moduleRef = this._moduleFactory.create(ngZoneInjector);
|
|
// ApplicationInitStatus.runInitializers() is marked @internal to core. So casting to any
|
|
// before accessing it.
|
|
(this._moduleRef.injector.get(ApplicationInitStatus) as any).runInitializers();
|
|
this._instantiated = true;
|
|
}
|
|
|
|
private _createCompilerAndModule(): Type<any> {
|
|
const providers = this._providers.concat([{provide: TestBed, useValue: this}]);
|
|
const declarations =
|
|
[...this._declarations, ...this._templateOverrides.map(entry => entry.templateOf)];
|
|
|
|
const rootScopeImports = [];
|
|
const rootProviderOverrides = this._rootProviderOverrides;
|
|
if (this._isRoot) {
|
|
@NgModule({
|
|
providers: [
|
|
...rootProviderOverrides,
|
|
],
|
|
jit: true,
|
|
})
|
|
class RootScopeModule {
|
|
}
|
|
rootScopeImports.push(RootScopeModule);
|
|
}
|
|
providers.push({provide: INJECTOR_SCOPE, useValue: this._isRoot ? 'root' : null});
|
|
|
|
const imports = [rootScopeImports, this.ngModule, this._imports];
|
|
const schemas = this._schemas;
|
|
|
|
@NgModule({providers, declarations, imports, schemas, jit: true})
|
|
class DynamicTestModule {
|
|
}
|
|
|
|
const compilerFactory = this.platform.injector.get(TestingCompilerFactory);
|
|
this._compiler = compilerFactory.createTestingCompiler(this._compilerOptions);
|
|
for (const summary of [this._testEnvAotSummaries, ...this._aotSummaries]) {
|
|
this._compiler.loadAotSummaries(summary);
|
|
}
|
|
this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1]));
|
|
this._componentOverrides.forEach(
|
|
(entry) => this._compiler.overrideComponent(entry[0], entry[1]));
|
|
this._directiveOverrides.forEach(
|
|
(entry) => this._compiler.overrideDirective(entry[0], entry[1]));
|
|
this._pipeOverrides.forEach((entry) => this._compiler.overridePipe(entry[0], entry[1]));
|
|
return DynamicTestModule;
|
|
}
|
|
|
|
private _assertNotInstantiated(methodName: string, methodDescription: string) {
|
|
if (this._instantiated) {
|
|
throw new Error(
|
|
`Cannot ${methodDescription} when the test module has already been instantiated. ` +
|
|
`Make sure you are not using \`inject\` before \`${methodName}\`.`);
|
|
}
|
|
}
|
|
|
|
inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T, flags?: InjectFlags): T;
|
|
inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue: null, flags?: InjectFlags): T
|
|
|null;
|
|
inject<T>(
|
|
token: Type<T>|InjectionToken<T>|AbstractType<T>, notFoundValue?: T|null,
|
|
flags?: InjectFlags): T|null {
|
|
this._initIfNeeded();
|
|
if (token as unknown === TestBed) {
|
|
return this as any;
|
|
}
|
|
// Tests can inject things from the ng module and from the compiler,
|
|
// but the ng module can't inject things from the compiler and vice versa.
|
|
const UNDEFINED = {};
|
|
const result = this._moduleRef.injector.get(token, UNDEFINED, flags);
|
|
return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue, flags) as any :
|
|
result;
|
|
}
|
|
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags): any;
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
get(token: any, notFoundValue?: any): any;
|
|
/** @deprecated from v9.0.0 use TestBed.inject */
|
|
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND,
|
|
flags: InjectFlags = InjectFlags.Default): any {
|
|
return this.inject(token, notFoundValue, flags);
|
|
}
|
|
|
|
execute(tokens: any[], fn: Function, context?: any): any {
|
|
this._initIfNeeded();
|
|
const params = tokens.map(t => this.inject(t));
|
|
return fn.apply(context, params);
|
|
}
|
|
|
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
|
|
this._assertNotInstantiated('overrideModule', 'override module metadata');
|
|
this._moduleOverrides.push([ngModule, override]);
|
|
}
|
|
|
|
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
|
|
this._assertNotInstantiated('overrideComponent', 'override component metadata');
|
|
this._componentOverrides.push([component, override]);
|
|
}
|
|
|
|
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
|
|
this._assertNotInstantiated('overrideDirective', 'override directive metadata');
|
|
this._directiveOverrides.push([directive, override]);
|
|
}
|
|
|
|
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
|
|
this._assertNotInstantiated('overridePipe', 'override pipe metadata');
|
|
this._pipeOverrides.push([pipe, override]);
|
|
}
|
|
|
|
/**
|
|
* Overwrites all providers for the given token with the given provider definition.
|
|
*/
|
|
overrideProvider(token: any, provider: {
|
|
useFactory: Function,
|
|
deps: any[],
|
|
}): void;
|
|
overrideProvider(token: any, provider: {useValue: any;}): void;
|
|
overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}):
|
|
void {
|
|
this._assertNotInstantiated('overrideProvider', 'override provider');
|
|
this.overrideProviderImpl(token, provider);
|
|
}
|
|
|
|
private overrideProviderImpl(
|
|
token: any, provider: {
|
|
useFactory?: Function,
|
|
useValue?: any,
|
|
deps?: any[],
|
|
},
|
|
deprecated = false): void {
|
|
let def: ɵɵInjectableDef<any>|null = null;
|
|
if (typeof token !== 'string' && (def = getInjectableDef(token)) && def.providedIn === 'root') {
|
|
if (provider.useFactory) {
|
|
this._rootProviderOverrides.push(
|
|
{provide: token, useFactory: provider.useFactory, deps: provider.deps || []});
|
|
} else {
|
|
this._rootProviderOverrides.push({provide: token, useValue: provider.useValue});
|
|
}
|
|
}
|
|
let flags: NodeFlags = 0;
|
|
let value: any;
|
|
if (provider.useFactory) {
|
|
flags |= NodeFlags.TypeFactoryProvider;
|
|
value = provider.useFactory;
|
|
} else {
|
|
flags |= NodeFlags.TypeValueProvider;
|
|
value = provider.useValue;
|
|
}
|
|
const deps = (provider.deps || []).map((dep) => {
|
|
let depFlags: DepFlags = DepFlags.None;
|
|
let depToken: any;
|
|
if (Array.isArray(dep)) {
|
|
dep.forEach((entry: any) => {
|
|
if (entry instanceof Optional) {
|
|
depFlags |= DepFlags.Optional;
|
|
} else if (entry instanceof SkipSelf) {
|
|
depFlags |= DepFlags.SkipSelf;
|
|
} else {
|
|
depToken = entry;
|
|
}
|
|
});
|
|
} else {
|
|
depToken = dep;
|
|
}
|
|
return [depFlags, depToken];
|
|
});
|
|
overrideProvider({token, flags, deps, value, deprecatedBehavior: deprecated});
|
|
}
|
|
|
|
overrideTemplateUsingTestingModule(component: Type<any>, template: string) {
|
|
this._assertNotInstantiated('overrideTemplateUsingTestingModule', 'override template');
|
|
|
|
@Component({selector: 'empty', template, jit: true})
|
|
class OverrideComponent {
|
|
}
|
|
|
|
this._templateOverrides.push({component, templateOf: OverrideComponent});
|
|
}
|
|
|
|
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
|
this._initIfNeeded();
|
|
const componentFactory = this._compiler.getComponentFactory(component);
|
|
|
|
if (!componentFactory) {
|
|
throw new Error(`Cannot create the component ${
|
|
stringify(component)} as it was not imported into the testing module!`);
|
|
}
|
|
|
|
// TODO: Don't cast as `InjectionToken<boolean>`, declared type is boolean[]
|
|
const noNgZone = this.inject(ComponentFixtureNoNgZone as InjectionToken<boolean>, false);
|
|
// TODO: Don't cast as `InjectionToken<boolean>`, declared type is boolean[]
|
|
const autoDetect: boolean =
|
|
this.inject(ComponentFixtureAutoDetect as InjectionToken<boolean>, false);
|
|
const ngZone: NgZone|null = noNgZone ? null : this.inject(NgZone, null);
|
|
const testComponentRenderer: TestComponentRenderer = this.inject(TestComponentRenderer);
|
|
const rootElId = `root${_nextRootElementId++}`;
|
|
testComponentRenderer.insertRootElement(rootElId);
|
|
|
|
const initComponent = () => {
|
|
const componentRef =
|
|
componentFactory.create(Injector.NULL, [], `#${rootElId}`, this._moduleRef);
|
|
return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
|
|
};
|
|
|
|
const fixture = !ngZone ? initComponent() : ngZone.run(initComponent);
|
|
this._activeFixtures.push(fixture);
|
|
return fixture;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description
|
|
* Configures and initializes environment for unit testing and provides methods for
|
|
* creating components and services in unit tests.
|
|
*
|
|
* `TestBed` is the primary api for writing unit tests for Angular applications and libraries.
|
|
*
|
|
* Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3`
|
|
* according to the compiler used.
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export const TestBed: TestBedStatic =
|
|
ivyEnabled ? TestBedRender3 as any as TestBedStatic : TestBedViewEngine as any as TestBedStatic;
|
|
|
|
/**
|
|
* Returns a singleton of the applicable `TestBed`.
|
|
*
|
|
* It will be either an instance of `TestBedViewEngine` or `TestBedRender3`.
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export const getTestBed: () => TestBed = ivyEnabled ? _getTestBedRender3 : _getTestBedViewEngine;
|
|
|
|
let testBed: TestBedViewEngine;
|
|
|
|
function _getTestBedViewEngine(): TestBedViewEngine {
|
|
return testBed = testBed || new TestBedViewEngine();
|
|
}
|
|
|
|
/**
|
|
* Allows injecting dependencies in `beforeEach()` and `it()`.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```
|
|
* beforeEach(inject([Dependency, AClass], (dep, object) => {
|
|
* // some code that uses `dep` and `object`
|
|
* // ...
|
|
* }));
|
|
*
|
|
* it('...', inject([AClass], (object) => {
|
|
* object.doSomething();
|
|
* expect(...);
|
|
* })
|
|
* ```
|
|
*
|
|
* Notes:
|
|
* - inject is currently a function because of some Traceur limitation the syntax should
|
|
* eventually
|
|
* becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });`
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export function inject(tokens: any[], fn: Function): () => any {
|
|
const testBed = getTestBed();
|
|
if (tokens.indexOf(AsyncTestCompleter) >= 0) {
|
|
// Not using an arrow function to preserve context passed from call site
|
|
return function(this: unknown) {
|
|
// Return an async test method that returns a Promise if AsyncTestCompleter is one of
|
|
// the injected tokens.
|
|
return testBed.compileComponents().then(() => {
|
|
const completer = testBed.inject(AsyncTestCompleter);
|
|
testBed.execute(tokens, fn, this);
|
|
return completer.promise;
|
|
});
|
|
};
|
|
} else {
|
|
// Not using an arrow function to preserve context passed from call site
|
|
return function(this: unknown) {
|
|
return testBed.execute(tokens, fn, this);
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
export class InjectSetupWrapper {
|
|
constructor(private _moduleDef: () => TestModuleMetadata) {}
|
|
|
|
private _addModule() {
|
|
const moduleDef = this._moduleDef();
|
|
if (moduleDef) {
|
|
getTestBed().configureTestingModule(moduleDef);
|
|
}
|
|
}
|
|
|
|
inject(tokens: any[], fn: Function): () => any {
|
|
const self = this;
|
|
// Not using an arrow function to preserve context passed from call site
|
|
return function(this: unknown) {
|
|
self._addModule();
|
|
return inject(tokens, fn).call(this);
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @publicApi
|
|
*/
|
|
export function withModule(moduleDef: TestModuleMetadata): InjectSetupWrapper;
|
|
export function withModule(moduleDef: TestModuleMetadata, fn: Function): () => any;
|
|
export function withModule(moduleDef: TestModuleMetadata, fn?: Function|null): (() => any)|
|
|
InjectSetupWrapper {
|
|
if (fn) {
|
|
// Not using an arrow function to preserve context passed from call site
|
|
return function(this: unknown) {
|
|
const testBed = getTestBed();
|
|
if (moduleDef) {
|
|
testBed.configureTestingModule(moduleDef);
|
|
}
|
|
return fn.apply(this);
|
|
};
|
|
}
|
|
return new InjectSetupWrapper(() => moduleDef);
|
|
}
|