From 1167626e5b8b295b04197dc760039d1708c162ff Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Mon, 27 Jul 2026 00:01:54 +0200 Subject: [PATCH] refactor(core): Migrate more tests off `fakeAsync` This will prevent to polute the agent context with outdated/bad practices. (cherry picked from commit c1025a0510520fa4398705869bdc73613332b337) --- .../test/acceptance/authoring/BUILD.bazel | 10 +- .../authoring/signal_inputs_spec.ts | 43 +-- .../test/acceptance/change_detection_spec.ts | 34 +- packages/core/test/acceptance/defer_spec.ts | 332 +++++++++--------- .../test/acceptance/injector_profiler_spec.ts | 6 +- .../core/test/acceptance/integration_spec.ts | 54 +-- .../core/test/acceptance/signal_debug_spec.ts | 78 ++-- packages/core/test/acceptance/tracing_spec.ts | 14 +- 8 files changed, 278 insertions(+), 293 deletions(-) diff --git a/packages/core/test/acceptance/authoring/BUILD.bazel b/packages/core/test/acceptance/authoring/BUILD.bazel index 577de26a587..aa364ef58f3 100644 --- a/packages/core/test/acceptance/authoring/BUILD.bazel +++ b/packages/core/test/acceptance/authoring/BUILD.bazel @@ -1,4 +1,4 @@ -load("//tools:defaults.bzl", "angular_jasmine_test", "js_binary", "js_run_binary", "ng_project", "ng_web_test_suite", "ts_project") +load("//tools:defaults.bzl", "js_binary", "js_run_binary", "ng_project", "ts_project", "zoneless_jasmine_test", "zoneless_web_test_suite") package(default_visibility = ["//visibility:private"]) @@ -61,28 +61,28 @@ ng_project( deps = TEST_DEPS, ) -angular_jasmine_test( +zoneless_jasmine_test( name = "test", data = [ ":test_lib", ], ) -angular_jasmine_test( +zoneless_jasmine_test( name = "test_jit", data = [ ":test_jit_lib", ], ) -ng_web_test_suite( +zoneless_web_test_suite( name = "test_web", deps = [ ":test_lib", ], ) -ng_web_test_suite( +zoneless_web_test_suite( name = "test_jit_web", deps = [ ":test_jit_lib", diff --git a/packages/core/test/acceptance/authoring/signal_inputs_spec.ts b/packages/core/test/acceptance/authoring/signal_inputs_spec.ts index e24c9a5d570..b83191d1917 100644 --- a/packages/core/test/acceptance/authoring/signal_inputs_spec.ts +++ b/packages/core/test/acceptance/authoring/signal_inputs_spec.ts @@ -29,11 +29,10 @@ import { ViewContainerRef, } from '@angular/core'; import {By} from '@angular/platform-browser'; -import {isNode} from '@angular/private/testing'; +import {isNode, timeout, useAutoTick} from '@angular/private/testing'; import {Subscription} from 'rxjs'; import {SIGNAL} from '../../../primitives/signals'; -import {fakeAsync, TestBed, tick} from '../../../testing'; -import {tickAnimationFrames} from '../../animation_utils/tick_animation_frames'; +import {TestBed} from '../../../testing'; describe('signal inputs', () => { beforeEach(() => @@ -311,12 +310,14 @@ describe('signal inputs', () => { }); describe('animation API', () => { + useAutoTick(); + if (isNode) { it('should pass', () => expect(true).toBe(true)); return; } - it('should support signal inputs', fakeAsync(() => { + it('should support signal inputs', async () => { const styles = ` .slide-in { animation: slide-in 1ms; @@ -366,7 +367,7 @@ describe('signal inputs', () => { const fixture = TestBed.createComponent(TestComponent); fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); const childCmp = fixture.debugElement.query(By.css('p')); expect(childCmp.nativeElement.className).toContain('fade-in'); @@ -375,11 +376,11 @@ describe('signal inputs', () => { new AnimationEvent('animationend', {animationName: 'fade-in'}), ); fixture.detectChanges(); - tick(); + await fixture.whenStable(); expect(childCmp.nativeElement.className).not.toContain('fade-in'); - })); + }); - it('should support content projection', fakeAsync(() => { + it('should support content projection', async () => { const animateStyles = ` .fade-in { animation: fade 1ms forwards; @@ -467,14 +468,14 @@ describe('signal inputs', () => { // show first time fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); const enterAppContent = fixture.nativeElement.querySelector('app-content'); expect(enterAppContent).not.toBeNull(); expect(enterAppContent.className).toEqual('fade-in'); enterAppContent.dispatchEvent(new AnimationEvent('animationstart')); enterAppContent.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); - tick(); + await fixture.whenStable(); expect(enterAppContent.className).not.toEqual('fade-in'); expect(fixture.debugElement.query(By.css('app-content'))).not.toBeNull(); @@ -482,7 +483,7 @@ describe('signal inputs', () => { // hide first time fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); const leaveAppContent = fixture.nativeElement.querySelector('app-content'); expect(fixture.nativeElement.outerHTML).toContain('app-content class="fade-out"'); @@ -490,7 +491,7 @@ describe('signal inputs', () => { leaveAppContent.dispatchEvent(new AnimationEvent('animationstart')); leaveAppContent.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); expect(fixture.debugElement.query(By.css('app-content'))).toBeNull(); @@ -499,7 +500,7 @@ describe('signal inputs', () => { // show second time fixture.detectChanges(); fixture.changeDetectorRef.markForCheck(); - tickAnimationFrames(1); + await timeout(17); const enterAppContent2 = fixture.nativeElement.querySelector('app-content'); expect(enterAppContent2).not.toBeNull(); @@ -508,7 +509,7 @@ describe('signal inputs', () => { fadeInEl.dispatchEvent(new AnimationEvent('animationstart')); fadeInEl.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); expect(fixture.nativeElement.querySelector('app-content').className).not.toEqual('fade-in'); expect(fixture.debugElement.query(By.css('app-content'))).not.toBeNull(); @@ -517,7 +518,7 @@ describe('signal inputs', () => { // hide second time fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); const leaveAppContent2 = fixture.nativeElement.querySelector('app-content'); expect(fixture.nativeElement.outerHTML).toContain('app-content class="fade-out"'); @@ -525,12 +526,12 @@ describe('signal inputs', () => { leaveAppContent2.dispatchEvent(new AnimationEvent('animationstart')); leaveAppContent2.dispatchEvent(new AnimationEvent('animationend', {animationName: 'fade'})); fixture.detectChanges(); - tickAnimationFrames(1); + await timeout(17); expect(fixture.debugElement.query(By.css('app-content'))).toBeNull(); - })); + }); - it('should run animations using the root injector so that the animation queue still runs when the component is destroyed before afterNextRender occurs', fakeAsync(() => { + it('should run animations using the root injector so that the animation queue still runs when the component is destroyed before afterNextRender occurs', async () => { const animateStyles = ` .fade-out { animation: fade-out 100ms; @@ -643,7 +644,7 @@ describe('signal inputs', () => { // remove the item from the list fixture.componentInstance.removeItem(fixture.componentInstance.list()[0].id); fixture.detectChanges(); // Detect changes for TestComponent to trigger leave animation - tickAnimationFrames(1); // Allow animation to start (will add 'fade-out' class) + await timeout(1); // Allow animation to start (will add 'fade-out' class) const fadingOut = fixture.nativeElement.querySelector('notification'); @@ -654,9 +655,9 @@ describe('signal inputs', () => { notification.dispatchEvent( new AnimationEvent('animationend', {animationName: 'fade-out', bubbles: true}), ); - tick(300); // Advance timers by animation duration (0.5s) + await timeout(300); // Advance timers by animation duration (0.5s) fixture.detectChanges(); // Detect changes after animation completes and element is removed expect(fixture.nativeElement.querySelector('notification')).toBeNull(); // Verify element is removed - })); + }); }); }); diff --git a/packages/core/test/acceptance/change_detection_spec.ts b/packages/core/test/acceptance/change_detection_spec.ts index 5f81177baf2..0bd8dffb5e1 100644 --- a/packages/core/test/acceptance/change_detection_spec.ts +++ b/packages/core/test/acceptance/change_detection_spec.ts @@ -40,13 +40,7 @@ import { ViewChildren, ViewContainerRef, } from '../../src/core'; -import { - ComponentFixture, - ComponentFixtureAutoDetect, - fakeAsync, - TestBed, - tick, -} from '../../testing'; +import {ComponentFixture, ComponentFixtureAutoDetect, TestBed} from '../../testing'; describe('change detection', () => { beforeEach(() => { @@ -121,7 +115,6 @@ describe('change detection', () => { @Component({ selector: 'onpush', template: '', - changeDetection: ChangeDetectionStrategy.OnPush, }) class OnPushComponent { checks = 0; @@ -220,7 +213,6 @@ describe('change detection', () => { @Component({ template: ``, - changeDetection: ChangeDetectionStrategy.OnPush, imports: [ViewManipulation], }) class App {} @@ -230,7 +222,6 @@ describe('change detection', () => {
{{ increment() }}
`, - changeDetection: ChangeDetectionStrategy.OnPush, }) class DynamicComp { increment() { @@ -272,7 +263,6 @@ describe('change detection', () => { selector: 'child', template: '{{data()}}', imports: [ViewManipulation], - changeDetection: ChangeDetectionStrategy.OnPush, }) class ChildComponent { data = data; @@ -312,7 +302,6 @@ describe('change detection', () => { @Component({ selector: `test-cmpt`, template: `{{ counter }}|`, - changeDetection: ChangeDetectionStrategy.OnPush, }) class TestCmpt { counter = 0; @@ -326,7 +315,6 @@ describe('change detection', () => { @Component({ selector: 'dynamic-cmpt', template: `dynamic|{{ binding }}`, - changeDetection: ChangeDetectionStrategy.OnPush, }) class DynamicCmpt { @Input() binding = 'binding'; @@ -429,7 +417,6 @@ describe('change detection', () => { describe('OnPush', () => { @Component({ selector: 'my-comp', - changeDetection: ChangeDetectionStrategy.OnPush, template: `{{ doCheckCount }} - {{ name }} `, standalone: false, }) @@ -570,7 +557,6 @@ describe('change detection', () => { @Component({ selector: 'button-parent', template: '{{ doCheckCount }} - ', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class ButtonParent implements DoCheck { @@ -624,7 +610,7 @@ describe('change detection', () => { expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy'); }); - it('should check parent OnPush components when child directive on a template emits event', fakeAsync(() => { + it('should check parent OnPush components when child directive on a template emits event', async () => { @Directive({ selector: '[emitter]', standalone: false, @@ -642,7 +628,6 @@ describe('change detection', () => { @Component({ selector: 'my-app', template: '{{message}} ', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class MyApp { @@ -655,10 +640,10 @@ describe('change detection', () => { fixture.detectChanges(); expect(fixture.nativeElement.textContent.trim()).toEqual('initial message'); - tick(); + await fixture.whenStable(); fixture.detectChanges(); expect(fixture.nativeElement.textContent.trim()).toEqual('new message'); - })); + }); }); describe('ChangeDetectorRef', () => { @@ -666,7 +651,6 @@ describe('change detection', () => { @Component({ selector: 'my-comp', template: '{{ name }}', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class MyComp implements DoCheck { @@ -1193,7 +1177,6 @@ describe('change detection', () => { @Component({ selector: 'on-push-comp', template: '{{ value }}', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class OnPushComp { @@ -1238,7 +1221,6 @@ describe('change detection', () => { @Component({ selector: 'on-push-comp', template: '{{ value }}', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class OnPushComp implements DoCheck { @@ -1255,7 +1237,6 @@ describe('change detection', () => { @Component({ template: '{{ value }} - ', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class OnPushParent { @@ -1317,7 +1298,6 @@ describe('change detection', () => { it('should ensure OnPush components in embedded views are checked', () => { @Component({ template: '{{ value }} - ', - changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) class EmbeddedViewParent { @@ -1358,7 +1338,6 @@ describe('change detection', () => { it('async pipe should trigger CD for embedded views where the declaration and insertion views are different', () => { @Component({ selector: 'insertion', - changeDetection: ChangeDetectionStrategy.OnPush, template: ` `, standalone: false, }) @@ -1369,7 +1348,6 @@ describe('change detection', () => { // This component uses async pipe (which calls markForCheck) in a view that has different // insertion and declaration views. @Component({ - changeDetection: ChangeDetectionStrategy.OnPush, template: ` @@ -1537,7 +1515,6 @@ describe('change detection', () => { }); @Component({ - changeDetection: ChangeDetectionStrategy.OnPush, template: '{{state}}{{resolveReadPromise()}}', }) class MyApp { @@ -1617,7 +1594,6 @@ describe('change detection', () => { @Component({ template: '{{state}}', - changeDetection: ChangeDetectionStrategy.OnPush, }) class NotUnidirectionalDataFlow { state = 1; @@ -1653,7 +1629,6 @@ describe('change detection', () => { it(`should be able to mark component as dirty from within ${hookName}`, () => { @Component({ selector: 'on-push-comp', - changeDetection: ChangeDetectionStrategy.OnPush, template: `

{{ text }}

`, standalone: false, }) @@ -1705,7 +1680,6 @@ describe('change detection', () => { it(`should not be able to mark component as dirty from within ${hookName}`, () => { @Component({ selector: 'on-push-comp', - changeDetection: ChangeDetectionStrategy.OnPush, template: `

{{ text }}

`, standalone: false, }) diff --git a/packages/core/test/acceptance/defer_spec.ts b/packages/core/test/acceptance/defer_spec.ts index dbce77f1376..f3b32916790 100644 --- a/packages/core/test/acceptance/defer_spec.ts +++ b/packages/core/test/acceptance/defer_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule, ɵPLATFORM_BROWSER_ID as PLATFORM_BROWSER_ID} from '@angular/common'; -import {isBrowser} from '@angular/private/testing'; +import {isBrowser, timeout} from '@angular/private/testing'; import {ActivatedRoute, provideRouter, Router, RouterOutlet} from '@angular/router'; import {Console} from '../../src/console'; import { @@ -48,15 +48,7 @@ import {ChainedInjector} from '../../src/render3/chained_injector'; import {getComponentDef} from '../../src/render3/def_getters'; import {getInjectorResolutionPath} from '../../src/render3/util/injector_discovery_utils'; import {global} from '../../src/util/global'; -import { - ComponentFixture, - DeferBlockBehavior, - DeferBlockState, - fakeAsync, - flush, - TestBed, - tick, -} from '../../testing'; +import {ComponentFixture, DeferBlockBehavior, DeferBlockState, TestBed} from '../../testing'; /** * Clears all associated directive defs from a given component class. @@ -113,18 +105,41 @@ async function verifyTimeline( fixture: ComponentFixture, ...slots: Array<[time: number, expected: string | VoidFunction]> ) { + let actualTime = 0; for (let i = 0; i < slots.length; i++) { - const timeToWait = i === 0 ? slots[0][0] : slots[i][0] - slots[i - 1][0]; + const targetVirtualTime = slots[i][0]; const slotValue = slots[i][1]; - // This is an action, just invoke a function. + if (typeof slotValue === 'function') { + const waitTime = Math.max(0, targetVirtualTime - actualTime); + if (waitTime > 0) { + await timeout(waitTime); + actualTime += waitTime; + } slotValue(); - } - tick(timeToWait); - fixture.detectChanges(); - if (typeof slotValue === 'string') { - const actual = fixture.nativeElement.textContent.trim(); - expect(actual).withContext(`${slots[i][0]}ms`).toBe(slotValue); + } else { + const waitTime = Math.max(0, targetVirtualTime - actualTime); + if (waitTime > 0) { + await timeout(waitTime); + actualTime += waitTime; + } + + let actual = ''; + let waited = 0; + const maxWait = 40; // max ms to poll for the expected state + + while (waited <= maxWait) { + fixture.detectChanges(); + actual = fixture.nativeElement.textContent.trim(); + if (actual === slotValue) { + break; + } + await timeout(1); + waited += 1; + actualTime += 1; + } + + expect(actual).withContext(`${targetVirtualTime}ms (actual ${actualTime}ms)`).toBe(slotValue); } } } @@ -884,7 +899,7 @@ describe('@defer', () => { }); describe('minimum and after conditions', () => { - it('should support minimum and after conditions', fakeAsync(() => { + it('should support minimum and after conditions', async () => { const {trigger, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -897,7 +912,7 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [50, 'Placeholder'], [100, trigger(170)], @@ -906,9 +921,9 @@ describe('@defer', () => { [300, 'Loading'], [450, 'Main'], ); - })); + }); - it('should support @placeholder with `minimum`', fakeAsync(() => { + it('should support @placeholder with `minimum`', async () => { const {trigger, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -917,10 +932,10 @@ describe('@defer', () => { } `); - verifyTimeline(fixture, [0, trigger(40)], [90, 'Placeholder'], [100, 'Main']); - })); + await verifyTimeline(fixture, [0, trigger(40)], [90, 'Placeholder'], [100, 'Main']); + }); - it('should keep rendering @placeholder if trigger happened later', fakeAsync(() => { + it('should keep rendering @placeholder if trigger happened later', async () => { const {trigger, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -929,18 +944,18 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [0, 'Placeholder'], [50, trigger(20)], [90, 'Placeholder'], [100, 'Main'], ); - })); + }); it( 'should transition from @placeholder to primary content ' + 'if it was prefetched', - fakeAsync(() => { + async () => { const {trigger, triggerPrefetch, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -949,7 +964,7 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [0, 'Placeholder'], [20, triggerPrefetch(20)], @@ -957,10 +972,10 @@ describe('@defer', () => { [200, trigger(0)], [225, 'Main'], ); - }), + }, ); - it('should support @loading with `minimum`', fakeAsync(() => { + it('should support @loading with `minimum`', async () => { const {trigger, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -969,7 +984,7 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [0, trigger(20)], // Even though loading happened in 20ms, @@ -978,9 +993,9 @@ describe('@defer', () => { [95, 'Loading'], [100, 'Main'], ); - })); + }); - it('should support @loading with `after` and `minimum`', fakeAsync(() => { + it('should support @loading with `after` and `minimum`', async () => { const {trigger, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -989,7 +1004,7 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [0, trigger(150)], [50, ''], @@ -1000,9 +1015,9 @@ describe('@defer', () => { // Render main content after `after` + `minimum` ms. [300, 'Main'], ); - })); + }); - it('should skip @loading when resources were prefetched', fakeAsync(() => { + it('should skip @loading when resources were prefetched', async () => { const {trigger, triggerPrefetch, fixture} = createFixture(` @defer (when trigger; prefetch when prefetchTrigger) { @@ -1011,7 +1026,7 @@ describe('@defer', () => { } `); - verifyTimeline( + await verifyTimeline( fixture, [0, triggerPrefetch(50)], [50, ''], @@ -1021,7 +1036,7 @@ describe('@defer', () => { // resources were already preloaded. [125, 'Main'], ); - })); + }); }); describe('error handling', () => { @@ -2882,7 +2897,7 @@ describe('@defer', () => { // Note: these cases specifically use `on interaction`, however // the resolution logic is the same for all triggers. describe('trigger resolution', () => { - it('should resolve a trigger is outside the defer block', fakeAsync(() => { + it('should resolve a trigger is outside the defer block', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -2910,11 +2925,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should resolve a trigger on a component outside the defer block', fakeAsync(() => { + it('should resolve a trigger on a component outside the defer block', async () => { @Component({ selector: 'some-comp', template: '', @@ -2950,11 +2965,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should resolve a trigger that is on a parent element', fakeAsync(() => { + it('should resolve a trigger that is on a parent element', async () => { @Component({ template: ` ', @@ -3056,11 +3071,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should resolve a trigger that is inside the placeholder', fakeAsync(() => { + it('should resolve a trigger that is inside the placeholder', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3085,11 +3100,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should resolve a trigger that is a component inside the placeholder', fakeAsync(() => { + it('should resolve a trigger that is a component inside the placeholder', async () => { @Component({ selector: 'some-comp', template: '', @@ -3122,13 +3137,13 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); }); describe('interaction triggers', () => { - it('should load the deferred content when the trigger is clicked', fakeAsync(() => { + it('should load the deferred content when the trigger is clicked', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3150,11 +3165,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should load the deferred content when the trigger receives a keyboard event', fakeAsync(() => { + it('should load the deferred content when the trigger receives a keyboard event', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3182,11 +3197,11 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('keydown')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should load the deferred content when an implicit trigger is clicked', fakeAsync(() => { + it('should load the deferred content when an implicit trigger is clicked', async () => { @Component({ template: ` @defer (on interaction) { @@ -3206,12 +3221,12 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); fixture.detectChanges(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should load the deferred content if a child of the trigger is clicked', fakeAsync(() => { + it('should load the deferred content if a child of the trigger is clicked', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3237,11 +3252,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should support multiple deferred blocks with the same trigger', fakeAsync(() => { + it('should support multiple deferred blocks with the same trigger', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3269,11 +3284,11 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content 1 Main content 2'); - })); + }); - it('should unbind the trigger events when the deferred block is loaded', fakeAsync(() => { + it('should unbind the trigger events when the deferred block is loaded', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3294,14 +3309,14 @@ describe('@defer', () => { button.click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('keydown', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should unbind the trigger events when the trigger is destroyed', fakeAsync(() => { + it('should unbind the trigger events when the trigger is destroyed', async () => { @Component({ template: ` @if (renderBlock) { @@ -3330,9 +3345,9 @@ describe('@defer', () => { expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('keydown', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should unbind the trigger events when the deferred block is destroyed', fakeAsync(() => { + it('should unbind the trigger events when the deferred block is destroyed', async () => { @Component({ template: ` @if (renderBlock) { @@ -3362,9 +3377,9 @@ describe('@defer', () => { expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('keydown', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should remove placeholder content on interaction', fakeAsync(() => { + it('should remove placeholder content on interaction', async () => { @Component({ template: ` @defer (on interaction(trigger)) { @@ -3393,11 +3408,11 @@ describe('@defer', () => { zone.run(() => { button.click(); }); - tick(); + await timeout(0); expect(componentRef.location.nativeElement.innerHTML).not.toContain('
placeholder
'); - })); + }); - it('should prefetch resources on interaction', fakeAsync(() => { + it('should prefetch resources on interaction', async () => { @Component({ selector: 'root-app', template: ` @@ -3439,12 +3454,12 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); - it('should prefetch resources on interaction with an implicit trigger', fakeAsync(() => { + it('should prefetch resources on interaction with an implicit trigger', async () => { @Component({ selector: 'root-app', template: ` @@ -3487,14 +3502,14 @@ describe('@defer', () => { fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); }); describe('hover triggers', () => { - it('should load the deferred content when the trigger is hovered', fakeAsync(() => { + it('should load the deferred content when the trigger is hovered', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3522,11 +3537,11 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should load the deferred content with an implicit trigger element', fakeAsync(() => { + it('should load the deferred content with an implicit trigger element', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3552,12 +3567,12 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); fixture.detectChanges(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should support multiple deferred blocks with the same hover trigger', fakeAsync(() => { + it('should support multiple deferred blocks with the same hover trigger', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3591,11 +3606,11 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content 1 Main content 2'); - })); + }); - it('should unbind the trigger events when the deferred block is loaded', fakeAsync(() => { + it('should unbind the trigger events when the deferred block is loaded', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3621,15 +3636,15 @@ describe('@defer', () => { button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(spy).toHaveBeenCalledTimes(3); expect(spy).toHaveBeenCalledWith('mouseenter', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('mouseover', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('focusin', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should unbind the trigger events when the trigger is destroyed', fakeAsync(() => { + it('should unbind the trigger events when the trigger is destroyed', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3664,9 +3679,9 @@ describe('@defer', () => { expect(spy).toHaveBeenCalledWith('mouseenter', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('mouseover', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('focusin', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should unbind the trigger events when the deferred block is destroyed', fakeAsync(() => { + it('should unbind the trigger events when the deferred block is destroyed', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3702,9 +3717,9 @@ describe('@defer', () => { expect(spy).toHaveBeenCalledWith('mouseenter', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('mouseover', jasmine.any(Function), jasmine.any(Object)); expect(spy).toHaveBeenCalledWith('focusin', jasmine.any(Function), jasmine.any(Object)); - })); + }); - it('should prefetch resources on hover', fakeAsync(() => { + it('should prefetch resources on hover', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3752,12 +3767,12 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); - it('should prefetch resources when an implicit trigger is hovered', fakeAsync(() => { + it('should prefetch resources when an implicit trigger is hovered', async () => { // Domino doesn't support creating custom events so we have to skip this test. if (!isBrowser) { return; @@ -3806,10 +3821,10 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); button.dispatchEvent(new Event('mouseenter')); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); }); describe('`on timer` triggers', () => { @@ -4028,7 +4043,7 @@ describe('@defer', () => { expect(loadingFnInvokedTimes).toBe(1); }); - it('should trigger prefetching and rendering based on `on timer` condition', fakeAsync(() => { + it('should trigger prefetching and rendering based on `on timer` condition', async () => { const {fixture} = createFixture(` @defer (on timer(200ms); prefetch on timer(100ms)) { @@ -4037,10 +4052,10 @@ describe('@defer', () => { } `); - verifyTimeline(fixture, [50, 'Placeholder'], [150, 'Placeholder'], [250, 'Main']); - })); + await verifyTimeline(fixture, [50, 'Placeholder'], [150, 'Placeholder'], [250, 'Main']); + }); - it('should clear timeout callbacks when defer block is triggered', fakeAsync(() => { + it('should clear timeout callbacks when defer block is triggered', async () => { const setSpy = spyOn(globalThis, 'setTimeout'); const clearSpy = spyOn(globalThis, 'clearTimeout'); @@ -4071,7 +4086,7 @@ describe('@defer', () => { // condition was triggered, which resulted in timers cleanup. expect(setSpy).toHaveBeenCalledTimes(2); expect(clearSpy).toHaveBeenCalledTimes(2); - })); + }); }); describe('viewport triggers', () => { @@ -4172,7 +4187,7 @@ describe('@defer', () => { } } - it('should load the deferred content when the trigger is in the viewport', fakeAsync(() => { + it('should load the deferred content when the trigger is in the viewport', async () => { @Component({ template: ` @defer (on viewport(trigger)) { @@ -4196,11 +4211,11 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should load the deferred content when an implicit trigger is in the viewport', fakeAsync(() => { + it('should load the deferred content when an implicit trigger is in the viewport', async () => { @Component({ template: ` @defer (on viewport) { @@ -4222,12 +4237,12 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); fixture.detectChanges(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should not load the content if the trigger is not in the view yet', fakeAsync(() => { + it('should not load the content if the trigger is not in the view yet', async () => { @Component({ template: ` @defer (on viewport(trigger)) { @@ -4251,22 +4266,22 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, false); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Placeholder'); MockIntersectionObserver.invokeCallbacksForElement(button, false); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Placeholder'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content'); - })); + }); - it('should support multiple deferred blocks with the same trigger', fakeAsync(() => { + it('should support multiple deferred blocks with the same trigger', async () => { @Component({ template: ` @defer (on viewport(trigger)) { @@ -4295,11 +4310,11 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(fixture.nativeElement.textContent.trim()).toBe('Main content 1 Main content 2'); - })); + }); - it('should stop observing the trigger when the deferred block is loaded', fakeAsync(() => { + it('should stop observing the trigger when the deferred block is loaded', async () => { @Component({ template: ` @defer (on viewport(trigger)) { @@ -4322,13 +4337,13 @@ describe('@defer', () => { MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(activeObservers.length).toBe(1); expect(activeObservers[0].observedElements.size).toBe(0); - })); + }); - it('should stop observing the trigger when the trigger is destroyed', fakeAsync(() => { + it('should stop observing the trigger when the trigger is destroyed', async () => { @Component({ template: ` @if (renderBlock) { @@ -4358,9 +4373,9 @@ describe('@defer', () => { expect(activeObservers.length).toBe(1); expect(activeObservers[0].observedElements.size).toBe(0); - })); + }); - it('should stop observing the trigger when the deferred block is destroyed', fakeAsync(() => { + it('should stop observing the trigger when the deferred block is destroyed', async () => { @Component({ template: ` @if (renderBlock) { @@ -4391,9 +4406,9 @@ describe('@defer', () => { expect(activeObservers.length).toBe(1); expect(activeObservers[0].observedElements.size).toBe(0); - })); + }); - it('should disconnect the intersection observer once all deferred blocks have been loaded', fakeAsync(() => { + it('should disconnect the intersection observer once all deferred blocks have been loaded', async () => { @Component({ template: ` @@ -4432,9 +4447,9 @@ describe('@defer', () => { expect(disconnectSpy).toHaveBeenCalled(); expect(observer.observedElements.size).toBe(0); - })); + }); - it('should prefetch resources when the trigger comes into the viewport', fakeAsync(() => { + it('should prefetch resources when the trigger comes into the viewport', async () => { @Component({ selector: 'root-app', template: ` @@ -4477,12 +4492,12 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); - it('should prefetch resources when an implicit trigger comes into the viewport', fakeAsync(() => { + it('should prefetch resources when an implicit trigger comes into the viewport', async () => { @Component({ selector: 'root-app', template: ` @@ -4526,12 +4541,12 @@ describe('@defer', () => { const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); MockIntersectionObserver.invokeCallbacksForElement(button, true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); expect(loadingFnInvokedTimes).toBe(1); - })); + }); - it('should load deferred content in a loop', fakeAsync(() => { + it('should load deferred content in a loop', async () => { // prettier-ignore @Component({ template: ` @@ -4540,7 +4555,8 @@ describe('@defer', () => { @placeholder {} } `, - changeDetection: ChangeDetectionStrategy.Eager,}) + changeDetection: ChangeDetectionStrategy.Eager, + }) class MyCmp { items = [1, 2, 3, 4, 5, 6]; } @@ -4557,7 +4573,7 @@ describe('@defer', () => { for (let i = 0; i < items.length / 2; i++) { MockIntersectionObserver.invokeCallbacksForElement(buttons[i], true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); } expect(fixture.nativeElement.textContent.trim()).toBe('d1 d2 d3 p4 p5 p6'); @@ -4565,12 +4581,12 @@ describe('@defer', () => { for (let i = items.length / 2; i < items.length; i++) { MockIntersectionObserver.invokeCallbacksForElement(buttons[i], true); fixture.detectChanges(); - flush(); + await fixture.whenStable(); } expect(fixture.nativeElement.textContent.trim()).toBe('d1 d2 d3 d4 d5 d6'); - })); + }); - it('should take the `on viewport` options into account when creating IntersectionObserver', fakeAsync(() => { + it('should take the `on viewport` options into account when creating IntersectionObserver', async () => { @Component({ template: ` @defer (on viewport({trigger, rootMargin: '123px', threshold: 0.5})) { @@ -4591,9 +4607,9 @@ describe('@defer', () => { expect(activeObservers[0].observedElements.size).toBe(1); expect(activeObservers[0].observedElements.has(button)).toBe(true); expect(activeObservers[0].options).toEqual({rootMargin: '123px', threshold: 0.5}); - })); + }); - it('should take the `prefetch on viewport` options into account when creating IntersectionObserver', fakeAsync(() => { + it('should take the `prefetch on viewport` options into account when creating IntersectionObserver', async () => { @Component({ template: ` @defer (prefetch on viewport({trigger, rootMargin: '123px', threshold: 0.5})) { @@ -4614,9 +4630,9 @@ describe('@defer', () => { expect(activeObservers[0].observedElements.size).toBe(1); expect(activeObservers[0].observedElements.has(button)).toBe(true); expect(activeObservers[0].options).toEqual({rootMargin: '123px', threshold: 0.5}); - })); + }); - it('should create different intersection observers depending on their options', fakeAsync(() => { + it('should create different intersection observers depending on their options', async () => { @Component({ template: ` @defer (on viewport(trigger)) { @@ -4658,7 +4674,7 @@ describe('@defer', () => { expect(activeObservers[2].observedElements.size).toBe(1); expect(activeObservers[2].observedElements.has(button)).toBe(true); expect(activeObservers[2].options).toEqual({rootMargin: '1vh'}); - })); + }); it('should not attach observer if rendering manually', async () => { @Component({ diff --git a/packages/core/test/acceptance/injector_profiler_spec.ts b/packages/core/test/acceptance/injector_profiler_spec.ts index 364a86549f0..f0425b3aacf 100644 --- a/packages/core/test/acceptance/injector_profiler_spec.ts +++ b/packages/core/test/acceptance/injector_profiler_spec.ts @@ -59,7 +59,7 @@ import { getInjectorProviders, getInjectorResolutionPath, } from '../../src/render3/util/injector_discovery_utils'; -import {fakeAsync} from '../../testing'; + import {TestBed} from '../../testing/src/test_bed'; describe('setProfiler', () => { @@ -936,14 +936,14 @@ describe('getInjectorProviders', () => { expect(myServiceProviderRecord!.token).toBe(MyService); }); - it('should be able to determine providers in an injector that was created manually', fakeAsync(() => { + it('should be able to determine providers in an injector that was created manually', () => { class MyService {} const injector = Injector.create({providers: [MyService]}) as EnvironmentInjector; const providers = getInjectorProviders(injector); expect(providers.length).toBe(1); expect(providers[0].token).toBe(MyService); expect(providers[0].provider).toBe(MyService); - })); + }); it('should be able to get injector providers for element injectors created by components rendering in an ngFor', () => { class MyService {} diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index 8ee399f865c..7bdcbe3fc53 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -40,7 +40,8 @@ import {getLViewById} from '../../src/render3/interfaces/lview_tracking'; import {isLView} from '../../src/render3/interfaces/type_checks'; import {ID, LView, PARENT, TVIEW} from '../../src/render3/interfaces/view'; import {getLView} from '../../src/render3/state'; -import {fakeAsync, flushMicrotasks, TestBed} from '../../testing'; +import {TestBed} from '../../testing'; +import {timeout} from '@angular/private/testing'; describe('acceptance integration tests', () => { beforeEach(() => { @@ -2890,8 +2891,8 @@ describe('acceptance integration tests', () => { {{ $any(val)?.foo!.bar }} `, standalone: false, - - changeDetection: ChangeDetectionStrategy.Eager,}) + changeDetection: ChangeDetectionStrategy.Eager, + }) class Comp { val: any = null; @@ -3487,7 +3488,7 @@ describe('acceptance integration tests', () => { }); describe('animations', () => { - it('should apply triggers for a list of items when they are sorted and reSorted', fakeAsync(() => { + it('should apply triggers for a list of items when they are sorted and reSorted', async () => { interface Item { value: any; id: number; @@ -3567,11 +3568,11 @@ describe('acceptance integration tests', () => { elements = queryAll(fixture.nativeElement, 'animation-comp'); expect(elements.length).toEqual(5); expect(elements.map((e) => e.textContent?.trim())).toEqual(['1', '2', '4', '5', '3']); - completeAnimations(); + await completeAnimations(); fixture.componentInstance.showWarningMessage = true; fixture.detectChanges(); - completeAnimations(); + await completeAnimations(); elements = queryAll(fixture.nativeElement, 'animation-comp'); expect(elements.length).toEqual(0); @@ -3582,9 +3583,9 @@ describe('acceptance integration tests', () => { elements = queryAll(fixture.nativeElement, 'animation-comp'); expect(elements.length).toEqual(5); - })); + }); - it('should insert and remove views in the correct order when animations are present', fakeAsync(() => { + it('should insert and remove views in the correct order when animations are present', async () => { @Component({ animations: [ trigger('root', [transition('* => *', [])]), @@ -3653,7 +3654,7 @@ describe('acceptance integration tests', () => { }); const fixture = TestBed.createComponent(Cmp); fixture.detectChanges(); - completeAnimations(); + await completeAnimations(); const comp = fixture.componentInstance; expect(comp.log).toEqual([ 'root', // insertion of the inner-comp content @@ -3663,7 +3664,7 @@ describe('acceptance integration tests', () => { comp.log = []; comp.showIfContents = false; fixture.detectChanges(); - completeAnimations(); + await completeAnimations(); expect(comp.log).toEqual([ 'host', // insertion of the inner-comp content @@ -3674,20 +3675,20 @@ describe('acceptance integration tests', () => { comp.log = []; comp.showRoot = false; fixture.detectChanges(); - completeAnimations(); + await completeAnimations(); expect(comp.log).toEqual([ 'root', // removal the root div container 'host', // removal of the inner-comp content 'inner', // removal of the inner comp element ]); - })); + }); }); describe('arrow functions', () => { it('should support a basic arrow function in an event listener', () => { @Component({ - template: ``, + template: ``, changeDetection: ChangeDetectionStrategy.Eager, }) @@ -3715,7 +3716,9 @@ describe('acceptance integration tests', () => { @if (true) { @let grandchild = 'grandchild'; @@ -3740,7 +3743,7 @@ describe('acceptance integration tests', () => { it('should support an arrow function in a binding', () => { @Component({ - template: `Result: {{((a) => a + b)(1)}}`, + template: `Result: {{ ((a) => a + b)(1) }}`, changeDetection: ChangeDetectionStrategy.Eager, }) @@ -3860,7 +3863,12 @@ describe('acceptance integration tests', () => { @if (true) { Result: - {{(a => b => c => d => a + b + c + d + componentProp + topLevelLet + nestedLet)(1)(2)(3)(4)}} + {{ + ( + (a) => (b) => (c) => (d) => + a + b + c + d + componentProp + topLevelLet + nestedLet + )(1)(2)(3)(4) + }} } } `, @@ -3884,7 +3892,7 @@ describe('acceptance integration tests', () => { @Component({ template: ` @if (true) { - Result: {{(() => componentProp?.a?.b?.c?.()?.()?.()?.())()}}. + Result: {{ (() => componentProp?.a?.b?.c?.()?.()?.()?.())() }}. } `, @@ -3932,7 +3940,7 @@ describe('acceptance integration tests', () => { } @Component({ - template: ` `, + template: ` `, imports: [TestDir], changeDetection: ChangeDetectionStrategy.Eager, @@ -3953,7 +3961,7 @@ describe('acceptance integration tests', () => { it('should be able to use $event in an arrow function', () => { @Component({ - template: ``, + template: ``, changeDetection: ChangeDetectionStrategy.Eager, }) @@ -3974,7 +3982,7 @@ describe('acceptance integration tests', () => { @Component({ template: ` @for (item of items; track $index) { - {{ item }}: {{(() => prefix + ($even ? 'even' : 'odd'))()}} + {{ item }}: {{ (() => prefix + ($even ? 'even' : 'odd'))() }} } `, @@ -3999,11 +4007,11 @@ describe('acceptance integration tests', () => { }); }); -function completeAnimations() { - flushMicrotasks(); +async function completeAnimations() { + await timeout(0); const log = MockAnimationDriver.log as MockAnimationPlayer[]; log.forEach((player) => player.finish()); - flushMicrotasks(); + await timeout(0); } function arraySwap(arr: any[], indexA: number, indexB: number): void { diff --git a/packages/core/test/acceptance/signal_debug_spec.ts b/packages/core/test/acceptance/signal_debug_spec.ts index b310cf0365f..203896820a6 100644 --- a/packages/core/test/acceptance/signal_debug_spec.ts +++ b/packages/core/test/acceptance/signal_debug_spec.ts @@ -27,7 +27,7 @@ import { import {setInjectorProfiler} from '../../src/render3/debug/injector_profiler'; import type {DebugSignalGraphEdge, DebugSignalGraphNode} from '../../primitives/devtools'; import {getSignalGraph} from '../../src/render3/util/signal_debug'; -import {fakeAsync, TestBed, tick} from '../../testing'; +import {TestBed} from '../../testing'; describe('getSignalGraph', () => { beforeEach(() => { @@ -58,16 +58,14 @@ describe('getSignalGraph', () => { })); } - it('should return the signal graph for a component with signals', fakeAsync(() => { + it('should return the signal graph for a component with signals', async () => { @Component({selector: 'component-with-signals', template: `{{ primitiveSignal() }}`}) class WithSignals { primitiveSignal = signal(123, {debugName: 'primitiveSignal'}); } - TestBed.configureTestingModule({imports: [WithSignals]}); const fixture = TestBed.createComponent(WithSignals); + await fixture.whenStable(); - tick(); - fixture.detectChanges(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -86,9 +84,9 @@ describe('getSignalGraph', () => { expect(signalNode).toBeDefined(); expect(signalNode.label).toBe('primitiveSignal'); expect(signalNode.value).toBe(123); - })); + }); - it('should return the signal graph for a component with effects', fakeAsync(() => { + it('should return the signal graph for a component with effects', async () => { @Component({selector: 'component-with-effect', template: ``}) class WithEffect { stateFromEffect = 0; @@ -104,11 +102,10 @@ describe('getSignalGraph', () => { ); } } - TestBed.configureTestingModule({imports: [WithEffect]}); + const fixture = TestBed.createComponent(WithEffect); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -130,9 +127,9 @@ describe('getSignalGraph', () => { const edgesWithNodes = mapEdgeIndicesIntoNodes(edges, nodes); expect(edgesWithNodes).toContain({consumer: effectNode, producer: signalNode}); - })); + }); - it('should return the signal graph for a component with a computed', fakeAsync(() => { + it('should return the signal graph for a component with a computed', async () => { @Component({selector: 'component-with-computed', template: `{{ computedSignal() }}`}) class WithComputed { primitiveSignal = signal(123, {debugName: 'primitiveSignal'}); @@ -141,11 +138,9 @@ describe('getSignalGraph', () => { debugName: 'computedSignal', }); } - TestBed.configureTestingModule({imports: [WithComputed]}); const fixture = TestBed.createComponent(WithComputed); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -192,9 +187,9 @@ describe('getSignalGraph', () => { consumer: computedSignalNode, producer: primitiveSignal2Node, }); - })); + }); - it('should return the signal graph for a component with unused reactive nodes', fakeAsync(() => { + it('should return the signal graph for a component with unused reactive nodes', async () => { @Component({selector: 'component-with-unused-signal', template: ``}) class WithUnusedReactiveNodes { primitiveSignal = signal(123, {debugName: 'primitiveSignal'}); @@ -202,11 +197,9 @@ describe('getSignalGraph', () => { debugName: 'computedSignal', }); } - TestBed.configureTestingModule({imports: [WithUnusedReactiveNodes]}); const fixture = TestBed.createComponent(WithUnusedReactiveNodes); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -214,19 +207,17 @@ describe('getSignalGraph', () => { const {nodes, edges} = signalGraph; expect(nodes.length).toBe(0); expect(edges.length).toBe(0); - })); + }); - it('should return the signal graph for a component with no component effect signal dependencies', fakeAsync(() => { + it('should return the signal graph for a component with no component effect signal dependencies', async () => { @Component({selector: 'component-with-zero-effect', template: ``}) class WithNoEffectSignalDependencies { primitiveSignal = signal(123, {debugName: 'primitiveSignal'}); primitiveSignalEffect = effect(() => {}, {debugName: 'primitiveSignalEffect'}); } - TestBed.configureTestingModule({imports: [WithNoEffectSignalDependencies]}); const fixture = TestBed.createComponent(WithNoEffectSignalDependencies); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -234,16 +225,14 @@ describe('getSignalGraph', () => { const {nodes, edges} = signalGraph; expect(nodes.length).toBe(1); // 1 effect node detected expect(edges.length).toBe(0); - })); + }); - it('should return the signal graph for a component with no signal dependencies in the template or component effects', fakeAsync(() => { + it('should return the signal graph for a component with no signal dependencies in the template or component effects', async () => { @Component({selector: 'component-with-no-effect-dependencies', template: ``}) class WithNoEffectDependencies {} - TestBed.configureTestingModule({imports: [WithNoEffectDependencies]}); const fixture = TestBed.createComponent(WithNoEffectDependencies); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -251,9 +240,9 @@ describe('getSignalGraph', () => { const {nodes, edges} = signalGraph; expect(nodes.length).toBe(0); expect(edges.length).toBe(0); - })); + }); - it('should capture signals created in external services in the signal graph', fakeAsync(() => { + it('should capture signals created in external services in the signal graph', async () => { @Injectable() class ExternalService { oneTwoThree = signal(123, {debugName: 'oneTwoThree'}); @@ -277,11 +266,9 @@ describe('getSignalGraph', () => { ); } } - TestBed.configureTestingModule({imports: [WithExternalService]}); const fixture = TestBed.createComponent(WithExternalService); - tick(); - fixture.detectChanges(); + await fixture.whenStable(); const injector = fixture.componentRef.injector; const signalGraph = getSignalGraph(injector); @@ -311,9 +298,9 @@ describe('getSignalGraph', () => { consumer: externalServiceEffectNode, producer: fourFiveSixNode, }); - })); + }); - it('should capture signals created in directives in the signal graph', () => { + it('should capture signals created in directives in the signal graph', async () => { @Directive({ selector: '[myDirective]', }) @@ -335,9 +322,8 @@ describe('getSignalGraph', () => { }) class WithDirective {} - TestBed.configureTestingModule({imports: [WithDirective]}); const fixture = TestBed.createComponent(WithDirective); - fixture.detectChanges(); + await fixture.whenStable(); const element = fixture.nativeElement.querySelector('#element-with-directive'); // get the directive instance @@ -366,7 +352,7 @@ describe('getSignalGraph', () => { expect(edgesWithNodes).toContain({consumer: barNode!, producer: fooNode!}); }); - it('should capture signals created in different directives in the signal graph', () => { + it('should capture signals created in different directives in the signal graph', async () => { @Directive({ selector: '[myDirectiveA]', }) @@ -402,9 +388,9 @@ describe('getSignalGraph', () => { }) class WithMultipleDirectives {} - TestBed.configureTestingModule({imports: [WithMultipleDirectives]}); const fixture = TestBed.createComponent(WithMultipleDirectives); - fixture.detectChanges(); + await fixture.whenStable(); + const element = fixture.nativeElement.querySelector('#element-with-directives'); // get the directive instances const directiveInstances = getDirectives(element); @@ -445,12 +431,12 @@ describe('getSignalGraph', () => { expect(edgesWithNodes).toContain({consumer: effectDNode!, producer: signalCNode!}); }); - it('should stop tracking effect when ref is destroyed', () => { + it('should stop tracking effect when ref is destroyed', async () => { @Component({template: ''}) class App {} const fixture = TestBed.createComponent(App); - fixture.detectChanges(); + await fixture.whenStable(); const injector = TestBed.inject(ApplicationRef).injector; expect(getFrameworkDIDebugData().resolverToEffects.has(injector)).toBe(false); @@ -462,12 +448,12 @@ describe('getSignalGraph', () => { expect(getFrameworkDIDebugData().resolverToEffects.get(injector)?.length).toBe(0); }); - it('should stop tracking afterRenderEffect when ref is destroyed', () => { + it('should stop tracking afterRenderEffect when ref is destroyed', async () => { @Component({template: ''}) class App {} const fixture = TestBed.createComponent(App); - fixture.detectChanges(); + await fixture.whenStable(); const injector = TestBed.inject(ApplicationRef).injector; expect(getFrameworkDIDebugData().resolverToEffects.has(injector)).toBe(false); diff --git a/packages/core/test/acceptance/tracing_spec.ts b/packages/core/test/acceptance/tracing_spec.ts index 467a1363f89..78fdd52da8c 100644 --- a/packages/core/test/acceptance/tracing_spec.ts +++ b/packages/core/test/acceptance/tracing_spec.ts @@ -15,7 +15,7 @@ import { ɵTracingService as TracingService, ɵTracingSnapshot as TracingSnapshot, } from '../../src/core'; -import {fakeAsync, TestBed} from '../../testing'; +import {TestBed} from '../../testing'; describe('TracingService', () => { beforeEach(() => { @@ -86,7 +86,7 @@ describe('TracingService', () => { expect(actions).toEqual([TracingAction.CHANGE_DETECTION, TracingAction.CHANGE_DETECTION]); }); - it('should take a snapshot after `afterRender`', fakeAsync(() => { + it('should take a snapshot after `afterRender`', async () => { TestBed.configureTestingModule({ providers: [{provide: TracingService, useValue: mockTracingService}], }); @@ -107,9 +107,9 @@ describe('TracingService', () => { TracingAction.CHANGE_DETECTION, TracingAction.AFTER_NEXT_RENDER, ]); - })); + }); - it('should be able to wrap event listeners through the tracing service', fakeAsync(() => { + it('should be able to wrap event listeners through the tracing service', async () => { TestBed.configureTestingModule({ providers: [{provide: TracingService, useValue: mockTracingService}], }); @@ -120,7 +120,7 @@ describe('TracingService', () => { } const fixture = TestBed.createComponent(App); - fixture.detectChanges(); + await fixture.whenStable(); expect(listeners).toEqual([ {event: 'click', handler: jasmine.any(Function)}, @@ -129,10 +129,10 @@ describe('TracingService', () => { expect(clickCount).toBe(0); fixture.nativeElement.querySelector('button').click(); - fixture.detectChanges(); + await fixture.whenStable(); expect(clickCount).toBe(1); - })); + }); it('should trace component creations', () => { TestBed.configureTestingModule({