mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(core): Migrate more tests off fakeAsync
This will prevent to polute the agent context with outdated/bad practices.
(cherry picked from commit c1025a0510)
This commit is contained in:
committed by
Alex Rickabaugh
parent
8c2e9838ff
commit
1167626e5b
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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: `<ng-template #vm="vm" viewManipulation></ng-template>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ViewManipulation],
|
||||
})
|
||||
class App {}
|
||||
@@ -230,7 +222,6 @@ describe('change detection', () => {
|
||||
<button (click)="noop()">Trigger change detection</button>
|
||||
<div>{{ increment() }}</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
class DynamicComp {
|
||||
increment() {
|
||||
@@ -272,7 +263,6 @@ describe('change detection', () => {
|
||||
selector: 'child',
|
||||
template: '<ng-container *viewManipulation>{{data()}}</ng-container>',
|
||||
imports: [ViewManipulation],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
class ChildComponent {
|
||||
data = data;
|
||||
@@ -312,7 +302,6 @@ describe('change detection', () => {
|
||||
@Component({
|
||||
selector: `test-cmpt`,
|
||||
template: `{{ counter }}|<ng-template #vc></ng-template>`,
|
||||
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 }} <button (click)="onClick()"></button>`,
|
||||
standalone: false,
|
||||
})
|
||||
@@ -570,7 +557,6 @@ describe('change detection', () => {
|
||||
@Component({
|
||||
selector: 'button-parent',
|
||||
template: '{{ doCheckCount }} - <my-comp></my-comp>',
|
||||
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}} <ng-template emitter (event)="message = $event"></ng-template>',
|
||||
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 }} - <on-push-comp></on-push-comp>',
|
||||
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 }} - <on-push-comp *ngIf="showing"></on-push-comp>',
|
||||
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: ` <ng-container [ngTemplateOutlet]="template"> </ng-container> `,
|
||||
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: `
|
||||
<insertion [template]="ref"></insertion>
|
||||
<ng-template #ref>
|
||||
@@ -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: `<p>{{ text }}</p>`,
|
||||
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: `<p>{{ text }}</p>`,
|
||||
standalone: false,
|
||||
})
|
||||
|
||||
@@ -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<unknown>,
|
||||
...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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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: '<button></button>',
|
||||
@@ -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: `
|
||||
<button #trigger>
|
||||
@@ -2980,11 +2995,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 a parent embedded view', fakeAsync(() => {
|
||||
it('should resolve a trigger that is inside a parent embedded view', async () => {
|
||||
@Component({
|
||||
template: `
|
||||
@if (cond) {
|
||||
@@ -3014,11 +3029,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 component in a parent embedded view', fakeAsync(() => {
|
||||
it('should resolve a trigger that is on a component in a parent embedded view', async () => {
|
||||
@Component({
|
||||
selector: 'some-comp',
|
||||
template: '<button></button>',
|
||||
@@ -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: '<button></button>',
|
||||
@@ -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('<div>placeholder</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
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)) {
|
||||
<nested-cmp [block]="'Main'" />
|
||||
@@ -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: `
|
||||
<button #triggerOne></button>
|
||||
@@ -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 {<button>p{{item}} </button>}
|
||||
}
|
||||
`,
|
||||
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({
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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: `<button (click)="value.update(prev => prev + 1)">Increment</button>`,
|
||||
template: `<button (click)="value.update((prev) => prev + 1)">Increment</button>`,
|
||||
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
})
|
||||
@@ -3715,7 +3716,9 @@ describe('acceptance integration tests', () => {
|
||||
@if (true) {
|
||||
@let grandchild = 'grandchild';
|
||||
<button
|
||||
(click)="value.update(prev => prev + '->' + grandchild + '->' + child + '->' + parent)"
|
||||
(click)="
|
||||
value.update((prev) => prev + '->' + grandchild + '->' + child + '->' + parent)
|
||||
"
|
||||
>
|
||||
Assign
|
||||
</button>
|
||||
@@ -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: `<button test [callback]="() => prop = prop + 1"></button> `,
|
||||
template: `<button test [callback]="() => (prop = prop + 1)"></button> `,
|
||||
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: `<button (click)="value.update(prev => $event.type + prev)">Click</button>`,
|
||||
template: `<button (click)="value.update((prev) => $event.type + prev)">Click</button>`,
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user