ripple 0.3.85 → 0.3.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/package.json +2 -2
- package/src/runtime/internal/client/blocks.js +53 -0
- package/src/runtime/internal/client/for.js +65 -13
- package/src/runtime/internal/client/switch.js +17 -3
- package/tests/client/for-single-root-items.test.tsrx +130 -0
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +463 -0
- package/tests/hydration/compiled/server/basic.js +27 -0
- package/tests/hydration/compiled/server/if-fragment-controlflow.js +227 -0
- package/tests/hydration/components/if-fragment-controlflow.tsrx +145 -0
- package/tests/hydration/if-fragment-controlflow.test.js +95 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import * as _$_ from 'ripple/internal/server';
|
|
3
|
+
|
|
4
|
+
export function IfFragmentForElement() {
|
|
5
|
+
return _$_.tsrx_element(() => {
|
|
6
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
7
|
+
const hasLoaded = true;
|
|
8
|
+
|
|
9
|
+
_$_.regular_block(() => {
|
|
10
|
+
let __out = '';
|
|
11
|
+
|
|
12
|
+
__out += '<div class="feed-c"><!--[-->';
|
|
13
|
+
|
|
14
|
+
if (hasLoaded) {
|
|
15
|
+
__out += '<!--[--><!--[-->';
|
|
16
|
+
|
|
17
|
+
for (const muze of muzes) {
|
|
18
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
__out += '<!--]--><span class="after">after</span><!--]-->';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
__out += '<!--]--></div>';
|
|
25
|
+
_$_.output_push(__out);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function IfFragmentForIfIf() {
|
|
31
|
+
return _$_.tsrx_element(() => {
|
|
32
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
33
|
+
const hasLoaded = true;
|
|
34
|
+
|
|
35
|
+
_$_.regular_block(() => {
|
|
36
|
+
let __out = '';
|
|
37
|
+
|
|
38
|
+
__out += '<div class="feed"><!--[-->';
|
|
39
|
+
|
|
40
|
+
if (hasLoaded) {
|
|
41
|
+
__out += '<!--[--><!--[-->';
|
|
42
|
+
|
|
43
|
+
for (const muze of muzes) {
|
|
44
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
__out += '<!--]--><!--[-->';
|
|
48
|
+
|
|
49
|
+
if (muzes.length > 0) {
|
|
50
|
+
__out += '<span class="has-items">has items</span>';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
__out += '<!--]--><!--[-->';
|
|
54
|
+
|
|
55
|
+
if (muzes.length === 0) {
|
|
56
|
+
__out += '<span class="empty">empty</span>';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
__out += '<!--]--><!--]-->';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
__out += '<!--]--></div>';
|
|
63
|
+
_$_.output_push(__out);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function IfFragmentElements() {
|
|
69
|
+
return _$_.tsrx_element(() => {
|
|
70
|
+
const hasLoaded = true;
|
|
71
|
+
|
|
72
|
+
_$_.regular_block(() => {
|
|
73
|
+
let __out = '';
|
|
74
|
+
|
|
75
|
+
__out += '<div class="feed-b"><!--[-->';
|
|
76
|
+
|
|
77
|
+
if (hasLoaded) {
|
|
78
|
+
__out += '<!--[--><p class="muze">b</p><p class="muze">c</p><!--]-->';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
__out += '<!--]--></div>';
|
|
82
|
+
_$_.output_push(__out);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function ComponentBodyFragmentControlFlow() {
|
|
88
|
+
return _$_.tsrx_element(() => {
|
|
89
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
90
|
+
|
|
91
|
+
_$_.regular_block(() => {
|
|
92
|
+
let __out = '';
|
|
93
|
+
|
|
94
|
+
__out += '<!--[--><!--[-->';
|
|
95
|
+
|
|
96
|
+
for (const muze of muzes) {
|
|
97
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
__out += '<!--]--><span class="after">after</span><!--]-->';
|
|
101
|
+
_$_.output_push(__out);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function ComponentBodyCodeBlockControlFlow() {
|
|
107
|
+
return _$_.tsrx_element(() => {
|
|
108
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
109
|
+
|
|
110
|
+
_$_.regular_block(() => {
|
|
111
|
+
let __out = '';
|
|
112
|
+
|
|
113
|
+
__out += '<!--[-->';
|
|
114
|
+
_$_.output_push(__out);
|
|
115
|
+
__out = '';
|
|
116
|
+
|
|
117
|
+
_$_.render_expression(_$_.tsrx_element(() => {
|
|
118
|
+
let __out = '';
|
|
119
|
+
const rows = muzes;
|
|
120
|
+
|
|
121
|
+
__out += '<!--[-->';
|
|
122
|
+
|
|
123
|
+
for (const muze of rows) {
|
|
124
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
__out += '<!--]-->';
|
|
128
|
+
_$_.output_push(__out);
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
__out += '<span class="after">after</span><!--]-->';
|
|
132
|
+
_$_.output_push(__out);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function IfCodeBlockControlFlow() {
|
|
138
|
+
return _$_.tsrx_element(() => {
|
|
139
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
140
|
+
const hasLoaded = true;
|
|
141
|
+
|
|
142
|
+
_$_.regular_block(() => {
|
|
143
|
+
let __out = '';
|
|
144
|
+
|
|
145
|
+
__out += '<div class="feed-f"><!--[-->';
|
|
146
|
+
|
|
147
|
+
if (hasLoaded) {
|
|
148
|
+
__out += '<!--[-->';
|
|
149
|
+
_$_.output_push(__out);
|
|
150
|
+
__out = '';
|
|
151
|
+
|
|
152
|
+
_$_.render_expression(_$_.tsrx_element(() => {
|
|
153
|
+
let __out = '';
|
|
154
|
+
const rows = muzes;
|
|
155
|
+
|
|
156
|
+
__out += '<!--[-->';
|
|
157
|
+
|
|
158
|
+
for (const muze of rows) {
|
|
159
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
__out += '<!--]-->';
|
|
163
|
+
_$_.output_push(__out);
|
|
164
|
+
}));
|
|
165
|
+
|
|
166
|
+
__out += '<span class="after">after</span><!--]-->';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
__out += '<!--]--></div>';
|
|
170
|
+
_$_.output_push(__out);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function IfElseFragment() {
|
|
176
|
+
return _$_.tsrx_element(() => {
|
|
177
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
178
|
+
const hasLoaded = false;
|
|
179
|
+
|
|
180
|
+
_$_.regular_block(() => {
|
|
181
|
+
let __out = '';
|
|
182
|
+
|
|
183
|
+
__out += '<div class="feed-d"><!--[-->';
|
|
184
|
+
|
|
185
|
+
if (hasLoaded) {
|
|
186
|
+
__out += '<span class="loading">loading</span>';
|
|
187
|
+
} else {
|
|
188
|
+
__out += '<!--[--><!--[-->';
|
|
189
|
+
|
|
190
|
+
for (const muze of muzes) {
|
|
191
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
__out += '<!--]--><span class="after">after</span><!--]-->';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
__out += '<!--]--></div>';
|
|
198
|
+
_$_.output_push(__out);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function IfDivFragment() {
|
|
204
|
+
return _$_.tsrx_element(() => {
|
|
205
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
206
|
+
const hasLoaded = true;
|
|
207
|
+
|
|
208
|
+
_$_.regular_block(() => {
|
|
209
|
+
let __out = '';
|
|
210
|
+
|
|
211
|
+
__out += '<div class="feed-e"><!--[-->';
|
|
212
|
+
|
|
213
|
+
if (hasLoaded) {
|
|
214
|
+
__out += '<section><!--[--><!--[-->';
|
|
215
|
+
|
|
216
|
+
for (const muze of muzes) {
|
|
217
|
+
__out += '<p class="muze">' + _$_.escape(muze.muzeId) + '</p>';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
__out += '<!--]--><span class="after">after</span><!--]--></section>';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
__out += '<!--]--></div>';
|
|
224
|
+
_$_.output_push(__out);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Hydration repro: an @if body whose only child is a fragment containing
|
|
2
|
+
// control-flow siblings (@for / @if). The fragment is lowered to a nested
|
|
3
|
+
// tsrx_element rendered via an expression container, which introduces client
|
|
4
|
+
// placeholder `<!>` nodes that the SSR markup has no dedicated marker for — so
|
|
5
|
+
// the hydration cursor must still line up with the server's control-flow
|
|
6
|
+
// boundary comments.
|
|
7
|
+
|
|
8
|
+
// @for + trailing element sibling inside the fragment.
|
|
9
|
+
export function IfFragmentForElement() @{
|
|
10
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
11
|
+
const hasLoaded = true;
|
|
12
|
+
<div class="feed-c">
|
|
13
|
+
@if (hasLoaded) {
|
|
14
|
+
<>
|
|
15
|
+
@for (const muze of muzes; key muze.muzeId) {
|
|
16
|
+
<p class="muze">{muze.muzeId}</p>
|
|
17
|
+
}
|
|
18
|
+
<span class="after">{'after'}</span>
|
|
19
|
+
</>
|
|
20
|
+
}
|
|
21
|
+
</div>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// @for + @if + @if siblings inside the fragment (the originally reported shape).
|
|
25
|
+
export function IfFragmentForIfIf() @{
|
|
26
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
27
|
+
const hasLoaded = true;
|
|
28
|
+
<div class="feed">
|
|
29
|
+
@if (hasLoaded) {
|
|
30
|
+
<>
|
|
31
|
+
@for (const muze of muzes; key muze.muzeId) {
|
|
32
|
+
<p class="muze">{muze.muzeId}</p>
|
|
33
|
+
}
|
|
34
|
+
@if (muzes.length > 0) {
|
|
35
|
+
<span class="has-items">{'has items'}</span>
|
|
36
|
+
}
|
|
37
|
+
@if (muzes.length === 0) {
|
|
38
|
+
<span class="empty">{'empty'}</span>
|
|
39
|
+
}
|
|
40
|
+
</>
|
|
41
|
+
}
|
|
42
|
+
</div>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Control variant that already worked: fragment of plain elements (no control
|
|
46
|
+
// flow). Guards against a regression in the simple case.
|
|
47
|
+
export function IfFragmentElements() @{
|
|
48
|
+
const hasLoaded = true;
|
|
49
|
+
<div class="feed-b">
|
|
50
|
+
@if (hasLoaded) {
|
|
51
|
+
<>
|
|
52
|
+
<p class="muze">{'b'}</p>
|
|
53
|
+
<p class="muze">{'c'}</p>
|
|
54
|
+
</>
|
|
55
|
+
}
|
|
56
|
+
</div>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Regression guard: a COMPONENT-body fragment containing control flow must keep
|
|
60
|
+
// working WITHOUT an extra block marker — it reuses the component's own
|
|
61
|
+
// boundary, so the control_flow_branch_body marker must NOT be emitted here.
|
|
62
|
+
export function ComponentBodyFragmentControlFlow() @{
|
|
63
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
64
|
+
<>
|
|
65
|
+
@for (const muze of muzes; key muze.muzeId) {
|
|
66
|
+
<p class="muze">{muze.muzeId}</p>
|
|
67
|
+
}
|
|
68
|
+
<span class="after">{'after'}</span>
|
|
69
|
+
</>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Component-body fragment whose first child is a `@{}` code block that renders
|
|
73
|
+
// control flow. The code block lowers to a render_expression(tsrx_element) on
|
|
74
|
+
// the client and emits its own SSR marker; hydration must keep the code block's
|
|
75
|
+
// boundary aligned.
|
|
76
|
+
export function ComponentBodyCodeBlockControlFlow() @{
|
|
77
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
78
|
+
<>
|
|
79
|
+
@{
|
|
80
|
+
const rows = muzes;
|
|
81
|
+
@for (const muze of rows; key muze.muzeId) {
|
|
82
|
+
<p class="muze">{muze.muzeId}</p>
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
<span class="after">{'after'}</span>
|
|
86
|
+
</>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// @if branch fragment whose first child is a `@{}` code block that renders
|
|
90
|
+
// control flow. The fragment is a control-flow branch body, so it is bracketed
|
|
91
|
+
// via the control_flow_branch_body flag.
|
|
92
|
+
export function IfCodeBlockControlFlow() @{
|
|
93
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
94
|
+
const hasLoaded = true;
|
|
95
|
+
<div class="feed-f">
|
|
96
|
+
@if (hasLoaded) {
|
|
97
|
+
<>
|
|
98
|
+
@{
|
|
99
|
+
const rows = muzes;
|
|
100
|
+
@for (const muze of rows; key muze.muzeId) {
|
|
101
|
+
<p class="muze">{muze.muzeId}</p>
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
<span class="after">{'after'}</span>
|
|
105
|
+
</>
|
|
106
|
+
}
|
|
107
|
+
</div>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// @else branch whose body is a fragment of control-flow siblings.
|
|
111
|
+
export function IfElseFragment() @{
|
|
112
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
113
|
+
const hasLoaded = false;
|
|
114
|
+
<div class="feed-d">
|
|
115
|
+
@if (hasLoaded) {
|
|
116
|
+
<span class="loading">{'loading'}</span>
|
|
117
|
+
} @else {
|
|
118
|
+
<>
|
|
119
|
+
@for (const muze of muzes; key muze.muzeId) {
|
|
120
|
+
<p class="muze">{muze.muzeId}</p>
|
|
121
|
+
}
|
|
122
|
+
<span class="after">{'after'}</span>
|
|
123
|
+
</>
|
|
124
|
+
}
|
|
125
|
+
</div>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Fragment-in-element nested inside a control-flow branch: the inner `<>` is a
|
|
129
|
+
// child of <section>, not the direct branch body.
|
|
130
|
+
export function IfDivFragment() @{
|
|
131
|
+
const muzes = [{ muzeId: 'b' }, { muzeId: 'c' }];
|
|
132
|
+
const hasLoaded = true;
|
|
133
|
+
<div class="feed-e">
|
|
134
|
+
@if (hasLoaded) {
|
|
135
|
+
<section>
|
|
136
|
+
<>
|
|
137
|
+
@for (const muze of muzes; key muze.muzeId) {
|
|
138
|
+
<p class="muze">{muze.muzeId}</p>
|
|
139
|
+
}
|
|
140
|
+
<span class="after">{'after'}</span>
|
|
141
|
+
</>
|
|
142
|
+
</section>
|
|
143
|
+
}
|
|
144
|
+
</div>
|
|
145
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { hydrateComponent, container } from '../setup-hydration.js';
|
|
3
|
+
|
|
4
|
+
import * as ServerComponents from './compiled/server/if-fragment-controlflow.js';
|
|
5
|
+
import * as ClientComponents from './compiled/client/if-fragment-controlflow.js';
|
|
6
|
+
|
|
7
|
+
describe('hydration > @if body is a fragment of control-flow siblings', () => {
|
|
8
|
+
it('hydrates @for + element sibling', async () => {
|
|
9
|
+
await hydrateComponent(
|
|
10
|
+
ServerComponents.IfFragmentForElement,
|
|
11
|
+
ClientComponents.IfFragmentForElement,
|
|
12
|
+
);
|
|
13
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
14
|
+
'b',
|
|
15
|
+
'c',
|
|
16
|
+
]);
|
|
17
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('hydrates @for + @if + @if siblings', async () => {
|
|
21
|
+
await hydrateComponent(ServerComponents.IfFragmentForIfIf, ClientComponents.IfFragmentForIfIf);
|
|
22
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
23
|
+
'b',
|
|
24
|
+
'c',
|
|
25
|
+
]);
|
|
26
|
+
expect(container.querySelector('.has-items')).not.toBeNull();
|
|
27
|
+
expect(container.querySelector('.empty')).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('hydrates a fragment of plain elements (control)', async () => {
|
|
31
|
+
await hydrateComponent(
|
|
32
|
+
ServerComponents.IfFragmentElements,
|
|
33
|
+
ClientComponents.IfFragmentElements,
|
|
34
|
+
);
|
|
35
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
36
|
+
'b',
|
|
37
|
+
'c',
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('hydrates a component-body fragment with control flow (no extra marker)', async () => {
|
|
42
|
+
await hydrateComponent(
|
|
43
|
+
ServerComponents.ComponentBodyFragmentControlFlow,
|
|
44
|
+
ClientComponents.ComponentBodyFragmentControlFlow,
|
|
45
|
+
);
|
|
46
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
47
|
+
'b',
|
|
48
|
+
'c',
|
|
49
|
+
]);
|
|
50
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('hydrates a component-body fragment leading with a @{} code block of control flow', async () => {
|
|
54
|
+
await hydrateComponent(
|
|
55
|
+
ServerComponents.ComponentBodyCodeBlockControlFlow,
|
|
56
|
+
ClientComponents.ComponentBodyCodeBlockControlFlow,
|
|
57
|
+
);
|
|
58
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
59
|
+
'b',
|
|
60
|
+
'c',
|
|
61
|
+
]);
|
|
62
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('hydrates an @if branch fragment leading with a @{} code block of control flow', async () => {
|
|
66
|
+
await hydrateComponent(
|
|
67
|
+
ServerComponents.IfCodeBlockControlFlow,
|
|
68
|
+
ClientComponents.IfCodeBlockControlFlow,
|
|
69
|
+
);
|
|
70
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
71
|
+
'b',
|
|
72
|
+
'c',
|
|
73
|
+
]);
|
|
74
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('hydrates an @else branch fragment of control-flow siblings', async () => {
|
|
78
|
+
await hydrateComponent(ServerComponents.IfElseFragment, ClientComponents.IfElseFragment);
|
|
79
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
80
|
+
'b',
|
|
81
|
+
'c',
|
|
82
|
+
]);
|
|
83
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
84
|
+
expect(container.querySelector('.loading')).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('hydrates a fragment-in-element nested in a control-flow branch', async () => {
|
|
88
|
+
await hydrateComponent(ServerComponents.IfDivFragment, ClientComponents.IfDivFragment);
|
|
89
|
+
expect(Array.from(container.querySelectorAll('.muze')).map((n) => n.textContent)).toEqual([
|
|
90
|
+
'b',
|
|
91
|
+
'c',
|
|
92
|
+
]);
|
|
93
|
+
expect(container.querySelector('.after')).not.toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|