|
8 | 8 |
|
9 | 9 | import {PercentPipe} from '@angular/common'; |
10 | 10 | import {inject} from '@angular/core'; |
11 | | -import {ClassProvider, Component, Directive, Inject, Injectable, InjectFlags, InjectionToken, Injector, NgModule, NgModuleRef, ViewChild} from '@angular/core/src/core'; |
| 11 | +import {afterRender, ClassProvider, Component, Directive, ElementRef, Injectable, InjectFlags, InjectionToken, InjectOptions, Injector, NgModule, NgModuleRef, ProviderToken, ViewChild} from '@angular/core/src/core'; |
12 | 12 | import {NullInjector} from '@angular/core/src/di/null_injector'; |
13 | 13 | import {isClassProvider, isExistingProvider, isFactoryProvider, isTypeProvider, isValueProvider} from '@angular/core/src/di/provider_collection'; |
14 | 14 | import {EnvironmentInjector, R3Injector} from '@angular/core/src/di/r3_injector'; |
15 | 15 | import {setupFrameworkInjectorProfiler} from '@angular/core/src/render3/debug/framework_injector_profiler'; |
16 | | -import {getInjectorProfilerContext, InjectedService, InjectedServiceEvent, InjectorCreatedInstanceEvent, InjectorProfilerEvent, InjectorProfilerEventType, ProviderConfiguredEvent, ProviderRecord, setInjectorProfiler} from '@angular/core/src/render3/debug/injector_profiler'; |
| 16 | +import {getInjectorProfilerContext, InjectedServiceEvent, InjectorCreatedInstanceEvent, InjectorProfilerEvent, InjectorProfilerEventType, ProviderConfiguredEvent, setInjectorProfiler} from '@angular/core/src/render3/debug/injector_profiler'; |
17 | 17 | import {getNodeInjectorLView, NodeInjector} from '@angular/core/src/render3/di'; |
18 | | -import {getDependenciesFromInjectable, getInjectorProviders, getInjectorResolutionPath} from '@angular/core/src/render3/util/injector_discovery_utils'; |
| 18 | +import {getDependenciesFromInjectable, getInjectorMetadata, getInjectorProviders, getInjectorResolutionPath} from '@angular/core/src/render3/util/injector_discovery_utils'; |
19 | 19 | import {fakeAsync, tick} from '@angular/core/testing'; |
20 | 20 | import {TestBed} from '@angular/core/testing/src/test_bed'; |
21 | 21 | import {BrowserModule} from '@angular/platform-browser'; |
@@ -211,6 +211,114 @@ describe('setProfiler', () => { |
211 | 211 | }); |
212 | 212 | }); |
213 | 213 |
|
| 214 | +describe('getInjectorMetadata', () => { |
| 215 | + it('should be able to determine injector type and name', fakeAsync(() => { |
| 216 | + class MyServiceA {} |
| 217 | + @NgModule({providers: [MyServiceA]}) |
| 218 | + class ModuleA { |
| 219 | + } |
| 220 | + |
| 221 | + class MyServiceB {} |
| 222 | + @NgModule({providers: [MyServiceB]}) |
| 223 | + class ModuleB { |
| 224 | + } |
| 225 | + |
| 226 | + @Component({ |
| 227 | + selector: 'lazy-comp', |
| 228 | + template: `lazy component`, |
| 229 | + standalone: true, |
| 230 | + imports: [ModuleB] |
| 231 | + }) |
| 232 | + class LazyComponent { |
| 233 | + lazyComponentNodeInjector = inject(Injector); |
| 234 | + elementRef = inject(ElementRef); |
| 235 | + |
| 236 | + constructor() { |
| 237 | + afterRender(() => afterLazyComponentRendered(this)); |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + @Component({ |
| 242 | + standalone: true, |
| 243 | + imports: [RouterOutlet, ModuleA], |
| 244 | + template: `<router-outlet/>`, |
| 245 | + }) |
| 246 | + class MyStandaloneComponent { |
| 247 | + @ViewChild(RouterOutlet, {read: ElementRef}) routerOutlet: ElementRef|undefined; |
| 248 | + elementRef = inject(ElementRef); |
| 249 | + } |
| 250 | + |
| 251 | + TestBed.configureTestingModule({ |
| 252 | + imports: [RouterModule.forRoot([{ |
| 253 | + path: 'lazy', |
| 254 | + loadComponent: () => LazyComponent, |
| 255 | + }])] |
| 256 | + }); |
| 257 | + |
| 258 | + const root = TestBed.createComponent(MyStandaloneComponent); |
| 259 | + TestBed.inject(Router).navigateByUrl('/lazy'); |
| 260 | + tick(); |
| 261 | + root.detectChanges(); |
| 262 | + |
| 263 | + function afterLazyComponentRendered(lazyComponent: LazyComponent) { |
| 264 | + const {lazyComponentNodeInjector} = lazyComponent; |
| 265 | + const myStandaloneComponent = |
| 266 | + lazyComponentNodeInjector.get(MyStandaloneComponent, null, {skipSelf: true})!; |
| 267 | + expect(myStandaloneComponent).toBeInstanceOf(MyStandaloneComponent); |
| 268 | + expect(myStandaloneComponent.routerOutlet).toBeInstanceOf(ElementRef); |
| 269 | + |
| 270 | + const injectorPath = getInjectorResolutionPath(lazyComponentNodeInjector); |
| 271 | + const injectorMetadata = injectorPath.map(injector => getInjectorMetadata(injector)); |
| 272 | + |
| 273 | + expect(injectorMetadata[0]).toBeDefined(); |
| 274 | + expect(injectorMetadata[1]).toBeDefined(); |
| 275 | + expect(injectorMetadata[2]).toBeDefined(); |
| 276 | + expect(injectorMetadata[3]).toBeDefined(); |
| 277 | + expect(injectorMetadata[4]).toBeDefined(); |
| 278 | + expect(injectorMetadata[5]).toBeDefined(); |
| 279 | + expect(injectorMetadata[6]).toBeDefined(); |
| 280 | + expect(injectorMetadata[7]).toBeDefined(); |
| 281 | + |
| 282 | + expect(injectorMetadata[0]!.source).toBe(lazyComponent.elementRef.nativeElement); |
| 283 | + expect(injectorMetadata[1]!.source) |
| 284 | + .toBe(myStandaloneComponent.routerOutlet!.nativeElement); |
| 285 | + expect(injectorMetadata[2]!.source).toBe(myStandaloneComponent.elementRef.nativeElement); |
| 286 | + expect(injectorMetadata[3]!.source).toBe('Standalone[LazyComponent]'); |
| 287 | + expect(injectorMetadata[4]!.source).toBe('Standalone[MyStandaloneComponent]'); |
| 288 | + expect(injectorMetadata[5]!.source).toBe('DynamicTestModule'); |
| 289 | + expect(injectorMetadata[6]!.source).toBe('Platform: core'); |
| 290 | + expect(injectorMetadata[7]!.source).toBeNull(); |
| 291 | + |
| 292 | + expect(injectorMetadata[0]!.type).toBe('element'); |
| 293 | + expect(injectorMetadata[1]!.type).toBe('element'); |
| 294 | + expect(injectorMetadata[2]!.type).toBe('element'); |
| 295 | + expect(injectorMetadata[3]!.type).toBe('environment'); |
| 296 | + expect(injectorMetadata[4]!.type).toBe('environment'); |
| 297 | + expect(injectorMetadata[5]!.type).toBe('environment'); |
| 298 | + expect(injectorMetadata[6]!.type).toBe('environment'); |
| 299 | + expect(injectorMetadata[7]!.type).toBe('null'); |
| 300 | + } |
| 301 | + })); |
| 302 | + |
| 303 | + it('should return null for injectors it does not recognize', () => { |
| 304 | + class MockInjector extends Injector { |
| 305 | + override get(): void { |
| 306 | + throw new Error('Method not implemented.'); |
| 307 | + } |
| 308 | + } |
| 309 | + const mockInjector = new MockInjector(); |
| 310 | + expect(getInjectorMetadata(mockInjector)).toBeNull(); |
| 311 | + }); |
| 312 | + |
| 313 | + it('should return null as the source for an R3Injector with no source.', () => { |
| 314 | + const emptyR3Injector = new R3Injector([], new NullInjector(), null, new Set()); |
| 315 | + const r3InjectorMetadata = getInjectorMetadata(emptyR3Injector); |
| 316 | + expect(r3InjectorMetadata).toBeDefined(); |
| 317 | + expect(r3InjectorMetadata!.source).toBeNull(); |
| 318 | + expect(r3InjectorMetadata!.type).toBe('environment'); |
| 319 | + }); |
| 320 | +}); |
| 321 | + |
214 | 322 | describe('getInjectorProviders', () => { |
215 | 323 | beforeEach(() => setupFrameworkInjectorProfiler()); |
216 | 324 | afterAll(() => setInjectorProfiler(null)); |
|
0 commit comments