rask-ui 0.28.3 → 0.28.4

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.
Files changed (38) hide show
  1. package/dist/tests/batch.test.js +202 -12
  2. package/dist/tests/createContext.test.js +50 -37
  3. package/dist/tests/error.test.js +25 -12
  4. package/dist/tests/renderCount.test.d.ts +2 -0
  5. package/dist/tests/renderCount.test.d.ts.map +1 -0
  6. package/dist/tests/renderCount.test.js +95 -0
  7. package/dist/tests/scopeEnforcement.test.d.ts +2 -0
  8. package/dist/tests/scopeEnforcement.test.d.ts.map +1 -0
  9. package/dist/tests/scopeEnforcement.test.js +157 -0
  10. package/dist/tests/useAction.test.d.ts +2 -0
  11. package/dist/tests/useAction.test.d.ts.map +1 -0
  12. package/dist/tests/useAction.test.js +132 -0
  13. package/dist/tests/useAsync.test.d.ts +2 -0
  14. package/dist/tests/useAsync.test.d.ts.map +1 -0
  15. package/dist/tests/useAsync.test.js +499 -0
  16. package/dist/tests/useDerived.test.d.ts +2 -0
  17. package/dist/tests/useDerived.test.d.ts.map +1 -0
  18. package/dist/tests/useDerived.test.js +407 -0
  19. package/dist/tests/useEffect.test.d.ts +2 -0
  20. package/dist/tests/useEffect.test.d.ts.map +1 -0
  21. package/dist/tests/useEffect.test.js +600 -0
  22. package/dist/tests/useLookup.test.d.ts +2 -0
  23. package/dist/tests/useLookup.test.d.ts.map +1 -0
  24. package/dist/tests/useLookup.test.js +299 -0
  25. package/dist/tests/useRef.test.d.ts +2 -0
  26. package/dist/tests/useRef.test.d.ts.map +1 -0
  27. package/dist/tests/useRef.test.js +189 -0
  28. package/dist/tests/useState.test.d.ts +2 -0
  29. package/dist/tests/useState.test.d.ts.map +1 -0
  30. package/dist/tests/useState.test.js +178 -0
  31. package/dist/tests/useSuspend.test.d.ts +2 -0
  32. package/dist/tests/useSuspend.test.d.ts.map +1 -0
  33. package/dist/tests/useSuspend.test.js +752 -0
  34. package/dist/tests/useView.test.d.ts +2 -0
  35. package/dist/tests/useView.test.d.ts.map +1 -0
  36. package/dist/tests/useView.test.js +305 -0
  37. package/dist/useState.js +4 -2
  38. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scopeEnforcement.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useAction.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAction.test.d.ts","sourceRoot":"","sources":["../../src/tests/useAction.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,132 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { useAction } from "../useAction";
