@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
package/test/tests-examples.ts
CHANGED
|
@@ -9,7 +9,7 @@ function setup() {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export default {
|
|
12
|
-
"Example 1: Counter - increment/reset buttons, basic state patching": () => {
|
|
12
|
+
"Example 1: Counter - increment/reset buttons, basic state patching": async () => {
|
|
13
13
|
const container = setup();
|
|
14
14
|
const state = createState({ count: 0 });
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ export default {
|
|
|
19
19
|
[BUTTON, { onclick: () => ({ count: 0 }), disabled: s.count === 0 }, "Reset"],
|
|
20
20
|
]);
|
|
21
21
|
|
|
22
|
-
expect(container).toMatch(
|
|
22
|
+
await expect(container).toMatch(
|
|
23
23
|
[DIV,
|
|
24
24
|
[H1, "Count: 0"],
|
|
25
25
|
[BUTTON, "Increment"],
|
|
@@ -29,7 +29,7 @@ export default {
|
|
|
29
29
|
|
|
30
30
|
state.patch({ count: 1 });
|
|
31
31
|
|
|
32
|
-
expect(container).toMatch(
|
|
32
|
+
await expect(container).toMatch(
|
|
33
33
|
[DIV,
|
|
34
34
|
[H1, "Count: 1"],
|
|
35
35
|
[BUTTON, "Increment"],
|
|
@@ -39,8 +39,8 @@ export default {
|
|
|
39
39
|
|
|
40
40
|
state.patch({ count: 0 });
|
|
41
41
|
|
|
42
|
-
expect(state.count).toEqual(0);
|
|
43
|
-
expect(container).toMatch(
|
|
42
|
+
await expect(state.count).toEqual(0);
|
|
43
|
+
await expect(container).toMatch(
|
|
44
44
|
[DIV,
|
|
45
45
|
[H1, "Count: 0"],
|
|
46
46
|
[BUTTON, "Increment"],
|
|
@@ -49,7 +49,7 @@ export default {
|
|
|
49
49
|
);
|
|
50
50
|
},
|
|
51
51
|
|
|
52
|
-
"Example 2: Todo List with State Context - nested state via context(), list rendering": () => {
|
|
52
|
+
"Example 2: Todo List with State Context - nested state via context(), list rendering": async () => {
|
|
53
53
|
const container = setup();
|
|
54
54
|
const state = createState({
|
|
55
55
|
todos: {
|
|
@@ -85,7 +85,7 @@ export default {
|
|
|
85
85
|
];
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
expect(container).toMatch(
|
|
88
|
+
await expect(container).toMatch(
|
|
89
89
|
[DIV,
|
|
90
90
|
[H1, "Todos"],
|
|
91
91
|
[INPUT],
|
|
@@ -105,10 +105,10 @@ export default {
|
|
|
105
105
|
|
|
106
106
|
state.patch({ todos: { filter: "active" } });
|
|
107
107
|
|
|
108
|
-
expect(state.todos.filter).toEqual("active");
|
|
109
|
-
expect(state.todos.items.length).toEqual(3);
|
|
108
|
+
await expect(state.todos.filter).toEqual("active");
|
|
109
|
+
await expect(state.todos.items.length).toEqual(3);
|
|
110
110
|
|
|
111
|
-
expect(container).toMatch(
|
|
111
|
+
await expect(container).toMatch(
|
|
112
112
|
[DIV,
|
|
113
113
|
[H1, "Todos"],
|
|
114
114
|
[INPUT],
|
|
@@ -127,7 +127,7 @@ export default {
|
|
|
127
127
|
|
|
128
128
|
state.patch({ todos: { filter: "done" } });
|
|
129
129
|
|
|
130
|
-
expect(container).toMatch(
|
|
130
|
+
await expect(container).toMatch(
|
|
131
131
|
[DIV,
|
|
132
132
|
[H1, "Todos"],
|
|
133
133
|
[INPUT],
|
|
@@ -144,10 +144,10 @@ export default {
|
|
|
144
144
|
);
|
|
145
145
|
|
|
146
146
|
state.patch({ todos: { filter: "all" } });
|
|
147
|
-
expect(state.todos.items.length).toEqual(3);
|
|
147
|
+
await expect(state.todos.items.length).toEqual(3);
|
|
148
148
|
},
|
|
149
149
|
|
|
150
|
-
"Example 3: Data Fetching - loading/error/success state machine with ternary branches": () => {
|
|
150
|
+
"Example 3: Data Fetching - loading/error/success state machine with ternary branches": async () => {
|
|
151
151
|
const container = setup();
|
|
152
152
|
const state = createState({
|
|
153
153
|
fetch: {
|
|
@@ -170,7 +170,7 @@ export default {
|
|
|
170
170
|
];
|
|
171
171
|
});
|
|
172
172
|
|
|
173
|
-
expect(container).toMatch(
|
|
173
|
+
await expect(container).toMatch(
|
|
174
174
|
[DIV,
|
|
175
175
|
[P, "Loading..."],
|
|
176
176
|
]
|
|
@@ -178,7 +178,7 @@ export default {
|
|
|
178
178
|
|
|
179
179
|
state.patch({ fetch: { status: "success", result: "Fetched data" } });
|
|
180
180
|
|
|
181
|
-
expect(container).toMatch(
|
|
181
|
+
await expect(container).toMatch(
|
|
182
182
|
[DIV,
|
|
183
183
|
[DIV, { class: "success" },
|
|
184
184
|
[P, "Result: ", "Fetched data"],
|
|
@@ -189,7 +189,7 @@ export default {
|
|
|
189
189
|
|
|
190
190
|
state.patch({ fetch: { status: "error", error: "Network error", result: null } });
|
|
191
191
|
|
|
192
|
-
expect(container).toMatch(
|
|
192
|
+
await expect(container).toMatch(
|
|
193
193
|
[DIV,
|
|
194
194
|
[DIV, { class: "error" },
|
|
195
195
|
[P, "Error: ", "Network error"],
|
|
@@ -200,7 +200,7 @@ export default {
|
|
|
200
200
|
);
|
|
201
201
|
},
|
|
202
202
|
|
|
203
|
-
"Example 4: Tabbed Panel - tab switching via conditional rendering": () => {
|
|
203
|
+
"Example 4: Tabbed Panel - tab switching via conditional rendering": async () => {
|
|
204
204
|
const container = setup();
|
|
205
205
|
const state = createState({
|
|
206
206
|
ui: {
|
|
@@ -227,7 +227,7 @@ export default {
|
|
|
227
227
|
];
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
expect(container).toMatch(
|
|
230
|
+
await expect(container).toMatch(
|
|
231
231
|
[DIV,
|
|
232
232
|
[NAV, { class: "tabs" },
|
|
233
233
|
[BUTTON, "Home"],
|
|
@@ -246,7 +246,7 @@ export default {
|
|
|
246
246
|
const ctx = context(state).ui;
|
|
247
247
|
ctx.activeTab.patch("settings");
|
|
248
248
|
|
|
249
|
-
expect(container).toMatch(
|
|
249
|
+
await expect(container).toMatch(
|
|
250
250
|
[DIV,
|
|
251
251
|
[NAV, { class: "tabs" },
|
|
252
252
|
[BUTTON, "Home"],
|
|
@@ -264,7 +264,7 @@ export default {
|
|
|
264
264
|
|
|
265
265
|
ctx.activeTab.patch("profile");
|
|
266
266
|
|
|
267
|
-
expect(container).toMatch(
|
|
267
|
+
await expect(container).toMatch(
|
|
268
268
|
[DIV,
|
|
269
269
|
[NAV, { class: "tabs" },
|
|
270
270
|
[BUTTON, "Home"],
|
|
@@ -281,7 +281,7 @@ export default {
|
|
|
281
281
|
);
|
|
282
282
|
},
|
|
283
283
|
|
|
284
|
-
"Example 5: Form Validation - live input validation with conditional error display": () => {
|
|
284
|
+
"Example 5: Form Validation - live input validation with conditional error display": async () => {
|
|
285
285
|
const container = setup();
|
|
286
286
|
const state = createState({
|
|
287
287
|
form: {
|
|
@@ -309,7 +309,7 @@ export default {
|
|
|
309
309
|
];
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
-
expect(container).toMatch(
|
|
312
|
+
await expect(container).toMatch(
|
|
313
313
|
[DIV,
|
|
314
314
|
[FORM,
|
|
315
315
|
[LABEL, "Email:"],
|
|
@@ -331,7 +331,7 @@ export default {
|
|
|
331
331
|
}
|
|
332
332
|
});
|
|
333
333
|
|
|
334
|
-
expect(container).toMatch(
|
|
334
|
+
await expect(container).toMatch(
|
|
335
335
|
[DIV,
|
|
336
336
|
[FORM,
|
|
337
337
|
[LABEL, "Email:"],
|
|
@@ -349,7 +349,7 @@ export default {
|
|
|
349
349
|
|
|
350
350
|
state.patch({
|
|
351
351
|
form: {
|
|
352
|
-
email: "user@
|
|
352
|
+
email: "user@ryupold.de",
|
|
353
353
|
password: "123",
|
|
354
354
|
errors: {
|
|
355
355
|
email: undefined,
|
|
@@ -358,7 +358,7 @@ export default {
|
|
|
358
358
|
},
|
|
359
359
|
});
|
|
360
360
|
|
|
361
|
-
expect(container).toMatch(
|
|
361
|
+
await expect(container).toMatch(
|
|
362
362
|
[DIV,
|
|
363
363
|
[FORM,
|
|
364
364
|
[LABEL, "Email:"],
|
|
@@ -381,7 +381,7 @@ export default {
|
|
|
381
381
|
},
|
|
382
382
|
});
|
|
383
383
|
|
|
384
|
-
expect(container).toMatch(
|
|
384
|
+
await expect(container).toMatch(
|
|
385
385
|
[DIV,
|
|
386
386
|
[FORM,
|
|
387
387
|
[LABEL, "Email:"],
|
|
@@ -397,7 +397,7 @@ export default {
|
|
|
397
397
|
);
|
|
398
398
|
},
|
|
399
399
|
|
|
400
|
-
"Example 6: Component Composition - nested components with dynamic props": () => {
|
|
400
|
+
"Example 6: Component Composition - nested components with dynamic props": async () => {
|
|
401
401
|
const container = setup();
|
|
402
402
|
const state = createState({
|
|
403
403
|
theme: "light" as "light" | "dark",
|
|
@@ -434,7 +434,7 @@ export default {
|
|
|
434
434
|
}, "Toggle Theme"],
|
|
435
435
|
]);
|
|
436
436
|
|
|
437
|
-
expect(container).toMatch(
|
|
437
|
+
await expect(container).toMatch(
|
|
438
438
|
[DIV,
|
|
439
439
|
[HEADER, { class: "header header-light" },
|
|
440
440
|
[H1, "App"],
|
|
@@ -453,7 +453,7 @@ export default {
|
|
|
453
453
|
|
|
454
454
|
state.patch({ theme: "dark" });
|
|
455
455
|
|
|
456
|
-
expect(container).toMatch(
|
|
456
|
+
await expect(container).toMatch(
|
|
457
457
|
[DIV,
|
|
458
458
|
[HEADER, { class: "header header-dark" },
|
|
459
459
|
[H1, "App"],
|
|
@@ -472,9 +472,9 @@ export default {
|
|
|
472
472
|
|
|
473
473
|
state.patch({ user: { name: "Bob", role: "User" } });
|
|
474
474
|
|
|
475
|
-
expect(state.user.name).toEqual("Bob");
|
|
476
|
-
expect(state.user.role).toEqual("User");
|
|
477
|
-
expect(container).toMatch(
|
|
475
|
+
await expect(state.user.name).toEqual("Bob");
|
|
476
|
+
await expect(state.user.role).toEqual("User");
|
|
477
|
+
await expect(container).toMatch(
|
|
478
478
|
[DIV,
|
|
479
479
|
[HEADER, { class: "header header-dark" },
|
|
480
480
|
[H1, "App"],
|
|
@@ -492,7 +492,7 @@ export default {
|
|
|
492
492
|
);
|
|
493
493
|
},
|
|
494
494
|
|
|
495
|
-
"Example 7: Multi-Context - multiple independent state contexts": () => {
|
|
495
|
+
"Example 7: Multi-Context - multiple independent state contexts": async () => {
|
|
496
496
|
const container = setup();
|
|
497
497
|
const state = createState({
|
|
498
498
|
panelA: {
|
|
@@ -508,8 +508,7 @@ export default {
|
|
|
508
508
|
app<typeof state>(container, state, (s) => {
|
|
509
509
|
const ctxA = context(s).panelA;
|
|
510
510
|
const ctxB = context(s).panelB;
|
|
511
|
-
return [
|
|
512
|
-
DIV,
|
|
511
|
+
return [DIV,
|
|
513
512
|
[SECTION, { class: "panel-a" },
|
|
514
513
|
[H2, ctxA.label.get()],
|
|
515
514
|
[P, `Count: ${s.panelA.count}`],
|
|
@@ -523,7 +522,7 @@ export default {
|
|
|
523
522
|
];
|
|
524
523
|
});
|
|
525
524
|
|
|
526
|
-
expect(container).toMatch(
|
|
525
|
+
await expect(container).toMatch(
|
|
527
526
|
[DIV,
|
|
528
527
|
[SECTION, { class: "panel-a" },
|
|
529
528
|
[H2, "Panel A"],
|
|
@@ -541,10 +540,10 @@ export default {
|
|
|
541
540
|
const ctxA = context(state).panelA;
|
|
542
541
|
ctxA.count.patch(5);
|
|
543
542
|
|
|
544
|
-
expect(state.panelA.count).toEqual(5);
|
|
545
|
-
expect(state.panelB.count).toEqual(0);
|
|
543
|
+
await expect(state.panelA.count).toEqual(5);
|
|
544
|
+
await expect(state.panelB.count).toEqual(0);
|
|
546
545
|
|
|
547
|
-
expect(container).toMatch(
|
|
546
|
+
await expect(container).toMatch(
|
|
548
547
|
[DIV,
|
|
549
548
|
[SECTION, { class: "panel-a" },
|
|
550
549
|
[H2, "Panel A"],
|
|
@@ -562,10 +561,10 @@ export default {
|
|
|
562
561
|
const ctxB = context(state).panelB;
|
|
563
562
|
ctxB.count.patch(10);
|
|
564
563
|
|
|
565
|
-
expect(state.panelA.count).toEqual(5);
|
|
566
|
-
expect(state.panelB.count).toEqual(10);
|
|
564
|
+
await expect(state.panelA.count).toEqual(5);
|
|
565
|
+
await expect(state.panelB.count).toEqual(10);
|
|
567
566
|
|
|
568
|
-
expect(container).toMatch(
|
|
567
|
+
await expect(container).toMatch(
|
|
569
568
|
[DIV,
|
|
570
569
|
[SECTION, { class: "panel-a" },
|
|
571
570
|
[H2, "Panel A"],
|
|
@@ -580,11 +579,11 @@ export default {
|
|
|
580
579
|
]
|
|
581
580
|
);
|
|
582
581
|
|
|
583
|
-
expect(ctxA.label.get()).toEqual("Panel A");
|
|
584
|
-
expect(ctxB.label.get()).toEqual("Panel B");
|
|
582
|
+
await expect(ctxA.label.get()).toEqual("Panel A");
|
|
583
|
+
await expect(ctxB.label.get()).toEqual("Panel B");
|
|
585
584
|
},
|
|
586
585
|
|
|
587
|
-
"Example 8: SVG Dynamic - SVG circle with dynamic radius/color": () => {
|
|
586
|
+
"Example 8: SVG Dynamic - SVG circle with dynamic radius/color": async () => {
|
|
588
587
|
const container = setup();
|
|
589
588
|
const state = createState({
|
|
590
589
|
svg: {
|
|
@@ -611,7 +610,7 @@ export default {
|
|
|
611
610
|
];
|
|
612
611
|
});
|
|
613
612
|
|
|
614
|
-
expect(container).toMatch(
|
|
613
|
+
await expect(container).toMatch(
|
|
615
614
|
[DIV,
|
|
616
615
|
[SVG, { xmlns: "http://www.w3.org/2000/svg", width: "200", height: "200" },
|
|
617
616
|
[CIRCLE, {
|
|
@@ -631,10 +630,10 @@ export default {
|
|
|
631
630
|
ctx.radius.patch(30);
|
|
632
631
|
ctx.color.patch("green");
|
|
633
632
|
|
|
634
|
-
expect(state.svg.radius).toEqual(30);
|
|
635
|
-
expect(state.svg.color).toEqual("green");
|
|
633
|
+
await expect(state.svg.radius).toEqual(30);
|
|
634
|
+
await expect(state.svg.color).toEqual("green");
|
|
636
635
|
|
|
637
|
-
expect(container).toMatch(
|
|
636
|
+
await expect(container).toMatch(
|
|
638
637
|
[DIV,
|
|
639
638
|
[SVG, { xmlns: "http://www.w3.org/2000/svg", width: "200", height: "200" },
|
|
640
639
|
[CIRCLE, {
|
|
@@ -653,7 +652,7 @@ export default {
|
|
|
653
652
|
ctx.radius.patch(50);
|
|
654
653
|
ctx.color.patch("blue");
|
|
655
654
|
|
|
656
|
-
expect(container).toMatch(
|
|
655
|
+
await expect(container).toMatch(
|
|
657
656
|
[DIV,
|
|
658
657
|
[SVG, { xmlns: "http://www.w3.org/2000/svg", width: "200", height: "200" },
|
|
659
658
|
[CIRCLE, {
|
|
@@ -670,15 +669,15 @@ export default {
|
|
|
670
669
|
);
|
|
671
670
|
},
|
|
672
671
|
|
|
673
|
-
"Example 9: Dynamic Attributes - conditional elements + attribute changes": () => {
|
|
672
|
+
"Example 9: Dynamic Attributes - conditional elements + attribute changes": async () => {
|
|
674
673
|
const container = setup();
|
|
675
674
|
const state = createState({
|
|
676
675
|
config: {
|
|
677
676
|
showImage: false,
|
|
678
|
-
imageUrl: "https://
|
|
677
|
+
imageUrl: "https://ryupold.de/main/assets/img/pot.webp",
|
|
679
678
|
alt: "Example image",
|
|
680
679
|
linkEnabled: true,
|
|
681
|
-
linkUrl: "https://
|
|
680
|
+
linkUrl: "https://ryupold.de",
|
|
682
681
|
boxWidth: "100px",
|
|
683
682
|
boxColor: "red",
|
|
684
683
|
},
|
|
@@ -711,11 +710,11 @@ export default {
|
|
|
711
710
|
];
|
|
712
711
|
});
|
|
713
712
|
|
|
714
|
-
expect(container).toMatch(
|
|
713
|
+
await expect(container).toMatch(
|
|
715
714
|
[DIV,
|
|
716
715
|
[BUTTON, "Show Image"],
|
|
717
716
|
[A, {
|
|
718
|
-
href: "https://
|
|
717
|
+
href: "https://ryupold.de",
|
|
719
718
|
"data-enabled": "true",
|
|
720
719
|
}, "Click me"],
|
|
721
720
|
[BUTTON, "Toggle Link"],
|
|
@@ -726,17 +725,17 @@ export default {
|
|
|
726
725
|
|
|
727
726
|
state.patch({ config: { showImage: true } });
|
|
728
727
|
|
|
729
|
-
expect(container).toMatch(
|
|
728
|
+
await expect(container).toMatch(
|
|
730
729
|
[DIV,
|
|
731
730
|
[IMG, {
|
|
732
|
-
src: "https://
|
|
731
|
+
src: "https://ryupold.de/main/assets/img/pot.webp",
|
|
733
732
|
alt: "Example image",
|
|
734
733
|
class: "dynamic-image",
|
|
735
734
|
"data-testid": "image",
|
|
736
735
|
}],
|
|
737
736
|
[BUTTON, "Hide Image"],
|
|
738
737
|
[A, {
|
|
739
|
-
href: "https://
|
|
738
|
+
href: "https://ryupold.de",
|
|
740
739
|
"data-enabled": "true",
|
|
741
740
|
}, "Click me"],
|
|
742
741
|
[BUTTON, "Toggle Link"],
|
|
@@ -747,11 +746,11 @@ export default {
|
|
|
747
746
|
|
|
748
747
|
state.patch({ config: { showImage: false } });
|
|
749
748
|
|
|
750
|
-
expect(container).toMatch(
|
|
749
|
+
await expect(container).toMatch(
|
|
751
750
|
[DIV,
|
|
752
751
|
[BUTTON, "Show Image"],
|
|
753
752
|
[A, {
|
|
754
|
-
href: "https://
|
|
753
|
+
href: "https://ryupold.de",
|
|
755
754
|
"data-enabled": "true",
|
|
756
755
|
}, "Click me"],
|
|
757
756
|
[BUTTON, "Toggle Link"],
|
|
@@ -762,7 +761,7 @@ export default {
|
|
|
762
761
|
|
|
763
762
|
state.patch({ config: { linkEnabled: false } });
|
|
764
763
|
|
|
765
|
-
expect(container).toMatch(
|
|
764
|
+
await expect(container).toMatch(
|
|
766
765
|
[DIV,
|
|
767
766
|
[BUTTON, "Show Image"],
|
|
768
767
|
[A, {
|
|
@@ -776,11 +775,11 @@ export default {
|
|
|
776
775
|
|
|
777
776
|
state.patch({ config: { boxWidth: "200px", boxColor: "blue" } });
|
|
778
777
|
|
|
779
|
-
expect(state.config.boxWidth).toEqual("200px");
|
|
780
|
-
expect(state.config.boxColor).toEqual("blue");
|
|
778
|
+
await expect(state.config.boxWidth).toEqual("200px");
|
|
779
|
+
await expect(state.config.boxColor).toEqual("blue");
|
|
781
780
|
},
|
|
782
781
|
|
|
783
|
-
"Example 10: Nested Vode-App - inner app with isolated state via memo + onMount": () => {
|
|
782
|
+
"Example 10: Nested Vode-App - inner app with isolated state via memo + onMount": async () => {
|
|
784
783
|
const container = setup();
|
|
785
784
|
|
|
786
785
|
const outerState = createState({ title: "Outer", visible: true });
|
|
@@ -831,7 +830,7 @@ export default {
|
|
|
831
830
|
]);
|
|
832
831
|
|
|
833
832
|
// initial state
|
|
834
|
-
expect(container).toMatch(
|
|
833
|
+
await expect(container).toMatch(
|
|
835
834
|
[DIV,
|
|
836
835
|
[H1, "Outer"],
|
|
837
836
|
[P, "Outer content"],
|
|
@@ -847,7 +846,7 @@ export default {
|
|
|
847
846
|
// patch inner state independently: inner updates, outer unchanged
|
|
848
847
|
innerState.patch({ counter: 7 });
|
|
849
848
|
|
|
850
|
-
expect(container).toMatch(
|
|
849
|
+
await expect(container).toMatch(
|
|
851
850
|
[DIV,
|
|
852
851
|
[H1, "Outer"],
|
|
853
852
|
[P, "Outer content"],
|
|
@@ -864,10 +863,10 @@ export default {
|
|
|
864
863
|
// so the inner counter stays at 7 (not reset to 0).
|
|
865
864
|
outerState.patch({ title: "Outer Updated" });
|
|
866
865
|
|
|
867
|
-
expect(outerState.title).toEqual("Outer Updated");
|
|
868
|
-
expect(innerState.counter).toEqual(7);
|
|
866
|
+
await expect(outerState.title).toEqual("Outer Updated");
|
|
867
|
+
await expect(innerState.counter).toEqual(7);
|
|
869
868
|
|
|
870
|
-
expect(container).toMatch(
|
|
869
|
+
await expect(container).toMatch(
|
|
871
870
|
[DIV,
|
|
872
871
|
[H1, "Outer Updated"],
|
|
873
872
|
[P, "Outer content"],
|
|
@@ -883,7 +882,7 @@ export default {
|
|
|
883
882
|
// hiding the outer wrapper removes the inner app entirely
|
|
884
883
|
outerState.patch({ visible: false });
|
|
885
884
|
|
|
886
|
-
expect(container).toMatch(
|
|
885
|
+
await expect(container).toMatch(
|
|
887
886
|
[DIV,
|
|
888
887
|
[H1, "Outer Updated"],
|
|
889
888
|
[P, "Outer content"],
|
|
@@ -892,7 +891,7 @@ export default {
|
|
|
892
891
|
);
|
|
893
892
|
},
|
|
894
893
|
|
|
895
|
-
"Example 11: Error Boundary - isolated component crash with catch recovery": () => {
|
|
894
|
+
"Example 11: Error Boundary - isolated component crash with catch recovery": async () => {
|
|
896
895
|
const container = setup();
|
|
897
896
|
const state = createState({
|
|
898
897
|
users: [
|
|
@@ -922,7 +921,7 @@ export default {
|
|
|
922
921
|
]
|
|
923
922
|
);
|
|
924
923
|
|
|
925
|
-
expect(container).toMatch(
|
|
924
|
+
await expect(container).toMatch(
|
|
926
925
|
[DIV,
|
|
927
926
|
[H1, "User List"],
|
|
928
927
|
[SECTION, [P, "Alice"]],
|
|
@@ -933,7 +932,7 @@ export default {
|
|
|
933
932
|
|
|
934
933
|
state.patch({ corruptId: 1 });
|
|
935
934
|
|
|
936
|
-
expect(container).toMatch(
|
|
935
|
+
await expect(container).toMatch(
|
|
937
936
|
[DIV,
|
|
938
937
|
[H1, "User List"],
|
|
939
938
|
[P, { class: "error" }, "⚠ Failed to load Alice"],
|
|
@@ -943,7 +942,7 @@ export default {
|
|
|
943
942
|
);
|
|
944
943
|
},
|
|
945
944
|
|
|
946
|
-
"Example 12: State Machine - sequential phase transitions via function patches": () => {
|
|
945
|
+
"Example 12: State Machine - sequential phase transitions via function patches": async () => {
|
|
947
946
|
const container = setup();
|
|
948
947
|
const state = createState({ phase: "idle", count: 0 });
|
|
949
948
|
type State = typeof state;
|
|
@@ -956,8 +955,8 @@ export default {
|
|
|
956
955
|
);
|
|
957
956
|
|
|
958
957
|
state.patch((s) => ({ phase: "running", count: 1 }));
|
|
959
|
-
expect(state.phase).toEqual("running");
|
|
960
|
-
expect(state.count).toEqual(1);
|
|
958
|
+
await expect(state.phase).toEqual("running");
|
|
959
|
+
await expect(state.count).toEqual(1);
|
|
961
960
|
|
|
962
961
|
function step(s: State) {
|
|
963
962
|
const next = s.count < 5
|
|
@@ -967,26 +966,26 @@ export default {
|
|
|
967
966
|
}
|
|
968
967
|
state.patch(step);
|
|
969
968
|
|
|
970
|
-
expect(state.count).toEqual(2);
|
|
971
|
-
|
|
969
|
+
await expect(state.count).toEqual(2);
|
|
970
|
+
|
|
972
971
|
state.patch(step);
|
|
973
|
-
|
|
974
|
-
expect(container).toMatch(
|
|
972
|
+
|
|
973
|
+
await expect(container).toMatch(
|
|
975
974
|
[DIV,
|
|
976
975
|
[P, "Phase: running"],
|
|
977
976
|
[P, "Count: 3"],
|
|
978
977
|
]
|
|
979
978
|
);
|
|
980
|
-
|
|
979
|
+
|
|
981
980
|
state.patch(step);
|
|
982
981
|
state.patch(step);
|
|
983
|
-
|
|
984
|
-
expect(state.count).toEqual(5);
|
|
985
|
-
expect(state.phase).toEqual("running");
|
|
986
|
-
|
|
982
|
+
|
|
983
|
+
await expect(state.count).toEqual(5);
|
|
984
|
+
await expect(state.phase).toEqual("running");
|
|
985
|
+
|
|
987
986
|
state.patch(step);
|
|
988
|
-
|
|
989
|
-
expect(state.count).toEqual(5);
|
|
990
|
-
expect(state.phase).toEqual("done", "reached done phase");
|
|
987
|
+
|
|
988
|
+
await expect(state.count).toEqual(5);
|
|
989
|
+
await expect(state.phase).toEqual("done", "reached done phase");
|
|
991
990
|
},
|
|
992
991
|
};
|
package/test/tests-hydrate.ts
CHANGED
|
@@ -3,100 +3,100 @@ import { hydrate, DIV, SPAN, P } from "../index";
|
|
|
3
3
|
import { FakeElement, FakeTextNode } from "./mocks";
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
|
-
"hydrate(): text node returns its text content": () => {
|
|
6
|
+
"hydrate(): text node returns its text content": async () => {
|
|
7
7
|
const text = new FakeTextNode("hello world");
|
|
8
8
|
|
|
9
|
-
expect(hydrate(text as any))
|
|
9
|
+
await expect(hydrate(text as any))
|
|
10
10
|
.toMatch("hello world");
|
|
11
11
|
},
|
|
12
12
|
|
|
13
|
-
"hydrate(): empty element returns a vode": () => {
|
|
13
|
+
"hydrate(): empty element returns a vode": async () => {
|
|
14
14
|
const el = new FakeElement("div");
|
|
15
15
|
const result = hydrate(el as any);
|
|
16
16
|
|
|
17
|
-
expect(result)
|
|
17
|
+
await expect(result)
|
|
18
18
|
.toMatch([DIV]);
|
|
19
19
|
},
|
|
20
20
|
|
|
21
|
-
"hydrate(): element with children returns full vode tree": () => {
|
|
21
|
+
"hydrate(): element with children returns full vode tree": async () => {
|
|
22
22
|
const parent = new FakeElement("div");
|
|
23
23
|
const child = new FakeElement("span");
|
|
24
24
|
parent.appendChild(child);
|
|
25
25
|
|
|
26
|
-
expect(hydrate(parent as any))
|
|
26
|
+
await expect(hydrate(parent as any))
|
|
27
27
|
.toMatch([DIV, [SPAN]]);
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
-
"hydrate(): element with text child": () => {
|
|
30
|
+
"hydrate(): element with text child": async () => {
|
|
31
31
|
const parent = new FakeElement("p");
|
|
32
32
|
const text = new FakeTextNode("hello");
|
|
33
33
|
parent.appendChild(text);
|
|
34
34
|
|
|
35
|
-
expect(hydrate(parent as any))
|
|
35
|
+
await expect(hydrate(parent as any))
|
|
36
36
|
.toMatch([P, "hello"]);
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
-
"hydrate(): element with attributes reads them into props": () => {
|
|
39
|
+
"hydrate(): element with attributes reads them into props": async () => {
|
|
40
40
|
const el = new FakeElement("div");
|
|
41
41
|
el.setAttribute("class", "foo");
|
|
42
42
|
el.setAttribute("id", "bar");
|
|
43
43
|
|
|
44
|
-
expect(hydrate(el as any))
|
|
44
|
+
await expect(hydrate(el as any))
|
|
45
45
|
.toMatch([DIV, { class: "foo", id: "bar" }]);
|
|
46
46
|
},
|
|
47
47
|
|
|
48
|
-
"hydrate(): unknown node type returns undefined": () => {
|
|
48
|
+
"hydrate(): unknown node type returns undefined": async () => {
|
|
49
49
|
const frag = { nodeType: 999 } as any;
|
|
50
50
|
|
|
51
|
-
expect(hydrate(frag))
|
|
51
|
+
await expect(hydrate(frag))
|
|
52
52
|
.toEqual(undefined);
|
|
53
53
|
},
|
|
54
54
|
|
|
55
|
-
"hydrate(): empty text node returns undefined": () => {
|
|
55
|
+
"hydrate(): empty text node returns undefined": async () => {
|
|
56
56
|
const text = new FakeTextNode(" ");
|
|
57
57
|
|
|
58
|
-
expect(hydrate(text as any))
|
|
58
|
+
await expect(hydrate(text as any))
|
|
59
59
|
.toEqual(undefined);
|
|
60
60
|
},
|
|
61
61
|
|
|
62
|
-
"hydrate(): only element and text nodes are supported": () => {
|
|
62
|
+
"hydrate(): only element and text nodes are supported": async () => {
|
|
63
63
|
const comment = { nodeType: Node.COMMENT_NODE } as any;
|
|
64
64
|
|
|
65
|
-
expect(hydrate(comment))
|
|
65
|
+
await expect(hydrate(comment))
|
|
66
66
|
.toEqual(undefined);
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
-
"hydrate(): prepareForRender returns text node for text input": () => {
|
|
69
|
+
"hydrate(): prepareForRender returns text node for text input": async () => {
|
|
70
70
|
const text = new FakeTextNode("hello");
|
|
71
71
|
|
|
72
72
|
const result = hydrate(text as any, true);
|
|
73
73
|
|
|
74
|
-
expect(result instanceof FakeTextNode).toEqual(true);
|
|
75
|
-
expect((result as any).nodeValue).toEqual("hello");
|
|
74
|
+
await expect(result instanceof FakeTextNode).toEqual(true);
|
|
75
|
+
await expect((result as any).nodeValue).toEqual("hello");
|
|
76
76
|
},
|
|
77
77
|
|
|
78
|
-
"hydrate(): prepareForRender attaches .node to element vode": () => {
|
|
78
|
+
"hydrate(): prepareForRender attaches .node to element vode": async () => {
|
|
79
79
|
const el = new FakeElement("div");
|
|
80
80
|
|
|
81
81
|
const result = hydrate(el as any, true) as any;
|
|
82
82
|
|
|
83
|
-
expect(Array.isArray(result)).toEqual(true);
|
|
84
|
-
expect(result[0]).toEqual("div");
|
|
85
|
-
expect(result.node instanceof FakeElement).toEqual(true);
|
|
86
|
-
expect(result.node.tagName).toEqual("DIV");
|
|
83
|
+
await expect(Array.isArray(result)).toEqual(true);
|
|
84
|
+
await expect(result[0]).toEqual("div");
|
|
85
|
+
await expect(result.node instanceof FakeElement).toEqual(true);
|
|
86
|
+
await expect(result.node.tagName).toEqual("DIV");
|
|
87
87
|
},
|
|
88
88
|
|
|
89
|
-
"hydrate(): prepareForRender removes whitespace text nodes": () => {
|
|
89
|
+
"hydrate(): prepareForRender removes whitespace text nodes": async () => {
|
|
90
90
|
const el = new FakeElement("div");
|
|
91
91
|
el.appendChild(new FakeTextNode(" "));
|
|
92
92
|
el.appendChild(new FakeElement("span"));
|
|
93
93
|
el.appendChild(new FakeTextNode(" "));
|
|
94
94
|
|
|
95
|
-
expect(el.childNodes.length).toEqual(3);
|
|
95
|
+
await expect(el.childNodes.length).toEqual(3);
|
|
96
96
|
|
|
97
97
|
const result = hydrate(el as any, true);
|
|
98
98
|
|
|
99
|
-
expect(el.childNodes.length).toEqual(1);
|
|
100
|
-
expect((el.childNodes[0] as any).tagName).toEqual("SPAN");
|
|
99
|
+
await expect(el.childNodes.length).toEqual(1);
|
|
100
|
+
await expect((el.childNodes[0] as any).tagName).toEqual("SPAN");
|
|
101
101
|
},
|
|
102
102
|
};
|