feat: introduce TestBed.overrideProvider
(#16725)
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
This commit is contained in:
@ -787,15 +787,46 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||
expect(child.get(Injector)).toBe(child);
|
||||
});
|
||||
|
||||
it('should allow to inject lazy providers via Injector.get from an eager provider that is declared earlier',
|
||||
() => {
|
||||
@NgModule({providers: [{provide: 'a', useFactory: () => 'aValue'}]})
|
||||
class SomeModule {
|
||||
public a: string;
|
||||
constructor(injector: Injector) { this.a = injector.get('a'); }
|
||||
}
|
||||
expect(createModule(SomeModule).instance.a).toBe('aValue');
|
||||
});
|
||||
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||
|
||||
it('should inject providers that were declared before it', () => {
|
||||
@NgModule({
|
||||
providers: [
|
||||
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||
{
|
||||
provide: 'eager',
|
||||
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||
deps: [Injector]
|
||||
},
|
||||
]
|
||||
})
|
||||
class MyModule {
|
||||
// NgModule is eager, which makes all of its deps eager
|
||||
constructor(@Inject('eager') eager: any) {}
|
||||
}
|
||||
|
||||
expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||
});
|
||||
|
||||
it('should inject providers that were declared after it', () => {
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'eager',
|
||||
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||
deps: [Injector]
|
||||
},
|
||||
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||
]
|
||||
})
|
||||
class MyModule {
|
||||
// NgModule is eager, which makes all of its deps eager
|
||||
constructor(@Inject('eager') eager: any) {}
|
||||
}
|
||||
|
||||
expect(createModule(MyModule).injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw when no provider defined', () => {
|
||||
const injector = createInjector([]);
|
||||
|
@ -342,6 +342,53 @@ export function main() {
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||
|
||||
it('should inject providers that were declared before it', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||
{
|
||||
provide: 'eager',
|
||||
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||
deps: [Injector]
|
||||
},
|
||||
]
|
||||
})
|
||||
class MyComp {
|
||||
// Component is eager, which makes all of its deps eager
|
||||
constructor(@Inject('eager') eager: any) {}
|
||||
}
|
||||
|
||||
const ctx =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||
expect(ctx.debugElement.injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||
});
|
||||
|
||||
it('should inject providers that were declared after it', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
{
|
||||
provide: 'eager',
|
||||
useFactory: (i: Injector) => `eagerValue: ${i.get('lazy')}`,
|
||||
deps: [Injector]
|
||||
},
|
||||
{provide: 'lazy', useFactory: () => 'lazyValue'},
|
||||
]
|
||||
})
|
||||
class MyComp {
|
||||
// Component is eager, which makes all of its deps eager
|
||||
constructor(@Inject('eager') eager: any) {}
|
||||
}
|
||||
|
||||
const ctx =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
|
||||
expect(ctx.debugElement.injector.get('eager')).toBe('eagerValue: lazyValue');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow injecting lazy providers via Injector.get from an eager provider that is declared earlier',
|
||||
() => {
|
||||
@Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''})
|
||||
|
@ -11,7 +11,7 @@ import {ArgumentType, BindingFlags, NodeCheckFn, NodeDef, NodeFlags, RootData, S
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
import {createEmbeddedView, createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`Embedded Views`, () => {
|
||||
@ -45,8 +45,7 @@ export function main() {
|
||||
]),
|
||||
parentContext);
|
||||
|
||||
const childView =
|
||||
Services.createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||
const childView = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||
expect(childView.component).toBe(parentContext);
|
||||
expect(childView.context).toBe(childContext);
|
||||
});
|
||||
@ -64,8 +63,8 @@ export function main() {
|
||||
]));
|
||||
const viewContainerData = asElementData(parentView, 1);
|
||||
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
|
||||
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
||||
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
||||
@ -95,8 +94,8 @@ export function main() {
|
||||
]));
|
||||
const viewContainerData = asElementData(parentView, 1);
|
||||
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
|
||||
attachEmbeddedView(parentView, viewContainerData, 0, childView0);
|
||||
attachEmbeddedView(parentView, viewContainerData, 1, childView1);
|
||||
@ -119,7 +118,7 @@ export function main() {
|
||||
elementDef(NodeFlags.None, null !, null !, 0, 'span', [['name', 'after']])
|
||||
]));
|
||||
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||
attachEmbeddedView(parentView, asElementData(parentView, 0), 0, childView0);
|
||||
|
||||
const rootNodes = rootRenderNodes(parentView);
|
||||
@ -146,7 +145,7 @@ export function main() {
|
||||
update))
|
||||
]));
|
||||
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
||||
|
||||
@ -180,7 +179,7 @@ export function main() {
|
||||
]))
|
||||
]));
|
||||
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(parentView, asElementData(parentView, 1), 0, childView0);
|
||||
Services.destroyView(parentView);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injector, NgModuleRef, RootRenderer, Sanitizer} from '@angular/core';
|
||||
import {ArgumentType, NodeCheckFn, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, NodeCheckFn, NodeDef, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
@ -37,6 +37,10 @@ export function createRootView(
|
||||
TestBed.get(NgModuleRef), context);
|
||||
}
|
||||
|
||||
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
|
||||
return Services.createEmbeddedView(parent, anchorDef, anchorDef.element !.template !, context);
|
||||
}
|
||||
|
||||
export let removeNodes: Node[];
|
||||
beforeEach(() => { removeNodes = []; });
|
||||
afterEach(() => { removeNodes.forEach((node) => getDOM().remove(node)); });
|
||||
|
@ -10,7 +10,7 @@ import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext,
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
import {createEmbeddedView, createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`View NgContent`, () => {
|
||||
@ -121,7 +121,7 @@ export function main() {
|
||||
])));
|
||||
|
||||
const componentView = asElementData(view, 0).componentView;
|
||||
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||
const view0 = createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(view, asElementData(componentView, 1), 0, view0);
|
||||
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(3);
|
||||
|
@ -12,7 +12,7 @@ import {BindingFlags, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryV
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootView} from './helper';
|
||||
import {createEmbeddedView, createRootView} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`Query Views`, () => {
|
||||
@ -155,7 +155,7 @@ export function main() {
|
||||
...contentQueryProviders(),
|
||||
]));
|
||||
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
@ -183,7 +183,7 @@ export function main() {
|
||||
anchorDef(NodeFlags.EmbeddedViews, null !, null !, 0),
|
||||
]));
|
||||
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
// attach at a different place than the one where the template was defined
|
||||
attachEmbeddedView(view, asElementData(view, 7), 0, childView);
|
||||
|
||||
@ -214,7 +214,7 @@ export function main() {
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.length).toBe(0);
|
||||
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
@ -245,7 +245,7 @@ export function main() {
|
||||
expect(comp.a.length).toBe(0);
|
||||
|
||||
const compView = asElementData(view, 0).componentView;
|
||||
const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]);
|
||||
const childView = createEmbeddedView(compView, compView.def.nodes[1]);
|
||||
attachEmbeddedView(view, asElementData(compView, 1), 0, childView);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
|
Reference in New Issue
Block a user