@ryupold/vode 1.8.4 → 1.8.5
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 +13 -0
- package/.github/workflows/tests.yml +17 -0
- package/dist/vode.cjs.min.js +2 -2
- package/dist/vode.es5.min.js +4 -4
- package/dist/vode.js +28 -8
- package/dist/vode.min.js +1 -1
- package/dist/vode.min.mjs +1 -1
- package/dist/vode.mjs +28 -8
- package/package.json +4 -3
- package/src/merge-style.ts +4 -1
- package/src/vode.ts +27 -8
- package/test/helper.ts +168 -0
- package/test/index.ts +80 -0
- package/test/mocks.ts +111 -0
- package/test/tests-app.ts +226 -0
- package/test/tests-children.ts +69 -0
- package/test/tests-createPatch.ts +28 -0
- package/test/tests-createState.ts +43 -0
- package/test/tests-defuse.ts +74 -0
- package/test/tests-hydrate.ts +68 -0
- package/test/tests-memo.ts +119 -0
- package/test/tests-mergeClass.ts +63 -0
- package/test/tests-mergeProps.ts +43 -0
- package/test/tests-mergeStyle.ts +39 -0
- package/test/tests-props.ts +34 -0
- package/test/tests-state-context.ts +106 -0
- package/test/tests-tag.ts +33 -0
- package/test/tests-vode.ts +27 -0
- package/tsconfig.test.json +18 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mergeProps, Props } from "../index";
|
|
2
|
+
import { expect } from "./helper";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
"mergeProps(): no args returns undefined": () => {
|
|
6
|
+
expect(mergeProps()).toEqual(undefined);
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
"mergeProps(): single arg returned as-is": () => {
|
|
10
|
+
const p = { class: "foo" };
|
|
11
|
+
expect(mergeProps(p) === p).toEqual(true);
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"mergeProps(): two plain objects merged": () => {
|
|
15
|
+
expect(mergeProps({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"mergeProps(): right overwrites left for simple keys": () => {
|
|
19
|
+
expect(mergeProps({ a: 1, b: "x" }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"mergeProps(): class merged via mergeClass": () => {
|
|
23
|
+
expect(mergeProps({ class: "foo" }, { class: "bar" })).toEqual({ class: "foo bar" });
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"mergeProps(): style merged via mergeStyle (strings)": () => {
|
|
27
|
+
const result = mergeProps({ style: "color: red" }, { style: "font-size: 14px" }) as Props;
|
|
28
|
+
expect((<string>result.style!).includes("color: red")).toEqual(true);
|
|
29
|
+
expect((<string>result.style!).includes("font-size: 14px")).toEqual(true);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
"mergeProps(): null and undefined entries skipped": () => {
|
|
33
|
+
expect(mergeProps({ a: 1 }, null, { b: 2 }, undefined)).toEqual({ a: 1, b: 2 });
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
"mergeProps(): multiple args (3+)": () => {
|
|
37
|
+
expect(mergeProps({ a: 1 }, { b: 2 }, { c: 3 })).toEqual({ a: 1, b: 2, c: 3 });
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
"mergeProps(): first arg null returns defined from later args": () => {
|
|
41
|
+
expect(mergeProps(null, { a: 1 })).toEqual({ a: 1 });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { mergeStyle } from "../index";
|
|
2
|
+
import { expect } from "./helper";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
"mergeStyle(): no args returns empty string": () => {
|
|
6
|
+
expect(mergeStyle()).toEqual("");
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
"mergeStyle(): object style sets properties, returns cssText": () => {
|
|
10
|
+
const result = mergeStyle({ color: "red", fontSize: "14px" });
|
|
11
|
+
expect(typeof result).toEqual("string");
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"mergeStyle(): single string starts with semicolon": () => {
|
|
15
|
+
expect(mergeStyle("color: red")).toEqual(";color: red");
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"mergeStyle(): two strings are concatenated": () => {
|
|
19
|
+
expect(mergeStyle("color: red", "font-size: 14px")).toEqual(";color: red;font-size: 14px");
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"mergeStyle(): object then string": () => {
|
|
23
|
+
const result = mergeStyle({ color: "red" }, "font-size: 14px") as string;
|
|
24
|
+
expect(result.indexOf("font-size: 14px") > 0).toEqual(true);
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"mergeStyle(): null and undefined entries are skipped": () => {
|
|
28
|
+
expect(mergeStyle(null, "color: red", undefined)).toEqual(";color: red");
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"mergeStyle(): multiple objects and strings alternate": () => {
|
|
32
|
+
const result = mergeStyle(
|
|
33
|
+
{ color: "red" },
|
|
34
|
+
"font-size: 14px",
|
|
35
|
+
{ background: "blue" }
|
|
36
|
+
) as string;
|
|
37
|
+
expect(result.includes("font-size: 14px")).toEqual(true);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { props, DIV, SPAN } from "../index";
|
|
2
|
+
import { expect } from "./helper";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
"props(): on vode with props returns props object": () => {
|
|
6
|
+
expect(props([DIV, { class: "foo" }, "hello"]))
|
|
7
|
+
.toEqual({ class: "foo" });
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
"props(): on just-tag vode returns undefined": () => {
|
|
11
|
+
expect(props([DIV])).toEqual(undefined);
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"props(): on text vode returns undefined": () => {
|
|
15
|
+
expect(props("hello")).toEqual(undefined);
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"props(): on vode where second element is an array (child) returns undefined": () => {
|
|
19
|
+
expect(props([DIV, [SPAN]] as any)).toEqual(undefined);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"props(): on vode where second element is null returns undefined": () => {
|
|
23
|
+
expect(props([DIV, null as any, "hi"])).toEqual(undefined);
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"props(): on falsy input returns undefined": () => {
|
|
27
|
+
expect(props(null as any)).toEqual(undefined);
|
|
28
|
+
expect(props(undefined as any)).toEqual(undefined);
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"props(): on vode with length 1 returns undefined": () => {
|
|
32
|
+
expect(props([DIV] as any)).toEqual(undefined);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { expect } from "./helper";
|
|
2
|
+
import { context } from "../src/state-context";
|
|
3
|
+
import { createState } from "../src/vode";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
"StateContext.state: returns the state reference": () => {
|
|
7
|
+
const state = createState({ x: 10 });
|
|
8
|
+
const ctx = context(state);
|
|
9
|
+
|
|
10
|
+
expect((ctx).state === state)
|
|
11
|
+
.toEqual(true);
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"StateContext.get() returns whole state": () => {
|
|
15
|
+
const state = createState({ a: 1, b: 2 });
|
|
16
|
+
const ctx = context(state);
|
|
17
|
+
|
|
18
|
+
expect(ctx.get())
|
|
19
|
+
.toEqual({ a: 1, b: 2 });
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
"StateContext.get(): deep nested": () => {
|
|
24
|
+
const state = createState({ a: { b: { c: 42 } } });
|
|
25
|
+
const ctx = context(state);
|
|
26
|
+
|
|
27
|
+
expect(ctx.a.b.c.get())
|
|
28
|
+
.toEqual(42);
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"StateContext.get(): missing nested path returns undefined": () => {
|
|
32
|
+
const state = createState({ a: {} });
|
|
33
|
+
const ctx = context(state);
|
|
34
|
+
|
|
35
|
+
expect((ctx.a as any).b.get())
|
|
36
|
+
.toEqual(undefined);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
"StateContext.put(): silently mutates state": () => {
|
|
40
|
+
const state = createState({ a: { b: 1 } });
|
|
41
|
+
const ctx = context(state);
|
|
42
|
+
ctx.a.b.put(2);
|
|
43
|
+
|
|
44
|
+
expect(state.a.b)
|
|
45
|
+
.toEqual(2);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
"StateContext.put() on nested object replaces the sub-object": () => {
|
|
49
|
+
const state = createState({ a: { b: { x: 1, y: 2 } } });
|
|
50
|
+
const ctx = context(state);
|
|
51
|
+
ctx.a.b.put({ y: 99 });
|
|
52
|
+
|
|
53
|
+
expect(state.a.b)
|
|
54
|
+
.toEqual({ y: 99 });
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
"StateContext.put() at root level with empty keys": () => {
|
|
58
|
+
const state = createState({ a: 1, b: 2 });
|
|
59
|
+
const ctx = context(state);
|
|
60
|
+
ctx.put({ b: undefined });
|
|
61
|
+
|
|
62
|
+
expect(state)
|
|
63
|
+
.toEqual({ a: 1 });
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
"StateContext.patch(): calls state.patch with proper deep partial": () => {
|
|
67
|
+
const state = createState({ a: { b: 1 } });
|
|
68
|
+
const ctx = context(state);
|
|
69
|
+
ctx.a.b.patch(2);
|
|
70
|
+
|
|
71
|
+
const patches = (state as any).patch.initialPatches;
|
|
72
|
+
|
|
73
|
+
expect(patches.length)
|
|
74
|
+
.toEqual(1);
|
|
75
|
+
expect(patches[0])
|
|
76
|
+
.toEqual({ a: { b: 2 } });
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
"StateContext.patch(): async wraps in array": () => {
|
|
80
|
+
const state = createState({ a: { b: 1 } });
|
|
81
|
+
const ctx = context(state);
|
|
82
|
+
ctx.a.b.patch(2, true);
|
|
83
|
+
|
|
84
|
+
const patches = (state as any).patch.initialPatches;
|
|
85
|
+
|
|
86
|
+
expect(patches.length)
|
|
87
|
+
.toEqual(1);
|
|
88
|
+
expect(Array.isArray(patches[0]))
|
|
89
|
+
.toEqual(true);
|
|
90
|
+
expect(patches[0][0])
|
|
91
|
+
.toEqual({ a: { b: 2 } });
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
"StateContext.patch() on nested deep path three levels": () => {
|
|
95
|
+
const state = createState({ x: { y: { z: 0 } } });
|
|
96
|
+
const ctx = context(state);
|
|
97
|
+
ctx.x.y.z.patch(100);
|
|
98
|
+
|
|
99
|
+
const patches = (state as any).patch.initialPatches;
|
|
100
|
+
|
|
101
|
+
expect(patches.length)
|
|
102
|
+
.toEqual(1);
|
|
103
|
+
expect(patches[0])
|
|
104
|
+
.toEqual({ x: { y: { z: 100 } } });
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { tag, DIV, SPAN } from "../index";
|
|
2
|
+
import { expect } from "./helper";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
"tag(): on a vode returns the tag name": () => {
|
|
6
|
+
expect(tag([DIV]))
|
|
7
|
+
.toEqual("div");
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
"tag(): on a vode with props and children": () => {
|
|
11
|
+
expect(tag([DIV, { class: "foo" }, [SPAN, "hi"]]))
|
|
12
|
+
.toEqual("div");
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
"tag(): on a text vode (string) returns #text": () => {
|
|
16
|
+
expect(tag("hello"))
|
|
17
|
+
.toEqual("#text");
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"tag(): on falsy values returns undefined": () => {
|
|
21
|
+
expect(tag(null)).toEqual(undefined);
|
|
22
|
+
expect(tag(undefined)).toEqual(undefined);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"tag(): on no-vode values returns undefined": () => {
|
|
26
|
+
expect(tag(0)).toEqual(undefined);
|
|
27
|
+
expect(tag(true)).toEqual(undefined);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
"tag(): on empty array returns undefined": () => {
|
|
31
|
+
expect(tag([])).toEqual(undefined);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { vode, DIV, Vode, SPAN, STRONG } from "../index";
|
|
2
|
+
import { expect } from "./helper";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
"vode(): passing an already constructed vode returns it": () => {
|
|
6
|
+
const testVode: Vode = [DIV, { class: 'test' }, "hello world"];
|
|
7
|
+
|
|
8
|
+
expect(vode(testVode)).toEqual(testVode);
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
"vode(): constructing a vode from parts": () => {
|
|
12
|
+
expect(
|
|
13
|
+
vode(DIV, { class: 'test' },
|
|
14
|
+
[SPAN, "hello"],
|
|
15
|
+
[STRONG, { style: 'color: green' }, "world"])
|
|
16
|
+
).toEqual(
|
|
17
|
+
[DIV, { class: 'test' },
|
|
18
|
+
[SPAN, "hello"],
|
|
19
|
+
[STRONG, { style: 'color: green' }, "world"]]
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
"vode(): passing an invalid tag fails": () => {
|
|
24
|
+
const err = expect(() => vode(null as any)).toFail();
|
|
25
|
+
expect(err.message).toEqual("first argument to vode() must be a tag name or a vode");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "test/",
|
|
5
|
+
"declaration": false,
|
|
6
|
+
"declarationMap": false,
|
|
7
|
+
"sourceMap": false,
|
|
8
|
+
"declarationDir": null,
|
|
9
|
+
"composite": false,
|
|
10
|
+
"noEmit": true
|
|
11
|
+
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"index.ts",
|
|
15
|
+
"src/**/*",
|
|
16
|
+
"test/**/*"
|
|
17
|
+
]
|
|
18
|
+
}
|