mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
test(core): unit tests for the injector profiler and injector debugging APIs (#48639)
Creates unit tests for the following APIs
- setInjectorProfiler
- getInjectorProviders
- getInjectorResolutionPath
- getDependenciesFromInjectable
Modifies existing tests in
- packages/examples/core/di/ts/injector_spec.ts
- packages/core/test/render3/jit/declare_injectable_spec.ts
- packages/core/test/render3/jit/declare_factory_spec.ts
because they setup framework injector context manually.
Exports setInjectorProfilerContext in packages/core/src/core_private_export.ts in order for use
in the the modified tests above.
PR Close #48639
This commit is contained in:
parent
98d262fd27
commit
c1dee4cfe3
5 changed files with 1003 additions and 7 deletions
|
|
@ -25,6 +25,7 @@ export {InitialRenderPendingTasks as ɵInitialRenderPendingTasks} from './initia
|
|||
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';
|
||||
export {clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, resolveComponentResources as ɵresolveComponentResources} from './metadata/resource_loading';
|
||||
export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities';
|
||||
export {InjectorProfilerContext as ɵInjectorProfilerContext, setInjectorProfilerContext as ɵsetInjectorProfilerContext} from './render3/debug/injector_profiler';
|
||||
export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass';
|
||||
export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer';
|
||||
export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer';
|
||||
|
|
|
|||
975
packages/core/test/acceptance/injector_profiler_spec.ts
Normal file
975
packages/core/test/acceptance/injector_profiler_spec.ts
Normal file
|
|
@ -0,0 +1,975 @@
|
|||
/**
|
||||
* @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 {PercentPipe} from '@angular/common';
|
||||
import {inject} from '@angular/core';
|
||||
import {ClassProvider, Component, Directive, Inject, Injectable, InjectFlags, InjectionToken, Injector, NgModule, NgModuleRef, ViewChild} from '@angular/core/src/core';
|
||||
import {NullInjector} from '@angular/core/src/di/null_injector';
|
||||
import {isClassProvider, isExistingProvider, isFactoryProvider, isTypeProvider, isValueProvider} from '@angular/core/src/di/provider_collection';
|
||||
import {EnvironmentInjector, R3Injector} from '@angular/core/src/di/r3_injector';
|
||||
import {setupFrameworkInjectorProfiler} from '@angular/core/src/render3/debug/framework_injector_profiler';
|
||||
import {getInjectorProfilerContext, InjectedService, InjectedServiceEvent, InjectorCreatedInstanceEvent, InjectorProfilerEvent, InjectorProfilerEventType, ProviderConfiguredEvent, ProviderRecord, setInjectorProfiler} from '@angular/core/src/render3/debug/injector_profiler';
|
||||
import {getNodeInjectorLView, NodeInjector} from '@angular/core/src/render3/di';
|
||||
import {getDependenciesFromInjectable, getInjectorProviders, getInjectorResolutionPath} from '@angular/core/src/render3/util/injector_discovery_utils';
|
||||
import {fakeAsync, tick} from '@angular/core/testing';
|
||||
import {TestBed} from '@angular/core/testing/src/test_bed';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {Router, RouterModule, RouterOutlet} from '@angular/router';
|
||||
|
||||
describe('setProfiler', () => {
|
||||
let injectEvents: InjectedServiceEvent[] = [];
|
||||
let createEvents: InjectorCreatedInstanceEvent[] = [];
|
||||
let providerConfiguredEvents: ProviderConfiguredEvent[] = [];
|
||||
|
||||
function searchForProfilerEvent<T extends InjectorProfilerEvent>(
|
||||
events: T[], condition: ((event: T) => boolean)): T|undefined {
|
||||
return events.find(event => condition(event)) as T;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
injectEvents = [];
|
||||
createEvents = [];
|
||||
providerConfiguredEvents = [];
|
||||
|
||||
setInjectorProfiler(((injectorProfilerEvent: InjectorProfilerEvent) => {
|
||||
const {type} = injectorProfilerEvent;
|
||||
if (type === InjectorProfilerEventType.Inject) {
|
||||
injectEvents.push(
|
||||
{service: injectorProfilerEvent.service, context: getInjectorProfilerContext(), type});
|
||||
}
|
||||
if (type === InjectorProfilerEventType.InstanceCreatedByInjector) {
|
||||
createEvents.push({
|
||||
instance: injectorProfilerEvent.instance,
|
||||
context: getInjectorProfilerContext(),
|
||||
type
|
||||
});
|
||||
}
|
||||
if (type === InjectorProfilerEventType.ProviderConfigured) {
|
||||
providerConfiguredEvents.push({
|
||||
providerRecord: injectorProfilerEvent.providerRecord,
|
||||
context: getInjectorProfilerContext(),
|
||||
type
|
||||
});
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
|
||||
it('should emit DI events when a component contains a provider and injects it', () => {
|
||||
class MyService {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: 'hello world',
|
||||
providers: [
|
||||
MyService,
|
||||
]
|
||||
})
|
||||
class MyComponent {
|
||||
myService = inject(MyService);
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComponent]});
|
||||
const fixture = TestBed.createComponent(MyComponent);
|
||||
const myComp = fixture.componentInstance;
|
||||
|
||||
// MyService should have been configured
|
||||
const myServiceProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyService);
|
||||
expect(myServiceProviderConfiguredEvent).toBeTruthy();
|
||||
|
||||
// inject(MyService) was called
|
||||
const myServiceInjectEvent = searchForProfilerEvent<InjectedServiceEvent>(
|
||||
injectEvents, (event) => event.service.token === MyService);
|
||||
expect(myServiceInjectEvent).toBeTruthy();
|
||||
expect(myServiceInjectEvent!.service.value).toBe(myComp.myService);
|
||||
expect(myServiceInjectEvent!.service.flags).toBe(InjectFlags.Default);
|
||||
|
||||
// myComp is an angular instance that is able to call `inject` in it's constructor, so a
|
||||
// create event should have been emitted for it
|
||||
const componentCreateEvent = searchForProfilerEvent<InjectorCreatedInstanceEvent>(
|
||||
createEvents, (event) => (event.instance.value === myComp));
|
||||
expect(componentCreateEvent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should emit the correct DI events when a service is injected with injection flags', () => {
|
||||
class MyService {}
|
||||
class MyServiceB {}
|
||||
class MyServiceC {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: 'hello world',
|
||||
providers: [MyService, {provide: MyServiceB, useValue: 0}]
|
||||
})
|
||||
class MyComponent {
|
||||
myService = inject(MyService, {self: true});
|
||||
myServiceD = inject(MyServiceB, {skipSelf: true});
|
||||
myServiceC = inject(MyServiceC, {optional: true});
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [MyServiceB, MyServiceC, {provide: MyServiceB, useValue: 1}],
|
||||
declarations: [MyComponent]
|
||||
});
|
||||
TestBed.createComponent(MyComponent);
|
||||
|
||||
const myServiceInjectEvent = searchForProfilerEvent<InjectedServiceEvent>(
|
||||
injectEvents, (event) => event.service.token === MyService);
|
||||
const myServiceBInjectEvent =
|
||||
searchForProfilerEvent(injectEvents, (event) => event.service.token === MyServiceB);
|
||||
const myServiceCInjectEvent =
|
||||
searchForProfilerEvent(injectEvents, (event) => event.service.token === MyServiceC);
|
||||
|
||||
expect(myServiceInjectEvent!.service.flags).toBe(InjectFlags.Self);
|
||||
expect(myServiceBInjectEvent!.service.flags).toBe(InjectFlags.SkipSelf);
|
||||
expect(myServiceBInjectEvent!.service.value).toBe(1);
|
||||
expect(myServiceCInjectEvent!.service.flags).toBe(InjectFlags.Optional);
|
||||
});
|
||||
|
||||
it('should emit correct DI events when providers are configured with useFactory, useExisting, useClass, useValue',
|
||||
() => {
|
||||
class MyService {}
|
||||
class MyServiceB {}
|
||||
class MyServiceC {}
|
||||
class MyServiceD {}
|
||||
class MyServiceE {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: 'hello world',
|
||||
providers: [
|
||||
MyService,
|
||||
{provide: MyServiceB, useFactory: () => new MyServiceB()},
|
||||
{provide: MyServiceC, useExisting: MyService},
|
||||
{provide: MyServiceD, useValue: 'hello world'},
|
||||
{provide: MyServiceE, useClass: class MyExampleClass {}},
|
||||
]
|
||||
})
|
||||
class MyComponent {
|
||||
myService = inject(MyService);
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComponent]});
|
||||
TestBed.createComponent(MyComponent);
|
||||
|
||||
// MyService should have been configured
|
||||
const myServiceProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyService);
|
||||
const myServiceBProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyServiceB);
|
||||
const myServiceCProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyServiceC);
|
||||
const myServiceDProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyServiceD);
|
||||
const myServiceEProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyServiceE);
|
||||
|
||||
expect(isTypeProvider(myServiceProviderConfiguredEvent!.providerRecord.provider!))
|
||||
.toBeTrue();
|
||||
expect(isFactoryProvider(myServiceBProviderConfiguredEvent!.providerRecord.provider!))
|
||||
.toBeTrue();
|
||||
expect(isExistingProvider(myServiceCProviderConfiguredEvent!.providerRecord.provider!))
|
||||
.toBeTrue();
|
||||
expect(isValueProvider(myServiceDProviderConfiguredEvent!.providerRecord.provider!))
|
||||
.toBeTrue();
|
||||
expect(isClassProvider(myServiceEProviderConfiguredEvent!.providerRecord.provider!))
|
||||
.toBeTrue();
|
||||
});
|
||||
|
||||
it('should emit correct DI events when providers are configured with multi', () => {
|
||||
class MyService {}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: 'hello world',
|
||||
providers: [
|
||||
{provide: MyService, useClass: MyService, multi: true},
|
||||
{provide: MyService, useFactory: () => new MyService(), multi: true},
|
||||
{provide: MyService, useValue: 'hello world', multi: true},
|
||||
]
|
||||
})
|
||||
class MyComponent {
|
||||
myService = inject(MyService);
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyComponent]});
|
||||
TestBed.createComponent(MyComponent);
|
||||
|
||||
// MyService should have been configured
|
||||
const myServiceProviderConfiguredEvent = searchForProfilerEvent<ProviderConfiguredEvent>(
|
||||
providerConfiguredEvents, (event) => event.providerRecord.token === MyService);
|
||||
|
||||
expect(((myServiceProviderConfiguredEvent!.providerRecord)?.provider as ClassProvider).multi)
|
||||
.toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInjectorProviders', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to get the providers from a components injector', () => {
|
||||
class MyService {}
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
{{b | percent:'4.3-5' }}
|
||||
`,
|
||||
providers: [MyService]
|
||||
})
|
||||
class MyComponent {
|
||||
b = 1.3495;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [MyComponent], imports: [PercentPipe]});
|
||||
const fixture = TestBed.createComponent(MyComponent);
|
||||
|
||||
const providers = getInjectorProviders(fixture.debugElement.injector);
|
||||
expect(providers.length).toBe(1);
|
||||
expect(providers[0].token).toBe(MyService);
|
||||
expect(providers[0].provider).toBe(MyService);
|
||||
expect(providers[0].isViewProvider).toBe(false);
|
||||
});
|
||||
|
||||
it('should be able to get determine if a provider is a view provider', () => {
|
||||
class MyService {}
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
{{b | percent:'4.3-5' }}
|
||||
`,
|
||||
viewProviders: [MyService]
|
||||
})
|
||||
class MyComponent {
|
||||
b = 1.3495;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [MyComponent], imports: [PercentPipe]});
|
||||
const fixture = TestBed.createComponent(MyComponent);
|
||||
|
||||
const providers = getInjectorProviders(fixture.debugElement.injector);
|
||||
expect(providers.length).toBe(1);
|
||||
expect(providers[0].token).toBe(MyService);
|
||||
expect(providers[0].provider).toBe(MyService);
|
||||
expect(providers[0].isViewProvider).toBe(true);
|
||||
});
|
||||
|
||||
it('should be able to determine import paths after module provider flattening in the NgModule bootstrap case',
|
||||
() => {
|
||||
// ┌─────────┐
|
||||
// │AppModule│
|
||||
// └────┬────┘
|
||||
// │
|
||||
// imports
|
||||
// │
|
||||
// ┌────▼────┐
|
||||
// ┌─imports─┤ ModuleD ├──imports─┐
|
||||
// │ └─────────┘ │
|
||||
// │ ┌─────▼─────┐
|
||||
// ┌───▼───┐ │ ModuleC │
|
||||
// │ModuleB│ │-MyServiceB│
|
||||
// └───┬───┘ └───────────┘
|
||||
// │
|
||||
// imports
|
||||
// │
|
||||
// ┌────▼─────┐
|
||||
// │ ModuleA │
|
||||
// │-MyService│
|
||||
// └──────────┘
|
||||
|
||||
class MyService {}
|
||||
class MyServiceB {}
|
||||
|
||||
@NgModule({providers: [MyService]})
|
||||
class ModuleA {
|
||||
}
|
||||
@NgModule({
|
||||
imports: [ModuleA],
|
||||
})
|
||||
class ModuleB {
|
||||
}
|
||||
|
||||
@NgModule({providers: [MyServiceB]})
|
||||
class ModuleC {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [ModuleB, ModuleC],
|
||||
})
|
||||
class ModuleD {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: 'hello world',
|
||||
})
|
||||
class MyComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [ModuleD, BrowserModule],
|
||||
declarations: [MyComponent],
|
||||
})
|
||||
class AppModule {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [AppModule]});
|
||||
const root = TestBed.createComponent(MyComponent);
|
||||
root.detectChanges();
|
||||
|
||||
const appModuleInjector = root.componentRef.injector.get(EnvironmentInjector);
|
||||
const providers = getInjectorProviders(appModuleInjector);
|
||||
|
||||
const myServiceProvider = providers.find(provider => provider.token === MyService);
|
||||
const myServiceBProvider = providers.find(provider => provider.token === MyServiceB);
|
||||
|
||||
const testModuleType = root.componentRef.injector.get(NgModuleRef).instance.constructor;
|
||||
|
||||
expect(myServiceProvider).toBeTruthy();
|
||||
expect(myServiceBProvider).toBeTruthy();
|
||||
|
||||
expect(myServiceProvider!.importPath).toBeInstanceOf(Array);
|
||||
expect(myServiceProvider!.importPath!.length).toBe(5);
|
||||
expect(myServiceProvider!.importPath![0]).toBe(testModuleType);
|
||||
expect(myServiceProvider!.importPath![1]).toBe(AppModule);
|
||||
expect(myServiceProvider!.importPath![2]).toBe(ModuleD);
|
||||
expect(myServiceProvider!.importPath![3]).toBe(ModuleB);
|
||||
expect(myServiceProvider!.importPath![4]).toBe(ModuleA);
|
||||
|
||||
expect(myServiceBProvider!.importPath).toBeInstanceOf(Array);
|
||||
expect(myServiceBProvider!.importPath!.length).toBe(4);
|
||||
expect(myServiceBProvider!.importPath![0]).toBe(testModuleType);
|
||||
expect(myServiceBProvider!.importPath![1]).toBe(AppModule);
|
||||
expect(myServiceBProvider!.importPath![2]).toBe(ModuleD);
|
||||
expect(myServiceBProvider!.importPath![3]).toBe(ModuleC);
|
||||
});
|
||||
|
||||
it('should be able to determine import paths after module provider flattening in the standalone component case',
|
||||
() => {
|
||||
// ┌────────────────────imports───────────────────────┐
|
||||
// │ │
|
||||
// │ ┌───────imports────────┐ │
|
||||
// │ │ │ │
|
||||
// │ │ │ │
|
||||
// ┌─────────┴─┴─────────┐ ┌─────────▼────────────┐ ┌──────────▼───────────┐
|
||||
// │MyStandaloneComponent│ │MyStandaloneComponentB│ │MyStandaloneComponentC│
|
||||
// └──────────┬──────────┘ └──────────┬────┬──────┘ └────┬────────┬────────┘
|
||||
// │ │ │ │ │
|
||||
// └──imports─┐ ┌imports┘ └────┐ │ │
|
||||
// │ │ │ │ imports
|
||||
// ┌▼─────▼┐ imports │ │
|
||||
// ┌────┤ModuleD├─────┐ │ imports │
|
||||
// imports └───────┘ │ │ │ ┌───▼────────┐
|
||||
// │ imports ┌──▼─────┐ │ │ ModuleE │
|
||||
// ┌──▼────┐ │ │ModuleF │ │ │-MyServiceC │
|
||||
// │ModuleB│ │ └────────┘ │ └────────────┘
|
||||
// └──┬────┘ ┌─────▼─────┐ │
|
||||
// imports │ ModuleC │ │
|
||||
// ┌────▼─────┐ │-MyServiceB│◄────────────┘
|
||||
// │ ModuleA │ └───────────┘
|
||||
// │-MyService│
|
||||
// └──────────┘
|
||||
|
||||
|
||||
class MyService {}
|
||||
class MyServiceB {}
|
||||
class MyServiceC {}
|
||||
|
||||
@NgModule({providers: [MyService]})
|
||||
class ModuleA {
|
||||
}
|
||||
@NgModule({
|
||||
imports: [ModuleA],
|
||||
})
|
||||
class ModuleB {
|
||||
}
|
||||
|
||||
@NgModule({providers: [MyServiceB]})
|
||||
class ModuleC {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [ModuleB, ModuleC],
|
||||
})
|
||||
class ModuleD {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [MyServiceC],
|
||||
})
|
||||
class ModuleE {
|
||||
}
|
||||
|
||||
@NgModule({})
|
||||
class ModuleF {
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp-c',
|
||||
template: 'hello world',
|
||||
imports: [ModuleE, ModuleC],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponentC {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp-b',
|
||||
template: 'hello world',
|
||||
imports: [ModuleD, ModuleF],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponentB {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
<my-comp-b/>
|
||||
<my-comp-c/>
|
||||
`,
|
||||
imports: [ModuleD, MyStandaloneComponentB, MyStandaloneComponentC],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponent {
|
||||
}
|
||||
|
||||
const root = TestBed.createComponent(MyStandaloneComponent);
|
||||
root.detectChanges();
|
||||
|
||||
const appComponentEnvironmentInjector = root.componentRef.injector.get(EnvironmentInjector);
|
||||
const providers = getInjectorProviders(appComponentEnvironmentInjector);
|
||||
|
||||
// There are 2 paths from MyStandaloneComponent to MyService
|
||||
//
|
||||
// path 1: MyStandaloneComponent -> ModuleD => ModuleB -> ModuleA
|
||||
// path 2: MyStandaloneComponent -> MyStandaloneComponentB -> ModuleD => ModuleB -> ModuleA
|
||||
//
|
||||
// Angular discovers this provider through the first path it visits
|
||||
// during it's postorder traversal (in this case path 1). Therefore
|
||||
// we expect myServiceProvider.importPath to have 4 DI containers
|
||||
//
|
||||
const myServiceProvider = providers.find(provider => provider.token === MyService);
|
||||
expect(myServiceProvider).toBeTruthy();
|
||||
expect(myServiceProvider!.importPath).toBeInstanceOf(Array);
|
||||
expect(myServiceProvider!.importPath!.length).toBe(4);
|
||||
expect(myServiceProvider!.importPath![0]).toBe(MyStandaloneComponent);
|
||||
expect(myServiceProvider!.importPath![1]).toBe(ModuleD);
|
||||
expect(myServiceProvider!.importPath![2]).toBe(ModuleB);
|
||||
expect(myServiceProvider!.importPath![3]).toBe(ModuleA);
|
||||
|
||||
// Similarly to above there are multiple paths from MyStandaloneComponent MyServiceB
|
||||
//
|
||||
// path 1: MyStandaloneComponent -> ModuleD => ModuleC
|
||||
// path 2: MyStandaloneComponent -> MyStandaloneComponentB -> ModuleD => ModuleC
|
||||
// path 3: MyStandaloneComponent -> MyStandaloneComponentC -> ModuleC
|
||||
//
|
||||
// Angular discovers this provider through the first path it visits
|
||||
// during it's postorder traversal (in this case path 1). Therefore
|
||||
// we expect myServiceProvider.importPath to have 4 DI containers
|
||||
//
|
||||
const myServiceBProvider = providers.find(provider => provider.token === MyServiceB);
|
||||
expect(myServiceBProvider).toBeTruthy();
|
||||
expect(myServiceBProvider!.importPath).toBeInstanceOf(Array);
|
||||
expect(myServiceBProvider!.importPath!.length).toBe(3);
|
||||
expect(myServiceBProvider!.importPath![0]).toBe(MyStandaloneComponent);
|
||||
expect(myServiceBProvider!.importPath![1]).toBe(ModuleD);
|
||||
expect(myServiceBProvider!.importPath![2]).toBe(ModuleC);
|
||||
});
|
||||
|
||||
it('should be able to determine import paths after module provider flattening in the standalone component case with lazy components',
|
||||
fakeAsync(() => {
|
||||
class MyService {}
|
||||
|
||||
@NgModule({providers: [MyService]})
|
||||
class ModuleA {
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'my-comp-b', template: 'hello world', imports: [ModuleA], standalone: true})
|
||||
class MyStandaloneComponentB {
|
||||
injector = inject(Injector);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `<router-outlet/>`,
|
||||
imports: [MyStandaloneComponentB, RouterOutlet],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponent {
|
||||
injector = inject(Injector);
|
||||
@ViewChild(RouterOutlet) routerOutlet: RouterOutlet|undefined;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterModule.forRoot([{
|
||||
path: 'lazy',
|
||||
loadComponent: () => MyStandaloneComponentB,
|
||||
}])]
|
||||
});
|
||||
const root = TestBed.createComponent(MyStandaloneComponent);
|
||||
TestBed.inject(Router).navigateByUrl('/lazy');
|
||||
tick();
|
||||
root.detectChanges();
|
||||
|
||||
const myStandaloneComponentNodeInjector = root.componentRef.injector;
|
||||
const myStandaloneComponentEnvironmentInjector =
|
||||
myStandaloneComponentNodeInjector.get(EnvironmentInjector);
|
||||
const myStandalonecomponentB =
|
||||
root.componentRef.instance!.routerOutlet!.component as MyStandaloneComponentB;
|
||||
const myComponentBNodeInjector = myStandalonecomponentB.injector;
|
||||
const myComponentBEnvironmentInjector = myComponentBNodeInjector.get(EnvironmentInjector);
|
||||
const myStandaloneComponentEnvironmentInjectorProviders =
|
||||
getInjectorProviders(myStandaloneComponentEnvironmentInjector);
|
||||
const myComponentBEnvironmentInjectorProviders =
|
||||
getInjectorProviders(myComponentBEnvironmentInjector);
|
||||
|
||||
// Lazy component should have its own environment injector and therefore different
|
||||
// providers
|
||||
expect(myStandaloneComponentEnvironmentInjectorProviders)
|
||||
.not.toEqual(myComponentBEnvironmentInjectorProviders);
|
||||
|
||||
const myServiceProviderRecord =
|
||||
myComponentBEnvironmentInjectorProviders.find(provider => provider.token === MyService);
|
||||
|
||||
expect(myServiceProviderRecord).toBeTruthy();
|
||||
expect(myServiceProviderRecord!.importPath).toBeInstanceOf(Array);
|
||||
expect(myServiceProviderRecord!.importPath!.length).toBe(2);
|
||||
expect(myServiceProviderRecord!.importPath![0]).toBe(MyStandaloneComponentB);
|
||||
expect(myServiceProviderRecord!.importPath![1]).toBe(ModuleA);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getDependenciesFromInjectable', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to determine which injector dependencies come from', fakeAsync(() => {
|
||||
class MyService {}
|
||||
class MyServiceB {}
|
||||
class MyServiceC {}
|
||||
class MyServiceD {}
|
||||
class MyServiceG {}
|
||||
class MyServiceH {}
|
||||
|
||||
const myInjectionToken = new InjectionToken('myInjectionToken');
|
||||
const myServiceCInstance = new MyServiceC();
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
MyService, {provide: MyServiceB, useValue: 'hello world'},
|
||||
{provide: MyServiceC, useFactory: () => 123},
|
||||
{provide: myInjectionToken, useValue: myServiceCInstance}
|
||||
]
|
||||
})
|
||||
class ModuleA {
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[my-directive]',
|
||||
standalone: true,
|
||||
})
|
||||
class MyStandaloneDirective {
|
||||
serviceFromHost = inject(MyServiceH, {host: true, optional: true});
|
||||
injector = inject(Injector);
|
||||
|
||||
ngOnInit() {
|
||||
onMyStandaloneDirectiveCreated(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp-c',
|
||||
template: 'hello world',
|
||||
imports: [],
|
||||
standalone: true,
|
||||
})
|
||||
class MyStandaloneComponentC {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp-b',
|
||||
template: '<my-comp-c my-directive/>',
|
||||
imports: [MyStandaloneComponentC, MyStandaloneDirective],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponentB {
|
||||
myService = inject(MyService);
|
||||
myServiceB = inject(MyServiceB, {optional: true});
|
||||
myServiceC = inject(MyServiceC, {skipSelf: true});
|
||||
myInjectionTokenValue = inject(myInjectionToken);
|
||||
injector = inject(Injector, {self: true, host: true});
|
||||
myServiceD = inject(MyServiceD);
|
||||
myServiceG = inject(MyServiceG);
|
||||
parentComponent = inject(MyStandaloneComponent);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `<router-outlet/>`,
|
||||
imports: [RouterOutlet, ModuleA],
|
||||
providers: [MyServiceG, {provide: MyServiceH, useValue: 'MyStandaloneComponent'}],
|
||||
standalone: true
|
||||
})
|
||||
class MyStandaloneComponent {
|
||||
injector = inject(Injector);
|
||||
@ViewChild(RouterOutlet) routerOutlet: RouterOutlet|undefined;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: MyServiceD, useValue: '123'}],
|
||||
imports:
|
||||
[RouterModule.forRoot([{path: 'lazy', loadComponent: () => MyStandaloneComponentB}])]
|
||||
});
|
||||
|
||||
const root = TestBed.createComponent(MyStandaloneComponent);
|
||||
TestBed.inject(Router).navigateByUrl('/lazy');
|
||||
tick();
|
||||
root.detectChanges();
|
||||
|
||||
const myStandalonecomponentB =
|
||||
root.componentRef.instance!.routerOutlet!.component as MyStandaloneComponentB;
|
||||
|
||||
const {dependencies: dependenciesOfMyStandaloneComponentB} =
|
||||
getDependenciesFromInjectable(myStandalonecomponentB.injector, MyStandaloneComponentB)!;
|
||||
const standaloneInjector =
|
||||
root.componentInstance.injector.get(EnvironmentInjector) as EnvironmentInjector;
|
||||
|
||||
expect(dependenciesOfMyStandaloneComponentB).toBeInstanceOf(Array);
|
||||
expect(dependenciesOfMyStandaloneComponentB.length).toBe(8);
|
||||
|
||||
const myServiceDep = dependenciesOfMyStandaloneComponentB[0];
|
||||
const myServiceBDep = dependenciesOfMyStandaloneComponentB[1];
|
||||
const myServiceCDep = dependenciesOfMyStandaloneComponentB[2];
|
||||
const myInjectionTokenValueDep = dependenciesOfMyStandaloneComponentB[3];
|
||||
const injectorDep = dependenciesOfMyStandaloneComponentB[4];
|
||||
const myServiceDDep = dependenciesOfMyStandaloneComponentB[5];
|
||||
const myServiceGDep = dependenciesOfMyStandaloneComponentB[6];
|
||||
const parentComponentDep = dependenciesOfMyStandaloneComponentB[7];
|
||||
|
||||
expect(myServiceDep.token).toBe(MyService);
|
||||
expect(myServiceBDep.token).toBe(MyServiceB);
|
||||
expect(myServiceCDep.token).toBe(MyServiceC);
|
||||
expect(myInjectionTokenValueDep.token).toBe(myInjectionToken);
|
||||
expect(injectorDep.token).toBe(Injector);
|
||||
expect(myServiceDDep.token).toBe(MyServiceD);
|
||||
expect(myServiceGDep.token).toBe(MyServiceG);
|
||||
expect(parentComponentDep.token).toBe(MyStandaloneComponent);
|
||||
|
||||
expect(dependenciesOfMyStandaloneComponentB[0].flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(myServiceBDep.flags).toEqual({
|
||||
optional: true,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(myServiceCDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: true,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(myInjectionTokenValueDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(injectorDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: true,
|
||||
host: true,
|
||||
});
|
||||
expect(myServiceDDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(myServiceGDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
expect(parentComponentDep.flags).toEqual({
|
||||
optional: false,
|
||||
skipSelf: false,
|
||||
self: false,
|
||||
host: false,
|
||||
});
|
||||
|
||||
|
||||
expect(dependenciesOfMyStandaloneComponentB[0].value).toBe(myStandalonecomponentB.myService);
|
||||
expect(myServiceBDep.value).toBe('hello world');
|
||||
expect(myServiceCDep.value).toBe(123);
|
||||
expect(myInjectionTokenValueDep.value).toBe(myServiceCInstance);
|
||||
expect(injectorDep.value).toBe(myStandalonecomponentB.injector);
|
||||
expect(myServiceDDep.value).toBe('123');
|
||||
expect(myServiceGDep.value).toBe(myStandalonecomponentB.myServiceG);
|
||||
expect(parentComponentDep.value).toBe(myStandalonecomponentB.parentComponent);
|
||||
|
||||
expect(dependenciesOfMyStandaloneComponentB[0].providedIn).toBe(standaloneInjector);
|
||||
expect(myServiceBDep.providedIn).toBe(standaloneInjector);
|
||||
expect(myServiceCDep.providedIn).toBe(standaloneInjector);
|
||||
expect(myInjectionTokenValueDep.providedIn).toBe(standaloneInjector);
|
||||
expect(injectorDep.providedIn).toBe(myStandalonecomponentB.injector);
|
||||
expect(myServiceDDep.providedIn).toBe(standaloneInjector.get(Injector, null, {
|
||||
skipSelf: true
|
||||
}) as Injector);
|
||||
expect(getNodeInjectorLView(myServiceGDep.providedIn as NodeInjector))
|
||||
.toBe(getNodeInjectorLView(
|
||||
myStandalonecomponentB.parentComponent.injector as NodeInjector));
|
||||
|
||||
function onMyStandaloneDirectiveCreated(myStandaloneDirective: MyStandaloneDirective) {
|
||||
const injector = myStandaloneDirective.injector;
|
||||
const deps = getDependenciesFromInjectable(injector, MyStandaloneDirective);
|
||||
expect(deps).not.toBeNull();
|
||||
expect(deps!.dependencies.length).toBe(2); // MyServiceH, Injector
|
||||
expect(deps!.dependencies[0].token).toBe(MyServiceH);
|
||||
expect(deps!.dependencies[0].flags)
|
||||
.toEqual({optional: true, host: true, self: false, skipSelf: false});
|
||||
// The NodeInjector that provides MyService is not in the host path of this injector.
|
||||
expect(deps!.dependencies[0].providedIn).toBeUndefined();
|
||||
}
|
||||
}));
|
||||
|
||||
it('should be able to recursively determine dependencies of dependencies by using the providedIn field',
|
||||
fakeAsync(() => {
|
||||
@Injectable()
|
||||
class MyService {
|
||||
myServiceB = inject(MyServiceB);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class MyServiceB {
|
||||
router = inject(Router);
|
||||
}
|
||||
|
||||
@NgModule({providers: [MyService]})
|
||||
class ModuleA {
|
||||
}
|
||||
|
||||
@NgModule({imports: [ModuleA]})
|
||||
class ModuleB {
|
||||
}
|
||||
|
||||
@NgModule({providers: [MyServiceB]})
|
||||
class ModuleC {
|
||||
}
|
||||
|
||||
@NgModule({imports: [ModuleB, ModuleC]})
|
||||
class ModuleD {
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'my-comp', template: 'hello world', imports: [ModuleD], standalone: true})
|
||||
class MyStandaloneComponent {
|
||||
myService = inject(MyService);
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [RouterModule]});
|
||||
const root = TestBed.createComponent(MyStandaloneComponent);
|
||||
|
||||
const {instance, dependencies} = getDependenciesFromInjectable(
|
||||
root.componentRef.injector, root.componentRef.componentType)!;
|
||||
const standaloneInjector = root.componentRef.injector.get(EnvironmentInjector);
|
||||
|
||||
expect(instance).toBeInstanceOf(MyStandaloneComponent);
|
||||
expect(dependencies).toBeInstanceOf(Array);
|
||||
expect(dependencies.length).toBe(1);
|
||||
|
||||
const myServiceDependency = dependencies[0];
|
||||
|
||||
expect(myServiceDependency.token).toBe(MyService);
|
||||
expect(myServiceDependency.value).toBe((instance as MyStandaloneComponent).myService);
|
||||
expect(myServiceDependency.flags)
|
||||
.toEqual({optional: false, skipSelf: false, self: false, host: false});
|
||||
expect(myServiceDependency.providedIn).toBe(standaloneInjector);
|
||||
|
||||
const {instance: myServiceInstance, dependencies: myServiceDependencies} =
|
||||
getDependenciesFromInjectable(
|
||||
myServiceDependency.providedIn!, myServiceDependency.token!)!;
|
||||
expect(myServiceDependencies).toBeInstanceOf(Array);
|
||||
expect(myServiceDependencies.length).toBe(1);
|
||||
const myServiceBDependency = myServiceDependencies[0];
|
||||
|
||||
expect(myServiceBDependency.token).toBe(MyServiceB);
|
||||
expect(myServiceBDependency.value).toBe((myServiceInstance as MyService).myServiceB);
|
||||
expect(myServiceBDependency.flags)
|
||||
.toEqual({optional: false, skipSelf: false, self: false, host: false});
|
||||
expect(myServiceBDependency.providedIn).toBe(standaloneInjector);
|
||||
|
||||
const {instance: myServiceBInstance, dependencies: myServiceBDependencies} =
|
||||
getDependenciesFromInjectable(
|
||||
myServiceBDependency.providedIn!, myServiceBDependency.token!)!;
|
||||
expect(myServiceBDependencies).toBeInstanceOf(Array);
|
||||
expect(myServiceBDependencies.length).toBe(1);
|
||||
const routerDependency = myServiceBDependencies[0];
|
||||
|
||||
expect(routerDependency.token).toBe(Router);
|
||||
expect(routerDependency.value).toBe((myServiceBInstance as MyServiceB).router);
|
||||
expect(routerDependency.flags)
|
||||
.toEqual({optional: false, skipSelf: false, self: false, host: false});
|
||||
expect(routerDependency.providedIn).toBe((standaloneInjector as R3Injector).parent);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getInjectorResolutionPath', () => {
|
||||
beforeEach(() => setupFrameworkInjectorProfiler());
|
||||
afterAll(() => setInjectorProfiler(null));
|
||||
|
||||
it('should be able to inspect injector hierarchy structure', fakeAsync(() => {
|
||||
class MyServiceA {}
|
||||
@NgModule({providers: [MyServiceA]})
|
||||
class ModuleA {
|
||||
}
|
||||
|
||||
class MyServiceB {}
|
||||
@NgModule({providers: [MyServiceB]})
|
||||
class ModuleB {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'lazy-comp',
|
||||
template: `lazy component`,
|
||||
standalone: true,
|
||||
imports: [ModuleB],
|
||||
})
|
||||
class LazyComponent {
|
||||
constructor() {
|
||||
onLazyComponentCreated();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, ModuleA],
|
||||
template: `<router-outlet/>`,
|
||||
})
|
||||
class MyStandaloneComponent {
|
||||
nodeInjector = inject(Injector);
|
||||
envInjector = inject(EnvironmentInjector);
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterModule.forRoot([{
|
||||
path: 'lazy',
|
||||
loadComponent: () => LazyComponent,
|
||||
}])]
|
||||
});
|
||||
const root = TestBed.createComponent(MyStandaloneComponent);
|
||||
TestBed.inject(Router).navigateByUrl('/lazy');
|
||||
tick();
|
||||
root.detectChanges();
|
||||
|
||||
function onLazyComponentCreated() {
|
||||
const lazyComponentNodeInjector = inject(Injector);
|
||||
const lazyComponentEnvironmentInjector = inject(EnvironmentInjector);
|
||||
|
||||
const routerOutletNodeInjector = inject(Injector, {skipSelf: true}) as NodeInjector;
|
||||
|
||||
const myStandaloneComponent = inject(MyStandaloneComponent);
|
||||
const myStandaloneComponentNodeInjector =
|
||||
myStandaloneComponent.nodeInjector as NodeInjector;
|
||||
|
||||
const path = getInjectorResolutionPath(lazyComponentNodeInjector);
|
||||
|
||||
/**
|
||||
*
|
||||
* Here is a diagram of the injectors in our application:
|
||||
*
|
||||
*
|
||||
*
|
||||
* ┌────────────┐
|
||||
* │NullInjector│
|
||||
* └─────▲──────┘
|
||||
* │
|
||||
* ┌────────────┴────────────────┐
|
||||
* │EnvironmentInjector(Platform)│
|
||||
* └────────────▲────────────────┘
|
||||
* │
|
||||
* ┌────────────┴────────────┐
|
||||
* │EnvironmentInjector(Root)│
|
||||
* └───────────────▲─────────┘
|
||||
* │
|
||||
* │
|
||||
* │
|
||||
*┌────────────────────────────────────┐ ┌─┴────────────────────────────────────────┐
|
||||
*│ NodeInjector(MyStandaloneComponent)├─►| EnvironmentInjector(MyStandaloneComponent│
|
||||
*└────────────────▲───────────────────┘ └────────────▲─────────────────────────────┘
|
||||
* │ │
|
||||
* │ │
|
||||
* │ │
|
||||
* ┌────────────┴─────────────┐ │
|
||||
* │NodeInjector(RouterOutlet)├──────────┐ │
|
||||
* └────────────▲─────────────┘ │ │
|
||||
* │ │ │
|
||||
* │ │ │
|
||||
* │ │ │
|
||||
* │ │ │
|
||||
* ┌─────────────┴──────────────┐ ┌──────▼──────────┴────────────────┐
|
||||
* │ NodeInjector(LazyComponent)├──►EnvironmentInjector(LazyComponent)│
|
||||
* └────────────────────────────┘ └──────────────────────────────────┘
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* The Resolution path if we start at NodeInjector(LazyComponent) should be
|
||||
* [
|
||||
* NodeInjector[LazyComponent],
|
||||
* NodeInjector[RouterOutlet],
|
||||
* NodeInjector[MyStandaloneComponent],
|
||||
* R3Injector[LazyComponent],
|
||||
* R3Injector[MyStandaloneComponent],
|
||||
* R3Injector[Root],
|
||||
* R3Injector[Platform],
|
||||
* NullInjector
|
||||
* ]
|
||||
*/
|
||||
expect(path.length).toBe(8);
|
||||
|
||||
expect(path[0]).toBe(lazyComponentNodeInjector);
|
||||
|
||||
expect(path[1]).toBeInstanceOf(NodeInjector);
|
||||
expect(getNodeInjectorLView(path[1] as NodeInjector))
|
||||
.toBe(getNodeInjectorLView(routerOutletNodeInjector));
|
||||
|
||||
expect(path[2]).toBeInstanceOf(NodeInjector);
|
||||
expect(getNodeInjectorLView(path[2] as NodeInjector))
|
||||
.toBe(getNodeInjectorLView(myStandaloneComponentNodeInjector));
|
||||
|
||||
expect(path[3]).toBeInstanceOf(R3Injector);
|
||||
expect(path[3]).toBe(lazyComponentEnvironmentInjector);
|
||||
expect((path[3] as R3Injector).scopes.has('environment')).toBeTrue();
|
||||
expect((path[3] as R3Injector).source).toBe('Standalone[LazyComponent]');
|
||||
|
||||
expect(path[4]).toBeInstanceOf(R3Injector);
|
||||
expect((path[4] as R3Injector).scopes.has('environment')).toBeTrue();
|
||||
expect((path[4] as R3Injector).source).toBe('Standalone[MyStandaloneComponent]');
|
||||
|
||||
expect(path[5]).toBeInstanceOf(R3Injector);
|
||||
expect((path[5] as R3Injector).scopes.has('environment')).toBeTrue();
|
||||
expect((path[5] as R3Injector).scopes.has('root')).toBeTrue();
|
||||
|
||||
expect(path[6]).toBeInstanceOf(R3Injector);
|
||||
expect((path[6] as R3Injector).scopes.has('platform')).toBeTrue();
|
||||
|
||||
expect(path[7]).toBeInstanceOf(NullInjector);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
|
@ -6,14 +6,22 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector, ɵcreateInjector, ɵɵFactoryTarget, ɵɵngDeclareFactory} from '@angular/core';
|
||||
import {Injector, ɵcreateInjector, ɵInjectorProfilerContext, ɵsetInjectorProfilerContext, ɵɵFactoryTarget, ɵɵngDeclareFactory} from '@angular/core';
|
||||
import {ɵɵdefineInjector} from '@angular/core/src/di';
|
||||
import {setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
|
||||
|
||||
describe('Factory declaration jit compilation', () => {
|
||||
let previousInjector: Injector|null|undefined;
|
||||
beforeEach(() => previousInjector = setCurrentInjector(ɵcreateInjector(TestInjector)));
|
||||
afterEach(() => setCurrentInjector(previousInjector));
|
||||
let previousInjectorProfilerContext: ɵInjectorProfilerContext;
|
||||
beforeEach(() => {
|
||||
const injector = ɵcreateInjector(TestInjector);
|
||||
previousInjector = setCurrentInjector(injector);
|
||||
previousInjectorProfilerContext = ɵsetInjectorProfilerContext({injector, token: null});
|
||||
});
|
||||
afterEach(() => {
|
||||
setCurrentInjector(previousInjector);
|
||||
previousInjectorProfilerContext = ɵsetInjectorProfilerContext(previousInjectorProfilerContext);
|
||||
});
|
||||
|
||||
it('should compile a simple factory declaration', () => {
|
||||
const factory = TestClass.ɵfac as Function;
|
||||
|
|
|
|||
|
|
@ -6,12 +6,20 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {forwardRef, InjectionToken, Injector, ɵcreateInjector, ɵsetCurrentInjector, ɵɵdefineInjector, ɵɵInjectableDeclaration, ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule} from '@angular/core';
|
||||
import {forwardRef, InjectionToken, Injector, ɵcreateInjector, ɵInjectorProfilerContext, ɵsetCurrentInjector, ɵsetInjectorProfilerContext, ɵɵdefineInjector, ɵɵInjectableDeclaration, ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule} from '@angular/core';
|
||||
|
||||
describe('Injectable declaration jit compilation', () => {
|
||||
let previousInjector: Injector|null|undefined;
|
||||
beforeEach(() => previousInjector = ɵsetCurrentInjector(ɵcreateInjector(TestInjector)));
|
||||
afterEach(() => ɵsetCurrentInjector(previousInjector));
|
||||
let previousInjectorProfilerContext: ɵInjectorProfilerContext;
|
||||
beforeEach(() => {
|
||||
const injector = ɵcreateInjector(TestInjector);
|
||||
previousInjector = ɵsetCurrentInjector(injector);
|
||||
previousInjectorProfilerContext = ɵsetInjectorProfilerContext({injector, token: null});
|
||||
});
|
||||
afterEach(() => {
|
||||
ɵsetCurrentInjector(previousInjector);
|
||||
previousInjectorProfilerContext = ɵsetInjectorProfilerContext(previousInjectorProfilerContext);
|
||||
});
|
||||
|
||||
it('should compile a minimal injectable declaration that delegates to `ɵfac`', () => {
|
||||
const provider = Minimal.ɵprov as ɵɵInjectableDeclaration<Minimal>;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {inject, InjectFlags, InjectionToken, InjectOptions, Injector, ProviderToken, ɵsetCurrentInjector as setCurrentInjector} from '@angular/core';
|
||||
import {inject, InjectFlags, InjectionToken, InjectOptions, Injector, ProviderToken, ɵInjectorProfilerContext, ɵsetCurrentInjector as setCurrentInjector, ɵsetInjectorProfilerContext} from '@angular/core';
|
||||
|
||||
|
||||
class MockRootScopeInjector implements Injector {
|
||||
constructor(readonly parent: Injector) {}
|
||||
|
|
@ -16,10 +17,13 @@ class MockRootScopeInjector implements Injector {
|
|||
flags: InjectFlags|InjectOptions = InjectFlags.Default): T {
|
||||
if ((token as any).ɵprov && (token as any).ɵprov.providedIn === 'root') {
|
||||
const old = setCurrentInjector(this);
|
||||
const previousInjectorProfilerContext =
|
||||
ɵsetInjectorProfilerContext({injector: this, token: null});
|
||||
try {
|
||||
return (token as any).ɵprov.factory();
|
||||
} finally {
|
||||
setCurrentInjector(old);
|
||||
ɵsetInjectorProfilerContext(previousInjectorProfilerContext);
|
||||
}
|
||||
}
|
||||
return this.parent.get(token, defaultValue, flags);
|
||||
|
|
|
|||
Loading…
Reference in a new issue