Skip to content

Commit

Permalink
refactor(core): ignore after and minimum when transition between …
Browse files Browse the repository at this point in the history
…states in tests

This commit updates the logic to ignore `after` and `minimum` conditions when `DeferBlockFixture.render` method is used in tests.

Resolves angular#52313.
  • Loading branch information
AndrewKushnir committed Oct 22, 2023
1 parent 3278966 commit 2aa1b40
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
14 changes: 10 additions & 4 deletions packages/core/src/defer/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,14 @@ function scheduleDelayedPrefetching(
* @param newState New state that should be applied to the defer block.
* @param tNode TNode that represents a defer block.
* @param lContainer Represents an instance of a defer block.
* @param skipTimerScheduling Indicates that `@loading` and `@placeholder` block
* should be rendered immediately, even if they have `after` or `minimum` config
* options setup. This flag to needed for testing APIs to transition defer block
* between states via `DeferFixture.render` method.
*/
export function renderDeferBlockState(
newState: DeferBlockState, tNode: TNode, lContainer: LContainer): void {
newState: DeferBlockState, tNode: TNode, lContainer: LContainer,
skipTimerScheduling = false): void {
const hostLView = lContainer[PARENT];
const hostTView = hostLView[TVIEW];

Expand All @@ -452,9 +457,10 @@ export function renderDeferBlockState(
if (isValidStateChange(currentState, newState) &&
isValidStateChange(lDetails[NEXT_DEFER_BLOCK_STATE] ?? -1, newState)) {
const tDetails = getTDeferBlockDetails(hostTView, tNode);
const needsScheduling = getLoadingBlockAfter(tDetails) !== null ||
getMinimumDurationForState(tDetails, DeferBlockState.Loading) !== null ||
getMinimumDurationForState(tDetails, DeferBlockState.Placeholder);
const needsScheduling = !skipTimerScheduling &&
(getLoadingBlockAfter(tDetails) !== null ||
getMinimumDurationForState(tDetails, DeferBlockState.Loading) !== null ||
getMinimumDurationForState(tDetails, DeferBlockState.Placeholder));

if (ngDevMode && needsScheduling) {
assertDefined(
Expand Down
42 changes: 42 additions & 0 deletions packages/core/test/defer_fixture_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,48 @@ describe('DeferFixture', () => {
}
});

it('should transition between states when `after` and `minimum` are used', async () => {
@Component({
selector: 'defer-comp',
standalone: true,
imports: [SecondDeferredComp],
template: `
<div>
@defer (on immediate) {
Main content
} @loading (after 1s) {
Loading
} @placeholder (minimum 2s) {
Placeholder
}
</div>
`
})
class DeferComp {
}

TestBed.configureTestingModule({
imports: [
DeferComp,
SecondDeferredComp,
],
providers: COMMON_PROVIDERS,
deferBlockBehavior: DeferBlockBehavior.Manual,
});

const componentFixture = TestBed.createComponent(DeferComp);
const deferBlock = (await componentFixture.getDeferBlocks())[0];

await deferBlock.render(DeferBlockState.Placeholder);
expect(componentFixture.nativeElement.outerHTML).toContain('Placeholder');

await deferBlock.render(DeferBlockState.Loading);
expect(componentFixture.nativeElement.outerHTML).toContain('Loading');

await deferBlock.render(DeferBlockState.Complete);
expect(componentFixture.nativeElement.outerHTML).toContain('Main');
});

it('should get child defer blocks', async () => {
@Component({
selector: 'deferred-comp',
Expand Down
5 changes: 4 additions & 1 deletion packages/core/testing/src/defer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export class DeferBlockFixture {
if (state === DeferBlockState.Complete) {
await triggerResourceLoading(this.block.tDetails, this.block.lView);
}
renderDeferBlockState(state, this.block.tNode, this.block.lContainer);
// If the `render` method is used explicitly - skip timer-based scheduling for
// `@placeholder` and `@loading` blocks and render them immediately.
const skipTimerScheduling = true;
renderDeferBlockState(state, this.block.tNode, this.block.lContainer, skipTimerScheduling);
this.componentFixture.detectChanges();
return this.componentFixture.whenStable();
}
Expand Down

0 comments on commit 2aa1b40

Please sign in to comment.