-
Notifications
You must be signed in to change notification settings - Fork 29.4k
Expand file tree
/
Copy pathdefer.ts
More file actions
101 lines (94 loc) · 3.3 KB
/
Copy pathdefer.ts
File metadata and controls
101 lines (94 loc) · 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
ɵCONTAINER_HEADER_OFFSET as CONTAINER_HEADER_OFFSET,
ɵDeferBlockDetails as DeferBlockDetails,
ɵDeferBlockState as DeferBlockState,
ɵgetDeferBlocks as getDeferBlocks,
ɵrenderDeferBlockState as renderDeferBlockState,
ɵtriggerResourceLoading as triggerResourceLoading,
} from '../../src/core';
import type {ComponentFixture} from './fixture';
/**
* Represents an individual defer block for testing purposes.
*
* @publicApi
*/
export class DeferBlockFixture {
/** @docs-private */
constructor(
private block: DeferBlockDetails,
private componentFixture: ComponentFixture<unknown>,
) {}
/**
* Renders the specified state of the defer fixture.
* @param state the defer state to render
*/
async render(state: DeferBlockState): Promise<void> {
if (!hasStateTemplate(state, this.block)) {
const stateAsString = getDeferBlockStateNameFromEnum(state);
throw new Error(
`Tried to render this defer block in the \`${stateAsString}\` state, ` +
`but there was no @${stateAsString.toLowerCase()} block defined in a template.`,
);
}
if (state === DeferBlockState.Complete) {
await triggerResourceLoading(this.block.tDetails, this.block.lView, this.block.tNode);
}
// 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();
}
/**
* Retrieves all nested child defer block fixtures
* in a given defer block.
*/
getDeferBlocks(): Promise<DeferBlockFixture[]> {
const deferBlocks: DeferBlockDetails[] = [];
// An LContainer that represents a defer block has at most 1 view, which is
// located right after an LContainer header. Get a hold of that view and inspect
// it for nested defer blocks.
const deferBlockFixtures = [];
if (this.block.lContainer.length >= CONTAINER_HEADER_OFFSET) {
const lView = this.block.lContainer[CONTAINER_HEADER_OFFSET];
getDeferBlocks(lView, deferBlocks);
for (const block of deferBlocks) {
deferBlockFixtures.push(new DeferBlockFixture(block, this.componentFixture));
}
}
return Promise.resolve(deferBlockFixtures);
}
}
function hasStateTemplate(state: DeferBlockState, block: DeferBlockDetails) {
switch (state) {
case DeferBlockState.Placeholder:
return block.tDetails.placeholderTmplIndex !== null;
case DeferBlockState.Loading:
return block.tDetails.loadingTmplIndex !== null;
case DeferBlockState.Error:
return block.tDetails.errorTmplIndex !== null;
case DeferBlockState.Complete:
return true;
default:
return false;
}
}
function getDeferBlockStateNameFromEnum(state: DeferBlockState) {
switch (state) {
case DeferBlockState.Placeholder:
return 'Placeholder';
case DeferBlockState.Loading:
return 'Loading';
case DeferBlockState.Error:
return 'Error';
default:
return 'Main';
}
}