@ryupold/vode 1.8.7 → 1.8.10
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/README.md +34 -56
- package/dist/vode.cjs.min.js +2 -2
- package/dist/vode.d.ts +10 -10
- package/dist/vode.es5.min.js +7 -7
- package/dist/vode.js +97 -113
- package/dist/vode.min.js +1 -1
- package/dist/vode.min.mjs +1 -1
- package/dist/vode.mjs +97 -113
- package/dist/vode.tests.mjs +5303 -0
- package/package.json +5 -5
- package/src/state-context.ts +6 -4
- package/src/vode.ts +114 -126
- package/test/helper.ts +304 -113
- package/test/index.ts +10 -47
- package/test/mocks.ts +199 -43
- package/test/run-tests.ts +61 -0
- package/test/tests-app.ts +154 -38
- package/test/tests-catch.ts +160 -0
- 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 +35 -14
- package/test/tests-examples.ts +991 -0
- package/test/tests-hydrate.ts +59 -25
- package/test/tests-memo.ts +106 -64
- 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 +177 -154
- package/test/tests-patch-advanced.ts +86 -0
- package/test/tests-patch-merge.ts +66 -0
- package/test/tests-props.ts +15 -15
- package/test/tests-state-context.ts +56 -25
- package/test/tests-tag.ts +14 -14
- package/test/tests-vode.ts +6 -6
package/test/tests-hydrate.ts
CHANGED
|
@@ -1,68 +1,102 @@
|
|
|
1
1
|
import { expect } from "./helper";
|
|
2
2
|
import { hydrate, DIV, SPAN, P } from "../index";
|
|
3
|
-
import {
|
|
3
|
+
import { FakeElement, FakeTextNode } from "./mocks";
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
|
-
"hydrate(): text node returns its text content": () => {
|
|
7
|
-
const text = new
|
|
6
|
+
"hydrate(): text node returns its text content": async () => {
|
|
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": () => {
|
|
14
|
-
const el = new
|
|
13
|
+
"hydrate(): empty element returns a vode": async () => {
|
|
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": () => {
|
|
22
|
-
const parent = new
|
|
23
|
-
const child = new
|
|
21
|
+
"hydrate(): element with children returns full vode tree": async () => {
|
|
22
|
+
const parent = new FakeElement("div");
|
|
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": () => {
|
|
31
|
-
const parent = new
|
|
32
|
-
const text = new
|
|
30
|
+
"hydrate(): element with text child": async () => {
|
|
31
|
+
const parent = new FakeElement("p");
|
|
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": () => {
|
|
40
|
-
const el = new
|
|
39
|
+
"hydrate(): element with attributes reads them into props": async () => {
|
|
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": () => {
|
|
56
|
-
const text = new
|
|
55
|
+
"hydrate(): empty text node returns undefined": async () => {
|
|
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
|
+
|
|
69
|
+
"hydrate(): prepareForRender returns text node for text input": async () => {
|
|
70
|
+
const text = new FakeTextNode("hello");
|
|
71
|
+
|
|
72
|
+
const result = hydrate(text as any, true);
|
|
73
|
+
|
|
74
|
+
await expect(result instanceof FakeTextNode).toEqual(true);
|
|
75
|
+
await expect((result as any).nodeValue).toEqual("hello");
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
"hydrate(): prepareForRender attaches .node to element vode": async () => {
|
|
79
|
+
const el = new FakeElement("div");
|
|
80
|
+
|
|
81
|
+
const result = hydrate(el as any, true) as any;
|
|
82
|
+
|
|
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
|
+
},
|
|
88
|
+
|
|
89
|
+
"hydrate(): prepareForRender removes whitespace text nodes": async () => {
|
|
90
|
+
const el = new FakeElement("div");
|
|
91
|
+
el.appendChild(new FakeTextNode(" "));
|
|
92
|
+
el.appendChild(new FakeElement("span"));
|
|
93
|
+
el.appendChild(new FakeTextNode(" "));
|
|
94
|
+
|
|
95
|
+
await expect(el.childNodes.length).toEqual(3);
|
|
96
|
+
|
|
97
|
+
const result = hydrate(el as any, true);
|
|
98
|
+
|
|
99
|
+
await expect(el.childNodes.length).toEqual(1);
|
|
100
|
+
await expect((el.childNodes[0] as any).tagName).toEqual("SPAN");
|
|
101
|
+
},
|
|
68
102
|
};
|
package/test/tests-memo.ts
CHANGED
|
@@ -1,50 +1,45 @@
|
|
|
1
1
|
import { expect } from "./helper";
|
|
2
|
-
import { memo, DIV, app, createState, SPAN } from "../index";
|
|
2
|
+
import { memo, DIV, app, createState, SPAN, H1, BR, P, UL, LI, Component } from "../index";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
|
-
"memo():
|
|
6
|
-
const fn = (s: any) => [DIV];
|
|
7
|
-
const result = memo([1, 2], fn);
|
|
8
|
-
expect(result === fn).toEqual(true);
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
"memo(): throws when compare is not an array": () => {
|
|
5
|
+
"memo(): throws when compare is not an array": async () => {
|
|
12
6
|
const err = expect(() => memo(null as any, (s: any) => [DIV]))
|
|
13
7
|
.toFail();
|
|
14
|
-
expect(err.message)
|
|
8
|
+
await expect(err.message)
|
|
15
9
|
.toEqual("first argument to memo() must be an array of values to compare");
|
|
16
10
|
},
|
|
17
11
|
|
|
18
|
-
"memo(): throws when componentOrProps is not a function": () => {
|
|
12
|
+
"memo(): throws when componentOrProps is not a function": async () => {
|
|
19
13
|
const err = expect(() => memo([1], null as any))
|
|
20
14
|
.toFail();
|
|
21
|
-
expect(err.message)
|
|
22
|
-
.toEqual("second argument to memo() must be a function that returns a vode
|
|
15
|
+
await expect(err.message)
|
|
16
|
+
.toEqual("second argument to memo() must be a function that returns a child vode");
|
|
23
17
|
},
|
|
24
18
|
|
|
25
|
-
"memo(): integration with app prevents re-render when deps match": () => {
|
|
19
|
+
"memo(): integration with app prevents re-render when deps match": async () => {
|
|
26
20
|
const state = createState({ count: 12 });
|
|
27
21
|
const root = document.createElement("div");
|
|
28
22
|
const container = document.createElement("div");
|
|
29
23
|
root.appendChild(container);
|
|
30
24
|
|
|
31
|
-
let callCount = 0;
|
|
25
|
+
let box: { callCount: number } = { callCount: 0 };
|
|
32
26
|
app<typeof state>(container, state, (s) => [DIV, memo(
|
|
33
27
|
[s.count],
|
|
34
28
|
(s) => {
|
|
35
|
-
callCount++;
|
|
29
|
+
box.callCount++;
|
|
36
30
|
return [DIV, [SPAN, `${s.count}`]];
|
|
37
31
|
}
|
|
38
32
|
)]);
|
|
39
33
|
|
|
40
34
|
|
|
41
|
-
expect(
|
|
35
|
+
await expect(box).toEqual({ callCount: 1 });
|
|
42
36
|
|
|
43
37
|
state.patch({ count: 12 }); //same value, should not re-render
|
|
44
|
-
expect(
|
|
38
|
+
await expect(box).toEqual({ callCount: 1 });
|
|
45
39
|
state.patch({ count: 13 }); //different value, should re-render
|
|
46
|
-
|
|
47
|
-
expect(
|
|
40
|
+
|
|
41
|
+
await expect(box).toEqual({ callCount: 2 });
|
|
42
|
+
await expect(container).toMatch(
|
|
48
43
|
[DIV,
|
|
49
44
|
[DIV,
|
|
50
45
|
[SPAN, "13"]
|
|
@@ -53,49 +48,7 @@ export default {
|
|
|
53
48
|
);
|
|
54
49
|
},
|
|
55
50
|
|
|
56
|
-
"memo():
|
|
57
|
-
const state = createState({ count: 12, prefix: "Count is: " });
|
|
58
|
-
const root = document.createElement("div");
|
|
59
|
-
const container = document.createElement("div");
|
|
60
|
-
root.appendChild(container);
|
|
61
|
-
|
|
62
|
-
let callCount = 0;
|
|
63
|
-
app<typeof state>(container, state, (s) => [DIV,
|
|
64
|
-
[DIV,
|
|
65
|
-
memo(
|
|
66
|
-
[s.count],
|
|
67
|
-
(s) => {
|
|
68
|
-
callCount++;
|
|
69
|
-
return {
|
|
70
|
-
class: {
|
|
71
|
-
low: s.count < 10,
|
|
72
|
-
high: s.count >= 10,
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
),
|
|
77
|
-
[SPAN, `${s.prefix}${s.count}`]
|
|
78
|
-
],
|
|
79
|
-
]);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
expect(callCount).toEqual(1);
|
|
83
|
-
state.patch({ count: 12 });
|
|
84
|
-
expect(callCount).toEqual(1); // unchanged count should not cause re-render
|
|
85
|
-
state.patch({ count: 13 });
|
|
86
|
-
expect(callCount).toEqual(2);
|
|
87
|
-
state.patch({ prefix: "count: " });
|
|
88
|
-
expect(callCount).toEqual(3);
|
|
89
|
-
expect(container).toMatch(
|
|
90
|
-
[DIV,
|
|
91
|
-
[DIV, { class: { low: false, high: true } },
|
|
92
|
-
[SPAN, "count: 13"]
|
|
93
|
-
]
|
|
94
|
-
]
|
|
95
|
-
);
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
"memo(): can be a nested component function": () => {
|
|
51
|
+
"memo(): can be used with a nested component function": async () => {
|
|
99
52
|
const state = createState({ count: 12 });
|
|
100
53
|
const root = document.createElement("div");
|
|
101
54
|
const container = document.createElement("div");
|
|
@@ -112,8 +65,97 @@ export default {
|
|
|
112
65
|
)]);
|
|
113
66
|
|
|
114
67
|
|
|
115
|
-
expect(callCount).toEqual(1);
|
|
68
|
+
await expect(callCount).toEqual(1);
|
|
116
69
|
state.patch({ count: 12 }); //same value, should not re-render
|
|
117
|
-
expect(callCount).toEqual(1);
|
|
70
|
+
await expect(callCount).toEqual(1);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
"memo(): can be used with the same component function": async () => {
|
|
74
|
+
const state = createState({ test: "foo" });
|
|
75
|
+
const root = document.createElement("div");
|
|
76
|
+
const container = document.createElement("div");
|
|
77
|
+
root.appendChild(container);
|
|
78
|
+
|
|
79
|
+
let box: { callCount: number } = { callCount: 0 };
|
|
80
|
+
const Comp: Component<typeof state> = (s) => {
|
|
81
|
+
box.callCount++;
|
|
82
|
+
return [DIV, [SPAN, s.test]];
|
|
83
|
+
};
|
|
84
|
+
app<typeof state>(container, state, (s) => [DIV,
|
|
85
|
+
memo(
|
|
86
|
+
[s.test],
|
|
87
|
+
Comp,
|
|
88
|
+
),
|
|
89
|
+
memo(
|
|
90
|
+
[s.test],
|
|
91
|
+
Comp,
|
|
92
|
+
),
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
await expect(box).toEqual({ callCount: 2 }, "Each memo should call the component function once on initial render, even if they are the same function");
|
|
97
|
+
state.patch({ test: "foo" });
|
|
98
|
+
await expect(box).toEqual({ callCount: 2 }, "Patching with the same value should not cause a re-render");
|
|
99
|
+
state.patch({ test: "bar" });
|
|
100
|
+
await expect(box).toEqual({ callCount: 4 }, "Patching with a different value should cause both memos to re-render, even if they use the same component function");
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
"memo(): memo with many item list": () => {
|
|
104
|
+
const root = document.createElement("div");
|
|
105
|
+
const container = document.createElement("div");
|
|
106
|
+
root.appendChild(container);
|
|
107
|
+
|
|
108
|
+
const state = createState({ title: "hello", body: "world" });
|
|
109
|
+
type State = typeof state;
|
|
110
|
+
|
|
111
|
+
const CompMemoList: Component<State> = (s) =>
|
|
112
|
+
[DIV, { class: "container" },
|
|
113
|
+
[H1, "Hello World"],
|
|
114
|
+
[BR],
|
|
115
|
+
[P, "This is a paragraph."],
|
|
116
|
+
memo(
|
|
117
|
+
[s.title, s.body],
|
|
118
|
+
(s) => {
|
|
119
|
+
const list = [UL];
|
|
120
|
+
for (let i = 0; i < 10000; i++) {
|
|
121
|
+
list.push(LI, `Item ${i}`);
|
|
122
|
+
}
|
|
123
|
+
return list;
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
app<State>(container, state, (s) => [DIV,
|
|
129
|
+
CompMemoList,
|
|
130
|
+
]);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
"memo(): double-wrapping ignores the inner memo dependencies, only the outer memo is checked": async () => {
|
|
134
|
+
const state = createState({ outer: 1, inner: 1 });
|
|
135
|
+
const root = document.createElement("div");
|
|
136
|
+
const container = document.createElement("div");
|
|
137
|
+
root.appendChild(container);
|
|
138
|
+
|
|
139
|
+
let box: { callCount: number } = { callCount: 0 };
|
|
140
|
+
const comp = (s: typeof state) => {
|
|
141
|
+
box.callCount++;
|
|
142
|
+
return [DIV, `${s.outer}`];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const memoed = (s: typeof state) => memo([s.inner], comp);
|
|
146
|
+
const doubleMemoed = (s: typeof state) => memo([s.outer], memoed);
|
|
147
|
+
|
|
148
|
+
expect(() => app(container, state, () => [DIV, doubleMemoed]))
|
|
149
|
+
.toSucceed();
|
|
150
|
+
|
|
151
|
+
await expect(box).toEqual({ callCount: 1 });
|
|
152
|
+
await expect(container).toMatch([DIV, [DIV, "1"]]);
|
|
153
|
+
|
|
154
|
+
state.patch({ outer: 2 });
|
|
155
|
+
await expect(box).toEqual({ callCount: 2 });
|
|
156
|
+
state.patch({ inner: 2 });
|
|
157
|
+
await expect(box).toEqual({ callCount: 2 });
|
|
158
|
+
state.patch({ outer: 3 });
|
|
159
|
+
await expect(box).toEqual({ callCount: 3 });
|
|
118
160
|
},
|
|
119
161
|
};
|
package/test/tests-mergeClass.ts
CHANGED
|
@@ -2,62 +2,62 @@ import { mergeClass } from "../index";
|
|
|
2
2
|
import { expect } from "./helper";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
|
-
"mergeClass(): no args returns null": () => {
|
|
6
|
-
expect(mergeClass()).toEqual(null);
|
|
5
|
+
"mergeClass(): no args returns null": async () => {
|
|
6
|
+
await expect(mergeClass()).toEqual(null);
|
|
7
7
|
},
|
|
8
8
|
|
|
9
|
-
"mergeClass(): single string returns it": () => {
|
|
10
|
-
expect(mergeClass("foo")).toEqual("foo");
|
|
9
|
+
"mergeClass(): single string returns it": async () => {
|
|
10
|
+
await expect(mergeClass("foo")).toEqual("foo");
|
|
11
11
|
},
|
|
12
12
|
|
|
13
|
-
"mergeClass(): two strings are joined and deduplicated": () => {
|
|
14
|
-
expect(mergeClass("foo", "bar")).toEqual("foo bar");
|
|
15
|
-
expect(mergeClass("foo bar", "bar baz")).toEqual("foo bar baz");
|
|
13
|
+
"mergeClass(): two strings are joined and deduplicated": async () => {
|
|
14
|
+
await expect(mergeClass("foo", "bar")).toEqual("foo bar");
|
|
15
|
+
await expect(mergeClass("foo bar", "bar baz")).toEqual("foo bar baz");
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
-
"mergeClass(): string and array": () => {
|
|
19
|
-
expect(mergeClass("foo", ["bar", "baz"])).toEqual("bar baz foo");
|
|
18
|
+
"mergeClass(): string and array": async () => {
|
|
19
|
+
await expect(mergeClass("foo", ["bar", "baz"])).toEqual("bar baz foo");
|
|
20
20
|
},
|
|
21
21
|
|
|
22
|
-
"mergeClass(): array and string": () => {
|
|
23
|
-
expect(mergeClass(["foo", "bar"], "baz")).toEqual("foo bar baz");
|
|
22
|
+
"mergeClass(): array and string": async () => {
|
|
23
|
+
await expect(mergeClass(["foo", "bar"], "baz")).toEqual("foo bar baz");
|
|
24
24
|
},
|
|
25
25
|
|
|
26
|
-
"mergeClass(): two arrays": () => {
|
|
27
|
-
expect(mergeClass(["foo", "bar"], ["baz", "qux"])).toEqual("foo bar baz qux");
|
|
26
|
+
"mergeClass(): two arrays": async () => {
|
|
27
|
+
await expect(mergeClass(["foo", "bar"], ["baz", "qux"])).toEqual("foo bar baz qux");
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
-
"mergeClass(): two string arrays with duplicates": () => {
|
|
31
|
-
expect(mergeClass(["foo", "bar"], ["bar", "baz"])).toEqual("foo bar baz");
|
|
30
|
+
"mergeClass(): two string arrays with duplicates": async () => {
|
|
31
|
+
await expect(mergeClass(["foo", "bar"], ["bar", "baz"])).toEqual("foo bar baz");
|
|
32
32
|
},
|
|
33
33
|
|
|
34
|
-
"mergeClass(): string and object": () => {
|
|
35
|
-
expect(mergeClass("foo", { bar: true, baz: false })).toEqual({ foo: true, bar: true, baz: false });
|
|
34
|
+
"mergeClass(): string and object": async () => {
|
|
35
|
+
await expect(mergeClass("foo", { bar: true, baz: false })).toEqual({ foo: true, bar: true, baz: false });
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
-
"mergeClass(): object and string": () => {
|
|
39
|
-
expect(mergeClass({ foo: true, bar: false }, "baz")).toEqual({ foo: true, bar: false, baz: true });
|
|
38
|
+
"mergeClass(): object and string": async () => {
|
|
39
|
+
await expect(mergeClass({ foo: true, bar: false }, "baz")).toEqual({ foo: true, bar: false, baz: true });
|
|
40
40
|
},
|
|
41
41
|
|
|
42
|
-
"mergeClass(): two objects": () => {
|
|
43
|
-
expect(mergeClass({ foo: true, bar: true }, { bar: false, baz: true })).toEqual({ foo: true, bar: false, baz: true });
|
|
42
|
+
"mergeClass(): two objects": async () => {
|
|
43
|
+
await expect(mergeClass({ foo: true, bar: true }, { bar: false, baz: true })).toEqual({ foo: true, bar: false, baz: true });
|
|
44
44
|
},
|
|
45
45
|
|
|
46
|
-
"mergeClass(): object and array": () => {
|
|
47
|
-
expect(mergeClass({ foo: true }, ["bar", "baz"])).toEqual({ foo: true, 0: "bar", 1: "baz" });
|
|
46
|
+
"mergeClass(): object and array": async () => {
|
|
47
|
+
await expect(mergeClass({ foo: true }, ["bar", "baz"])).toEqual({ foo: true, 0: "bar", 1: "baz" });
|
|
48
48
|
},
|
|
49
49
|
|
|
50
|
-
"mergeClass(): array and object": () => {
|
|
51
|
-
expect(mergeClass(["foo", "bar"], { baz: true, qux: false })).toEqual({ 0: "foo", 1: "bar", baz: true, qux: false });
|
|
50
|
+
"mergeClass(): array and object": async () => {
|
|
51
|
+
await expect(mergeClass(["foo", "bar"], { baz: true, qux: false })).toEqual({ 0: "foo", 1: "bar", baz: true, qux: false });
|
|
52
52
|
},
|
|
53
53
|
|
|
54
|
-
"mergeClass(): falsy entries are skipped": () => {
|
|
55
|
-
expect(mergeClass("foo", null, "bar")).toEqual("foo bar");
|
|
56
|
-
expect(mergeClass(null, "foo", undefined, "bar")).toEqual("foo bar");
|
|
54
|
+
"mergeClass(): falsy entries are skipped": async () => {
|
|
55
|
+
await expect(mergeClass("foo", null, "bar")).toEqual("foo bar");
|
|
56
|
+
await expect(mergeClass(null, "foo", undefined, "bar")).toEqual("foo bar");
|
|
57
57
|
},
|
|
58
58
|
|
|
59
|
-
"mergeClass(): multiple args (3+)": () => {
|
|
60
|
-
expect(mergeClass("a", "b", "c")).toEqual("a b c");
|
|
61
|
-
expect(mergeClass("x", null, ["y", "z"], "w")).toEqual("y z x w");
|
|
59
|
+
"mergeClass(): multiple args (3+)": async () => {
|
|
60
|
+
await expect(mergeClass("a", "b", "c")).toEqual("a b c");
|
|
61
|
+
await expect(mergeClass("x", null, ["y", "z"], "w")).toEqual("y z x w");
|
|
62
62
|
}
|
|
63
63
|
};
|
package/test/tests-mergeProps.ts
CHANGED
|
@@ -2,42 +2,42 @@ import { mergeProps, Props } from "../index";
|
|
|
2
2
|
import { expect } from "./helper";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
|
-
"mergeProps(): no args returns undefined": () => {
|
|
6
|
-
expect(mergeProps()).toEqual(undefined);
|
|
5
|
+
"mergeProps(): no args returns undefined": async () => {
|
|
6
|
+
await expect(mergeProps()).toEqual(undefined);
|
|
7
7
|
},
|
|
8
8
|
|
|
9
|
-
"mergeProps(): single arg returned as-is": () => {
|
|
9
|
+
"mergeProps(): single arg returned as-is": async () => {
|
|
10
10
|
const p = { class: "foo" };
|
|
11
|
-
expect(mergeProps(p) === p).toEqual(true);
|
|
11
|
+
await expect(mergeProps(p) === p).toEqual(true);
|
|
12
12
|
},
|
|
13
13
|
|
|
14
|
-
"mergeProps(): two plain objects merged": () => {
|
|
15
|
-
expect(mergeProps({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
14
|
+
"mergeProps(): two plain objects merged": async () => {
|
|
15
|
+
await expect(mergeProps({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
-
"mergeProps(): right overwrites left for simple keys": () => {
|
|
19
|
-
expect(mergeProps({ a: 1, b: "x" }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
18
|
+
"mergeProps(): right overwrites left for simple keys": async () => {
|
|
19
|
+
await expect(mergeProps({ a: 1, b: "x" }, { b: 2 })).toEqual({ a: 1, b: 2 });
|
|
20
20
|
},
|
|
21
21
|
|
|
22
|
-
"mergeProps(): class merged via mergeClass": () => {
|
|
23
|
-
expect(mergeProps({ class: "foo" }, { class: "bar" })).toEqual({ class: "foo bar" });
|
|
22
|
+
"mergeProps(): class merged via mergeClass": async () => {
|
|
23
|
+
await expect(mergeProps({ class: "foo" }, { class: "bar" })).toEqual({ class: "foo bar" });
|
|
24
24
|
},
|
|
25
25
|
|
|
26
|
-
"mergeProps(): style merged via mergeStyle (strings)": () => {
|
|
26
|
+
"mergeProps(): style merged via mergeStyle (strings)": async () => {
|
|
27
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);
|
|
28
|
+
await expect((<string>result.style!).includes("color: red")).toEqual(true);
|
|
29
|
+
await expect((<string>result.style!).includes("font-size: 14px")).toEqual(true);
|
|
30
30
|
},
|
|
31
31
|
|
|
32
|
-
"mergeProps(): null and undefined entries skipped": () => {
|
|
33
|
-
expect(mergeProps({ a: 1 }, null, { b: 2 }, undefined)).toEqual({ a: 1, b: 2 });
|
|
32
|
+
"mergeProps(): null and undefined entries skipped": async () => {
|
|
33
|
+
await expect(mergeProps({ a: 1 }, null, { b: 2 }, undefined)).toEqual({ a: 1, b: 2 });
|
|
34
34
|
},
|
|
35
35
|
|
|
36
|
-
"mergeProps(): multiple args (3+)": () => {
|
|
37
|
-
expect(mergeProps({ a: 1 }, { b: 2 }, { c: 3 })).toEqual({ a: 1, b: 2, c: 3 });
|
|
36
|
+
"mergeProps(): multiple args (3+)": async () => {
|
|
37
|
+
await expect(mergeProps({ a: 1 }, { b: 2 }, { c: 3 })).toEqual({ a: 1, b: 2, c: 3 });
|
|
38
38
|
},
|
|
39
39
|
|
|
40
|
-
"mergeProps(): first arg null returns defined from later args": () => {
|
|
41
|
-
expect(mergeProps(null, { a: 1 })).toEqual({ a: 1 });
|
|
40
|
+
"mergeProps(): first arg null returns defined from later args": async () => {
|
|
41
|
+
await expect(mergeProps(null, { a: 1 })).toEqual({ a: 1 });
|
|
42
42
|
}
|
|
43
43
|
};
|
package/test/tests-mergeStyle.ts
CHANGED
|
@@ -1,39 +1,53 @@
|
|
|
1
1
|
import { mergeStyle } from "../index";
|
|
2
2
|
import { expect } from "./helper";
|
|
3
3
|
|
|
4
|
+
// Helper to normalize style strings for comparison (browser normalizes CSS differently)
|
|
5
|
+
function normalizeStyle(s: string): string {
|
|
6
|
+
return s.replace(/;\s*/g, ';').replace(/:\s*/g, ':').replace(/^;/, '').replace(/;$/, '').toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hasStyle(result: string, prop: string, value: string): boolean {
|
|
10
|
+
const normalized = normalizeStyle(result);
|
|
11
|
+
return normalized.includes(`${prop}:${value}`.toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
export default {
|
|
5
|
-
"mergeStyle(): no args returns empty string": () => {
|
|
6
|
-
expect(mergeStyle()).toEqual("");
|
|
15
|
+
"mergeStyle(): no args returns empty string": async () => {
|
|
16
|
+
await expect(mergeStyle()).toEqual("");
|
|
7
17
|
},
|
|
8
18
|
|
|
9
|
-
"mergeStyle(): object style sets properties, returns cssText": () => {
|
|
19
|
+
"mergeStyle(): object style sets properties, returns cssText": async () => {
|
|
10
20
|
const result = mergeStyle({ color: "red", fontSize: "14px" });
|
|
11
|
-
expect(typeof result).toEqual("string");
|
|
21
|
+
await expect(typeof result).toEqual("string");
|
|
12
22
|
},
|
|
13
23
|
|
|
14
|
-
"mergeStyle(): single string
|
|
15
|
-
|
|
24
|
+
"mergeStyle(): single string includes the style": async () => {
|
|
25
|
+
const result = mergeStyle("color: red") as string;
|
|
26
|
+
await expect(hasStyle(result, "color", "red")).toEqual(true);
|
|
16
27
|
},
|
|
17
28
|
|
|
18
|
-
"mergeStyle(): two strings are concatenated": () => {
|
|
19
|
-
|
|
29
|
+
"mergeStyle(): two strings are concatenated": async () => {
|
|
30
|
+
const result = mergeStyle("color: red", "font-size: 14px") as string;
|
|
31
|
+
await expect(hasStyle(result, "color", "red")).toEqual(true);
|
|
32
|
+
await expect(hasStyle(result, "font-size", "14px")).toEqual(true);
|
|
20
33
|
},
|
|
21
34
|
|
|
22
|
-
"mergeStyle(): object then string": () => {
|
|
35
|
+
"mergeStyle(): object then string": async () => {
|
|
23
36
|
const result = mergeStyle({ color: "red" }, "font-size: 14px") as string;
|
|
24
|
-
expect(result
|
|
37
|
+
await expect(hasStyle(result, "font-size", "14px")).toEqual(true);
|
|
25
38
|
},
|
|
26
39
|
|
|
27
|
-
"mergeStyle(): null and undefined entries are skipped": () => {
|
|
28
|
-
|
|
40
|
+
"mergeStyle(): null and undefined entries are skipped": async () => {
|
|
41
|
+
const result = mergeStyle(null, "color: red", undefined) as string;
|
|
42
|
+
await expect(hasStyle(result, "color", "red")).toEqual(true);
|
|
29
43
|
},
|
|
30
44
|
|
|
31
|
-
"mergeStyle(): multiple objects and strings alternate": () => {
|
|
45
|
+
"mergeStyle(): multiple objects and strings alternate": async () => {
|
|
32
46
|
const result = mergeStyle(
|
|
33
47
|
{ color: "red" },
|
|
34
48
|
"font-size: 14px",
|
|
35
49
|
{ background: "blue" }
|
|
36
50
|
) as string;
|
|
37
|
-
expect(result
|
|
51
|
+
await expect(hasStyle(result, "font-size", "14px")).toEqual(true);
|
|
38
52
|
}
|
|
39
53
|
};
|