|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | import {AsyncPipe} from '@angular/common'; |
10 | | -import {ApplicationRef, ChangeDetectorRef, Component, ComponentRef, createComponent, DebugElement, ElementRef, EnvironmentInjector, ErrorHandler, getDebugNode, inject, Injectable, Input, NgZone, PLATFORM_ID, signal, TemplateRef, Type, ViewChild, ViewContainerRef, ɵprovideZonelessChangeDetection as provideZonelessChangeDetection} from '@angular/core'; |
| 10 | +import {PLATFORM_BROWSER_ID} from '@angular/common/src/platform_id'; |
| 11 | +import {ApplicationRef, ChangeDetectorRef, Component, ComponentRef, createComponent, DebugElement, destroyPlatform, ElementRef, EnvironmentInjector, ErrorHandler, getDebugNode, inject, Injectable, Input, NgZone, PLATFORM_ID, signal, TemplateRef, Type, ViewChild, ViewContainerRef, ɵprovideZonelessChangeDetection as provideZonelessChangeDetection} from '@angular/core'; |
11 | 12 | import {toSignal} from '@angular/core/rxjs-interop'; |
12 | 13 | import {TestBed} from '@angular/core/testing'; |
| 14 | +import {bootstrapApplication} from '@angular/platform-browser'; |
| 15 | +import {withBody} from '@angular/private/testing'; |
13 | 16 | import {BehaviorSubject, firstValueFrom} from 'rxjs'; |
14 | 17 | import {filter, take, tap} from 'rxjs/operators'; |
15 | 18 |
|
16 | 19 | describe('Angular with NoopNgZone', () => { |
17 | | - function whenStable(): Promise<boolean> { |
18 | | - return firstValueFrom(TestBed.inject(EnvironmentInjector) |
19 | | - .get(ApplicationRef) |
20 | | - .isStable.pipe(filter(stable => stable))); |
| 20 | + function whenStable(applicationRef = TestBed.inject(ApplicationRef)): Promise<boolean> { |
| 21 | + return firstValueFrom(applicationRef.isStable.pipe(filter(stable => stable))); |
21 | 22 | } |
22 | 23 |
|
23 | | - function isStable(): boolean { |
24 | | - const injector = TestBed.inject(EnvironmentInjector); |
| 24 | + function isStable(injector = TestBed.inject(EnvironmentInjector)): boolean { |
25 | 25 | return toSignal(injector.get(ApplicationRef).isStable, {requireSync: true, injector})(); |
26 | 26 | } |
27 | 27 |
|
@@ -216,7 +216,7 @@ describe('Angular with NoopNgZone', () => { |
216 | 216 | expect(componentRef.location.nativeElement.innerText).toEqual('binding'); |
217 | 217 | }); |
218 | 218 |
|
219 | | - it('when destroying a view', async () => { |
| 219 | + it('when destroying a view (with animations)', async () => { |
220 | 220 | @Component({ |
221 | 221 | template: '{{"binding"}}', |
222 | 222 | standalone: true, |
@@ -249,10 +249,48 @@ describe('Angular with NoopNgZone', () => { |
249 | 249 | await whenStable(); |
250 | 250 | expect(fixture.location.nativeElement.innerText).toEqual('binding'); |
251 | 251 | component2.destroy(); |
| 252 | + expect(isStable()).toBe(false); |
252 | 253 | await whenStable(); |
253 | 254 | expect(fixture.location.nativeElement.innerText).toEqual(''); |
254 | 255 | }); |
255 | 256 |
|
| 257 | + it('when destroying a view (*no* animations)', withBody('<app></app>', async () => { |
| 258 | + destroyPlatform(); |
| 259 | + @Component({ |
| 260 | + template: '{{"binding"}}', |
| 261 | + standalone: true, |
| 262 | + }) |
| 263 | + class DynamicCmp { |
| 264 | + elementRef = inject(ElementRef); |
| 265 | + } |
| 266 | + @Component({ |
| 267 | + selector: 'app', |
| 268 | + template: '<ng-template #ref></ng-template>', |
| 269 | + standalone: true, |
| 270 | + }) |
| 271 | + class App { |
| 272 | + @ViewChild('ref', {read: ViewContainerRef}) viewContainer!: ViewContainerRef; |
| 273 | + } |
| 274 | + const applicationRef = await bootstrapApplication(App, { |
| 275 | + providers: [ |
| 276 | + provideZonelessChangeDetection(), |
| 277 | + {provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID}, |
| 278 | + ] |
| 279 | + }); |
| 280 | + const appViewRef = (applicationRef as any)._views[0] as {context: App, rootNodes: any[]}; |
| 281 | + await whenStable(applicationRef); |
| 282 | + |
| 283 | + const component2 = |
| 284 | + createComponent(DynamicCmp, {environmentInjector: applicationRef.injector}); |
| 285 | + appViewRef.context.viewContainer.insert(component2.hostView); |
| 286 | + expect(isStable(applicationRef.injector)).toBe(false); |
| 287 | + await whenStable(applicationRef); |
| 288 | + component2.destroy(); |
| 289 | + expect(isStable(applicationRef.injector)).toBe(true); |
| 290 | + expect(appViewRef.rootNodes[0].innerText).toEqual(''); |
| 291 | + destroyPlatform(); |
| 292 | + })); |
| 293 | + |
256 | 294 | it('when attaching view to ApplicationRef', async () => { |
257 | 295 | @Component({ |
258 | 296 | selector: 'dynamic-cmp', |
|
0 commit comments