@ryupold/vode 1.8.8 → 1.8.11
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/.github/workflows/publish.yml +5 -0
- package/.github/workflows/tests.yml +1 -0
- package/README.md +36 -2
- package/dist/vode.cjs.min.js +1 -1
- package/dist/vode.d.ts +5 -6
- package/dist/vode.es5.min.js +1 -1
- package/dist/vode.js +54 -44
- package/dist/vode.min.js +1 -1
- package/dist/vode.min.mjs +1 -1
- package/dist/vode.mjs +54 -44
- package/dist/vode.tests.mjs +5475 -0
- package/log.txt +1 -0
- package/package.json +5 -5
- package/src/vode.ts +63 -52
- package/test/helper.ts +299 -146
- package/test/index.ts +2 -64
- package/test/mocks.ts +83 -9
- package/test/run-tests.ts +61 -0
- package/test/tests-app.ts +48 -48
- package/test/tests-catch.ts +15 -15
- package/test/tests-children.ts +31 -31
- package/test/tests-createPatch.ts +12 -12
- package/test/tests-createState.ts +11 -11
- package/test/tests-defuse.ts +18 -18
- package/test/tests-examples.ts +87 -88
- package/test/tests-hydrate.ts +28 -28
- package/test/tests-memo.ts +29 -28
- package/test/tests-mergeClass.ts +31 -31
- package/test/tests-mergeProps.ts +19 -19
- package/test/tests-mergeStyle.ts +28 -14
- package/test/tests-mount-unmount.ts +368 -268
- package/test/tests-patch-advanced.ts +127 -19
- package/test/tests-patch-merge.ts +15 -15
- package/test/tests-props.ts +15 -15
- package/test/tests-state-context.ts +33 -33
- package/test/tests-tag.ts +14 -14
- package/test/tests-vode.ts +6 -6
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { app, createState, memo } from "../src/vode"
|
|
1
|
+
import { app, Component, ContainerNode, createState, memo } from "../src/vode"
|
|
2
2
|
import { ARTICLE, ASIDE, DIV, INPUT, MAIN, NAV, P, SECTION, SPAN } from "../src/vode-tags";
|
|
3
|
-
import { expect } from "./helper";
|
|
3
|
+
import { expect, ExpectationError } from "./helper";
|
|
4
4
|
|
|
5
5
|
function setup() {
|
|
6
6
|
const root = document.createElement("div");
|
|
7
7
|
const container = document.createElement("div");
|
|
8
8
|
root.appendChild(container);
|
|
9
|
-
return container;
|
|
9
|
+
return container as unknown as ContainerNode;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export default {
|
|
13
|
-
"onMount(): called when node is attached to the DOM": () => {
|
|
13
|
+
"onMount(): called when node is attached to the DOM": async () => {
|
|
14
14
|
const container = setup();
|
|
15
15
|
let mountCalled = false;
|
|
16
16
|
app(container, {}, () =>
|
|
@@ -18,7 +18,7 @@ export default {
|
|
|
18
18
|
[ARTICLE,
|
|
19
19
|
{
|
|
20
20
|
onMount: (s: unknown, ele: HTMLElement) => {
|
|
21
|
-
|
|
21
|
+
if (ele.tagName !== "ARTICLE") throw new ExpectationError(expect(ele), `Expected ARTICLE, got ${ele.tagName}`);
|
|
22
22
|
mountCalled = true;
|
|
23
23
|
}
|
|
24
24
|
},
|
|
@@ -27,11 +27,11 @@ export default {
|
|
|
27
27
|
]
|
|
28
28
|
);
|
|
29
29
|
|
|
30
|
-
expect(mountCalled)
|
|
30
|
+
await expect(mountCalled)
|
|
31
31
|
.toEqual(true);
|
|
32
32
|
},
|
|
33
33
|
|
|
34
|
-
"onMount(): called in order of child nodes first, then parent onMounts": () => {
|
|
34
|
+
"onMount(): called in order of child nodes first, then parent onMounts": async () => {
|
|
35
35
|
const container = setup();
|
|
36
36
|
const mounts: string[] = [];
|
|
37
37
|
app(container, {}, () =>
|
|
@@ -55,11 +55,11 @@ export default {
|
|
|
55
55
|
]
|
|
56
56
|
);
|
|
57
57
|
|
|
58
|
-
expect(mounts)
|
|
58
|
+
await expect(mounts)
|
|
59
59
|
.toEqual(["mount inner", "mount outer"]);
|
|
60
60
|
},
|
|
61
61
|
|
|
62
|
-
"onMount(): deep nesting 4+ levels with onMount at each level": () => {
|
|
62
|
+
"onMount(): deep nesting 4+ levels with onMount at each level": async () => {
|
|
63
63
|
const container = setup();
|
|
64
64
|
const mounts: string[] = [];
|
|
65
65
|
app(container, {}, () =>
|
|
@@ -96,7 +96,7 @@ export default {
|
|
|
96
96
|
]
|
|
97
97
|
);
|
|
98
98
|
|
|
99
|
-
expect(mounts)
|
|
99
|
+
await expect(mounts)
|
|
100
100
|
.toEqual([
|
|
101
101
|
"mount article",
|
|
102
102
|
"mount section",
|
|
@@ -105,7 +105,7 @@ export default {
|
|
|
105
105
|
]);
|
|
106
106
|
},
|
|
107
107
|
|
|
108
|
-
"onMount(): multiple siblings with onMount on initial render": () => {
|
|
108
|
+
"onMount(): multiple siblings with onMount on initial render": async () => {
|
|
109
109
|
const container = setup();
|
|
110
110
|
const mounts: string[] = [];
|
|
111
111
|
app(container, {}, () =>
|
|
@@ -129,11 +129,11 @@ export default {
|
|
|
129
129
|
]
|
|
130
130
|
);
|
|
131
131
|
|
|
132
|
-
expect(mounts)
|
|
132
|
+
await expect(mounts)
|
|
133
133
|
.toEqual(["mount p", "mount span"]);
|
|
134
134
|
},
|
|
135
135
|
|
|
136
|
-
"onMount(): A->A path - onMount added during update does NOT fire": () => {
|
|
136
|
+
"onMount(): A->A path - onMount added during update does NOT fire": async () => {
|
|
137
137
|
const container = setup();
|
|
138
138
|
const mounts: string[] = [];
|
|
139
139
|
const state = createState({ addMount: false });
|
|
@@ -150,12 +150,12 @@ export default {
|
|
|
150
150
|
]
|
|
151
151
|
);
|
|
152
152
|
|
|
153
|
-
expect(mounts).toEqual([]);
|
|
153
|
+
await expect(mounts).toEqual([]);
|
|
154
154
|
patch({ addMount: true });
|
|
155
|
-
expect(mounts).toEqual([]);
|
|
155
|
+
await expect(mounts).toEqual([]);
|
|
156
156
|
},
|
|
157
157
|
|
|
158
|
-
"onMount(): A->A path - onMount removed during update does not cause issues": () => {
|
|
158
|
+
"onMount(): A->A path - onMount removed during update does not cause issues": async () => {
|
|
159
159
|
const container = setup();
|
|
160
160
|
const mounts: string[] = [];
|
|
161
161
|
const state = createState({ removeMount: false });
|
|
@@ -172,12 +172,12 @@ export default {
|
|
|
172
172
|
]
|
|
173
173
|
);
|
|
174
174
|
|
|
175
|
-
expect(mounts).toEqual(["mount p"]);
|
|
175
|
+
await expect(mounts).toEqual(["mount p"]);
|
|
176
176
|
patch({ removeMount: true });
|
|
177
|
-
expect(mounts).toEqual(["mount p"]);
|
|
177
|
+
await expect(mounts).toEqual(["mount p"]);
|
|
178
178
|
},
|
|
179
179
|
|
|
180
|
-
"onMount(): A->A path - onMount changed during update does NOT fire the new one": () => {
|
|
180
|
+
"onMount(): A->A path - onMount changed during update does NOT fire the new one": async () => {
|
|
181
181
|
const container = setup();
|
|
182
182
|
const mounts: string[] = [];
|
|
183
183
|
const state = createState({ version: "a" });
|
|
@@ -194,12 +194,12 @@ export default {
|
|
|
194
194
|
]
|
|
195
195
|
);
|
|
196
196
|
|
|
197
|
-
expect(mounts).toEqual(["mount a"]);
|
|
197
|
+
await expect(mounts).toEqual(["mount a"]);
|
|
198
198
|
patch({ version: "b" });
|
|
199
|
-
expect(mounts).toEqual(["mount a"]);
|
|
199
|
+
await expect(mounts).toEqual(["mount a"]);
|
|
200
200
|
},
|
|
201
201
|
|
|
202
|
-
"onMount(): A->B path - element replaced with different tag fires new onMount": () => {
|
|
202
|
+
"onMount(): A->B path - element replaced with different tag fires new onMount": async () => {
|
|
203
203
|
const container = setup();
|
|
204
204
|
const mounts: string[] = [];
|
|
205
205
|
const state = createState({ showArticle: true });
|
|
@@ -225,12 +225,12 @@ export default {
|
|
|
225
225
|
]
|
|
226
226
|
);
|
|
227
227
|
|
|
228
|
-
expect(mounts).toEqual(["mount article"]);
|
|
228
|
+
await expect(mounts).toEqual(["mount article"]);
|
|
229
229
|
patch({ showArticle: false });
|
|
230
|
-
expect(mounts).toEqual(["mount article", "mount aside"]);
|
|
230
|
+
await expect(mounts).toEqual(["mount article", "mount aside"]);
|
|
231
231
|
},
|
|
232
232
|
|
|
233
|
-
"onMount(): A->B path - swap back fires the other element's onMount": () => {
|
|
233
|
+
"onMount(): A->B path - swap back fires the other element's onMount": async () => {
|
|
234
234
|
const container = setup();
|
|
235
235
|
const mounts: string[] = [];
|
|
236
236
|
const state = createState({ showArticle: true });
|
|
@@ -256,13 +256,13 @@ export default {
|
|
|
256
256
|
]
|
|
257
257
|
);
|
|
258
258
|
|
|
259
|
-
expect(mounts).toEqual(["mount article"]);
|
|
259
|
+
await expect(mounts).toEqual(["mount article"]);
|
|
260
260
|
patch({ showArticle: false });
|
|
261
|
-
expect(mounts).toEqual(["mount article", "mount aside"]);
|
|
261
|
+
await expect(mounts).toEqual(["mount article", "mount aside"]);
|
|
262
262
|
patch({ showArticle: true });
|
|
263
|
-
expect(mounts).toEqual(["mount article", "mount aside", "mount article"]);
|
|
263
|
+
await expect(mounts).toEqual(["mount article", "mount aside", "mount article"]);
|
|
264
264
|
patch({ showArticle: false });
|
|
265
|
-
expect(mounts)
|
|
265
|
+
await expect(mounts)
|
|
266
266
|
.toEqual([
|
|
267
267
|
"mount article",
|
|
268
268
|
"mount aside",
|
|
@@ -271,7 +271,7 @@ export default {
|
|
|
271
271
|
]);
|
|
272
272
|
},
|
|
273
273
|
|
|
274
|
-
"onMount(): A->B path - children's onMounts also fire in new tree": () => {
|
|
274
|
+
"onMount(): A->B path - children's onMounts also fire in new tree": async () => {
|
|
275
275
|
const container = setup();
|
|
276
276
|
const mounts: string[] = [];
|
|
277
277
|
const state = createState({ showArticle: true });
|
|
@@ -301,12 +301,12 @@ export default {
|
|
|
301
301
|
]
|
|
302
302
|
);
|
|
303
303
|
|
|
304
|
-
expect(mounts).toEqual(["mount p"]);
|
|
304
|
+
await expect(mounts).toEqual(["mount p"]);
|
|
305
305
|
patch({ showArticle: false });
|
|
306
|
-
expect(mounts).toEqual(["mount p", "mount div"]);
|
|
306
|
+
await expect(mounts).toEqual(["mount p", "mount div"]);
|
|
307
307
|
},
|
|
308
308
|
|
|
309
|
-
"onMount(): text -> element fires new element's onMount": () => {
|
|
309
|
+
"onMount(): text -> element fires new element's onMount": async () => {
|
|
310
310
|
const container = setup();
|
|
311
311
|
const mounts: string[] = [];
|
|
312
312
|
const state = createState({ showElement: false });
|
|
@@ -325,12 +325,12 @@ export default {
|
|
|
325
325
|
]
|
|
326
326
|
);
|
|
327
327
|
|
|
328
|
-
expect(mounts).toEqual([]);
|
|
328
|
+
await expect(mounts).toEqual([]);
|
|
329
329
|
patch({ showElement: true });
|
|
330
|
-
expect(mounts).toEqual(["mount article"]);
|
|
330
|
+
await expect(mounts).toEqual(["mount article"]);
|
|
331
331
|
},
|
|
332
332
|
|
|
333
|
-
"onMount(): mixed onMount presence in tree": () => {
|
|
333
|
+
"onMount(): mixed onMount presence in tree": async () => {
|
|
334
334
|
const container = setup();
|
|
335
335
|
const mounts: string[] = [];
|
|
336
336
|
app(container, {}, () =>
|
|
@@ -358,10 +358,10 @@ export default {
|
|
|
358
358
|
]
|
|
359
359
|
);
|
|
360
360
|
|
|
361
|
-
expect(mounts).toEqual(["mount p", "mount section"]);
|
|
361
|
+
await expect(mounts).toEqual(["mount p", "mount section"]);
|
|
362
362
|
},
|
|
363
363
|
|
|
364
|
-
"onMount(): sibling subtree depths fire in correct order": () => {
|
|
364
|
+
"onMount(): sibling subtree depths fire in correct order": async () => {
|
|
365
365
|
const container = setup();
|
|
366
366
|
const mounts: string[] = [];
|
|
367
367
|
app(container, {}, () =>
|
|
@@ -394,7 +394,7 @@ export default {
|
|
|
394
394
|
]
|
|
395
395
|
);
|
|
396
396
|
|
|
397
|
-
expect(mounts)
|
|
397
|
+
await expect(mounts)
|
|
398
398
|
.toEqual([
|
|
399
399
|
"mount p-deep",
|
|
400
400
|
"mount div",
|
|
@@ -402,7 +402,7 @@ export default {
|
|
|
402
402
|
]);
|
|
403
403
|
},
|
|
404
404
|
|
|
405
|
-
"onMount(): added children from count increase fire onMount": () => {
|
|
405
|
+
"onMount(): added children from count increase fire onMount": async () => {
|
|
406
406
|
const container = setup();
|
|
407
407
|
const mounts: string[] = [];
|
|
408
408
|
const state = createState({ count: 1 });
|
|
@@ -416,14 +416,14 @@ export default {
|
|
|
416
416
|
]
|
|
417
417
|
);
|
|
418
418
|
|
|
419
|
-
expect(mounts).toEqual(["mount p0"]);
|
|
419
|
+
await expect(mounts).toEqual(["mount p0"]);
|
|
420
420
|
patch({ count: 2 });
|
|
421
|
-
expect(mounts).toEqual(["mount p0", "mount p1"]);
|
|
421
|
+
await expect(mounts).toEqual(["mount p0", "mount p1"]);
|
|
422
422
|
patch({ count: 3 });
|
|
423
|
-
expect(mounts).toEqual(["mount p0", "mount p1", "mount p2"]);
|
|
423
|
+
await expect(mounts).toEqual(["mount p0", "mount p1", "mount p2"]);
|
|
424
424
|
},
|
|
425
425
|
|
|
426
|
-
"onMount(): conditional child fires onMount when added": () => {
|
|
426
|
+
"onMount(): conditional child fires onMount when added": async () => {
|
|
427
427
|
const container = setup();
|
|
428
428
|
const mounts: string[] = [];
|
|
429
429
|
const state = createState({ show: false });
|
|
@@ -443,12 +443,98 @@ export default {
|
|
|
443
443
|
]
|
|
444
444
|
);
|
|
445
445
|
|
|
446
|
-
expect(mounts).toEqual([]);
|
|
446
|
+
await expect(mounts).toEqual([]);
|
|
447
447
|
patch({ show: true });
|
|
448
|
-
expect(mounts).toEqual(["mount span"]);
|
|
448
|
+
await expect(mounts).toEqual(["mount span"]);
|
|
449
449
|
},
|
|
450
450
|
|
|
451
|
-
"
|
|
451
|
+
"onMount(): with catched component, replacement vode's onMount fires when error occurs": async () => {
|
|
452
|
+
const container = setup();
|
|
453
|
+
const mounts: string[] = [];
|
|
454
|
+
const broken: any = () => { throw new Error("boom"); };
|
|
455
|
+
app(container, {}, () =>
|
|
456
|
+
[DIV,
|
|
457
|
+
{
|
|
458
|
+
catch: [SECTION,
|
|
459
|
+
{
|
|
460
|
+
onMount: (s: unknown, ele: HTMLElement) => {
|
|
461
|
+
mounts.push("mount fallback");
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
"fallback"
|
|
465
|
+
]
|
|
466
|
+
},
|
|
467
|
+
broken
|
|
468
|
+
]
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
await expect(mounts).toEqual(["mount fallback"]);
|
|
472
|
+
},
|
|
473
|
+
|
|
474
|
+
"onMount(): with catched component, returned vode's onMount fires and receives error": async () => {
|
|
475
|
+
const container = setup();
|
|
476
|
+
const mounts: string[] = [];
|
|
477
|
+
const caughtErrors: string[] = [];
|
|
478
|
+
const broken: any = () => { throw new Error("boom"); };
|
|
479
|
+
app(container, {}, () =>
|
|
480
|
+
[DIV,
|
|
481
|
+
{
|
|
482
|
+
catch: (s: unknown, err: Error) => {
|
|
483
|
+
caughtErrors.push(err.message);
|
|
484
|
+
return [SECTION,
|
|
485
|
+
{
|
|
486
|
+
onMount: (s: unknown, ele: HTMLElement) => {
|
|
487
|
+
mounts.push("mount fallback");
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
"fallback"
|
|
491
|
+
];
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
broken
|
|
495
|
+
]
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
await expect(mounts).toEqual(["mount fallback"]);
|
|
499
|
+
await expect(caughtErrors).toEqual(["boom"]);
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
"onMount(): with catched component, original element's onMount does NOT fire when error caused replacement": async () => {
|
|
503
|
+
const container = setup();
|
|
504
|
+
const logs: string[] = [];
|
|
505
|
+
const broken: any = () => { throw new Error("boom"); };
|
|
506
|
+
app(container, {}, () =>
|
|
507
|
+
[DIV,
|
|
508
|
+
{
|
|
509
|
+
catch: [ARTICLE,
|
|
510
|
+
{
|
|
511
|
+
onMount: (s: unknown, ele: HTMLElement) => {
|
|
512
|
+
logs.push("mount fallback");
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
"fallback"
|
|
516
|
+
]
|
|
517
|
+
},
|
|
518
|
+
[SECTION,
|
|
519
|
+
{
|
|
520
|
+
onMount: (s: unknown, ele: HTMLElement) => {
|
|
521
|
+
logs.push("mount original section");
|
|
522
|
+
},
|
|
523
|
+
onUnmount: (s: unknown, ele: HTMLElement) => {
|
|
524
|
+
logs.push("unmount original section");
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
broken
|
|
528
|
+
]
|
|
529
|
+
]
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
// SECTION never finishes mounting (its child broke), so its onMount must not fire.
|
|
533
|
+
// The catch on DIV replaces the broken subtree with ARTICLE whose onMount must fire.
|
|
534
|
+
await expect(logs).toEqual(["mount fallback"]);
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
"onUnmount(): called when node is removed from the DOM": async () => {
|
|
452
538
|
const container = setup();
|
|
453
539
|
const unmounts: string[] = [];
|
|
454
540
|
const state = createState({ showArticle: true });
|
|
@@ -465,12 +551,12 @@ export default {
|
|
|
465
551
|
]
|
|
466
552
|
);
|
|
467
553
|
|
|
468
|
-
expect(unmounts).toEqual([]);
|
|
554
|
+
await expect(unmounts).toEqual([]);
|
|
469
555
|
patch({ showArticle: false });
|
|
470
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
556
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
471
557
|
},
|
|
472
558
|
|
|
473
|
-
"onUnmount(): called for all child nodes that have registerd when parent node is removed from the DOM": () => {
|
|
559
|
+
"onUnmount(): called for all child nodes that have registerd when parent node is removed from the DOM": async () => {
|
|
474
560
|
const container = setup();
|
|
475
561
|
const unmounts: string[] = [];
|
|
476
562
|
const state = createState({ showArticle: true });
|
|
@@ -495,13 +581,13 @@ export default {
|
|
|
495
581
|
]
|
|
496
582
|
);
|
|
497
583
|
|
|
498
|
-
expect(unmounts).toEqual([]);
|
|
584
|
+
await expect(unmounts).toEqual([]);
|
|
499
585
|
patch({ showArticle: false });
|
|
500
|
-
expect(unmounts).toEqual(["unmount inner", "unmount outer"]);
|
|
586
|
+
await expect(unmounts).toEqual(["unmount inner", "unmount outer"]);
|
|
501
587
|
},
|
|
502
588
|
|
|
503
|
-
"onUnmount(): A->A path - onUnmount added during update fires on later removal": () => {
|
|
504
|
-
const container = setup();
|
|
589
|
+
"onUnmount(): A->A path - onUnmount added during update fires on later removal": async () => {
|
|
590
|
+
const container = setup() as unknown as ContainerNode;
|
|
505
591
|
const unmounts: string[] = [];
|
|
506
592
|
const state = createState({ toggle: false, remove: false });
|
|
507
593
|
const patch = app<typeof state>(container, state, (s) =>
|
|
@@ -512,26 +598,35 @@ export default {
|
|
|
512
598
|
unmounts.push("unmount section");
|
|
513
599
|
}
|
|
514
600
|
} : {},
|
|
515
|
-
[P,
|
|
601
|
+
[P, {
|
|
602
|
+
onUnmount: s.toggle && ((s: unknown, ele: HTMLElement) => {
|
|
603
|
+
unmounts.push("unmount p");
|
|
604
|
+
})
|
|
605
|
+
}, "text"]
|
|
516
606
|
]
|
|
517
607
|
]
|
|
518
608
|
);
|
|
519
609
|
|
|
520
|
-
expect(unmounts).toEqual([]);
|
|
610
|
+
await expect(unmounts).toEqual([]);
|
|
611
|
+
let before = container._vode.stats.syncRenderCount;
|
|
521
612
|
patch({ toggle: true });
|
|
522
|
-
expect(
|
|
613
|
+
await expect(() => expect(container._vode.stats.syncRenderCount).toBeGreaterThan(before))
|
|
614
|
+
.toSucceedAsync();
|
|
615
|
+
await expect(unmounts).toEqual([]);
|
|
616
|
+
before = container._vode.stats.syncRenderCount;
|
|
523
617
|
patch({ remove: true });
|
|
524
|
-
expect(
|
|
618
|
+
await expect(() => expect(container._vode.stats.syncRenderCount).toBeGreaterThan(before)).toSucceedAsync();
|
|
619
|
+
await expect(unmounts).toEqual(["unmount p", "unmount section"]);
|
|
525
620
|
},
|
|
526
621
|
|
|
527
|
-
"onUnmount(): A->A path - onUnmount removed during update does not fire": () => {
|
|
622
|
+
"onUnmount(): A->A path - onUnmount removed during update does not fire": async () => {
|
|
528
623
|
const container = setup();
|
|
529
624
|
const unmounts: string[] = [];
|
|
530
625
|
const state = createState({ toggle: false, remove: false });
|
|
531
626
|
const patch = app<typeof state>(container, state, (s) =>
|
|
532
627
|
[DIV,
|
|
533
628
|
!s.remove && [SECTION,
|
|
534
|
-
s.toggle
|
|
629
|
+
!s.toggle && {
|
|
535
630
|
onUnmount: (s: unknown, ele: HTMLElement) => {
|
|
536
631
|
unmounts.push("unmount section");
|
|
537
632
|
}
|
|
@@ -541,14 +636,12 @@ export default {
|
|
|
541
636
|
]
|
|
542
637
|
);
|
|
543
638
|
|
|
544
|
-
expect(unmounts).toEqual([]);
|
|
545
|
-
patch({
|
|
546
|
-
expect(unmounts).toEqual([]);
|
|
547
|
-
patch({ remove: true });
|
|
548
|
-
expect(unmounts).toEqual([]);
|
|
639
|
+
await expect(unmounts).toEqual([]);
|
|
640
|
+
patch({ remove: true, toggle: false });
|
|
641
|
+
await expect(unmounts).toEqual([]);
|
|
549
642
|
},
|
|
550
643
|
|
|
551
|
-
"onUnmount(): A->A path - onUnmount changed during update fires the new one": () => {
|
|
644
|
+
"onUnmount(): A->A path - onUnmount changed during update fires the new one": async () => {
|
|
552
645
|
const container = setup();
|
|
553
646
|
const unmounts: string[] = [];
|
|
554
647
|
const state = createState({ version: "a", remove: false });
|
|
@@ -565,14 +658,17 @@ export default {
|
|
|
565
658
|
]
|
|
566
659
|
);
|
|
567
660
|
|
|
568
|
-
expect(unmounts).toEqual([]);
|
|
661
|
+
await expect(unmounts).toEqual([]);
|
|
662
|
+
const before = container._vode.stats.syncRenderCount;
|
|
569
663
|
patch({ version: "b" });
|
|
570
|
-
expect(
|
|
664
|
+
await expect(async () => await expect(container._vode.stats.syncRenderCount).toBeGreaterThan(before))
|
|
665
|
+
.toSucceedAsync();
|
|
666
|
+
await expect(unmounts).toEqual([]);
|
|
571
667
|
patch({ remove: true });
|
|
572
|
-
expect(unmounts).toEqual(["unmount b"]);
|
|
668
|
+
await expect(unmounts).toEqual(["unmount b"]);
|
|
573
669
|
},
|
|
574
670
|
|
|
575
|
-
"onUnmount(): A->B path - element replaced with different tag fires old onUnmount": () => {
|
|
671
|
+
"onUnmount(): A->B path - element replaced with different tag fires old onUnmount": async () => {
|
|
576
672
|
const container = setup();
|
|
577
673
|
const unmounts: string[] = [];
|
|
578
674
|
const state = createState({ showArticle: true });
|
|
@@ -598,12 +694,12 @@ export default {
|
|
|
598
694
|
]
|
|
599
695
|
);
|
|
600
696
|
|
|
601
|
-
expect(unmounts).toEqual([]);
|
|
697
|
+
await expect(unmounts).toEqual([]);
|
|
602
698
|
patch({ showArticle: false });
|
|
603
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
699
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
604
700
|
},
|
|
605
701
|
|
|
606
|
-
"onUnmount(): A->B path - swap back fires the other element's onUnmount": () => {
|
|
702
|
+
"onUnmount(): A->B path - swap back fires the other element's onUnmount": async () => {
|
|
607
703
|
const container = setup();
|
|
608
704
|
const unmounts: string[] = [];
|
|
609
705
|
const state = createState({ showArticle: true });
|
|
@@ -629,18 +725,18 @@ export default {
|
|
|
629
725
|
]
|
|
630
726
|
);
|
|
631
727
|
|
|
632
|
-
expect(unmounts).toEqual([]);
|
|
728
|
+
await expect(unmounts).toEqual([]);
|
|
633
729
|
patch({ showArticle: false });
|
|
634
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
730
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
635
731
|
unmounts.length = 0;
|
|
636
732
|
patch({ showArticle: true });
|
|
637
|
-
expect(unmounts).toEqual(["unmount aside"]);
|
|
733
|
+
await expect(unmounts).toEqual(["unmount aside"]);
|
|
638
734
|
unmounts.length = 0;
|
|
639
735
|
patch({ showArticle: false });
|
|
640
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
736
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
641
737
|
},
|
|
642
738
|
|
|
643
|
-
"onUnmount(): A->B path - replaced element's children onUnmounts also fire": () => {
|
|
739
|
+
"onUnmount(): A->B path - replaced element's children onUnmounts also fire": async () => {
|
|
644
740
|
const container = setup();
|
|
645
741
|
const unmounts: string[] = [];
|
|
646
742
|
const state = createState({ showArticle: true });
|
|
@@ -673,12 +769,12 @@ export default {
|
|
|
673
769
|
]
|
|
674
770
|
);
|
|
675
771
|
|
|
676
|
-
expect(unmounts).toEqual([]);
|
|
772
|
+
await expect(unmounts).toEqual([]);
|
|
677
773
|
patch({ showArticle: false });
|
|
678
|
-
expect(unmounts).toEqual(["unmount p", "unmount article"]);
|
|
774
|
+
await expect(unmounts).toEqual(["unmount p", "unmount article"]);
|
|
679
775
|
},
|
|
680
776
|
|
|
681
|
-
"onUnmount(): element -> text fires onUnmount": () => {
|
|
777
|
+
"onUnmount(): element -> text fires onUnmount": async () => {
|
|
682
778
|
const container = setup();
|
|
683
779
|
const unmounts: string[] = [];
|
|
684
780
|
const state = createState({ showElement: true });
|
|
@@ -697,12 +793,12 @@ export default {
|
|
|
697
793
|
]
|
|
698
794
|
);
|
|
699
795
|
|
|
700
|
-
expect(unmounts).toEqual([]);
|
|
796
|
+
await expect(unmounts).toEqual([]);
|
|
701
797
|
patch({ showElement: false });
|
|
702
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
798
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
703
799
|
},
|
|
704
800
|
|
|
705
|
-
"onUnmount(): text -> element registers onUnmount that fires later": () => {
|
|
801
|
+
"onUnmount(): text -> element registers onUnmount that fires later": async () => {
|
|
706
802
|
const container = setup();
|
|
707
803
|
const unmounts: string[] = [];
|
|
708
804
|
const state = createState({ showElement: false, remove: false });
|
|
@@ -722,14 +818,17 @@ export default {
|
|
|
722
818
|
]
|
|
723
819
|
);
|
|
724
820
|
|
|
725
|
-
expect(unmounts).toEqual([]);
|
|
821
|
+
await expect(unmounts).toEqual([]);
|
|
822
|
+
const before = container._vode.stats.syncRenderCount;
|
|
726
823
|
patch({ showElement: true });
|
|
727
|
-
expect(
|
|
824
|
+
await expect(() => expect(container._vode.stats.syncRenderCount).toBeGreaterThan(before))
|
|
825
|
+
.toSucceedAsync();
|
|
826
|
+
await expect(unmounts).toEqual([]);
|
|
728
827
|
patch({ remove: true });
|
|
729
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
828
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
730
829
|
},
|
|
731
830
|
|
|
732
|
-
"onUnmount(): deep nesting 4+ levels with onUnmount at each level": () => {
|
|
831
|
+
"onUnmount(): deep nesting 4+ levels with onUnmount at each level": async () => {
|
|
733
832
|
const container = setup();
|
|
734
833
|
const unmounts: string[] = [];
|
|
735
834
|
const state = createState({ show: true });
|
|
@@ -767,9 +866,9 @@ export default {
|
|
|
767
866
|
]
|
|
768
867
|
);
|
|
769
868
|
|
|
770
|
-
expect(unmounts).toEqual([]);
|
|
869
|
+
await expect(unmounts).toEqual([]);
|
|
771
870
|
patch({ show: false });
|
|
772
|
-
expect(unmounts)
|
|
871
|
+
await expect(unmounts)
|
|
773
872
|
.toEqual([
|
|
774
873
|
"unmount article",
|
|
775
874
|
"unmount section",
|
|
@@ -778,7 +877,7 @@ export default {
|
|
|
778
877
|
]);
|
|
779
878
|
},
|
|
780
879
|
|
|
781
|
-
"onUnmount(): multiple siblings - remove one fires only that sibling's subtree": () => {
|
|
880
|
+
"onUnmount(): multiple siblings - remove one fires only that sibling's subtree": async () => {
|
|
782
881
|
const container = setup();
|
|
783
882
|
const unmounts: string[] = [];
|
|
784
883
|
const state = createState({ showFirst: true, showSecond: true });
|
|
@@ -817,12 +916,12 @@ export default {
|
|
|
817
916
|
]
|
|
818
917
|
);
|
|
819
918
|
|
|
820
|
-
expect(unmounts).toEqual([]);
|
|
919
|
+
await expect(unmounts).toEqual([]);
|
|
821
920
|
patch({ showFirst: false });
|
|
822
|
-
expect(unmounts).toEqual(["unmount first-child", "unmount first"]);
|
|
921
|
+
await expect(unmounts).toEqual(["unmount first-child", "unmount first"]);
|
|
823
922
|
},
|
|
824
923
|
|
|
825
|
-
"onUnmount(): multiple siblings - remove parent fires all": () => {
|
|
924
|
+
"onUnmount(): multiple siblings - remove parent fires all": async () => {
|
|
826
925
|
const container = setup();
|
|
827
926
|
const unmounts: string[] = [];
|
|
828
927
|
const state = createState({ show: true });
|
|
@@ -849,12 +948,12 @@ export default {
|
|
|
849
948
|
]
|
|
850
949
|
);
|
|
851
950
|
|
|
852
|
-
expect(unmounts).toEqual([]);
|
|
951
|
+
await expect(unmounts).toEqual([]);
|
|
853
952
|
patch({ show: false });
|
|
854
|
-
expect(unmounts).toEqual(["unmount second", "unmount first"]);
|
|
953
|
+
await expect(unmounts).toEqual(["unmount second", "unmount first"]);
|
|
855
954
|
},
|
|
856
955
|
|
|
857
|
-
"onUnmount(): stale children cleanup - fewer new children than old fires removed children's onUnmounts": () => {
|
|
956
|
+
"onUnmount(): stale children cleanup - fewer new children than old fires removed children's onUnmounts": async () => {
|
|
858
957
|
const container = setup();
|
|
859
958
|
const unmounts: string[] = [];
|
|
860
959
|
const state = createState({ count: 3 });
|
|
@@ -868,12 +967,12 @@ export default {
|
|
|
868
967
|
]
|
|
869
968
|
);
|
|
870
969
|
|
|
871
|
-
expect(unmounts).toEqual([]);
|
|
970
|
+
await expect(unmounts).toEqual([]);
|
|
872
971
|
patch({ count: 1 });
|
|
873
|
-
expect(unmounts).toEqual(["unmount p1", "unmount p2"]);
|
|
972
|
+
await expect(unmounts).toEqual(["unmount p1", "unmount p2"]);
|
|
874
973
|
},
|
|
875
974
|
|
|
876
|
-
"onUnmount(): mixed onUnmount presence in tree - only elements with onUnmount fire": () => {
|
|
975
|
+
"onUnmount(): mixed onUnmount presence in tree - only elements with onUnmount fire": async () => {
|
|
877
976
|
const container = setup();
|
|
878
977
|
const unmounts: string[] = [];
|
|
879
978
|
const state = createState({ show: true });
|
|
@@ -902,12 +1001,12 @@ export default {
|
|
|
902
1001
|
]
|
|
903
1002
|
);
|
|
904
1003
|
|
|
905
|
-
expect(unmounts).toEqual([]);
|
|
1004
|
+
await expect(unmounts).toEqual([]);
|
|
906
1005
|
patch({ show: false });
|
|
907
|
-
expect(unmounts).toEqual(["unmount p", "unmount section"]);
|
|
1006
|
+
await expect(unmounts).toEqual(["unmount p", "unmount section"]);
|
|
908
1007
|
},
|
|
909
1008
|
|
|
910
|
-
"onUnmount(): sibling ordering - sibling subtree depths": () => {
|
|
1009
|
+
"onUnmount(): sibling ordering - sibling subtree depths": async () => {
|
|
911
1010
|
const container = setup();
|
|
912
1011
|
const unmounts: string[] = [];
|
|
913
1012
|
const state = createState({ show: true });
|
|
@@ -941,12 +1040,12 @@ export default {
|
|
|
941
1040
|
]
|
|
942
1041
|
);
|
|
943
1042
|
|
|
944
|
-
expect(unmounts).toEqual([]);
|
|
1043
|
+
await expect(unmounts).toEqual([]);
|
|
945
1044
|
patch({ show: false });
|
|
946
|
-
expect(unmounts).toEqual(["unmount nav", "unmount p-deep", "unmount div"]);
|
|
1045
|
+
await expect(unmounts).toEqual(["unmount nav", "unmount p-deep", "unmount div"]);
|
|
947
1046
|
},
|
|
948
1047
|
|
|
949
|
-
"onUnmount(): A->A path - children's unmounts shift when previous sibling's subtree changes": () => {
|
|
1048
|
+
"onUnmount(): A->A path - children's unmounts shift when previous sibling's subtree changes": async () => {
|
|
950
1049
|
const container = setup();
|
|
951
1050
|
const unmounts: string[] = [];
|
|
952
1051
|
const state = createState({ showExtraChild: true, remove: false });
|
|
@@ -981,11 +1080,11 @@ export default {
|
|
|
981
1080
|
]
|
|
982
1081
|
);
|
|
983
1082
|
|
|
984
|
-
expect(unmounts).toEqual([]);
|
|
1083
|
+
await expect(unmounts).toEqual([]);
|
|
985
1084
|
patch({ showExtraChild: false });
|
|
986
|
-
expect(unmounts).toEqual(["unmount span"]);
|
|
1085
|
+
await expect(unmounts).toEqual(["unmount span"]);
|
|
987
1086
|
patch({ remove: true });
|
|
988
|
-
expect(unmounts)
|
|
1087
|
+
await expect(unmounts)
|
|
989
1088
|
.toEqual([
|
|
990
1089
|
"unmount span",
|
|
991
1090
|
"unmount aside",
|
|
@@ -993,7 +1092,7 @@ export default {
|
|
|
993
1092
|
]);
|
|
994
1093
|
},
|
|
995
1094
|
|
|
996
|
-
"onUnmount(): root element replacement fires root's onUnmount": () => {
|
|
1095
|
+
"onUnmount(): root element replacement fires root's onUnmount": async () => {
|
|
997
1096
|
const container = setup();
|
|
998
1097
|
const unmounts: string[] = [];
|
|
999
1098
|
const state = createState({ showDiv: true });
|
|
@@ -1017,12 +1116,12 @@ export default {
|
|
|
1017
1116
|
]
|
|
1018
1117
|
);
|
|
1019
1118
|
|
|
1020
|
-
expect(unmounts).toEqual([]);
|
|
1119
|
+
await expect(unmounts).toEqual([]);
|
|
1021
1120
|
patch({ showDiv: false });
|
|
1022
|
-
expect(unmounts).toEqual(["unmount div"]);
|
|
1121
|
+
await expect(unmounts).toEqual(["unmount div"]);
|
|
1023
1122
|
},
|
|
1024
1123
|
|
|
1025
|
-
"onUnmount(): child onUnmount fires when element is falsified after onUnmount was added via A->A update": () => {
|
|
1124
|
+
"onUnmount(): child onUnmount fires when element is falsified after onUnmount was added via A->A update": async () => {
|
|
1026
1125
|
const container = setup();
|
|
1027
1126
|
const unmounts: string[] = [];
|
|
1028
1127
|
const state = createState({ addUnmount: false, show: true });
|
|
@@ -1039,14 +1138,17 @@ export default {
|
|
|
1039
1138
|
]
|
|
1040
1139
|
);
|
|
1041
1140
|
|
|
1042
|
-
expect(unmounts).toEqual([]);
|
|
1141
|
+
await expect(unmounts).toEqual([]);
|
|
1142
|
+
const before = container._vode.stats.syncRenderCount;
|
|
1043
1143
|
patch({ addUnmount: true });
|
|
1044
|
-
expect(
|
|
1144
|
+
await expect(async () => await expect(container._vode.stats.syncRenderCount).toEqual(before + 1))
|
|
1145
|
+
.toSucceedAsync();
|
|
1146
|
+
await expect(unmounts).toEqual([]);
|
|
1045
1147
|
patch({ show: false });
|
|
1046
|
-
expect(unmounts).toEqual(["unmount article"]);
|
|
1148
|
+
await expect(unmounts).toEqual(["unmount article"]);
|
|
1047
1149
|
},
|
|
1048
1150
|
|
|
1049
|
-
"onUnmount(): A->B path - onUnmount from old children fire when switching tags": () => {
|
|
1151
|
+
"onUnmount(): A->B path - onUnmount from old children fire when switching tags": async () => {
|
|
1050
1152
|
const container = setup();
|
|
1051
1153
|
const unmounts: string[] = [];
|
|
1052
1154
|
const state = createState({ showArticle: true });
|
|
@@ -1076,12 +1178,12 @@ export default {
|
|
|
1076
1178
|
]
|
|
1077
1179
|
);
|
|
1078
1180
|
|
|
1079
|
-
expect(unmounts).toEqual([]);
|
|
1181
|
+
await expect(unmounts).toEqual([]);
|
|
1080
1182
|
patch({ showArticle: false });
|
|
1081
|
-
expect(unmounts).toEqual(["unmount p-inner"]);
|
|
1183
|
+
await expect(unmounts).toEqual(["unmount p-inner"]);
|
|
1082
1184
|
},
|
|
1083
1185
|
|
|
1084
|
-
"onUnmount(): memo hit + earlier sibling growth corrupts unmount indices": () => {
|
|
1186
|
+
"onUnmount(): memo hit + earlier sibling growth corrupts unmount indices": async () => {
|
|
1085
1187
|
const container = setup();
|
|
1086
1188
|
const fired: string[] = [];
|
|
1087
1189
|
const state = createState({ expanded: false, showB: true });
|
|
@@ -1112,16 +1214,16 @@ export default {
|
|
|
1112
1214
|
]
|
|
1113
1215
|
);
|
|
1114
1216
|
|
|
1115
|
-
expect(fired).toEqual([]);
|
|
1217
|
+
await expect(fired).toEqual([]);
|
|
1116
1218
|
|
|
1117
1219
|
patch({ expanded: true });
|
|
1118
|
-
expect(fired).toEqual([]);
|
|
1220
|
+
await expect(fired).toEqual([]);
|
|
1119
1221
|
|
|
1120
1222
|
patch({ showB: false });
|
|
1121
|
-
expect(fired).toEqual(["unmount B"]);
|
|
1223
|
+
await expect(fired).toEqual(["unmount B"]);
|
|
1122
1224
|
},
|
|
1123
1225
|
|
|
1124
|
-
"onUnmount(): excess child removal + same-render sibling growth": () => {
|
|
1226
|
+
"onUnmount(): excess child removal + same-render sibling growth": async () => {
|
|
1125
1227
|
const container = setup();
|
|
1126
1228
|
const fired: string[] = [];
|
|
1127
1229
|
const state = createState({ expanded: false, showB: true });
|
|
@@ -1152,120 +1254,12 @@ export default {
|
|
|
1152
1254
|
]
|
|
1153
1255
|
);
|
|
1154
1256
|
|
|
1155
|
-
expect(fired).toEqual([]);
|
|
1257
|
+
await expect(fired).toEqual([]);
|
|
1156
1258
|
patch({ expanded: true, showB: false });
|
|
1157
|
-
expect(fired).toEqual(["unmount B"]);
|
|
1259
|
+
await expect(fired).toEqual(["unmount B"]);
|
|
1158
1260
|
},
|
|
1159
1261
|
|
|
1160
|
-
"
|
|
1161
|
-
const container = setup();
|
|
1162
|
-
const state = createState({
|
|
1163
|
-
startTime: 0,
|
|
1164
|
-
inputReady: false,
|
|
1165
|
-
showInput: true,
|
|
1166
|
-
showTimer: true
|
|
1167
|
-
});
|
|
1168
|
-
type State = typeof state;
|
|
1169
|
-
const logs: string[] = [];
|
|
1170
|
-
|
|
1171
|
-
const patch = app<State>(container, state, (s) =>
|
|
1172
|
-
[DIV,
|
|
1173
|
-
s.showInput && [INPUT, {
|
|
1174
|
-
type: 'text',
|
|
1175
|
-
placeholder: 'Auto-focused on mount',
|
|
1176
|
-
onMount: (s: State, ele: HTMLElement) => {
|
|
1177
|
-
//(ele as HTMLInputElement).focus();
|
|
1178
|
-
logs.push('Input mounted');
|
|
1179
|
-
return { inputReady: true };
|
|
1180
|
-
},
|
|
1181
|
-
onUnmount: (s: State, ele: HTMLElement) => {
|
|
1182
|
-
// console.log('Input removed');
|
|
1183
|
-
logs.push('Input removed');
|
|
1184
|
-
return { inputReady: false };
|
|
1185
|
-
}
|
|
1186
|
-
}],
|
|
1187
|
-
|
|
1188
|
-
s.showTimer && [P, {
|
|
1189
|
-
onMount: (s: State, ele: HTMLElement) => {
|
|
1190
|
-
logs.push('Timer started');
|
|
1191
|
-
s.patch({ startTime: Date.now() });
|
|
1192
|
-
},
|
|
1193
|
-
onUnmount: (s: State, ele: HTMLElement) => {
|
|
1194
|
-
console.log('Timer stopped after', Date.now() - s.startTime, 'ms');
|
|
1195
|
-
logs.push('Timer removed');
|
|
1196
|
-
}
|
|
1197
|
-
}, 'Mount/unmount lifecycle demo']
|
|
1198
|
-
]
|
|
1199
|
-
);
|
|
1200
|
-
|
|
1201
|
-
expect(state.inputReady)
|
|
1202
|
-
.toEqual(true);
|
|
1203
|
-
expect(state.startTime != 0)
|
|
1204
|
-
.toEqual(true);
|
|
1205
|
-
patch({ showInput: false });
|
|
1206
|
-
expect(state.inputReady)
|
|
1207
|
-
.toEqual(false);
|
|
1208
|
-
patch({ showTimer: false });
|
|
1209
|
-
expect(logs).toEqual([
|
|
1210
|
-
'Input mounted',
|
|
1211
|
-
'Timer started',
|
|
1212
|
-
'Input removed',
|
|
1213
|
-
'Timer removed'
|
|
1214
|
-
]);
|
|
1215
|
-
},
|
|
1216
|
-
|
|
1217
|
-
"onMount(): with catched component, replacement vode's onMount fires when error occurs": () => {
|
|
1218
|
-
const container = setup();
|
|
1219
|
-
const mounts: string[] = [];
|
|
1220
|
-
const broken: any = () => { throw new Error("boom"); };
|
|
1221
|
-
app(container, {}, () =>
|
|
1222
|
-
[DIV,
|
|
1223
|
-
{
|
|
1224
|
-
catch: [SECTION,
|
|
1225
|
-
{
|
|
1226
|
-
onMount: (s: unknown, ele: HTMLElement) => {
|
|
1227
|
-
mounts.push("mount fallback");
|
|
1228
|
-
}
|
|
1229
|
-
},
|
|
1230
|
-
"fallback"
|
|
1231
|
-
]
|
|
1232
|
-
},
|
|
1233
|
-
broken
|
|
1234
|
-
]
|
|
1235
|
-
);
|
|
1236
|
-
|
|
1237
|
-
expect(mounts).toEqual(["mount fallback"]);
|
|
1238
|
-
},
|
|
1239
|
-
|
|
1240
|
-
"onMount(): with catched component, returned vode's onMount fires and receives error": () => {
|
|
1241
|
-
const container = setup();
|
|
1242
|
-
const mounts: string[] = [];
|
|
1243
|
-
const caughtErrors: string[] = [];
|
|
1244
|
-
const broken: any = () => { throw new Error("boom"); };
|
|
1245
|
-
app(container, {}, () =>
|
|
1246
|
-
[DIV,
|
|
1247
|
-
{
|
|
1248
|
-
catch: (s: unknown, err: Error) => {
|
|
1249
|
-
caughtErrors.push(err.message);
|
|
1250
|
-
return [SECTION,
|
|
1251
|
-
{
|
|
1252
|
-
onMount: (s: unknown, ele: HTMLElement) => {
|
|
1253
|
-
mounts.push("mount fallback");
|
|
1254
|
-
}
|
|
1255
|
-
},
|
|
1256
|
-
"fallback"
|
|
1257
|
-
];
|
|
1258
|
-
}
|
|
1259
|
-
},
|
|
1260
|
-
broken
|
|
1261
|
-
]
|
|
1262
|
-
);
|
|
1263
|
-
|
|
1264
|
-
expect(mounts).toEqual(["mount fallback"]);
|
|
1265
|
-
expect(caughtErrors).toEqual(["boom"]);
|
|
1266
|
-
},
|
|
1267
|
-
|
|
1268
|
-
"onUnmount(): with catched component, replacement vode's onUnmount fires when removed": () => {
|
|
1262
|
+
"onUnmount(): with catched component, replacement vode's onUnmount fires when removed": async () => {
|
|
1269
1263
|
const container = setup();
|
|
1270
1264
|
const unmounts: string[] = [];
|
|
1271
1265
|
const state = createState({ show: true });
|
|
@@ -1288,12 +1282,12 @@ export default {
|
|
|
1288
1282
|
]
|
|
1289
1283
|
);
|
|
1290
1284
|
|
|
1291
|
-
expect(unmounts).toEqual([]);
|
|
1285
|
+
await expect(unmounts).toEqual([]);
|
|
1292
1286
|
patch({ show: false });
|
|
1293
|
-
expect(unmounts).toEqual(["unmount fallback"]);
|
|
1287
|
+
await expect(unmounts).toEqual(["unmount fallback"]);
|
|
1294
1288
|
},
|
|
1295
1289
|
|
|
1296
|
-
"onUnmount(): with catched component, deep replacement tree fires in post-order": () => {
|
|
1290
|
+
"onUnmount(): with catched component, deep replacement tree fires in post-order": async () => {
|
|
1297
1291
|
const container = setup();
|
|
1298
1292
|
const unmounts: string[] = [];
|
|
1299
1293
|
const state = createState({ show: true });
|
|
@@ -1331,12 +1325,12 @@ export default {
|
|
|
1331
1325
|
]
|
|
1332
1326
|
);
|
|
1333
1327
|
|
|
1334
|
-
expect(unmounts).toEqual([]);
|
|
1328
|
+
await expect(unmounts).toEqual([]);
|
|
1335
1329
|
patch({ show: false });
|
|
1336
|
-
expect(unmounts).toEqual(["unmount span", "unmount p", "unmount article"]);
|
|
1330
|
+
await expect(unmounts).toEqual(["unmount span", "unmount p", "unmount article"]);
|
|
1337
1331
|
},
|
|
1338
1332
|
|
|
1339
|
-
"onMount()
|
|
1333
|
+
"onMount() + onUnmount(): with catched component, full lifecycle symmetry of catch replacement": async () => {
|
|
1340
1334
|
const container = setup();
|
|
1341
1335
|
const logs: string[] = [];
|
|
1342
1336
|
const state = createState({ show: true });
|
|
@@ -1362,43 +1356,149 @@ export default {
|
|
|
1362
1356
|
]
|
|
1363
1357
|
);
|
|
1364
1358
|
|
|
1365
|
-
expect(logs).toEqual(["mount article"]);
|
|
1359
|
+
await expect(logs).toEqual(["mount article"]);
|
|
1366
1360
|
patch({ show: false });
|
|
1367
|
-
expect(logs).toEqual(["mount article", "unmount article"]);
|
|
1361
|
+
await expect(logs).toEqual(["mount article", "unmount article"]);
|
|
1368
1362
|
},
|
|
1369
1363
|
|
|
1370
|
-
"onMount()
|
|
1364
|
+
"onMount() + onUnmount: symmetry of calls": async () => {
|
|
1371
1365
|
const container = setup();
|
|
1366
|
+
const state = createState({
|
|
1367
|
+
startTime: 0,
|
|
1368
|
+
inputReady: false,
|
|
1369
|
+
showInput: true,
|
|
1370
|
+
showTimer: true
|
|
1371
|
+
});
|
|
1372
|
+
type State = typeof state;
|
|
1372
1373
|
const logs: string[] = [];
|
|
1373
|
-
|
|
1374
|
-
app(container,
|
|
1374
|
+
|
|
1375
|
+
const patch = app<State>(container, state, (s) => {
|
|
1376
|
+
return [DIV,
|
|
1377
|
+
s.showInput && [INPUT, {
|
|
1378
|
+
type: 'text',
|
|
1379
|
+
placeholder: 'Auto-focused on mount',
|
|
1380
|
+
onMount: (s: State, ele: HTMLElement) => {
|
|
1381
|
+
logs.push('Input mounted');
|
|
1382
|
+
return { inputReady: true };
|
|
1383
|
+
},
|
|
1384
|
+
onUnmount: (s: State, ele: HTMLElement) => {
|
|
1385
|
+
logs.push('Input removed');
|
|
1386
|
+
return { inputReady: false };
|
|
1387
|
+
}
|
|
1388
|
+
}],
|
|
1389
|
+
|
|
1390
|
+
s.showTimer && [P, {
|
|
1391
|
+
onMount: (s: State, ele: HTMLElement) => {
|
|
1392
|
+
logs.push('Timer started');
|
|
1393
|
+
return { startTime: Date.now() };
|
|
1394
|
+
},
|
|
1395
|
+
onUnmount: (s: State, ele: HTMLElement) => {
|
|
1396
|
+
logs.push('Timer removed');
|
|
1397
|
+
}
|
|
1398
|
+
}, 'Mount/unmount lifecycle demo']
|
|
1399
|
+
]
|
|
1400
|
+
}
|
|
1401
|
+
);
|
|
1402
|
+
|
|
1403
|
+
await expect(state.inputReady)
|
|
1404
|
+
.toEqual(true);
|
|
1405
|
+
await expect(state.startTime != 0)
|
|
1406
|
+
.toEqual(true);
|
|
1407
|
+
patch({ showInput: false });
|
|
1408
|
+
|
|
1409
|
+
await expect(
|
|
1410
|
+
async () => await expect(state.inputReady).toEqual(false, "expected: inputReady == false")
|
|
1411
|
+
).toSucceedAsync();
|
|
1412
|
+
|
|
1413
|
+
patch({ showTimer: false });
|
|
1414
|
+
|
|
1415
|
+
await expect(
|
|
1416
|
+
async () => await expect(container._vode.stats.syncRenderCount >= 4)
|
|
1417
|
+
.toEqual(true)
|
|
1418
|
+
).toSucceedAsync();
|
|
1419
|
+
|
|
1420
|
+
await expect(logs).toEqual([
|
|
1421
|
+
'Input mounted',
|
|
1422
|
+
'Timer started',
|
|
1423
|
+
'Input removed',
|
|
1424
|
+
'Timer removed'
|
|
1425
|
+
]);
|
|
1426
|
+
},
|
|
1427
|
+
|
|
1428
|
+
"onMount() + onUnmount(): Not called when DOM does not require element creation or removal (same TAGs)": async () => {
|
|
1429
|
+
const container = setup();
|
|
1430
|
+
const logs = <string[]>[];
|
|
1431
|
+
|
|
1432
|
+
const Comp: (name: string) => Component = (name: string) => () => [ARTICLE,
|
|
1375
1433
|
[DIV,
|
|
1376
1434
|
{
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
onMount: (s: unknown, ele: HTMLElement) => {
|
|
1380
|
-
logs.push("mount fallback");
|
|
1381
|
-
}
|
|
1382
|
-
},
|
|
1383
|
-
"fallback"
|
|
1384
|
-
]
|
|
1435
|
+
onMount: () => logs.push("mount " + name),
|
|
1436
|
+
onUnmount: () => logs.push("unmount " + name)
|
|
1385
1437
|
},
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1438
|
+
"Component " + name]
|
|
1439
|
+
];
|
|
1440
|
+
|
|
1441
|
+
const state = createState({ showB: false, showD: false });
|
|
1442
|
+
app<typeof state>(container, state, s => [DIV,
|
|
1443
|
+
// this way they both "share a slot"
|
|
1444
|
+
s.showB ? Comp("B") : Comp("A"),
|
|
1445
|
+
|
|
1446
|
+
// this way each component occupies its own "slot"
|
|
1447
|
+
!s.showD && Comp("C"),
|
|
1448
|
+
s.showD && Comp("D"),
|
|
1449
|
+
]);
|
|
1450
|
+
|
|
1451
|
+
await expect(container).toMatch(
|
|
1452
|
+
[DIV,
|
|
1453
|
+
[ARTICLE,
|
|
1454
|
+
[DIV, "Component A"],
|
|
1455
|
+
],
|
|
1456
|
+
[ARTICLE,
|
|
1457
|
+
[DIV, "Component C"],
|
|
1458
|
+
],
|
|
1397
1459
|
]
|
|
1398
1460
|
);
|
|
1461
|
+
await expect(logs).toEqual(["mount A", "mount C"]);
|
|
1399
1462
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
expect(
|
|
1463
|
+
state.patch({ showB: true });
|
|
1464
|
+
|
|
1465
|
+
await expect(container).toMatch(
|
|
1466
|
+
[DIV,
|
|
1467
|
+
[ARTICLE,
|
|
1468
|
+
[DIV, "Component B"],
|
|
1469
|
+
],
|
|
1470
|
+
[ARTICLE,
|
|
1471
|
+
[DIV, "Component C"],
|
|
1472
|
+
],
|
|
1473
|
+
]
|
|
1474
|
+
);
|
|
1475
|
+
|
|
1476
|
+
// as both components result in the same structure
|
|
1477
|
+
// of element types the unmount of A
|
|
1478
|
+
// and mount of B does not occur
|
|
1479
|
+
await expect(logs).toEqual(["mount A", "mount C"]);
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
state.patch({ showD: true });
|
|
1483
|
+
|
|
1484
|
+
await expect(container).toMatch(
|
|
1485
|
+
[DIV,
|
|
1486
|
+
[ARTICLE,
|
|
1487
|
+
[DIV, "Component B"],
|
|
1488
|
+
],
|
|
1489
|
+
[ARTICLE,
|
|
1490
|
+
[DIV, "Component D"],
|
|
1491
|
+
],
|
|
1492
|
+
]
|
|
1493
|
+
);
|
|
1494
|
+
|
|
1495
|
+
// when the components occupy different slots in the vdom
|
|
1496
|
+
// their mount/unmount functions are called
|
|
1497
|
+
await expect(logs).toEqual([
|
|
1498
|
+
"mount A",
|
|
1499
|
+
"mount C",
|
|
1500
|
+
"unmount C",
|
|
1501
|
+
"mount D",
|
|
1502
|
+
]);
|
|
1403
1503
|
},
|
|
1404
1504
|
}
|