rask-ui 0.2.5 → 0.2.6
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/dist/vdom/AbstractVNode.d.ts +20 -1
- package/dist/vdom/AbstractVNode.d.ts.map +1 -1
- package/dist/vdom/AbstractVNode.js +142 -11
- package/dist/vdom/ComponentVNode.d.ts +2 -2
- package/dist/vdom/ComponentVNode.d.ts.map +1 -1
- package/dist/vdom/ComponentVNode.js +10 -5
- package/dist/vdom/ElementVNode.d.ts +2 -8
- package/dist/vdom/ElementVNode.d.ts.map +1 -1
- package/dist/vdom/ElementVNode.js +11 -31
- package/dist/vdom/FragmentVNode.d.ts +2 -2
- package/dist/vdom/FragmentVNode.d.ts.map +1 -1
- package/dist/vdom/FragmentVNode.js +11 -6
- package/dist/vdom/RootVNode.d.ts +2 -2
- package/dist/vdom/RootVNode.d.ts.map +1 -1
- package/dist/vdom/RootVNode.js +7 -5
- package/package.json +1 -1
- package/dist/component.d.ts +0 -38
- package/dist/component.d.ts.map +0 -1
- package/dist/component.js +0 -130
- package/dist/context.d.ts +0 -5
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -29
- package/dist/createAsync.test.d.ts +0 -2
- package/dist/createAsync.test.d.ts.map +0 -1
- package/dist/createAsync.test.js +0 -110
- package/dist/createAsyncState.d.ts +0 -16
- package/dist/createAsyncState.d.ts.map +0 -1
- package/dist/createAsyncState.js +0 -24
- package/dist/createContext.test.d.ts +0 -2
- package/dist/createContext.test.d.ts.map +0 -1
- package/dist/createContext.test.js +0 -136
- package/dist/createMutation.test.d.ts +0 -2
- package/dist/createMutation.test.d.ts.map +0 -1
- package/dist/createMutation.test.js +0 -168
- package/dist/createQuery.test.d.ts +0 -2
- package/dist/createQuery.test.d.ts.map +0 -1
- package/dist/createQuery.test.js +0 -156
- package/dist/createRef.test.d.ts +0 -2
- package/dist/createRef.test.d.ts.map +0 -1
- package/dist/createRef.test.js +0 -80
- package/dist/createState.test.d.ts +0 -2
- package/dist/createState.test.d.ts.map +0 -1
- package/dist/createState.test.js +0 -111
- package/dist/createView.test.d.ts +0 -2
- package/dist/createView.test.d.ts.map +0 -1
- package/dist/createView.test.js +0 -203
- package/dist/error.test.d.ts +0 -2
- package/dist/error.test.d.ts.map +0 -1
- package/dist/error.test.js +0 -144
- package/dist/integration.test.d.ts +0 -2
- package/dist/integration.test.d.ts.map +0 -1
- package/dist/integration.test.js +0 -155
- package/dist/jsx.d.ts.map +0 -1
- package/dist/jsx.js +0 -42
- package/dist/observation.test.d.ts +0 -2
- package/dist/observation.test.d.ts.map +0 -1
- package/dist/observation.test.js +0 -113
- package/dist/render-test.d.ts +0 -2
- package/dist/render-test.d.ts.map +0 -1
- package/dist/render-test.js +0 -21
- package/dist/render.d.ts +0 -7
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -77
- package/dist/suspense.d.ts +0 -25
- package/dist/suspense.d.ts.map +0 -1
- package/dist/suspense.js +0 -97
- package/dist/tests/class.test.d.ts +0 -2
- package/dist/tests/class.test.d.ts.map +0 -1
- package/dist/tests/class.test.js +0 -185
- package/dist/tests/complex-rendering.test.d.ts +0 -2
- package/dist/tests/complex-rendering.test.d.ts.map +0 -1
- package/dist/tests/complex-rendering.test.js +0 -400
- package/dist/tests/component.cleanup.test.d.ts +0 -2
- package/dist/tests/component.cleanup.test.d.ts.map +0 -1
- package/dist/tests/component.cleanup.test.js +0 -325
- package/dist/tests/component.counter.test.d.ts +0 -2
- package/dist/tests/component.counter.test.d.ts.map +0 -1
- package/dist/tests/component.counter.test.js +0 -124
- package/dist/tests/component.interaction.test.d.ts +0 -2
- package/dist/tests/component.interaction.test.d.ts.map +0 -1
- package/dist/tests/component.interaction.test.js +0 -73
- package/dist/tests/component.props.test.d.ts +0 -2
- package/dist/tests/component.props.test.d.ts.map +0 -1
- package/dist/tests/component.props.test.js +0 -334
- package/dist/tests/component.return-types.test.d.ts +0 -2
- package/dist/tests/component.return-types.test.d.ts.map +0 -1
- package/dist/tests/component.return-types.test.js +0 -357
- package/dist/tests/component.state.test.d.ts +0 -2
- package/dist/tests/component.state.test.d.ts.map +0 -1
- package/dist/tests/component.state.test.js +0 -135
- package/dist/tests/component.test.d.ts +0 -2
- package/dist/tests/component.test.d.ts.map +0 -1
- package/dist/tests/component.test.js +0 -63
- package/dist/tests/createAsync.test.d.ts +0 -2
- package/dist/tests/createAsync.test.d.ts.map +0 -1
- package/dist/tests/createAsync.test.js +0 -110
- package/dist/tests/createContext.test.d.ts +0 -2
- package/dist/tests/createContext.test.d.ts.map +0 -1
- package/dist/tests/createContext.test.js +0 -141
- package/dist/tests/createMutation.test.d.ts +0 -2
- package/dist/tests/createMutation.test.d.ts.map +0 -1
- package/dist/tests/createMutation.test.js +0 -168
- package/dist/tests/createQuery.test.d.ts +0 -2
- package/dist/tests/createQuery.test.d.ts.map +0 -1
- package/dist/tests/createQuery.test.js +0 -156
- package/dist/tests/createRef.test.d.ts +0 -2
- package/dist/tests/createRef.test.d.ts.map +0 -1
- package/dist/tests/createRef.test.js +0 -84
- package/dist/tests/createState.test.d.ts +0 -2
- package/dist/tests/createState.test.d.ts.map +0 -1
- package/dist/tests/createState.test.js +0 -103
- package/dist/tests/createView.test.d.ts +0 -2
- package/dist/tests/createView.test.d.ts.map +0 -1
- package/dist/tests/createView.test.js +0 -203
- package/dist/tests/edge-cases.test.d.ts +0 -2
- package/dist/tests/edge-cases.test.d.ts.map +0 -1
- package/dist/tests/edge-cases.test.js +0 -637
- package/dist/tests/error-no-boundary.test.d.ts +0 -2
- package/dist/tests/error-no-boundary.test.d.ts.map +0 -1
- package/dist/tests/error-no-boundary.test.js +0 -174
- package/dist/tests/error.test.d.ts +0 -2
- package/dist/tests/error.test.d.ts.map +0 -1
- package/dist/tests/error.test.js +0 -199
- package/dist/tests/fragment.test.d.ts +0 -2
- package/dist/tests/fragment.test.d.ts.map +0 -1
- package/dist/tests/fragment.test.js +0 -618
- package/dist/tests/integration.test.d.ts +0 -2
- package/dist/tests/integration.test.d.ts.map +0 -1
- package/dist/tests/integration.test.js +0 -192
- package/dist/tests/keys.test.d.ts +0 -2
- package/dist/tests/keys.test.d.ts.map +0 -1
- package/dist/tests/keys.test.js +0 -293
- package/dist/tests/mount.test.d.ts +0 -2
- package/dist/tests/mount.test.d.ts.map +0 -1
- package/dist/tests/mount.test.js +0 -91
- package/dist/tests/observation.test.d.ts +0 -2
- package/dist/tests/observation.test.d.ts.map +0 -1
- package/dist/tests/observation.test.js +0 -113
- package/dist/tests/patch.test.d.ts +0 -2
- package/dist/tests/patch.test.d.ts.map +0 -1
- package/dist/tests/patch.test.js +0 -498
- package/dist/tests/patchChildren.test.d.ts +0 -2
- package/dist/tests/patchChildren.test.d.ts.map +0 -1
- package/dist/tests/patchChildren.test.js +0 -405
- package/dist/tests/primitives.test.d.ts +0 -2
- package/dist/tests/primitives.test.d.ts.map +0 -1
- package/dist/tests/primitives.test.js +0 -132
- package/dist/vdom/class.test.d.ts +0 -2
- package/dist/vdom/class.test.d.ts.map +0 -1
- package/dist/vdom/class.test.js +0 -143
- package/dist/vdom/complex-rendering.test.d.ts +0 -2
- package/dist/vdom/complex-rendering.test.d.ts.map +0 -1
- package/dist/vdom/complex-rendering.test.js +0 -400
- package/dist/vdom/component.cleanup.test.d.ts +0 -2
- package/dist/vdom/component.cleanup.test.d.ts.map +0 -1
- package/dist/vdom/component.cleanup.test.js +0 -323
- package/dist/vdom/component.counter.test.d.ts +0 -2
- package/dist/vdom/component.counter.test.d.ts.map +0 -1
- package/dist/vdom/component.counter.test.js +0 -124
- package/dist/vdom/component.interaction.test.d.ts +0 -2
- package/dist/vdom/component.interaction.test.d.ts.map +0 -1
- package/dist/vdom/component.interaction.test.js +0 -73
- package/dist/vdom/component.props.test.d.ts +0 -2
- package/dist/vdom/component.props.test.d.ts.map +0 -1
- package/dist/vdom/component.props.test.js +0 -88
- package/dist/vdom/component.return-types.test.d.ts +0 -2
- package/dist/vdom/component.return-types.test.d.ts.map +0 -1
- package/dist/vdom/component.return-types.test.js +0 -357
- package/dist/vdom/component.state.test.d.ts +0 -2
- package/dist/vdom/component.state.test.d.ts.map +0 -1
- package/dist/vdom/component.state.test.js +0 -129
- package/dist/vdom/component.test.d.ts +0 -2
- package/dist/vdom/component.test.d.ts.map +0 -1
- package/dist/vdom/component.test.js +0 -63
- package/dist/vdom/edge-cases.test.d.ts +0 -2
- package/dist/vdom/edge-cases.test.d.ts.map +0 -1
- package/dist/vdom/edge-cases.test.js +0 -637
- package/dist/vdom/fragment.test.d.ts +0 -2
- package/dist/vdom/fragment.test.d.ts.map +0 -1
- package/dist/vdom/fragment.test.js +0 -618
- package/dist/vdom/keys.test.d.ts +0 -2
- package/dist/vdom/keys.test.d.ts.map +0 -1
- package/dist/vdom/keys.test.js +0 -293
- package/dist/vdom/mount.test.d.ts +0 -2
- package/dist/vdom/mount.test.d.ts.map +0 -1
- package/dist/vdom/mount.test.js +0 -91
- package/dist/vdom/patch.test.d.ts +0 -2
- package/dist/vdom/patch.test.d.ts.map +0 -1
- package/dist/vdom/patch.test.js +0 -498
- package/dist/vdom/patchChildren.test.d.ts +0 -2
- package/dist/vdom/patchChildren.test.d.ts.map +0 -1
- package/dist/vdom/patchChildren.test.js +0 -392
- package/dist/vdom/primitives.test.d.ts +0 -2
- package/dist/vdom/primitives.test.d.ts.map +0 -1
- package/dist/vdom/primitives.test.js +0 -132
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { Signal, Observer, getCurrentObserver } from "../observation";
|
|
3
|
-
describe("Signal", () => {
|
|
4
|
-
it("should allow subscribing to notifications", () => {
|
|
5
|
-
const signal = new Signal();
|
|
6
|
-
const callback = vi.fn();
|
|
7
|
-
signal.subscribe(callback);
|
|
8
|
-
signal.notify();
|
|
9
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
10
|
-
});
|
|
11
|
-
it("should return a disposer function", () => {
|
|
12
|
-
const signal = new Signal();
|
|
13
|
-
const callback = vi.fn();
|
|
14
|
-
const dispose = signal.subscribe(callback);
|
|
15
|
-
dispose();
|
|
16
|
-
signal.notify();
|
|
17
|
-
expect(callback).not.toHaveBeenCalled();
|
|
18
|
-
});
|
|
19
|
-
it("should handle multiple subscribers", () => {
|
|
20
|
-
const signal = new Signal();
|
|
21
|
-
const callback1 = vi.fn();
|
|
22
|
-
const callback2 = vi.fn();
|
|
23
|
-
signal.subscribe(callback1);
|
|
24
|
-
signal.subscribe(callback2);
|
|
25
|
-
signal.notify();
|
|
26
|
-
expect(callback1).toHaveBeenCalledTimes(1);
|
|
27
|
-
expect(callback2).toHaveBeenCalledTimes(1);
|
|
28
|
-
});
|
|
29
|
-
it("should allow unsubscribing individual callbacks", () => {
|
|
30
|
-
const signal = new Signal();
|
|
31
|
-
const callback1 = vi.fn();
|
|
32
|
-
const callback2 = vi.fn();
|
|
33
|
-
const dispose1 = signal.subscribe(callback1);
|
|
34
|
-
signal.subscribe(callback2);
|
|
35
|
-
dispose1();
|
|
36
|
-
signal.notify();
|
|
37
|
-
expect(callback1).not.toHaveBeenCalled();
|
|
38
|
-
expect(callback2).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe("Observer", () => {
|
|
42
|
-
it("should track signals during observation", () => {
|
|
43
|
-
const callback = vi.fn();
|
|
44
|
-
const observer = new Observer(callback);
|
|
45
|
-
const signal = new Signal();
|
|
46
|
-
const dispose = observer.observe();
|
|
47
|
-
observer.subscribeSignal(signal);
|
|
48
|
-
dispose();
|
|
49
|
-
signal.notify();
|
|
50
|
-
return new Promise((resolve) => {
|
|
51
|
-
queueMicrotask(() => {
|
|
52
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
53
|
-
resolve(undefined);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
it("should clear signals when observing again", async () => {
|
|
58
|
-
let callCount = 0;
|
|
59
|
-
const observer = new Observer(() => {
|
|
60
|
-
callCount++;
|
|
61
|
-
});
|
|
62
|
-
const signal1 = new Signal();
|
|
63
|
-
const signal2 = new Signal();
|
|
64
|
-
// First observation
|
|
65
|
-
let dispose = observer.observe();
|
|
66
|
-
observer.subscribeSignal(signal1);
|
|
67
|
-
dispose();
|
|
68
|
-
// Second observation - should clear previous signals
|
|
69
|
-
dispose = observer.observe();
|
|
70
|
-
observer.subscribeSignal(signal2);
|
|
71
|
-
dispose();
|
|
72
|
-
// Notify first signal - should not trigger observer
|
|
73
|
-
signal1.notify();
|
|
74
|
-
await new Promise((resolve) => queueMicrotask(() => resolve()));
|
|
75
|
-
expect(callCount).toBe(0);
|
|
76
|
-
// Notify second signal - should trigger observer
|
|
77
|
-
signal2.notify();
|
|
78
|
-
await new Promise((resolve) => queueMicrotask(() => resolve()));
|
|
79
|
-
expect(callCount).toBe(1);
|
|
80
|
-
});
|
|
81
|
-
it("should dispose of all signal subscriptions", async () => {
|
|
82
|
-
const callback = vi.fn();
|
|
83
|
-
const observer = new Observer(callback);
|
|
84
|
-
const signal = new Signal();
|
|
85
|
-
const dispose = observer.observe();
|
|
86
|
-
observer.subscribeSignal(signal);
|
|
87
|
-
dispose();
|
|
88
|
-
observer.dispose();
|
|
89
|
-
signal.notify();
|
|
90
|
-
await new Promise((resolve) => queueMicrotask(() => resolve()));
|
|
91
|
-
expect(callback).not.toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
it("should set current observer during observation", () => {
|
|
94
|
-
const observer = new Observer(() => { });
|
|
95
|
-
expect(getCurrentObserver()).toBeUndefined();
|
|
96
|
-
const dispose = observer.observe();
|
|
97
|
-
expect(getCurrentObserver()).toBe(observer);
|
|
98
|
-
dispose();
|
|
99
|
-
expect(getCurrentObserver()).toBeUndefined();
|
|
100
|
-
});
|
|
101
|
-
it("should handle nested observations with stack", () => {
|
|
102
|
-
const observer1 = new Observer(() => { });
|
|
103
|
-
const observer2 = new Observer(() => { });
|
|
104
|
-
const dispose1 = observer1.observe();
|
|
105
|
-
expect(getCurrentObserver()).toBe(observer1);
|
|
106
|
-
const dispose2 = observer2.observe();
|
|
107
|
-
expect(getCurrentObserver()).toBe(observer2);
|
|
108
|
-
dispose2();
|
|
109
|
-
expect(getCurrentObserver()).toBe(observer1);
|
|
110
|
-
dispose1();
|
|
111
|
-
expect(getCurrentObserver()).toBeUndefined();
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"patch.test.d.ts","sourceRoot":"","sources":["../../src/tests/patch.test.ts"],"names":[],"mappings":""}
|
package/dist/tests/patch.test.js
DELETED
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { jsx, render } from "../vdom/index";
|
|
3
|
-
import { createState } from "../createState";
|
|
4
|
-
describe("VDOM Patch", () => {
|
|
5
|
-
it("should patch a div element with a span element", async () => {
|
|
6
|
-
const container = document.createElement("div");
|
|
7
|
-
let stateFn;
|
|
8
|
-
const App = () => {
|
|
9
|
-
const state = createState({ useSpan: false });
|
|
10
|
-
stateFn = state;
|
|
11
|
-
return () => state.useSpan
|
|
12
|
-
? jsx("span", { id: "patched" })
|
|
13
|
-
: jsx("div", { id: "initial" });
|
|
14
|
-
};
|
|
15
|
-
render(jsx(App, {}), container);
|
|
16
|
-
// Verify initial render
|
|
17
|
-
expect(container.children.length).toBe(1);
|
|
18
|
-
expect(container.children[0].tagName).toBe("DIV");
|
|
19
|
-
// Trigger patch by changing state
|
|
20
|
-
stateFn.useSpan = true;
|
|
21
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
22
|
-
// Verify the element was replaced
|
|
23
|
-
expect(container.children.length).toBe(1);
|
|
24
|
-
expect(container.children[0].tagName).toBe("SPAN");
|
|
25
|
-
});
|
|
26
|
-
it("should patch element by adding children", async () => {
|
|
27
|
-
const container = document.createElement("div");
|
|
28
|
-
let stateFn;
|
|
29
|
-
const App = () => {
|
|
30
|
-
const state = createState({ hasChildren: false });
|
|
31
|
-
stateFn = state;
|
|
32
|
-
return () => state.hasChildren
|
|
33
|
-
? jsx("div", {
|
|
34
|
-
id: "parent",
|
|
35
|
-
children: [
|
|
36
|
-
jsx("span", { id: "child1" }),
|
|
37
|
-
jsx("div", { id: "child2" }),
|
|
38
|
-
],
|
|
39
|
-
})
|
|
40
|
-
: jsx("div", { id: "parent" });
|
|
41
|
-
};
|
|
42
|
-
render(jsx(App, {}), container);
|
|
43
|
-
// Verify initial state (no children)
|
|
44
|
-
const parent = container.children[0];
|
|
45
|
-
expect(parent.children.length).toBe(0);
|
|
46
|
-
// Trigger patch to add children
|
|
47
|
-
stateFn.hasChildren = true;
|
|
48
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
49
|
-
// Verify children were added
|
|
50
|
-
expect(parent.children.length).toBe(2);
|
|
51
|
-
expect(parent.children[0].tagName).toBe("SPAN");
|
|
52
|
-
expect(parent.children[1].tagName).toBe("DIV");
|
|
53
|
-
});
|
|
54
|
-
it("should patch element by removing children", async () => {
|
|
55
|
-
const container = document.createElement("div");
|
|
56
|
-
let stateFn;
|
|
57
|
-
const App = () => {
|
|
58
|
-
const state = createState({ hasChildren: true });
|
|
59
|
-
stateFn = state;
|
|
60
|
-
return () => state.hasChildren
|
|
61
|
-
? jsx("div", {
|
|
62
|
-
id: "parent",
|
|
63
|
-
children: [
|
|
64
|
-
jsx("span", { id: "child1" }),
|
|
65
|
-
jsx("div", { id: "child2" }),
|
|
66
|
-
jsx("span", { id: "child3" }),
|
|
67
|
-
],
|
|
68
|
-
})
|
|
69
|
-
: jsx("div", { id: "parent" });
|
|
70
|
-
};
|
|
71
|
-
render(jsx(App, {}), container);
|
|
72
|
-
// Verify initial state (3 children)
|
|
73
|
-
const parent = container.children[0];
|
|
74
|
-
expect(parent.children.length).toBe(3);
|
|
75
|
-
// Trigger patch to remove children
|
|
76
|
-
stateFn.hasChildren = false;
|
|
77
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
78
|
-
// Verify children were removed
|
|
79
|
-
expect(parent.children.length).toBe(0);
|
|
80
|
-
});
|
|
81
|
-
it("should patch element by changing child types", async () => {
|
|
82
|
-
const container = document.createElement("div");
|
|
83
|
-
let stateFn;
|
|
84
|
-
const App = () => {
|
|
85
|
-
const state = createState({ version: 1 });
|
|
86
|
-
stateFn = state;
|
|
87
|
-
return () => state.version === 1
|
|
88
|
-
? jsx("div", {
|
|
89
|
-
id: "parent",
|
|
90
|
-
children: [
|
|
91
|
-
jsx("span", { id: "child1" }),
|
|
92
|
-
jsx("span", { id: "child2" }),
|
|
93
|
-
],
|
|
94
|
-
})
|
|
95
|
-
: jsx("div", {
|
|
96
|
-
id: "parent",
|
|
97
|
-
children: [
|
|
98
|
-
jsx("div", { id: "child1" }),
|
|
99
|
-
jsx("button", { id: "child2" }),
|
|
100
|
-
],
|
|
101
|
-
});
|
|
102
|
-
};
|
|
103
|
-
render(jsx(App, {}), container);
|
|
104
|
-
// Verify initial state (2 span children)
|
|
105
|
-
const parent = container.children[0];
|
|
106
|
-
expect(parent.children.length).toBe(2);
|
|
107
|
-
expect(parent.children[0].tagName).toBe("SPAN");
|
|
108
|
-
expect(parent.children[1].tagName).toBe("SPAN");
|
|
109
|
-
// Trigger patch to change child types
|
|
110
|
-
stateFn.version = 2;
|
|
111
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
112
|
-
// Verify children types were changed
|
|
113
|
-
expect(parent.children.length).toBe(2);
|
|
114
|
-
expect(parent.children[0].tagName).toBe("DIV");
|
|
115
|
-
expect(parent.children[1].tagName).toBe("BUTTON");
|
|
116
|
-
});
|
|
117
|
-
it("should patch element by adding data/aria attributes", async () => {
|
|
118
|
-
const container = document.createElement("div");
|
|
119
|
-
let stateFn;
|
|
120
|
-
const App = () => {
|
|
121
|
-
const state = createState({ hasAttributes: false });
|
|
122
|
-
stateFn = state;
|
|
123
|
-
return () => state.hasAttributes
|
|
124
|
-
? jsx("div", {
|
|
125
|
-
"data-testid": "test-component",
|
|
126
|
-
"aria-label": "Test element",
|
|
127
|
-
})
|
|
128
|
-
: jsx("div", {});
|
|
129
|
-
};
|
|
130
|
-
render(jsx(App, {}), container);
|
|
131
|
-
const div = container.children[0];
|
|
132
|
-
// Verify no attributes initially
|
|
133
|
-
expect(div.getAttribute("data-testid")).toBeNull();
|
|
134
|
-
expect(div.getAttribute("aria-label")).toBeNull();
|
|
135
|
-
// Trigger patch to add attributes
|
|
136
|
-
stateFn.hasAttributes = true;
|
|
137
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
138
|
-
// Verify attributes were added
|
|
139
|
-
expect(div.getAttribute("data-testid")).toBe("test-component");
|
|
140
|
-
expect(div.getAttribute("aria-label")).toBe("Test element");
|
|
141
|
-
});
|
|
142
|
-
it("should patch element by removing data/aria attributes", async () => {
|
|
143
|
-
const container = document.createElement("div");
|
|
144
|
-
let stateFn;
|
|
145
|
-
const App = () => {
|
|
146
|
-
const state = createState({ hasAttributes: true });
|
|
147
|
-
stateFn = state;
|
|
148
|
-
return () => state.hasAttributes
|
|
149
|
-
? jsx("div", {
|
|
150
|
-
"data-testid": "test-component",
|
|
151
|
-
"data-value": "123",
|
|
152
|
-
"aria-label": "Test element",
|
|
153
|
-
})
|
|
154
|
-
: jsx("div", {});
|
|
155
|
-
};
|
|
156
|
-
render(jsx(App, {}), container);
|
|
157
|
-
const div = container.children[0];
|
|
158
|
-
// Verify attributes exist
|
|
159
|
-
expect(div.getAttribute("data-testid")).toBe("test-component");
|
|
160
|
-
expect(div.getAttribute("data-value")).toBe("123");
|
|
161
|
-
expect(div.getAttribute("aria-label")).toBe("Test element");
|
|
162
|
-
// Trigger patch to remove attributes
|
|
163
|
-
stateFn.hasAttributes = false;
|
|
164
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
165
|
-
// Verify attributes were removed
|
|
166
|
-
expect(div.getAttribute("data-testid")).toBeNull();
|
|
167
|
-
expect(div.getAttribute("data-value")).toBeNull();
|
|
168
|
-
expect(div.getAttribute("aria-label")).toBeNull();
|
|
169
|
-
});
|
|
170
|
-
it("should patch element by updating data/aria attributes", async () => {
|
|
171
|
-
const container = document.createElement("div");
|
|
172
|
-
let stateFn;
|
|
173
|
-
const App = () => {
|
|
174
|
-
const state = createState({ version: 1 });
|
|
175
|
-
stateFn = state;
|
|
176
|
-
return () => state.version === 1
|
|
177
|
-
? jsx("div", {
|
|
178
|
-
"data-testid": "old-id",
|
|
179
|
-
"data-value": "old-value",
|
|
180
|
-
"aria-label": "Old label",
|
|
181
|
-
})
|
|
182
|
-
: jsx("div", {
|
|
183
|
-
"data-testid": "new-id",
|
|
184
|
-
"data-value": "new-value",
|
|
185
|
-
"aria-label": "New label",
|
|
186
|
-
});
|
|
187
|
-
};
|
|
188
|
-
render(jsx(App, {}), container);
|
|
189
|
-
const div = container.children[0];
|
|
190
|
-
// Verify initial attributes
|
|
191
|
-
expect(div.getAttribute("data-testid")).toBe("old-id");
|
|
192
|
-
expect(div.getAttribute("data-value")).toBe("old-value");
|
|
193
|
-
expect(div.getAttribute("aria-label")).toBe("Old label");
|
|
194
|
-
// Trigger patch to update attributes
|
|
195
|
-
stateFn.version = 2;
|
|
196
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
197
|
-
// Verify attributes were updated
|
|
198
|
-
expect(div.getAttribute("data-testid")).toBe("new-id");
|
|
199
|
-
expect(div.getAttribute("data-value")).toBe("new-value");
|
|
200
|
-
expect(div.getAttribute("aria-label")).toBe("New label");
|
|
201
|
-
});
|
|
202
|
-
it("should patch element by updating event listeners", async () => {
|
|
203
|
-
const container = document.createElement("div");
|
|
204
|
-
let stateFn;
|
|
205
|
-
const oldHandler = vi.fn();
|
|
206
|
-
const newHandler = vi.fn();
|
|
207
|
-
const App = () => {
|
|
208
|
-
const state = createState({ version: 1 });
|
|
209
|
-
stateFn = state;
|
|
210
|
-
return () => jsx("button", {
|
|
211
|
-
onClick: state.version === 1 ? oldHandler : newHandler,
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
render(jsx(App, {}), container);
|
|
215
|
-
const button = container.children[0];
|
|
216
|
-
// Trigger click to verify old handler works
|
|
217
|
-
button.click();
|
|
218
|
-
expect(oldHandler).toHaveBeenCalledTimes(1);
|
|
219
|
-
expect(newHandler).toHaveBeenCalledTimes(0);
|
|
220
|
-
// Trigger patch to update event listener
|
|
221
|
-
stateFn.version = 2;
|
|
222
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
223
|
-
// Trigger click again
|
|
224
|
-
button.click();
|
|
225
|
-
// Verify old handler was not called again and new handler was called
|
|
226
|
-
expect(oldHandler).toHaveBeenCalledTimes(1);
|
|
227
|
-
expect(newHandler).toHaveBeenCalledTimes(1);
|
|
228
|
-
});
|
|
229
|
-
it("should patch complex nested UI structure", async () => {
|
|
230
|
-
const container = document.createElement("div");
|
|
231
|
-
let stateFn;
|
|
232
|
-
const App = () => {
|
|
233
|
-
const state = createState({ version: 1 });
|
|
234
|
-
stateFn = state;
|
|
235
|
-
return () => state.version === 1
|
|
236
|
-
? jsx("div", {
|
|
237
|
-
className: "app",
|
|
238
|
-
children: [
|
|
239
|
-
jsx("header", {
|
|
240
|
-
className: "header",
|
|
241
|
-
children: [
|
|
242
|
-
jsx("h1", { children: ["Title"] }),
|
|
243
|
-
jsx("nav", {
|
|
244
|
-
children: [
|
|
245
|
-
jsx("a", { href: "#home", children: ["Home"] }),
|
|
246
|
-
jsx("a", { href: "#about", children: ["About"] }),
|
|
247
|
-
],
|
|
248
|
-
}),
|
|
249
|
-
],
|
|
250
|
-
}),
|
|
251
|
-
jsx("main", {
|
|
252
|
-
className: "content",
|
|
253
|
-
children: [
|
|
254
|
-
jsx("p", { children: ["Paragraph 1"] }),
|
|
255
|
-
jsx("p", { children: ["Paragraph 2"] }),
|
|
256
|
-
],
|
|
257
|
-
}),
|
|
258
|
-
],
|
|
259
|
-
})
|
|
260
|
-
: jsx("div", {
|
|
261
|
-
className: "app-updated",
|
|
262
|
-
children: [
|
|
263
|
-
jsx("header", {
|
|
264
|
-
className: "header-updated",
|
|
265
|
-
children: [
|
|
266
|
-
jsx("h1", { children: ["New Title"] }),
|
|
267
|
-
jsx("nav", {
|
|
268
|
-
children: [
|
|
269
|
-
jsx("a", { href: "#home", children: ["Home"] }),
|
|
270
|
-
jsx("a", { href: "#about", children: ["About"] }),
|
|
271
|
-
jsx("a", { href: "#contact", children: ["Contact"] }),
|
|
272
|
-
],
|
|
273
|
-
}),
|
|
274
|
-
],
|
|
275
|
-
}),
|
|
276
|
-
jsx("main", {
|
|
277
|
-
className: "content",
|
|
278
|
-
children: [
|
|
279
|
-
jsx("p", { children: ["Updated Paragraph 1"] }),
|
|
280
|
-
jsx("div", { children: ["New div element"] }),
|
|
281
|
-
jsx("p", { children: ["Paragraph 3"] }),
|
|
282
|
-
],
|
|
283
|
-
}),
|
|
284
|
-
],
|
|
285
|
-
});
|
|
286
|
-
};
|
|
287
|
-
render(jsx(App, {}), container);
|
|
288
|
-
const app = container.children[0];
|
|
289
|
-
// Verify initial structure
|
|
290
|
-
expect(app.children.length).toBe(2);
|
|
291
|
-
expect(app.children[0].tagName).toBe("HEADER");
|
|
292
|
-
expect(app.children[1].tagName).toBe("MAIN");
|
|
293
|
-
const initialHeader = app.children[0];
|
|
294
|
-
expect(initialHeader.children.length).toBe(2);
|
|
295
|
-
expect(initialHeader.children[0].textContent).toBe("Title");
|
|
296
|
-
const initialNav = initialHeader.children[1];
|
|
297
|
-
expect(initialNav.children.length).toBe(2);
|
|
298
|
-
const initialMain = app.children[1];
|
|
299
|
-
expect(initialMain.children.length).toBe(2);
|
|
300
|
-
// Trigger patch
|
|
301
|
-
stateFn.version = 2;
|
|
302
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
303
|
-
// Verify patched structure
|
|
304
|
-
expect(app.className).toBe("app-updated");
|
|
305
|
-
expect(app.children.length).toBe(2);
|
|
306
|
-
const patchedHeader = app.children[0];
|
|
307
|
-
expect(patchedHeader.className).toBe("header-updated");
|
|
308
|
-
expect(patchedHeader.children[0].textContent).toBe("New Title");
|
|
309
|
-
const patchedNav = patchedHeader.children[1];
|
|
310
|
-
expect(patchedNav.children.length).toBe(3);
|
|
311
|
-
expect(patchedNav.children[2].textContent).toBe("Contact");
|
|
312
|
-
const patchedMain = app.children[1];
|
|
313
|
-
expect(patchedMain.children.length).toBe(3);
|
|
314
|
-
expect(patchedMain.children[0].textContent).toBe("Updated Paragraph 1");
|
|
315
|
-
expect(patchedMain.children[1].tagName).toBe("DIV");
|
|
316
|
-
expect(patchedMain.children[2].textContent).toBe("Paragraph 3");
|
|
317
|
-
});
|
|
318
|
-
it("should patch nested elements independently", async () => {
|
|
319
|
-
const container = document.createElement("div");
|
|
320
|
-
let stateFn;
|
|
321
|
-
const App = () => {
|
|
322
|
-
const state = createState({ sidebarVersion: 1, contentVersion: 1 });
|
|
323
|
-
stateFn = state;
|
|
324
|
-
return () => jsx("div", {
|
|
325
|
-
className: "layout",
|
|
326
|
-
children: [
|
|
327
|
-
state.sidebarVersion === 1
|
|
328
|
-
? jsx("aside", {
|
|
329
|
-
className: "sidebar",
|
|
330
|
-
children: [
|
|
331
|
-
jsx("div", { className: "widget", children: ["Widget 1"] }),
|
|
332
|
-
jsx("div", { className: "widget", children: ["Widget 2"] }),
|
|
333
|
-
],
|
|
334
|
-
})
|
|
335
|
-
: jsx("aside", {
|
|
336
|
-
className: "sidebar-updated",
|
|
337
|
-
children: [
|
|
338
|
-
jsx("div", {
|
|
339
|
-
className: "widget",
|
|
340
|
-
children: ["Widget 1 Updated"],
|
|
341
|
-
}),
|
|
342
|
-
jsx("div", { className: "widget", children: ["Widget 2"] }),
|
|
343
|
-
jsx("div", { className: "widget", children: ["Widget 3"] }),
|
|
344
|
-
],
|
|
345
|
-
}),
|
|
346
|
-
state.contentVersion === 1
|
|
347
|
-
? jsx("main", {
|
|
348
|
-
children: [
|
|
349
|
-
jsx("article", {
|
|
350
|
-
children: [
|
|
351
|
-
jsx("h2", { children: ["Article Title"] }),
|
|
352
|
-
jsx("p", { children: ["Article content"] }),
|
|
353
|
-
],
|
|
354
|
-
}),
|
|
355
|
-
],
|
|
356
|
-
})
|
|
357
|
-
: jsx("main", {
|
|
358
|
-
children: [
|
|
359
|
-
jsx("article", {
|
|
360
|
-
children: [
|
|
361
|
-
jsx("h2", { children: ["Updated Article Title"] }),
|
|
362
|
-
jsx("p", { children: ["Updated article content"] }),
|
|
363
|
-
jsx("footer", { children: ["Article footer"] }),
|
|
364
|
-
],
|
|
365
|
-
}),
|
|
366
|
-
],
|
|
367
|
-
}),
|
|
368
|
-
],
|
|
369
|
-
});
|
|
370
|
-
};
|
|
371
|
-
render(jsx(App, {}), container);
|
|
372
|
-
const layout = container.children[0];
|
|
373
|
-
const initialSidebar = layout.children[0];
|
|
374
|
-
const initialMain = layout.children[1];
|
|
375
|
-
// First patch: Update only the sidebar
|
|
376
|
-
stateFn.sidebarVersion = 2;
|
|
377
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
378
|
-
// Verify only sidebar changed
|
|
379
|
-
expect(initialSidebar.className).toBe("sidebar-updated");
|
|
380
|
-
expect(initialSidebar.children.length).toBe(3);
|
|
381
|
-
expect(initialSidebar.children[0].textContent).toBe("Widget 1 Updated");
|
|
382
|
-
expect(initialSidebar.children[2].textContent).toBe("Widget 3");
|
|
383
|
-
// Main content should be unchanged
|
|
384
|
-
const article = initialMain.children[0];
|
|
385
|
-
expect(article.children[0].textContent).toBe("Article Title");
|
|
386
|
-
expect(article.children[1].textContent).toBe("Article content");
|
|
387
|
-
// Second patch: Update only the content
|
|
388
|
-
stateFn.contentVersion = 2;
|
|
389
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
390
|
-
// Sidebar should remain from first patch
|
|
391
|
-
expect(initialSidebar.className).toBe("sidebar-updated");
|
|
392
|
-
expect(initialSidebar.children.length).toBe(3);
|
|
393
|
-
// Main content should now be updated
|
|
394
|
-
const updatedArticle = initialMain.children[0];
|
|
395
|
-
expect(updatedArticle.children.length).toBe(3);
|
|
396
|
-
expect(updatedArticle.children[0].textContent).toBe("Updated Article Title");
|
|
397
|
-
expect(updatedArticle.children[1].textContent).toBe("Updated article content");
|
|
398
|
-
expect(updatedArticle.children[2].tagName).toBe("FOOTER");
|
|
399
|
-
});
|
|
400
|
-
it("should handle multiple sequential patches", async () => {
|
|
401
|
-
const container = document.createElement("div");
|
|
402
|
-
let stateFn;
|
|
403
|
-
const App = () => {
|
|
404
|
-
const state = createState({ count: 0 });
|
|
405
|
-
stateFn = state;
|
|
406
|
-
return () => jsx("div", {
|
|
407
|
-
className: "counter",
|
|
408
|
-
children: [
|
|
409
|
-
jsx("span", { children: [`Count: ${state.count}`] }),
|
|
410
|
-
jsx("button", {
|
|
411
|
-
children: [state.count < 3 ? "Increment" : "Reset"],
|
|
412
|
-
}),
|
|
413
|
-
],
|
|
414
|
-
});
|
|
415
|
-
};
|
|
416
|
-
render(jsx(App, {}), container);
|
|
417
|
-
const counterDiv = container.children[0];
|
|
418
|
-
// Verify initial state
|
|
419
|
-
expect(counterDiv.children[0].textContent).toBe("Count: 0");
|
|
420
|
-
// Patch 1: Update count to 1
|
|
421
|
-
stateFn.count = 1;
|
|
422
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
423
|
-
expect(counterDiv.children[0].textContent).toBe("Count: 1");
|
|
424
|
-
// Patch 2: Update count to 2
|
|
425
|
-
stateFn.count = 2;
|
|
426
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
427
|
-
expect(counterDiv.children[0].textContent).toBe("Count: 2");
|
|
428
|
-
// Patch 3: Update count to 3 and change button text
|
|
429
|
-
stateFn.count = 3;
|
|
430
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
431
|
-
expect(counterDiv.children[0].textContent).toBe("Count: 3");
|
|
432
|
-
expect(counterDiv.children[1].textContent).toBe("Reset");
|
|
433
|
-
// Patch 4: Reset to 0 and add a new element
|
|
434
|
-
stateFn.count = 0;
|
|
435
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
436
|
-
expect(counterDiv.children[0].textContent).toBe("Count: 0");
|
|
437
|
-
expect(counterDiv.children[1].textContent).toBe("Increment");
|
|
438
|
-
});
|
|
439
|
-
it("should patch deeply nested child independently", async () => {
|
|
440
|
-
const container = document.createElement("div");
|
|
441
|
-
let stateFn;
|
|
442
|
-
const App = () => {
|
|
443
|
-
const state = createState({ version: 1 });
|
|
444
|
-
stateFn = state;
|
|
445
|
-
return () => jsx("div", {
|
|
446
|
-
className: "root",
|
|
447
|
-
children: [
|
|
448
|
-
jsx("div", {
|
|
449
|
-
className: "level-1",
|
|
450
|
-
children: [
|
|
451
|
-
jsx("div", {
|
|
452
|
-
className: "level-2",
|
|
453
|
-
children: [
|
|
454
|
-
jsx("div", {
|
|
455
|
-
className: "level-3",
|
|
456
|
-
children: [
|
|
457
|
-
jsx("span", {
|
|
458
|
-
children: [
|
|
459
|
-
state.version === 1
|
|
460
|
-
? "Deep content"
|
|
461
|
-
: "Updated deep content",
|
|
462
|
-
],
|
|
463
|
-
id: state.version === 1 ? "target" : "target-updated",
|
|
464
|
-
}),
|
|
465
|
-
],
|
|
466
|
-
}),
|
|
467
|
-
jsx("div", { children: ["Sibling at level-3"] }),
|
|
468
|
-
],
|
|
469
|
-
}),
|
|
470
|
-
jsx("div", { children: ["Sibling at level-2"] }),
|
|
471
|
-
],
|
|
472
|
-
}),
|
|
473
|
-
jsx("div", { children: ["Sibling at level-1"] }),
|
|
474
|
-
],
|
|
475
|
-
});
|
|
476
|
-
};
|
|
477
|
-
render(jsx(App, {}), container);
|
|
478
|
-
const root = container.children[0];
|
|
479
|
-
// Navigate to the deep element
|
|
480
|
-
const level1 = root.children[0];
|
|
481
|
-
const level2 = level1.children[0];
|
|
482
|
-
const level3 = level2.children[0];
|
|
483
|
-
const targetSpan = level3.children[0];
|
|
484
|
-
// Verify initial state
|
|
485
|
-
expect(targetSpan.textContent).toBe("Deep content");
|
|
486
|
-
expect(targetSpan.id).toBe("target");
|
|
487
|
-
// Trigger patch
|
|
488
|
-
stateFn.version = 2;
|
|
489
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
490
|
-
// Verify the deep element was updated
|
|
491
|
-
expect(targetSpan.textContent).toBe("Updated deep content");
|
|
492
|
-
expect(targetSpan.id).toBe("target-updated");
|
|
493
|
-
// Verify siblings remained unchanged
|
|
494
|
-
expect(level2.children[1].textContent).toBe("Sibling at level-3");
|
|
495
|
-
expect(level1.children[1].textContent).toBe("Sibling at level-2");
|
|
496
|
-
expect(root.children[1].textContent).toBe("Sibling at level-1");
|
|
497
|
-
});
|
|
498
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"patchChildren.test.d.ts","sourceRoot":"","sources":["../../src/tests/patchChildren.test.ts"],"names":[],"mappings":""}
|