rask-ui 0.28.3 → 0.29.0
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 +1 -1
- package/dist/component.d.ts +4 -3
- package/dist/component.d.ts.map +1 -1
- package/dist/component.js +37 -57
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/render.js +2 -2
- package/dist/scheduler.d.ts +2 -3
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +31 -104
- package/dist/tests/batch.test.js +202 -12
- package/dist/tests/createContext.test.js +50 -37
- package/dist/tests/error.test.js +25 -12
- package/dist/tests/renderCount.test.d.ts +2 -0
- package/dist/tests/renderCount.test.d.ts.map +1 -0
- package/dist/tests/renderCount.test.js +95 -0
- package/dist/tests/scopeEnforcement.test.d.ts +2 -0
- package/dist/tests/scopeEnforcement.test.d.ts.map +1 -0
- package/dist/tests/scopeEnforcement.test.js +157 -0
- package/dist/tests/useAction.test.d.ts +2 -0
- package/dist/tests/useAction.test.d.ts.map +1 -0
- package/dist/tests/useAction.test.js +132 -0
- package/dist/tests/useAsync.test.d.ts +2 -0
- package/dist/tests/useAsync.test.d.ts.map +1 -0
- package/dist/tests/useAsync.test.js +499 -0
- package/dist/tests/useDerived.test.d.ts +2 -0
- package/dist/tests/useDerived.test.d.ts.map +1 -0
- package/dist/tests/useDerived.test.js +407 -0
- package/dist/tests/useEffect.test.d.ts +2 -0
- package/dist/tests/useEffect.test.d.ts.map +1 -0
- package/dist/tests/useEffect.test.js +600 -0
- package/dist/tests/useLookup.test.d.ts +2 -0
- package/dist/tests/useLookup.test.d.ts.map +1 -0
- package/dist/tests/useLookup.test.js +299 -0
- package/dist/tests/useRef.test.d.ts +2 -0
- package/dist/tests/useRef.test.d.ts.map +1 -0
- package/dist/tests/useRef.test.js +189 -0
- package/dist/tests/useState.test.d.ts +2 -0
- package/dist/tests/useState.test.d.ts.map +1 -0
- package/dist/tests/useState.test.js +178 -0
- package/dist/tests/useSuspend.test.d.ts +2 -0
- package/dist/tests/useSuspend.test.d.ts.map +1 -0
- package/dist/tests/useSuspend.test.js +752 -0
- package/dist/tests/useView.test.d.ts +2 -0
- package/dist/tests/useView.test.d.ts.map +1 -0
- package/dist/tests/useView.test.js +305 -0
- package/dist/useAsync.d.ts.map +1 -1
- package/dist/useAsync.js +12 -11
- package/dist/useDerived.d.ts +1 -1
- package/dist/useDerived.d.ts.map +1 -1
- package/dist/useDerived.js +9 -63
- package/dist/useEffect.d.ts.map +1 -1
- package/dist/useEffect.js +4 -19
- package/dist/useLookup.d.ts.map +1 -1
- package/dist/useLookup.js +9 -14
- package/dist/useRef.d.ts.map +1 -1
- package/dist/useRef.js +4 -8
- package/dist/useRouter.d.ts.map +1 -1
- package/dist/useRouter.js +4 -8
- package/dist/useState.d.ts +0 -1
- package/dist/useState.d.ts.map +1 -1
- package/dist/useState.js +2 -100
- package/dist/useSuspend.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "rask-ui/jsx-runtime";
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
3
|
import { createContext } from "../createContext";
|
|
4
4
|
import { render } from "../";
|
|
5
5
|
describe("createContext", () => {
|
|
6
|
-
it("should create a context
|
|
7
|
-
const
|
|
8
|
-
expect(
|
|
9
|
-
expect(
|
|
6
|
+
it("should create a context with use and inject methods", () => {
|
|
7
|
+
const MyContext = createContext(() => ({ value: "test" }));
|
|
8
|
+
expect(typeof MyContext.use).toBe("function");
|
|
9
|
+
expect(typeof MyContext.inject).toBe("function");
|
|
10
10
|
});
|
|
11
11
|
it("should allow setting and getting context values", () => {
|
|
12
|
-
const ThemeContext = createContext();
|
|
12
|
+
const ThemeContext = createContext(() => ({ theme: "dark" }));
|
|
13
13
|
function Parent() {
|
|
14
|
-
ThemeContext.inject(
|
|
14
|
+
ThemeContext.inject();
|
|
15
15
|
return () => _jsx(Child, {});
|
|
16
16
|
}
|
|
17
17
|
function Child() {
|
|
18
|
-
const theme = ThemeContext.
|
|
18
|
+
const theme = ThemeContext.use();
|
|
19
19
|
return () => _jsx("div", { children: theme.theme });
|
|
20
20
|
}
|
|
21
21
|
const container = document.createElement("div");
|
|
@@ -25,16 +25,16 @@ describe("createContext", () => {
|
|
|
25
25
|
document.body.removeChild(container);
|
|
26
26
|
});
|
|
27
27
|
it("should traverse parent components to find context", () => {
|
|
28
|
-
const ThemeContext = createContext();
|
|
28
|
+
const ThemeContext = createContext(() => ({ theme: "light" }));
|
|
29
29
|
function GrandParent() {
|
|
30
|
-
ThemeContext.inject(
|
|
30
|
+
ThemeContext.inject();
|
|
31
31
|
return () => _jsx(Parent, {});
|
|
32
32
|
}
|
|
33
33
|
function Parent() {
|
|
34
34
|
return () => _jsx(Child, {});
|
|
35
35
|
}
|
|
36
36
|
function Child() {
|
|
37
|
-
const theme = ThemeContext.
|
|
37
|
+
const theme = ThemeContext.use();
|
|
38
38
|
return () => _jsx("div", { children: theme.theme });
|
|
39
39
|
}
|
|
40
40
|
const container = document.createElement("div");
|
|
@@ -44,11 +44,11 @@ describe("createContext", () => {
|
|
|
44
44
|
document.body.removeChild(container);
|
|
45
45
|
});
|
|
46
46
|
it("should throw error when context is not found", () => {
|
|
47
|
-
const ThemeContext = createContext();
|
|
47
|
+
const ThemeContext = createContext(() => ({ theme: "dark" }));
|
|
48
48
|
function Child() {
|
|
49
49
|
expect(() => {
|
|
50
|
-
ThemeContext.
|
|
51
|
-
}).toThrow("
|
|
50
|
+
ThemeContext.use();
|
|
51
|
+
}).toThrow("No context available");
|
|
52
52
|
return () => _jsx("div", { children: "Child" });
|
|
53
53
|
}
|
|
54
54
|
const container = document.createElement("div");
|
|
@@ -57,33 +57,33 @@ describe("createContext", () => {
|
|
|
57
57
|
document.body.removeChild(container);
|
|
58
58
|
});
|
|
59
59
|
it("should throw error when setting context outside component", () => {
|
|
60
|
-
const ThemeContext = createContext();
|
|
60
|
+
const ThemeContext = createContext(() => ({ theme: "dark" }));
|
|
61
61
|
expect(() => {
|
|
62
|
-
ThemeContext.inject(
|
|
63
|
-
}).toThrow("
|
|
62
|
+
ThemeContext.inject();
|
|
63
|
+
}).toThrow("Only use useInjectContext in component setup");
|
|
64
64
|
});
|
|
65
65
|
it("should throw error when getting context outside component", () => {
|
|
66
|
-
const ThemeContext = createContext();
|
|
66
|
+
const ThemeContext = createContext(() => ({ theme: "dark" }));
|
|
67
67
|
expect(() => {
|
|
68
|
-
ThemeContext.
|
|
69
|
-
}).toThrow("
|
|
68
|
+
ThemeContext.use();
|
|
69
|
+
}).toThrow("Only use useContext in component setup");
|
|
70
70
|
});
|
|
71
71
|
it("should allow overriding context in nested components", () => {
|
|
72
|
-
const ThemeContext = createContext();
|
|
72
|
+
const ThemeContext = createContext((theme) => ({ theme }));
|
|
73
73
|
function GrandParent() {
|
|
74
|
-
ThemeContext.inject(
|
|
74
|
+
ThemeContext.inject("light");
|
|
75
75
|
return () => (_jsxs("div", { children: [_jsx(Parent, {}), _jsx(ChildOfGrandParent, {})] }));
|
|
76
76
|
}
|
|
77
77
|
function Parent() {
|
|
78
|
-
ThemeContext.inject(
|
|
78
|
+
ThemeContext.inject("dark");
|
|
79
79
|
return () => _jsx(ChildOfParent, {});
|
|
80
80
|
}
|
|
81
81
|
function ChildOfParent() {
|
|
82
|
-
const theme = ThemeContext.
|
|
82
|
+
const theme = ThemeContext.use();
|
|
83
83
|
return () => _jsx("div", { class: "child-of-parent", children: theme.theme });
|
|
84
84
|
}
|
|
85
85
|
function ChildOfGrandParent() {
|
|
86
|
-
const theme = ThemeContext.
|
|
86
|
+
const theme = ThemeContext.use();
|
|
87
87
|
return () => _jsx("div", { class: "child-of-grandparent", children: theme.theme });
|
|
88
88
|
}
|
|
89
89
|
const container = document.createElement("div");
|
|
@@ -96,16 +96,16 @@ describe("createContext", () => {
|
|
|
96
96
|
document.body.removeChild(container);
|
|
97
97
|
});
|
|
98
98
|
it("should support multiple different contexts", () => {
|
|
99
|
-
const ThemeContext = createContext();
|
|
100
|
-
const UserContext = createContext();
|
|
99
|
+
const ThemeContext = createContext(() => ({ theme: "dark" }));
|
|
100
|
+
const UserContext = createContext(() => ({ name: "Alice" }));
|
|
101
101
|
function Parent() {
|
|
102
|
-
ThemeContext.inject(
|
|
103
|
-
UserContext.inject(
|
|
102
|
+
ThemeContext.inject();
|
|
103
|
+
UserContext.inject();
|
|
104
104
|
return () => _jsx(Child, {});
|
|
105
105
|
}
|
|
106
106
|
function Child() {
|
|
107
|
-
const theme = ThemeContext.
|
|
108
|
-
const user = UserContext.
|
|
107
|
+
const theme = ThemeContext.use();
|
|
108
|
+
const user = UserContext.use();
|
|
109
109
|
return () => _jsx("div", { children: `${theme.theme} - ${user.name}` });
|
|
110
110
|
}
|
|
111
111
|
const container = document.createElement("div");
|
|
@@ -115,16 +115,16 @@ describe("createContext", () => {
|
|
|
115
115
|
document.body.removeChild(container);
|
|
116
116
|
});
|
|
117
117
|
it("should handle context values of different types", () => {
|
|
118
|
-
const NumberContext = createContext();
|
|
119
|
-
const ArrayContext = createContext();
|
|
118
|
+
const NumberContext = createContext(() => 42);
|
|
119
|
+
const ArrayContext = createContext(() => ["a", "b", "c"]);
|
|
120
120
|
function Parent() {
|
|
121
|
-
NumberContext.inject(
|
|
122
|
-
ArrayContext.inject(
|
|
121
|
+
NumberContext.inject();
|
|
122
|
+
ArrayContext.inject();
|
|
123
123
|
return () => _jsx(Child, {});
|
|
124
124
|
}
|
|
125
125
|
function Child() {
|
|
126
|
-
const num = NumberContext.
|
|
127
|
-
const arr = ArrayContext.
|
|
126
|
+
const num = NumberContext.use();
|
|
127
|
+
const arr = ArrayContext.use();
|
|
128
128
|
return () => _jsx("div", { children: `${num} - ${arr.join(",")}` });
|
|
129
129
|
}
|
|
130
130
|
const container = document.createElement("div");
|
|
@@ -133,4 +133,17 @@ describe("createContext", () => {
|
|
|
133
133
|
expect(container.textContent).toContain("42 - a,b,c");
|
|
134
134
|
document.body.removeChild(container);
|
|
135
135
|
});
|
|
136
|
+
it("should allow using context in the root component that injects it", () => {
|
|
137
|
+
const ThemeContext = createContext(() => ({ theme: "root-theme" }));
|
|
138
|
+
function RootComponent() {
|
|
139
|
+
ThemeContext.inject();
|
|
140
|
+
const theme = ThemeContext.use();
|
|
141
|
+
return () => _jsx("div", { children: theme.theme });
|
|
142
|
+
}
|
|
143
|
+
const container = document.createElement("div");
|
|
144
|
+
document.body.appendChild(container);
|
|
145
|
+
render(_jsx(RootComponent, {}), container);
|
|
146
|
+
expect(container.textContent).toContain("root-theme");
|
|
147
|
+
document.body.removeChild(container);
|
|
148
|
+
});
|
|
136
149
|
});
|
package/dist/tests/error.test.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "rask-ui/jsx-runtime";
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { useCatchError } from "../useCatchError";
|
|
4
4
|
import { render } from "../";
|
|
5
|
-
describe("
|
|
5
|
+
describe("useCatchError", () => {
|
|
6
6
|
it("should render children when no error occurs", async () => {
|
|
7
7
|
function SafeChild() {
|
|
8
8
|
return () => _jsx("div", { children: "Safe content" });
|
|
9
9
|
}
|
|
10
10
|
function TestComponent() {
|
|
11
|
-
|
|
11
|
+
const errorState = useCatchError();
|
|
12
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", String(errorState.error)] })) : (_jsx(SafeChild, {}));
|
|
12
13
|
}
|
|
13
14
|
const container = document.createElement("div");
|
|
14
15
|
document.body.appendChild(container);
|
|
@@ -26,7 +27,8 @@ describe("ErrorBoundary", () => {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
function TestComponent() {
|
|
29
|
-
|
|
30
|
+
const errorState = useCatchError();
|
|
31
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", String(errorState.error)] })) : (_jsx(ThrowingChild, {}));
|
|
30
32
|
}
|
|
31
33
|
const container = document.createElement("div");
|
|
32
34
|
document.body.appendChild(container);
|
|
@@ -44,7 +46,8 @@ describe("ErrorBoundary", () => {
|
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
48
|
function TestComponent() {
|
|
47
|
-
|
|
49
|
+
const errorState = useCatchError();
|
|
50
|
+
return () => errorState.error ? (_jsxs("div", { class: "error-ui", children: [_jsx("h1", { children: "Oops!" }), _jsx("p", { children: String(errorState.error) })] })) : (_jsx(ThrowingChild, {}));
|
|
48
51
|
}
|
|
49
52
|
const container = document.createElement("div");
|
|
50
53
|
document.body.appendChild(container);
|
|
@@ -64,7 +67,8 @@ describe("ErrorBoundary", () => {
|
|
|
64
67
|
return () => _jsx("div", { children: "Child 2" });
|
|
65
68
|
}
|
|
66
69
|
function TestComponent() {
|
|
67
|
-
|
|
70
|
+
const errorState = useCatchError();
|
|
71
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", String(errorState.error)] })) : (_jsxs(_Fragment, { children: [_jsx(SafeChild1, {}), _jsx(SafeChild2, {})] }));
|
|
68
72
|
}
|
|
69
73
|
const container = document.createElement("div");
|
|
70
74
|
document.body.appendChild(container);
|
|
@@ -85,7 +89,8 @@ describe("ErrorBoundary", () => {
|
|
|
85
89
|
return () => _jsx(DeepChild, {});
|
|
86
90
|
}
|
|
87
91
|
function TestComponent() {
|
|
88
|
-
|
|
92
|
+
const errorState = useCatchError();
|
|
93
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Caught: ", String(errorState.error)] })) : (_jsx(MiddleChild, {}));
|
|
89
94
|
}
|
|
90
95
|
const container = document.createElement("div");
|
|
91
96
|
document.body.appendChild(container);
|
|
@@ -102,8 +107,13 @@ describe("ErrorBoundary", () => {
|
|
|
102
107
|
return _jsx("div", {});
|
|
103
108
|
};
|
|
104
109
|
}
|
|
110
|
+
function InnerBoundary() {
|
|
111
|
+
const errorState = useCatchError();
|
|
112
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Inner: ", String(errorState.error)] })) : (_jsx(ThrowingChild, {}));
|
|
113
|
+
}
|
|
105
114
|
function TestComponent() {
|
|
106
|
-
|
|
115
|
+
const errorState = useCatchError();
|
|
116
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Outer: ", String(errorState.error)] })) : (_jsx(InnerBoundary, {}));
|
|
107
117
|
}
|
|
108
118
|
const container = document.createElement("div");
|
|
109
119
|
document.body.appendChild(container);
|
|
@@ -122,7 +132,8 @@ describe("ErrorBoundary", () => {
|
|
|
122
132
|
};
|
|
123
133
|
}
|
|
124
134
|
function TestComponent() {
|
|
125
|
-
|
|
135
|
+
const errorState = useCatchError();
|
|
136
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", String(errorState.error)] })) : (_jsx(ThrowingChild, {}));
|
|
126
137
|
}
|
|
127
138
|
const container = document.createElement("div");
|
|
128
139
|
document.body.appendChild(container);
|
|
@@ -139,7 +150,8 @@ describe("ErrorBoundary", () => {
|
|
|
139
150
|
};
|
|
140
151
|
}
|
|
141
152
|
function TestComponent() {
|
|
142
|
-
|
|
153
|
+
const errorState = useCatchError();
|
|
154
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", errorState.error.message, " (Code:", " ", errorState.error.code, ")"] })) : (_jsx(ThrowingChild, {}));
|
|
143
155
|
}
|
|
144
156
|
const container = document.createElement("div");
|
|
145
157
|
document.body.appendChild(container);
|
|
@@ -156,7 +168,8 @@ describe("ErrorBoundary", () => {
|
|
|
156
168
|
return () => _jsx("div", { children: "Safe content" });
|
|
157
169
|
}
|
|
158
170
|
function TestComponent() {
|
|
159
|
-
|
|
171
|
+
const errorState = useCatchError();
|
|
172
|
+
return () => errorState.error ? (_jsxs("div", { children: ["Error: ", String(errorState.error)] })) : (_jsx(SafeChild, {}));
|
|
160
173
|
}
|
|
161
174
|
const container = document.createElement("div");
|
|
162
175
|
document.body.appendChild(container);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderCount.test.d.ts","sourceRoot":"","sources":["../../src/tests/renderCount.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "rask-ui/jsx-runtime";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { useState } from "../useState";
|
|
4
|
+
import { render } from "../";
|
|
5
|
+
describe("Child render count test", () => {
|
|
6
|
+
it("should track how many times Child renders when count changes", async () => {
|
|
7
|
+
let childRenderCount = 0;
|
|
8
|
+
let state;
|
|
9
|
+
function Child(props) {
|
|
10
|
+
return () => {
|
|
11
|
+
childRenderCount++;
|
|
12
|
+
return (_jsxs("div", { children: ["State count: ", props.state.count, ", Props count: ", props.count] }));
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function MyComp() {
|
|
16
|
+
state = useState({
|
|
17
|
+
count: 0,
|
|
18
|
+
});
|
|
19
|
+
return () => (_jsx("div", { children: _jsx(Child, { state: state, count: state.count }) }));
|
|
20
|
+
}
|
|
21
|
+
const container = document.createElement("div");
|
|
22
|
+
render(_jsx(MyComp, {}), container);
|
|
23
|
+
// Initial render
|
|
24
|
+
expect(childRenderCount).toBe(1);
|
|
25
|
+
expect(container.textContent).toBe("State count: 0, Props count: 0");
|
|
26
|
+
// Simulate click to increment count
|
|
27
|
+
state.count++;
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
29
|
+
// After first increment
|
|
30
|
+
expect(childRenderCount).toBe(2);
|
|
31
|
+
expect(container.textContent).toBe("State count: 1, Props count: 1");
|
|
32
|
+
state.count++;
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
34
|
+
// After second increment
|
|
35
|
+
expect(childRenderCount).toBe(3);
|
|
36
|
+
expect(container.textContent).toBe("State count: 2, Props count: 2");
|
|
37
|
+
});
|
|
38
|
+
it("should show Child renders only once when only state prop changes", async () => {
|
|
39
|
+
let childRenderCount = 0;
|
|
40
|
+
let state;
|
|
41
|
+
function Child(props) {
|
|
42
|
+
childRenderCount++;
|
|
43
|
+
return () => _jsxs("div", { children: ["State count: ", props.state.count] });
|
|
44
|
+
}
|
|
45
|
+
function MyComp() {
|
|
46
|
+
state = useState({
|
|
47
|
+
count: 0,
|
|
48
|
+
});
|
|
49
|
+
return () => (_jsx("div", { children: _jsx(Child, { state: state }) }));
|
|
50
|
+
}
|
|
51
|
+
const container = document.createElement("div");
|
|
52
|
+
render(_jsx(MyComp, {}), container);
|
|
53
|
+
// Initial render
|
|
54
|
+
expect(childRenderCount).toBe(1);
|
|
55
|
+
expect(container.textContent).toBe("State count: 0");
|
|
56
|
+
// Simulate click to increment count
|
|
57
|
+
state.count++;
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
59
|
+
// Child shouldn't re-render because state object reference didn't change
|
|
60
|
+
expect(childRenderCount).toBe(1);
|
|
61
|
+
expect(container.textContent).toBe("State count: 1");
|
|
62
|
+
});
|
|
63
|
+
it("should show Child renders when primitive prop changes", async () => {
|
|
64
|
+
let childRenderCount = 0;
|
|
65
|
+
let state;
|
|
66
|
+
function Child(props) {
|
|
67
|
+
return () => {
|
|
68
|
+
childRenderCount++;
|
|
69
|
+
return _jsxs("div", { children: ["Props count: ", props.count] });
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function MyComp() {
|
|
73
|
+
state = useState({
|
|
74
|
+
count: 0,
|
|
75
|
+
});
|
|
76
|
+
return () => (_jsx("div", { children: _jsx(Child, { count: state.count }) }));
|
|
77
|
+
}
|
|
78
|
+
const container = document.createElement("div");
|
|
79
|
+
render(_jsx(MyComp, {}), container);
|
|
80
|
+
// Initial render
|
|
81
|
+
expect(childRenderCount).toBe(1);
|
|
82
|
+
expect(container.textContent).toBe("Props count: 0");
|
|
83
|
+
// Simulate click to increment count
|
|
84
|
+
state.count++;
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
86
|
+
// Child re-renders because count prop changed
|
|
87
|
+
expect(childRenderCount).toBe(2);
|
|
88
|
+
expect(container.textContent).toBe("Props count: 1");
|
|
89
|
+
// Another click
|
|
90
|
+
state.count++;
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
92
|
+
expect(childRenderCount).toBe(3);
|
|
93
|
+
expect(container.textContent).toBe("Props count: 2");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopeEnforcement.test.d.ts","sourceRoot":"","sources":["../../src/tests/scopeEnforcement.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "rask-ui/jsx-runtime";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { useState } from "../useState";
|
|
4
|
+
import { useEffect } from "../useEffect";
|
|
5
|
+
import { useDerived } from "../useDerived";
|
|
6
|
+
import { render } from "../index";
|
|
7
|
+
describe("Scope Enforcement", () => {
|
|
8
|
+
describe("useState", () => {
|
|
9
|
+
it("should allow useState in global scope", () => {
|
|
10
|
+
expect(() => {
|
|
11
|
+
const state = useState({ count: 0 });
|
|
12
|
+
}).not.toThrow();
|
|
13
|
+
});
|
|
14
|
+
it("should allow useState in component setup", () => {
|
|
15
|
+
function Component() {
|
|
16
|
+
const state = useState({ count: 0 });
|
|
17
|
+
return () => _jsx("div", { children: state.count });
|
|
18
|
+
}
|
|
19
|
+
const container = document.createElement("div");
|
|
20
|
+
expect(() => {
|
|
21
|
+
render(_jsx(Component, {}), container);
|
|
22
|
+
}).not.toThrow();
|
|
23
|
+
});
|
|
24
|
+
it("should throw when useState is called during render", () => {
|
|
25
|
+
function Component() {
|
|
26
|
+
const state = useState({ count: 0 });
|
|
27
|
+
return () => {
|
|
28
|
+
// This should throw - useState in render scope
|
|
29
|
+
expect(() => {
|
|
30
|
+
useState({ nested: 1 });
|
|
31
|
+
}).toThrow("useState cannot be called during render");
|
|
32
|
+
return _jsx("div", { children: state.count });
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const container = document.createElement("div");
|
|
36
|
+
render(_jsx(Component, {}), container);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe("useEffect", () => {
|
|
40
|
+
it("should throw when useEffect is called outside component setup", () => {
|
|
41
|
+
expect(() => {
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
// Effect logic
|
|
44
|
+
});
|
|
45
|
+
}).toThrow("Only use useEffect in component setup");
|
|
46
|
+
});
|
|
47
|
+
it("should allow useEffect in component setup", () => {
|
|
48
|
+
function Component() {
|
|
49
|
+
const state = useState({ count: 0 });
|
|
50
|
+
let effectRan = false;
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
effectRan = true;
|
|
53
|
+
state.count;
|
|
54
|
+
});
|
|
55
|
+
expect(effectRan).toBe(true);
|
|
56
|
+
return () => _jsx("div", { children: state.count });
|
|
57
|
+
}
|
|
58
|
+
const container = document.createElement("div");
|
|
59
|
+
expect(() => {
|
|
60
|
+
render(_jsx(Component, {}), container);
|
|
61
|
+
}).not.toThrow();
|
|
62
|
+
});
|
|
63
|
+
it("should throw when useEffect is called during render", () => {
|
|
64
|
+
function Component() {
|
|
65
|
+
const state = useState({ count: 0 });
|
|
66
|
+
return () => {
|
|
67
|
+
// This should throw - useEffect in render scope
|
|
68
|
+
expect(() => {
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
state.count;
|
|
71
|
+
});
|
|
72
|
+
}).toThrow("Only use useEffect in component setup");
|
|
73
|
+
return _jsx("div", { children: state.count });
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const container = document.createElement("div");
|
|
77
|
+
render(_jsx(Component, {}), container);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("useDerived", () => {
|
|
81
|
+
it("should throw when useDerived is called outside component setup", () => {
|
|
82
|
+
expect(() => {
|
|
83
|
+
useDerived({
|
|
84
|
+
doubled: () => 2 * 2,
|
|
85
|
+
});
|
|
86
|
+
}).toThrow("Only use useDerived in component setup");
|
|
87
|
+
});
|
|
88
|
+
it("should allow useDerived in component setup", () => {
|
|
89
|
+
function Component() {
|
|
90
|
+
const state = useState({ count: 5 });
|
|
91
|
+
const computed = useDerived({
|
|
92
|
+
doubled: () => state.count * 2,
|
|
93
|
+
});
|
|
94
|
+
return () => _jsx("div", { children: computed.doubled });
|
|
95
|
+
}
|
|
96
|
+
const container = document.createElement("div");
|
|
97
|
+
expect(() => {
|
|
98
|
+
render(_jsx(Component, {}), container);
|
|
99
|
+
}).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
it("should throw when useDerived is called during render", () => {
|
|
102
|
+
function Component() {
|
|
103
|
+
const state = useState({ count: 0 });
|
|
104
|
+
return () => {
|
|
105
|
+
// This should throw - useDerived in render scope
|
|
106
|
+
expect(() => {
|
|
107
|
+
useDerived({
|
|
108
|
+
doubled: () => state.count * 2,
|
|
109
|
+
});
|
|
110
|
+
}).toThrow("Only use useDerived in component setup");
|
|
111
|
+
return _jsx("div", { children: state.count });
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const container = document.createElement("div");
|
|
115
|
+
render(_jsx(Component, {}), container);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe("Mixed scenarios", () => {
|
|
119
|
+
it("should allow all reactive primitives in setup but not in render", () => {
|
|
120
|
+
function Component() {
|
|
121
|
+
// All of these should work in setup
|
|
122
|
+
const state = useState({ count: 0 });
|
|
123
|
+
const computed = useDerived({
|
|
124
|
+
doubled: () => state.count * 2,
|
|
125
|
+
});
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
state.count;
|
|
128
|
+
});
|
|
129
|
+
return () => {
|
|
130
|
+
// None of these should work in render
|
|
131
|
+
expect(() => useState({ bad: 1 })).toThrow();
|
|
132
|
+
expect(() => useEffect(() => { })).toThrow();
|
|
133
|
+
expect(() => useDerived({ bad: () => 1 })).toThrow();
|
|
134
|
+
return _jsx("div", { children: computed.doubled });
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const container = document.createElement("div");
|
|
138
|
+
render(_jsx(Component, {}), container);
|
|
139
|
+
});
|
|
140
|
+
it("should properly enforce scope in nested renders", () => {
|
|
141
|
+
function Child() {
|
|
142
|
+
const childState = useState({ value: "child" });
|
|
143
|
+
return () => _jsx("div", { children: childState.value });
|
|
144
|
+
}
|
|
145
|
+
function Parent() {
|
|
146
|
+
const parentState = useState({ value: "parent" });
|
|
147
|
+
return () => {
|
|
148
|
+
// This should fail in render
|
|
149
|
+
expect(() => useState({ bad: 1 })).toThrow();
|
|
150
|
+
return (_jsxs("div", { children: [parentState.value, _jsx(Child, {})] }));
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const container = document.createElement("div");
|
|
154
|
+
render(_jsx(Parent, {}), container);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAction.test.d.ts","sourceRoot":"","sources":["../../src/tests/useAction.test.ts"],"names":[],"mappings":""}
|