3
+ describe("useAction", () => {
4
+ it("should initialize with idle state", () => {
5
+ const [state] = useAction(async () => "result");
6
+ expect(state.isPending).toBe(false);
7
+ expect(state.params).toBe(null);
8
+ expect(state.result).toBe(null);
9
+ expect(state.error).toBe(null);
10
+ });
11
+ it("should handle successful action without parameters", async () => {
12
+ const fn = vi.fn(async () => "success");
13
+ const [state, run] = useAction(fn);
14
+ run();
15
+ expect(state.isPending).toBe(true);
16
+ expect(state.params).toBe(null);
17
+ expect(state.result).toBe(null);
18
+ expect(state.error).toBe(null);
19
+ await new Promise((resolve) => setTimeout(resolve, 10));
20
+ expect(state.isPending).toBe(false);
21
+ expect(state.result).toBe("success");
22
+ expect(state.error).toBe(null);
23
+ expect(fn).toHaveBeenCalledTimes(1);
24
+ });
25
+ it("should handle successful action with parameters", async () => {
26
+ const fn = vi.fn(async (x) => x * 2);
27
+ const [state, run] = useAction(fn);
28
+ run(5);
29
+ expect(state.isPending).toBe(true);
30
+ expect(state.params).toBe(5);
31
+ expect(state.result).toBe(null);
32
+ await new Promise((resolve) => setTimeout(resolve, 10));
33
+ expect(state.isPending).toBe(false);
34
+ expect(state.params).toBe(5);
35
+ expect(state.result).toBe(10);
36
+ expect(state.error).toBe(null);
37
+ });
38
+ it("should handle errors", async () => {
39
+ const fn = vi.fn(async () => {
40
+ throw new Error("Test error");
41
+ });
42
+ const [state, run] = useAction(fn);
43
+ run();
44
+ expect(state.isPending).toBe(true);
45
+ await new Promise((resolve) => setTimeout(resolve, 10));
46
+ expect(state.isPending).toBe(false);
47
+ expect(state.result).toBe(null);
48
+ expect(state.error?.message).toBe("Test error");
49
+ });
50
+ it("should abort previous action when new action starts", async () => {
51
+ let resolveFirst = null;
52
+ let resolveSecond = null;
53
+ const fn = vi.fn(async (id) => {
54
+ if (id === 1) {
55
+ return new Promise((resolve) => {
56
+ resolveFirst = resolve;
57
+ });
58
+ }
59
+ else {
60
+ return new Promise((resolve) => {
61
+ resolveSecond = resolve;
62
+ });
63
+ }
64
+ });
65
+ const [state, run] = useAction(fn);
66
+ // Start first action
67
+ run(1);
68
+ expect(state.isPending).toBe(true);
69
+ expect(state.params).toBe(1);
70
+ await new Promise((resolve) => setTimeout(resolve, 10));
71
+ // Start second action (should abort first)
72
+ run(2);
73
+ expect(state.isPending).toBe(true);
74
+ expect(state.params).toBe(2);
75
+ // Resolve first action (should be ignored due to abort)
76
+ resolveFirst?.("first");
77
+ await new Promise((resolve) => setTimeout(resolve, 10));
78
+ // State should not reflect first action
79
+ expect(state.result).toBe(null);
80
+ // Resolve second action
81
+ resolveSecond?.("second");
82
+ await new Promise((resolve) => setTimeout(resolve, 10));
83
+ // State should reflect second action
84
+ expect(state.isPending).toBe(false);
85
+ expect(state.params).toBe(2);
86
+ expect(state.result).toBe("second");
87
+ });
88
+ it("should track params even on error", async () => {
89
+ const fn = vi.fn(async (x) => {
90
+ throw new Error("Failed");
91
+ });
92
+ const [state, run] = useAction(fn);
93
+ run(42);
94
+ await new Promise((resolve) => setTimeout(resolve, 10));
95
+ expect(state.isPending).toBe(false);
96
+ expect(state.params).toBe(42);
97
+ expect(state.error?.message).toBe("Failed");
98
+ expect(state.result).toBe(null);
99
+ });
100
+ it("should handle rapid consecutive calls", async () => {
101
+ let callCount = 0;
102
+ const fn = vi.fn(async (x) => {
103
+ callCount++;
104
+ await new Promise((resolve) => setTimeout(resolve, 5));
105
+ return x;
106
+ });
107
+ const [state, run] = useAction(fn);
108
+ // Fire off multiple calls
109
+ run(1);
110
+ run(2);
111
+ run(3);
112
+ // Wait for all to settle
113
+ await new Promise((resolve) => setTimeout(resolve, 50));
114
+ // Should have called function 3 times (not aborted, just ignored results)
115
+ expect(callCount).toBe(3);
116
+ // State should reflect the last call
117
+ expect(state.isPending).toBe(false);
118
+ expect(state.params).toBe(3);
119
+ expect(state.result).toBe(3);
120
+ });
121
+ it("should handle null as valid param value", async () => {
122
+ const fn = vi.fn(async (x) => {
123
+ return x === null ? "was null" : x;
124
+ });
125
+ const [state, run] = useAction(fn);
126
+ run(null);
127
+ await new Promise((resolve) => setTimeout(resolve, 10));
128
+ expect(state.isPending).toBe(false);
129
+ expect(state.params).toBe(null);
130
+ expect(state.result).toBe("was null");
131
+ });
132
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useAsync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAsync.test.d.ts","sourceRoot":"","sources":["../../src/tests/useAsync.test.tsx"],"names":[],"mappings":""}