Skip to content

Commit

Permalink
feat: introduce TestBed.overrideProvider (#16725)
Browse files Browse the repository at this point in the history
This allows to overwrite all providers for a token, not matter
where they were defined.

This can be used to test JIT and AOT’ed code in the same way.

Design doc: https://docs.google.com/document/d/1VmTkz0EbEVSWfEEWEvQ5sXyQXSCvtMOw4t7pKU-jOwc/edit?usp=sharing
  • Loading branch information
tbosch authored and jasonaden committed May 15, 2017
1 parent 1eba623 commit 39b92f7
Show file tree
Hide file tree
Showing 20 changed files with 735 additions and 121 deletions.
8 changes: 1 addition & 7 deletions packages/core/src/application_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/

import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
import {ApplicationInitStatus} from './application_init';
import {ApplicationRef, ApplicationRef_} from './application_ref';
import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
import {Inject, Optional, SkipSelf} from './di/metadata';
import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler';
import {NgModule} from './metadata';
import {initServicesIfNeeded} from './view/index';

export function _iterableDiffersFactory() {
return defaultIterableDiffers;
Expand All @@ -28,10 +27,6 @@ export function _localeFactory(locale?: string): string {
return locale || 'en-US';
}

export function _initViewEngine() {
initServicesIfNeeded();
}

/**
* This module includes the providers of @angular/core that are needed
* to bootstrap components via `ApplicationRef`.
Expand All @@ -52,7 +47,6 @@ export function _initViewEngine() {
useFactory: _localeFactory,
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
},
{provide: APP_INITIALIZER, useValue: _initViewEngine, multi: true},
]
})
export class ApplicationModule {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/core_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
export {clearProviderOverrides as ɵclearProviderOverrides, overrideProvider as ɵoverrideProvider} from './view/index';
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
50 changes: 50 additions & 0 deletions packages/core/src/view/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Injector} from '../di/injector';
import {NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
import {Type} from '../type';

import {initServicesIfNeeded} from './services';
import {NgModuleDefinitionFactory, ProviderOverride, Services} from './types';
import {resolveDefinition} from './util';

export function overrideProvider(override: ProviderOverride) {
initServicesIfNeeded();
return Services.overrideProvider(override);
}

export function clearProviderOverrides() {
initServicesIfNeeded();
return Services.clearProviderOverrides();
}

// Attention: this function is called as top level function.
// Putting any logic in here will destroy closure tree shaking!
export function createNgModuleFactory(
ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
}

class NgModuleFactory_ extends NgModuleFactory<any> {
constructor(
public readonly moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
private _ngModuleDefFactory: NgModuleDefinitionFactory) {
// Attention: this ctor is called as top level function.
// Putting any logic in here will destroy closure tree shaking!
super();
}

create(parentInjector: Injector|null): NgModuleRef<any> {
initServicesIfNeeded();
const def = resolveDefinition(this._ngModuleDefFactory);
return Services.createNgModuleRef(
this.moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
}
}
2 changes: 1 addition & 1 deletion packages/core/src/view/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
*/

export {anchorDef, elementDef} from './element';
export {clearProviderOverrides, createNgModuleFactory, overrideProvider} from './entrypoint';
export {ngContentDef} from './ng_content';
export {moduleDef, moduleProvideDef} from './ng_module';
export {directiveDef, pipeDef, providerDef} from './provider';
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
export {queryDef} from './query';
export {ViewRef_, createComponentFactory, getComponentViewDefinitionFactory, nodeValue} from './refs';
export {createNgModuleFactory} from './refs';
export {initServicesIfNeeded} from './services';
export {textDef} from './text';
export {EMPTY_ARRAY, EMPTY_MAP, createRendererType2, elementEventFullName, inlineInterpolate, interpolate, rootRenderNodes, tokenKey, unwrapValue} from './util';
Expand Down
22 changes: 6 additions & 16 deletions packages/core/src/view/ng_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {NgModuleRef} from '../linker/ng_module_factory';

import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NgModuleProviderDef, NodeFlags} from './types';
import {tokenKey} from './util';
import {splitDepsDsl, tokenKey} from './util';

const NOT_CREATED = new Object();

Expand All @@ -20,17 +20,7 @@ const NgModuleRefTokenKey = tokenKey(NgModuleRef);
export function moduleProvideDef(
flags: NodeFlags, token: any, value: any,
deps: ([DepFlags, any] | any)[]): NgModuleProviderDef {
const depDefs: DepDef[] = deps.map(value => {
let token: any;
let flags: DepFlags;
if (Array.isArray(value)) {
[flags, token] = value;
} else {
flags = DepFlags.None;
token = value;
}
return {flags, token, tokenKey: tokenKey(token)};
});
const depDefs = splitDepsDsl(deps);
return {
// will bet set by the module definition
index: -1,
Expand Down Expand Up @@ -97,16 +87,16 @@ function _createProviderInstance(ngModule: NgModuleData, providerDef: NgModulePr
let injectable: any;
switch (providerDef.flags & NodeFlags.Types) {
case NodeFlags.TypeClassProvider:
injectable = _createClass(ngModule, providerDef !.value, providerDef !.deps);
injectable = _createClass(ngModule, providerDef.value, providerDef.deps);
break;
case NodeFlags.TypeFactoryProvider:
injectable = _callFactory(ngModule, providerDef !.value, providerDef !.deps);
injectable = _callFactory(ngModule, providerDef.value, providerDef.deps);
break;
case NodeFlags.TypeUseExistingProvider:
injectable = resolveNgModuleDep(ngModule, providerDef !.deps[0]);
injectable = resolveNgModuleDep(ngModule, providerDef.deps[0]);
break;
case NodeFlags.TypeValueProvider:
injectable = providerDef !.value;
injectable = providerDef.value;
break;
}
return injectable;
Expand Down
14 changes: 2 additions & 12 deletions packages/core/src/view/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {Renderer as RendererV1, Renderer2} from '../render/api';

import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData} from './types';
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';

const RendererV1TokenKey = tokenKey(RendererV1);
const Renderer2TokenKey = tokenKey(Renderer2);
Expand Down Expand Up @@ -78,17 +78,7 @@ export function _def(
bindings = [];
}

const depDefs: DepDef[] = deps.map(value => {
let token: any;
let flags: DepFlags;
if (Array.isArray(value)) {
[flags, token] = value;
} else {
flags = DepFlags.None;
token = value;
}
return {flags, token, tokenKey: tokenKey(token)};
});
const depDefs = splitDepsDsl(deps);

return {
// will bet set by the view definition
Expand Down
39 changes: 10 additions & 29 deletions packages/core/src/view/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {Injector} from '../di/injector';
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef} from '../linker/element_ref';
import {InternalNgModuleRef, NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
import {InternalNgModuleRef, NgModuleRef} from '../linker/ng_module_factory';
import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import {EmbeddedViewRef, InternalViewRef, ViewRef} from '../linker/view_ref';
Expand All @@ -22,7 +22,7 @@ import {stringify} from '../util';
import {VERSION} from '../version';

import {callNgModuleLifecycle, initNgModule, resolveNgModuleDep} from './ng_module';
import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NgModuleDefinitionFactory, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
import {DepFlags, ElementData, NgModuleData, NgModuleDefinition, NodeDef, NodeFlags, Services, TemplateData, ViewContainerData, ViewData, ViewDefinitionFactory, ViewState, asElementData, asProviderData, asTextData} from './types';
import {markParentViewsForCheck, resolveDefinition, rootRenderNodes, splitNamespace, tokenKey, viewParentEl} from './util';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView, renderDetachView} from './view_attach';

Expand All @@ -43,14 +43,6 @@ export function getComponentViewDefinitionFactory(componentFactory: ComponentFac
return (componentFactory as ComponentFactory_).viewDefFactory;
}

// Attention: this function is called as top level function.
// Putting any logic in here will destroy closure tree shaking!
export function createNgModuleFactory(
ngModuleType: Type<any>, bootstrapComponents: Type<any>[],
defFactory: NgModuleDefinitionFactory): NgModuleFactory<any> {
return new NgModuleFactory_(ngModuleType, bootstrapComponents, defFactory);
}

class ComponentFactory_ extends ComponentFactory<any> {
/**
* @internal
Expand Down Expand Up @@ -312,7 +304,8 @@ class TemplateRef_ extends TemplateRef<any> implements TemplateData {
constructor(private _parentView: ViewData, private _def: NodeDef) { super(); }

createEmbeddedView(context: any): EmbeddedViewRef<any> {
return new ViewRef_(Services.createEmbeddedView(this._parentView, this._def, context));
return new ViewRef_(Services.createEmbeddedView(
this._parentView, this._def, this._def.element !.template !, context));
}

get elementRef(): ElementRef {
Expand Down Expand Up @@ -464,22 +457,10 @@ class RendererAdapter implements RendererV1 {
}


class NgModuleFactory_ extends NgModuleFactory<any> {
constructor(
private _moduleType: Type<any>, private _bootstrapComponents: Type<any>[],
private _ngModuleDefFactory: NgModuleDefinitionFactory, ) {
// Attention: this ctor is called as top level function.
// Putting any logic in here will destroy closure tree shaking!
super();
}

get moduleType(): Type<any> { return this._moduleType; }

create(parentInjector: Injector|null): NgModuleRef<any> {
const def = resolveDefinition(this._ngModuleDefFactory);
return new NgModuleRef_(
this._moduleType, parentInjector || Injector.NULL, this._bootstrapComponents, def);
}
export function createNgModuleRef(
moduleType: Type<any>, parent: Injector, bootstrapComponents: Type<any>[],
def: NgModuleDefinition): NgModuleRef<any> {
return new NgModuleRef_(moduleType, parent, bootstrapComponents, def);
}

class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
Expand All @@ -488,8 +469,8 @@ class NgModuleRef_ implements NgModuleData, InternalNgModuleRef<any> {
public _providers: any[];

constructor(
private _moduleType: any, public _parent: Injector, public _bootstrapComponents: Type<any>[],
public _def: NgModuleDefinition) {
private _moduleType: Type<any>, public _parent: Injector,
public _bootstrapComponents: Type<any>[], public _def: NgModuleDefinition) {
initNgModule(this);
}

Expand Down
Loading

0 comments on commit 39b92f7

Please sign in to comment.