@ersbeth/picoflow 2.0.1 → 2.0.3

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 (154) hide show
  1. package/README.md +10 -0
  2. package/SKILL.md +106 -0
  3. package/dist/types/api/base/flowSubscribable.d.ts +3 -3
  4. package/dist/types/api/base/flowSubscribable.d.ts.map +1 -1
  5. package/package.json +5 -1
  6. package/.gitlab-ci.yml +0 -24
  7. package/.vscode/settings.json +0 -5
  8. package/CHANGELOG.md +0 -94
  9. package/biome.json +0 -47
  10. package/docs/.vitepress/config.mts +0 -145
  11. package/docs/api/functions/array.md +0 -35
  12. package/docs/api/functions/constant.md +0 -33
  13. package/docs/api/functions/constantAsync.md +0 -69
  14. package/docs/api/functions/derivation.md +0 -34
  15. package/docs/api/functions/derivationAsync.md +0 -34
  16. package/docs/api/functions/from.md +0 -129
  17. package/docs/api/functions/isDisposable.md +0 -27
  18. package/docs/api/functions/map.md +0 -36
  19. package/docs/api/functions/signal.md +0 -21
  20. package/docs/api/functions/state.md +0 -67
  21. package/docs/api/functions/stateAsync.md +0 -69
  22. package/docs/api/functions/subscribe.md +0 -40
  23. package/docs/api/functions/writableDerivation.md +0 -33
  24. package/docs/api/functions/writableDerivationAsync.md +0 -34
  25. package/docs/api/index.md +0 -61
  26. package/docs/api/interfaces/FlowArray.md +0 -439
  27. package/docs/api/interfaces/FlowConstant.md +0 -220
  28. package/docs/api/interfaces/FlowConstantAsync.md +0 -221
  29. package/docs/api/interfaces/FlowDerivation.md +0 -241
  30. package/docs/api/interfaces/FlowDerivationAsync.md +0 -242
  31. package/docs/api/interfaces/FlowDisposable.md +0 -59
  32. package/docs/api/interfaces/FlowEffect.md +0 -64
  33. package/docs/api/interfaces/FlowMap.md +0 -374
  34. package/docs/api/interfaces/FlowObservable.md +0 -155
  35. package/docs/api/interfaces/FlowSignal.md +0 -156
  36. package/docs/api/interfaces/FlowState.md +0 -269
  37. package/docs/api/interfaces/FlowStateAsync.md +0 -268
  38. package/docs/api/interfaces/FlowSubscribable.md +0 -55
  39. package/docs/api/interfaces/FlowTracker.md +0 -61
  40. package/docs/api/interfaces/FlowValue.md +0 -222
  41. package/docs/api/interfaces/FlowWritableDerivation.md +0 -292
  42. package/docs/api/interfaces/FlowWritableDerivationAsync.md +0 -293
  43. package/docs/api/type-aliases/DerivationFunction.md +0 -28
  44. package/docs/api/type-aliases/DerivationFunctionAsync.md +0 -28
  45. package/docs/api/type-aliases/FlowArrayAction.md +0 -60
  46. package/docs/api/type-aliases/FlowDataTracker.md +0 -33
  47. package/docs/api/type-aliases/FlowMapAction.md +0 -48
  48. package/docs/api/type-aliases/FlowOnDataListener.md +0 -33
  49. package/docs/api/type-aliases/FlowOnErrorListener.md +0 -27
  50. package/docs/api/type-aliases/FlowOnPendingListener.md +0 -21
  51. package/docs/api/type-aliases/FlowReadonly.md +0 -22
  52. package/docs/api/type-aliases/InitFunction.md +0 -21
  53. package/docs/api/type-aliases/InitFunctionAsync.md +0 -21
  54. package/docs/api/type-aliases/NotPromise.md +0 -21
  55. package/docs/api/type-aliases/UpdateFunction.md +0 -27
  56. package/docs/api/type-aliases/UpdateFunctionAsync.md +0 -27
  57. package/docs/api/typedoc-sidebar.json +0 -65
  58. package/docs/examples/examples.md +0 -2311
  59. package/docs/examples/patterns.md +0 -649
  60. package/docs/guide/advanced/architecture.md +0 -1234
  61. package/docs/guide/advanced/disposal.md +0 -426
  62. package/docs/guide/advanced/migration-v1.md +0 -464
  63. package/docs/guide/advanced/migration-v2.md +0 -204
  64. package/docs/guide/advanced/solidjs.md +0 -135
  65. package/docs/guide/introduction/concepts.md +0 -57
  66. package/docs/guide/introduction/conventions.md +0 -30
  67. package/docs/guide/introduction/getting-started.md +0 -139
  68. package/docs/guide/introduction/lifecycle.md +0 -368
  69. package/docs/guide/primitives/array.md +0 -286
  70. package/docs/guide/primitives/constant.md +0 -207
  71. package/docs/guide/primitives/derivations.md +0 -281
  72. package/docs/guide/primitives/effects.md +0 -372
  73. package/docs/guide/primitives/map.md +0 -265
  74. package/docs/guide/primitives/overview.md +0 -92
  75. package/docs/guide/primitives/signal.md +0 -222
  76. package/docs/guide/primitives/state.md +0 -272
  77. package/docs/index.md +0 -47
  78. package/docs/public/logo.svg +0 -1
  79. package/src/api/base/flowDisposable.ts +0 -44
  80. package/src/api/base/flowObservable.ts +0 -28
  81. package/src/api/base/flowSubscribable.ts +0 -87
  82. package/src/api/base/flowTracker.ts +0 -7
  83. package/src/api/base/index.ts +0 -4
  84. package/src/api/index.ts +0 -2
  85. package/src/api/nodes/async/flowConstantAsync.ts +0 -36
  86. package/src/api/nodes/async/flowDerivationAsync.ts +0 -42
  87. package/src/api/nodes/async/flowStateAsync.ts +0 -47
  88. package/src/api/nodes/async/flowWritableDerivationAsync.ts +0 -33
  89. package/src/api/nodes/async/index.ts +0 -4
  90. package/src/api/nodes/collections/flowArray.ts +0 -155
  91. package/src/api/nodes/collections/flowMap.ts +0 -115
  92. package/src/api/nodes/collections/index.ts +0 -2
  93. package/src/api/nodes/flowEffect.ts +0 -42
  94. package/src/api/nodes/flowSignal.ts +0 -28
  95. package/src/api/nodes/flowValue.ts +0 -37
  96. package/src/api/nodes/index.ts +0 -7
  97. package/src/api/nodes/sync/flowConstant.ts +0 -33
  98. package/src/api/nodes/sync/flowDerivation.ts +0 -41
  99. package/src/api/nodes/sync/flowState.ts +0 -45
  100. package/src/api/nodes/sync/flowWritableDerivation.ts +0 -31
  101. package/src/api/nodes/sync/index.ts +0 -4
  102. package/src/api/nodes/utils.ts +0 -24
  103. package/src/base/disposable.ts +0 -18
  104. package/src/base/executionStack.ts +0 -42
  105. package/src/base/index.ts +0 -5
  106. package/src/base/node.ts +0 -98
  107. package/src/base/observable.ts +0 -92
  108. package/src/base/observer.ts +0 -51
  109. package/src/converters/index.ts +0 -1
  110. package/src/converters/solid.ts +0 -109
  111. package/src/index.ts +0 -2
  112. package/src/nodes/arrayNode.ts +0 -180
  113. package/src/nodes/effectNode.ts +0 -58
  114. package/src/nodes/index.ts +0 -7
  115. package/src/nodes/mapNode.ts +0 -125
  116. package/src/nodes/signalNode.ts +0 -19
  117. package/src/nodes/valueAsyncNode.ts +0 -85
  118. package/src/nodes/valueNode.ts +0 -148
  119. package/src/nodes/valueSyncNode.ts +0 -125
  120. package/src/schedulers/asyncResolver.ts +0 -78
  121. package/src/schedulers/asyncScheduler.ts +0 -66
  122. package/src/schedulers/index.ts +0 -4
  123. package/src/schedulers/pendingError.ts +0 -13
  124. package/src/schedulers/scheduler.ts +0 -9
  125. package/src/schedulers/syncResolver.ts +0 -69
  126. package/src/schedulers/syncScheduler.ts +0 -55
  127. package/test/base/pendingError.test.ts +0 -67
  128. package/test/converters/solid.derivation.browser.test.tsx +0 -69
  129. package/test/converters/solid.node.test.ts +0 -654
  130. package/test/converters/solid.state.browser.test.tsx +0 -1592
  131. package/test/reactivity/flowSignal.test.ts +0 -226
  132. package/test/reactivity/nodes/async/asyncScheduler/asyncResolver.test.ts +0 -593
  133. package/test/reactivity/nodes/async/asyncScheduler/asyncScheduler.test.ts +0 -317
  134. package/test/reactivity/nodes/async/flowConstantAsync.test.ts +0 -652
  135. package/test/reactivity/nodes/async/flowDerivation.test.ts +0 -898
  136. package/test/reactivity/nodes/async/flowDerivationAsync.test.ts +0 -1716
  137. package/test/reactivity/nodes/async/flowStateAsync.test.ts +0 -708
  138. package/test/reactivity/nodes/async/flowWritableDerivationAsync.test.ts +0 -614
  139. package/test/reactivity/nodes/collections/flowArray.asyncStates.test.ts +0 -1289
  140. package/test/reactivity/nodes/collections/flowArray.scalars.test.ts +0 -961
  141. package/test/reactivity/nodes/collections/flowArray.states.test.ts +0 -1035
  142. package/test/reactivity/nodes/collections/flowMap.asyncStates.test.ts +0 -960
  143. package/test/reactivity/nodes/collections/flowMap.scalars.test.ts +0 -775
  144. package/test/reactivity/nodes/collections/flowMap.states.test.ts +0 -958
  145. package/test/reactivity/nodes/sync/flowConstant.test.ts +0 -377
  146. package/test/reactivity/nodes/sync/flowDerivation.test.ts +0 -896
  147. package/test/reactivity/nodes/sync/flowState.test.ts +0 -341
  148. package/test/reactivity/nodes/sync/flowWritableDerivation.test.ts +0 -603
  149. package/test/vitest.d.ts +0 -10
  150. package/tsconfig.json +0 -37
  151. package/typedoc.json +0 -37
  152. package/vite.config.ts +0 -31
  153. package/vitest.browser.config.ts +0 -21
  154. package/vitest.config.ts +0 -17
@@ -1,1592 +0,0 @@
1
- import { cleanup, render } from "@solidjs/testing-library";
2
- import { type Component, createResource, ErrorBoundary, Match, Suspense, Switch } from "solid-js";
3
- import { beforeEach, describe, expect, it, vi } from "vitest";
4
- import { userEvent } from "vitest/browser";
5
- import { type FlowState, from, state, stateAsync } from "~";
6
-
7
- describe("solid converter", () => {
8
- beforeEach(() => {
9
- cleanup();
10
- });
11
-
12
- describe("initialization", () => {
13
- describe("happy path", () => {
14
- it("init-happy-path: should convert FlowState to SolidResource", async () => {
15
- const Comp: Component = () => {
16
- const $state = state(42);
17
- const $$state = from($state);
18
-
19
- return <div>{$$state()}</div>;
20
- };
21
-
22
- const { getByText } = render(() => <Comp />);
23
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
24
- });
25
-
26
- it("init-happy-path: should convert AsyncFlowState to SolidResource", async () => {
27
- const Comp: Component = () => {
28
- const $stateAsync = stateAsync(Promise.resolve(42));
29
- const $$stateAsync = from($stateAsync);
30
-
31
- return <div>{$$stateAsync()}</div>;
32
- };
33
-
34
- const { getByText } = render(() => <Comp />);
35
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
36
- });
37
-
38
- it("init-happy-path: should convert getter function to SolidResource", async () => {
39
- const Comp: Component = () => {
40
- const $a = state(10);
41
- const $b = state(20);
42
-
43
- const $$result = from((t) => $a.get(t) + $b.get(t));
44
-
45
- return <div>{$$result()}</div>;
46
- };
47
-
48
- const { getByText } = render(() => <Comp />);
49
-
50
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
51
- });
52
-
53
- it("init-happy-path: should convert async getter function to SolidResource", async () => {
54
- const Comp: Component = () => {
55
- const $a = stateAsync<number>(Promise.resolve(10));
56
- const $b = state(20);
57
-
58
- const $$result = from((t) => $a.get(t) + $b.get(t));
59
-
60
- return <div>{$$result()}</div>;
61
- };
62
-
63
- const { getByText } = render(() => <Comp />);
64
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
65
- });
66
- });
67
-
68
- describe("suspense", () => {
69
- describe("flag", () => {
70
- it("init-suspense-flag: should handle suspense in FlowStateAsync", async () => {
71
- const Comp: Component = () => {
72
- const $stateAsync = stateAsync<number>(Promise.resolve(42));
73
- const $$stateAsync = from($stateAsync);
74
-
75
- return (
76
- <Switch>
77
- <Match when={$$stateAsync.loading}>
78
- <div>Loading</div>
79
- </Match>
80
- <Match when={$$stateAsync()}>
81
- <div>{$$stateAsync()}</div>
82
- </Match>
83
- </Switch>
84
- );
85
- };
86
-
87
- const { getByText } = render(() => <Comp />);
88
-
89
- await expect.element(getByText("Loading")).toBeInTheDocument();
90
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
91
- });
92
-
93
- it("init-suspense-flag: should handle suspense in async getter function", async () => {
94
- const Comp: Component = () => {
95
- const $a = stateAsync<number>(Promise.resolve(10));
96
- const $b = state(20);
97
-
98
- const $$result = from((t) => $a.get(t) + $b.get(t));
99
-
100
- return (
101
- <Switch>
102
- <Match when={$$result.loading}>
103
- <div>Loading</div>
104
- </Match>
105
- <Match when={$$result()}>
106
- <div>{$$result()}</div>
107
- </Match>
108
- </Switch>
109
- );
110
- };
111
-
112
- const { getByText } = render(() => <Comp />);
113
-
114
- await expect.element(getByText("Loading")).toBeInTheDocument();
115
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
116
- });
117
- });
118
-
119
- describe("component", () => {
120
- it("init-suspense-component: should handle suspense in FlowState", async () => {
121
- const Comp: Component = () => {
122
- const $state = state<number>(42);
123
- const $$state = from($state);
124
-
125
- return (
126
- <Suspense fallback="Loading">
127
- <div>{$$state()}</div>
128
- </Suspense>
129
- );
130
- };
131
-
132
- const { getByText } = render(() => <Comp />);
133
-
134
- await expect.element(getByText("Loading")).toBeInTheDocument();
135
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
136
- });
137
-
138
- it("init-suspense-component: should handle suspense in AsyncFlowState", async () => {
139
- const Comp: Component = () => {
140
- const $stateAsync = stateAsync<number>(Promise.resolve(42));
141
- const $$stateAsync = from($stateAsync);
142
-
143
- return (
144
- <Suspense fallback="Loading">
145
- <div>{$$stateAsync()}</div>
146
- </Suspense>
147
- );
148
- };
149
-
150
- const { getByText } = render(() => <Comp />);
151
-
152
- await expect.element(getByText("Loading")).toBeInTheDocument();
153
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
154
- });
155
-
156
- it("init-suspense-component: should handle suspense in getter function", async () => {
157
- const Comp: Component = () => {
158
- const $a = state<number>(10);
159
- const $b = state(20);
160
-
161
- const $$result = from((t) => $a.get(t) + $b.get(t));
162
- return (
163
- <Suspense fallback="Loading">
164
- <div>{$$result()}</div>
165
- </Suspense>
166
- );
167
- };
168
-
169
- const { getByText } = render(() => <Comp />);
170
-
171
- await expect.element(getByText("Loading")).toBeInTheDocument();
172
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
173
- });
174
-
175
- it("init-suspense-component: should handle suspense in async getter function", async () => {
176
- const Comp: Component = () => {
177
- const $a = stateAsync<number>(Promise.resolve(10));
178
- const $b = state(20);
179
-
180
- const $$result = from((t) => $a.get(t) + $b.get(t));
181
- return (
182
- <Suspense fallback="Loading">
183
- <div>{$$result()}</div>
184
- </Suspense>
185
- );
186
- };
187
-
188
- const { getByText } = render(() => <Comp />);
189
-
190
- await expect.element(getByText("Loading")).toBeInTheDocument();
191
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
192
- });
193
- });
194
- });
195
-
196
- describe("error", () => {
197
- describe("flag", () => {
198
- it("init-error-flag: should handle error in FlowState", async () => {
199
- const Comp: Component = () => {
200
- const $state = state<number>(() => {
201
- throw new Error("Test error");
202
- });
203
- const $$state = from($state);
204
- return (
205
- <Switch>
206
- <Match when={$$state.error}>
207
- <div>Error</div>
208
- </Match>
209
- <Match when={$$state()}>
210
- <div>{$$state()}</div>
211
- </Match>
212
- </Switch>
213
- );
214
- };
215
-
216
- const { getByText } = render(() => <Comp />);
217
-
218
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
219
- });
220
-
221
- it("init-error-flag: should handle error in AsyncFlowState", async () => {
222
- const Comp: Component = () => {
223
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
224
- const $$stateAsync = from($stateAsync);
225
-
226
- return (
227
- <Switch>
228
- <Match when={$$stateAsync.error}>
229
- <div>Error</div>
230
- </Match>
231
- <Match when={$$stateAsync()}>
232
- <div>{$$stateAsync()}</div>
233
- </Match>
234
- </Switch>
235
- );
236
- };
237
-
238
- const { getByText } = render(() => <Comp />);
239
-
240
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
241
- });
242
-
243
- it("init-error-flag: should handle error in getter function", async () => {
244
- const Comp: Component = () => {
245
- const $a = state<number>(() => {
246
- throw new Error("Test error");
247
- });
248
- const $b = state(20);
249
-
250
- const $$result = from((t) => $a.get(t) + $b.get(t));
251
-
252
- return (
253
- <Switch>
254
- <Match when={$$result.error}>
255
- <div>Error</div>
256
- </Match>
257
- <Match when={$$result()}>
258
- <div>{$$result()}</div>
259
- </Match>
260
- </Switch>
261
- );
262
- };
263
-
264
- const { getByText } = render(() => <Comp />);
265
-
266
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
267
- });
268
-
269
- it("init-error-flag: should handle error in async getter function", async () => {
270
- const Comp: Component = () => {
271
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
272
- const $b = state(20);
273
-
274
- const $$result = from((t) => $a.get(t) + $b.get(t));
275
-
276
- return (
277
- <Switch>
278
- <Match when={$$result.error}>
279
- <div>Error</div>
280
- </Match>
281
- <Match when={$$result()}>
282
- <div>{$$result()}</div>
283
- </Match>
284
- </Switch>
285
- );
286
- };
287
-
288
- const { getByText } = render(() => <Comp />);
289
-
290
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
291
- });
292
- });
293
-
294
- describe("component", () => {
295
- it("init-error-component: should handle error in FlowState", async () => {
296
- const Comp: Component = () => {
297
- const $state = state<number>(() => {
298
- throw new Error("Test error");
299
- });
300
- const $$state = from($state);
301
-
302
- return (
303
- <ErrorBoundary fallback="Error">
304
- <div>{$$state()}</div>
305
- </ErrorBoundary>
306
- );
307
- };
308
-
309
- const { getByText } = render(() => <Comp />);
310
-
311
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
312
- });
313
-
314
- it("init-error-component: should handle error in AsyncFlowState", async () => {
315
- const Comp: Component = () => {
316
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
317
- const $$stateAsync = from($stateAsync);
318
-
319
- return (
320
- <ErrorBoundary fallback="Error">
321
- <div>{$$stateAsync()}</div>
322
- </ErrorBoundary>
323
- );
324
- };
325
-
326
- const { getByText } = render(() => <Comp />);
327
-
328
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
329
- });
330
-
331
- it("init-error-component: should handle error in getter function", async () => {
332
- const Comp: Component = () => {
333
- const $a = state<number>(() => {
334
- throw new Error("Test error");
335
- });
336
- const $b = state(20);
337
-
338
- const $$result = from((t) => $a.get(t) + $b.get(t));
339
-
340
- return (
341
- <ErrorBoundary fallback="Error">
342
- <div>{$$result()}</div>
343
- </ErrorBoundary>
344
- );
345
- };
346
-
347
- const { getByText } = render(() => <Comp />);
348
-
349
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
350
- });
351
-
352
- it("init-error-component: should handle error in async getter function", async () => {
353
- const Comp: Component = () => {
354
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
355
- const $b = state(20);
356
-
357
- const $$result = from((t) => $a.get(t) + $b.get(t));
358
-
359
- return (
360
- <ErrorBoundary fallback="Error">
361
- <div>{$$result()}</div>
362
- </ErrorBoundary>
363
- );
364
- };
365
-
366
- const { getByText } = render(() => <Comp />);
367
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
368
- });
369
- });
370
- });
371
-
372
- describe("suspense-error", () => {
373
- describe("flag", () => {
374
- it("init-suspense-error-flag: should handle suspense and error in AsyncFlowState", async () => {
375
- const Comp: Component = () => {
376
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
377
- const $$stateAsync = from($stateAsync);
378
-
379
- return (
380
- <Switch>
381
- <Match when={$$stateAsync.error}>
382
- <div>Error</div>
383
- </Match>
384
- <Match when={$$stateAsync.loading}>
385
- <div>Loading</div>
386
- </Match>
387
- <Match when={$$stateAsync()}>
388
- <div>{$$stateAsync()}</div>
389
- </Match>
390
- </Switch>
391
- );
392
- };
393
-
394
- const { getByText } = render(() => <Comp />);
395
-
396
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
397
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
398
- });
399
-
400
- it("init-suspense-error-flag: should handle suspense and error in async getter function", async () => {
401
- const Comp: Component = () => {
402
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
403
- const $b = state(20);
404
-
405
- const $$result = from((t) => $a.get(t) + $b.get(t));
406
-
407
- return (
408
- <Switch>
409
- <Match when={$$result.error}>
410
- <div>Error</div>
411
- </Match>
412
- <Match when={$$result.loading}>
413
- <div>Loading</div>
414
- </Match>
415
- <Match when={$$result()}>
416
- <div>{$$result()}</div>
417
- </Match>
418
- </Switch>
419
- );
420
- };
421
-
422
- const { getByText } = render(() => <Comp />);
423
-
424
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
425
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
426
- });
427
- });
428
-
429
- describe("component", () => {
430
- it("init-suspense-error-component: should handle suspense and error in FlowState", async () => {
431
- const Comp: Component = () => {
432
- const $state = state<number>(() => {
433
- throw new Error("Test error");
434
- });
435
- const $$state = from($state);
436
-
437
- return (
438
- <Suspense fallback="Loading">
439
- <ErrorBoundary fallback="Error">
440
- <div>{$$state()}</div>
441
- </ErrorBoundary>
442
- </Suspense>
443
- );
444
- };
445
-
446
- const { getByText } = render(() => <Comp />);
447
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
448
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
449
- });
450
-
451
- it("init-suspense-error-component: should handle suspense and error in AsyncFlowState", async () => {
452
- const Comp: Component = () => {
453
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
454
- const $$stateAsync = from($stateAsync);
455
-
456
- return (
457
- <Suspense fallback="Loading">
458
- <ErrorBoundary fallback="Error">
459
- <div>{$$stateAsync()}</div>
460
- </ErrorBoundary>
461
- </Suspense>
462
- );
463
- };
464
-
465
- const { getByText } = render(() => <Comp />);
466
-
467
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
468
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
469
- });
470
-
471
- it("init-suspense-error-component: should handle suspense and error in getter function", async () => {
472
- const Comp: Component = () => {
473
- const $a = state<number>(() => {
474
- throw new Error("Test error");
475
- });
476
- const $b = state(20);
477
-
478
- const $$result = from((t) => $a.get(t) + $b.get(t));
479
-
480
- return (
481
- <Suspense fallback="Loading">
482
- <ErrorBoundary fallback="Error">
483
- <div>{$$result()}</div>
484
- </ErrorBoundary>
485
- </Suspense>
486
- );
487
- };
488
-
489
- const { getByText } = render(() => <Comp />);
490
-
491
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
492
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
493
- });
494
-
495
- it("init-suspense-error-component: should handle suspense and error in async getter function", async () => {
496
- const Comp: Component = () => {
497
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
498
- const $b = state(20);
499
-
500
- const $$result = from((t) => $a.get(t) + $b.get(t));
501
-
502
- return (
503
- <Suspense fallback="Loading">
504
- <ErrorBoundary fallback="Error">
505
- <div>{$$result()}</div>
506
- </ErrorBoundary>
507
- </Suspense>
508
- );
509
- };
510
-
511
- const { getByText } = render(() => <Comp />);
512
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
513
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
514
- });
515
- });
516
- });
517
- });
518
-
519
- describe("updates", () => {
520
- describe("happy path", () => {
521
- it("update-happy-path: should update FlowState", async () => {
522
- const Comp: Component = () => {
523
- const $state = state<number>(42);
524
- const $$state = from($state);
525
-
526
- return (
527
- <div>
528
- <div>{$$state()}</div>
529
- <button type="button" onClick={() => $state.set(43)}>
530
- Update
531
- </button>
532
- </div>
533
- );
534
- };
535
-
536
- const { getByText } = render(() => <Comp />);
537
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
538
- await userEvent.click(getByText("Update"));
539
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
540
- });
541
-
542
- it("update-happy-path: should update AsyncFlowState", async () => {
543
- const Comp: Component = () => {
544
- const $stateAsync = stateAsync<number>(Promise.resolve(42));
545
- const $$stateAsync = from($stateAsync);
546
-
547
- return (
548
- <div>
549
- <div>{$$stateAsync()}</div>
550
- <button type="button" onClick={() => $stateAsync.set(Promise.resolve(43))}>
551
- Update
552
- </button>
553
- </div>
554
- );
555
- };
556
-
557
- const { getByText } = render(() => <Comp />);
558
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
559
- await userEvent.click(getByText("Update"));
560
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
561
- });
562
-
563
- it("update-happy-path: should update getter function", async () => {
564
- const Comp: Component = () => {
565
- const $a = state<number>(10);
566
- const $b = state(20);
567
-
568
- const $$result = from((t) => $a.get(t) + $b.get(t));
569
-
570
- return (
571
- <div>
572
- <div>{$$result()}</div>
573
- <button type="button" onClick={() => $a.set(11)}>
574
- Update
575
- </button>
576
- </div>
577
- );
578
- };
579
-
580
- const { getByText } = render(() => <Comp />);
581
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
582
- await userEvent.click(getByText("Update"));
583
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
584
- });
585
-
586
- it("update-happy-path: should update async getter function", async () => {
587
- const Comp: Component = () => {
588
- const $a = stateAsync<number>(Promise.resolve(10));
589
- const $b = state(20);
590
-
591
- const $$result = from((t) => $a.get(t) + $b.get(t));
592
- return (
593
- <div>
594
- <div>{$$result()}</div>
595
- <button type="button" onClick={() => $a.set(Promise.resolve(11))}>
596
- Update
597
- </button>
598
- </div>
599
- );
600
- };
601
-
602
- const { getByText } = render(() => <Comp />);
603
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
604
- await userEvent.click(getByText("Update"));
605
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
606
- });
607
- });
608
-
609
- describe("suspense", () => {
610
- describe("flag", () => {
611
- it("update-suspense-flag: should handle suspense in AsyncFlowState", async () => {
612
- const Comp: Component = () => {
613
- const $stateAsync = stateAsync<number>(Promise.resolve(42));
614
- const $$stateAsync = from($stateAsync);
615
-
616
- return (
617
- <Switch>
618
- <Match when={$$stateAsync.loading}>
619
- <div>Loading</div>
620
- </Match>
621
- <Match when={$$stateAsync()}>
622
- <div>{$$stateAsync()}</div>
623
- <button
624
- type="button"
625
- onClick={() =>
626
- $stateAsync.set(
627
- new Promise((resolve) => setTimeout(() => resolve(43), 100)),
628
- )
629
- }
630
- >
631
- Update
632
- </button>
633
- </Match>
634
- </Switch>
635
- );
636
- };
637
-
638
- const { getByText } = render(() => <Comp />);
639
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
640
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
641
- await userEvent.click(getByText("Update"));
642
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
643
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
644
- });
645
-
646
- it("update-suspense-flag: should handle suspense in async getter function", async () => {
647
- const Comp: Component = () => {
648
- const $a = stateAsync<number>(Promise.resolve(10));
649
- const $b = state(20);
650
-
651
- const $$result = from((t) => $a.get(t) + $b.get(t));
652
-
653
- return (
654
- <Switch>
655
- <Match when={$$result.loading}>
656
- <div>Loading</div>
657
- </Match>
658
- <Match when={$$result()}>
659
- <div>{$$result()}</div>
660
- <button
661
- type="button"
662
- onClick={() =>
663
- $a.set(new Promise((resolve) => setTimeout(() => resolve(11), 100)))
664
- }
665
- >
666
- Update
667
- </button>
668
- </Match>
669
- </Switch>
670
- );
671
- };
672
-
673
- const { getByText } = render(() => <Comp />);
674
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
675
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
676
- await userEvent.click(getByText("Update"));
677
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
678
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
679
- });
680
- });
681
-
682
- describe("component", () => {
683
- it("update-suspense-component: should handle suspense in FlowState", async () => {
684
- const Comp: Component = () => {
685
- const $state = state<number>(42);
686
- const $$state = from($state);
687
-
688
- return (
689
- <Suspense fallback="Loading">
690
- <div>{$$state()}</div>
691
- <button type="button" onClick={() => $state.set(43)}>
692
- Update
693
- </button>
694
- </Suspense>
695
- );
696
- };
697
-
698
- const { getByText } = render(() => <Comp />);
699
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
700
-
701
- await userEvent.click(getByText("Update"));
702
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
703
- });
704
-
705
- it.skip("temp", async () => {
706
- const Comp: Component = () => {
707
- let value = 42;
708
-
709
- const [resource, { refetch }] = createResource<number>(() => {
710
- return new Promise((resolve) => setTimeout(() => resolve(value), 500));
711
- });
712
-
713
- return (
714
- <Suspense fallback="Loading">
715
- <div>{resource()}</div>
716
- <button
717
- type="button"
718
- onClick={() => {
719
- value = 43;
720
- refetch();
721
- }}
722
- >
723
- Update
724
- </button>
725
- </Suspense>
726
- );
727
- };
728
-
729
- const { getByText } = render(() => <Comp />);
730
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
731
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
732
-
733
- await userEvent.click(getByText("Update"));
734
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
735
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
736
- });
737
-
738
- it("update-suspense-component: should handle suspense in AsyncFlowState", async () => {
739
- const Comp: Component = () => {
740
- const $stateAsync = stateAsync<number>(
741
- new Promise((resolve) => setTimeout(() => resolve(42), 500)),
742
- );
743
- const $$stateAsync = from($stateAsync);
744
-
745
- return (
746
- <Suspense fallback="Loading">
747
- <div>{$$stateAsync()}</div>
748
- <button
749
- type="button"
750
- onClick={() =>
751
- $stateAsync.set(new Promise((resolve) => setTimeout(() => resolve(43), 500)))
752
- }
753
- >
754
- Update
755
- </button>
756
- </Suspense>
757
- );
758
- };
759
-
760
- const { getByText } = render(() => <Comp />);
761
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
762
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
763
-
764
- await userEvent.click(getByText("Update"));
765
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
766
- await vi.waitFor(() => expect.element(getByText("43")).toBeInTheDocument());
767
- });
768
-
769
- it("update-suspense-component: should handle suspense in getter function", async () => {
770
- const Comp: Component = () => {
771
- const $a = state<number>(10);
772
- const $b = state(20);
773
-
774
- const $$result = from((t) => $a.get(t) + $b.get(t));
775
- return (
776
- <Suspense fallback="Loading">
777
- <div>{$$result()}</div>
778
- <button type="button" onClick={() => $a.set(11)}>
779
- Update
780
- </button>
781
- </Suspense>
782
- );
783
- };
784
-
785
- const { getByText } = render(() => <Comp />);
786
- await expect.element(getByText("Loading")).toBeInTheDocument();
787
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
788
- await userEvent.click(getByText("Update"));
789
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
790
- });
791
-
792
- it("update-suspense-component: should handle suspense in async getter function", async () => {
793
- const Comp: Component = () => {
794
- const $a = stateAsync<number>(Promise.resolve(10));
795
- const $b = state(20);
796
-
797
- const $$result = from((t) => $a.get(t) + $b.get(t));
798
- return (
799
- <div>
800
- <Suspense fallback="Loading">
801
- <div>{$$result()}</div>
802
- </Suspense>
803
- <button
804
- type="button"
805
- onClick={() => $a.set(new Promise((resolve) => setTimeout(() => resolve(11), 100)))}
806
- >
807
- Update
808
- </button>
809
- </div>
810
- );
811
- };
812
-
813
- const { getByText } = render(() => <Comp />);
814
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
815
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
816
-
817
- await userEvent.click(getByText("Update"));
818
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
819
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
820
- });
821
- });
822
- });
823
-
824
- describe("error", () => {
825
- describe("flag", () => {
826
- it("update-error-flag: should recover from initial error in FlowState", async () => {
827
- const Comp: Component = () => {
828
- const $state = state<number>(() => {
829
- throw new Error("Test error");
830
- });
831
- const $$state = from($state);
832
- return (
833
- <div>
834
- <Switch>
835
- <Match when={$$state.error}>
836
- <div>Error</div>
837
- </Match>
838
- <Match when={$$state()}>
839
- <div>{$$state()}</div>
840
- </Match>
841
- </Switch>
842
- <button type="button" onClick={() => $state.set(42)}>
843
- Update Value
844
- </button>
845
- </div>
846
- );
847
- };
848
-
849
- const { getByText } = render(() => <Comp />);
850
-
851
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
852
- await userEvent.click(getByText("Update Value"));
853
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
854
- });
855
-
856
- it("update-error-flag: should recover from initial error in AsyncFlowState", async () => {
857
- const Comp: Component = () => {
858
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
859
- const $$stateAsync = from($stateAsync);
860
-
861
- return (
862
- <div>
863
- <Switch>
864
- <Match when={$$stateAsync.error}>
865
- <div>Error</div>
866
- </Match>
867
- <Match when={$$stateAsync()}>
868
- <div>{$$stateAsync()}</div>
869
- </Match>
870
- </Switch>
871
- <button type="button" onClick={() => $stateAsync.set(Promise.resolve(42))}>
872
- Update Value
873
- </button>
874
- </div>
875
- );
876
- };
877
-
878
- const { getByText } = render(() => <Comp />);
879
-
880
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
881
- await userEvent.click(getByText("Update Value"));
882
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
883
- });
884
-
885
- it("update-error-flag: should recover from initial error in getter function", async () => {
886
- const Comp: Component = () => {
887
- const $a = state<number>(() => {
888
- throw new Error("Test error");
889
- });
890
- const $b = state(20);
891
-
892
- const $$result = from((t) => $a.get(t) + $b.get(t));
893
-
894
- return (
895
- <div>
896
- <Switch>
897
- <Match when={$$result.error}>
898
- <div>Error</div>
899
- </Match>
900
- <Match when={$$result()}>
901
- <div>{$$result()}</div>
902
- </Match>
903
- </Switch>
904
- <button type="button" onClick={() => $a.set(11)}>
905
- Update Value
906
- </button>
907
- </div>
908
- );
909
- };
910
-
911
- const { getByText } = render(() => <Comp />);
912
-
913
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
914
- await userEvent.click(getByText("Update Value"));
915
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
916
- });
917
-
918
- it("update-error-flag: should recover from initial error in async getter function", async () => {
919
- const Comp: Component = () => {
920
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
921
- const $b = state(20);
922
-
923
- const $$result = from((t) => $a.get(t) + $b.get(t));
924
-
925
- return (
926
- <div>
927
- <Switch>
928
- <Match when={$$result.error}>
929
- <div>Error</div>
930
- </Match>
931
- <Match when={$$result()}>
932
- <div>{$$result()}</div>
933
- </Match>
934
- </Switch>
935
- <button type="button" onClick={() => $a.set(Promise.resolve(11))}>
936
- Update Value
937
- </button>
938
- </div>
939
- );
940
- };
941
-
942
- const { getByText } = render(() => <Comp />);
943
-
944
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
945
- await userEvent.click(getByText("Update Value"));
946
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
947
- });
948
-
949
- it("update-error-flag: should handle error in getter function", async () => {
950
- const Comp: Component = () => {
951
- const $a = state<number>(10);
952
- const $b = state(20);
953
-
954
- const $$result = from((t) => {
955
- if ($a.get(t) > 10) {
956
- throw new Error("Test error");
957
- } else {
958
- return $a.get(t) + $b.get(t);
959
- }
960
- });
961
-
962
- return (
963
- <div>
964
- <Switch>
965
- <Match when={$$result.error}>
966
- <div>Error</div>
967
- </Match>
968
- <Match when={$$result()}>
969
- <div>{$$result()}</div>
970
- </Match>
971
- </Switch>
972
- <button type="button" onClick={() => $a.set((v) => (v ? v + 1 : 0))}>
973
- Increment Value
974
- </button>
975
- <button type="button" onClick={() => $a.set((v) => (v ? v - 1 : 0))}>
976
- Decrement Value
977
- </button>
978
- </div>
979
- );
980
- };
981
-
982
- const { getByText } = render(() => <Comp />);
983
-
984
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
985
- await userEvent.click(getByText("Increment Value"));
986
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
987
- await userEvent.click(getByText("Decrement Value"));
988
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
989
- });
990
-
991
- it("update-error-flag: should handle error in async getter function", async () => {
992
- const Comp: Component = () => {
993
- const $a = stateAsync<number>(Promise.resolve(10));
994
- const $b = state(20);
995
-
996
- const $$result = from((t) => {
997
- if ($a.get(t) > 10) {
998
- throw new Error("Test error");
999
- } else {
1000
- return $a.get(t) + $b.get(t);
1001
- }
1002
- });
1003
-
1004
- return (
1005
- <div>
1006
- <Switch>
1007
- <Match when={$$result.error}>
1008
- <div>Error</div>
1009
- </Match>
1010
- <Match when={$$result()}>
1011
- <div>{$$result()}</div>
1012
- </Match>
1013
- </Switch>
1014
- <button type="button" onClick={() => $a.set(async (v) => (v ? v + 1 : 0))}>
1015
- Increment Value
1016
- </button>
1017
- <button type="button" onClick={() => $a.set(async (v) => (v ? v - 1 : 0))}>
1018
- Decrement Value
1019
- </button>
1020
- </div>
1021
- );
1022
- };
1023
-
1024
- const { getByText } = render(() => <Comp />);
1025
-
1026
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1027
- await userEvent.click(getByText("Increment Value"));
1028
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1029
- await userEvent.click(getByText("Decrement Value"));
1030
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1031
- });
1032
- });
1033
-
1034
- describe("component", () => {
1035
- it("update-error-component: should recover from initial error in FlowState", async () => {
1036
- const Comp: Component = () => {
1037
- const $state = state<number>(() => {
1038
- throw new Error("Test error");
1039
- });
1040
- const $$state = from($state);
1041
- return (
1042
- <div>
1043
- <ErrorBoundary fallback="Error">
1044
- <div>{$$state()}</div>
1045
- </ErrorBoundary>
1046
- <button type="button" onClick={() => $state.set(42)}>
1047
- Update Value
1048
- </button>
1049
- </div>
1050
- );
1051
- };
1052
-
1053
- const { getByText } = render(() => <Comp />);
1054
-
1055
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1056
- await userEvent.click(getByText("Update Value"));
1057
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
1058
- });
1059
-
1060
- it("update-error-component: should recover from initial error in AsyncFlowState", async () => {
1061
- const Comp: Component = () => {
1062
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
1063
- const $$stateAsync = from($stateAsync);
1064
-
1065
- return (
1066
- <div>
1067
- <ErrorBoundary fallback="Error">
1068
- <div>{$$stateAsync()}</div>
1069
- </ErrorBoundary>
1070
- <button type="button" onClick={() => $stateAsync.set(Promise.resolve(42))}>
1071
- Update Value
1072
- </button>
1073
- </div>
1074
- );
1075
- };
1076
-
1077
- const { getByText } = render(() => <Comp />);
1078
-
1079
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1080
- await userEvent.click(getByText("Update Value"));
1081
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
1082
- });
1083
-
1084
- it("update-error-component: should recover from initial error in getter function", async () => {
1085
- const Comp: Component = () => {
1086
- const $a = state<number>(() => {
1087
- throw new Error("Test error");
1088
- });
1089
- const $b = state(20);
1090
-
1091
- const $$result = from((t) => $a.get(t) + $b.get(t));
1092
-
1093
- return (
1094
- <div>
1095
- <ErrorBoundary fallback="Error">
1096
- <div>{$$result()}</div>
1097
- </ErrorBoundary>
1098
- <button type="button" onClick={() => $a.set(11)}>
1099
- Update Value
1100
- </button>
1101
- </div>
1102
- );
1103
- };
1104
-
1105
- const { getByText } = render(() => <Comp />);
1106
-
1107
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1108
- await userEvent.click(getByText("Update Value"));
1109
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1110
- });
1111
-
1112
- it("update-error-component: should recover from initial error in async getter function", async () => {
1113
- const Comp: Component = () => {
1114
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
1115
- const $b = state(20);
1116
-
1117
- const $$result = from((t) => $a.get(t) + $b.get(t));
1118
-
1119
- return (
1120
- <div>
1121
- <ErrorBoundary fallback="Error">
1122
- <div>{$$result()}</div>
1123
- </ErrorBoundary>
1124
- <button type="button" onClick={() => $a.set(Promise.resolve(11))}>
1125
- Update Value
1126
- </button>
1127
- </div>
1128
- );
1129
- };
1130
-
1131
- const { getByText } = render(() => <Comp />);
1132
-
1133
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1134
- await userEvent.click(getByText("Update Value"));
1135
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1136
- });
1137
-
1138
- it("update-error-component: should handle error in getter function", async () => {
1139
- const Comp: Component = () => {
1140
- const $a = state<number>(10);
1141
- const $b = state(20);
1142
-
1143
- const $$result = from((t) => {
1144
- if ($a.get(t) > 10) {
1145
- throw new Error("Test error");
1146
- } else {
1147
- return $a.get(t) + $b.get(t);
1148
- }
1149
- });
1150
-
1151
- return (
1152
- <div>
1153
- <ErrorBoundary fallback="Error">
1154
- <div>{$$result()}</div>
1155
- </ErrorBoundary>
1156
- <button type="button" onClick={() => $a.set((v) => (v ? v + 1 : 0))}>
1157
- Increment Value
1158
- </button>
1159
- <button type="button" onClick={() => $a.set((v) => (v ? v - 1 : 0))}>
1160
- Decrement Value
1161
- </button>
1162
- </div>
1163
- );
1164
- };
1165
-
1166
- const { getByText } = render(() => <Comp />);
1167
-
1168
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1169
- await userEvent.click(getByText("Increment Value"));
1170
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1171
- await userEvent.click(getByText("Decrement Value"));
1172
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1173
- });
1174
-
1175
- it("update-error-component: should handle error in async getter function", async () => {
1176
- const Comp: Component = () => {
1177
- const $a = stateAsync<number>(Promise.resolve(10));
1178
- const $b = state(20);
1179
-
1180
- const $$result = from((t) => {
1181
- if ($a.get(t) > 10) {
1182
- throw new Error("Test error");
1183
- } else {
1184
- return $a.get(t) + $b.get(t);
1185
- }
1186
- });
1187
-
1188
- return (
1189
- <div>
1190
- <ErrorBoundary fallback="Error">
1191
- <div>{$$result()}</div>
1192
- </ErrorBoundary>
1193
- <button type="button" onClick={() => $a.set(async (v) => (v ? v + 1 : 0))}>
1194
- Increment Value
1195
- </button>
1196
- <button type="button" onClick={() => $a.set(async (v) => (v ? v - 1 : 0))}>
1197
- Decrement Value
1198
- </button>
1199
- </div>
1200
- );
1201
- };
1202
-
1203
- const { getByText } = render(() => <Comp />);
1204
-
1205
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1206
- await userEvent.click(getByText("Increment Value"));
1207
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1208
- await userEvent.click(getByText("Decrement Value"));
1209
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1210
- });
1211
- });
1212
- });
1213
-
1214
- describe("suspense-error", () => {
1215
- describe("flag", () => {
1216
- it("update-suspense-error-flag: should recover from initial error in AsyncFlowState", async () => {
1217
- const Comp: Component = () => {
1218
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
1219
- const $$stateAsync = from($stateAsync);
1220
-
1221
- return (
1222
- <div>
1223
- <Switch>
1224
- <Match when={$$stateAsync.error}>
1225
- <div>Error</div>
1226
- </Match>
1227
- <Match when={$$stateAsync.loading}>
1228
- <div>Loading</div>
1229
- </Match>
1230
- <Match when={$$stateAsync()}>
1231
- <div>{$$stateAsync()}</div>
1232
- </Match>
1233
- </Switch>
1234
- <button type="button" onClick={() => $stateAsync.set(Promise.resolve(42))}>
1235
- Update Value
1236
- </button>
1237
- </div>
1238
- );
1239
- };
1240
-
1241
- const { getByText } = render(() => <Comp />);
1242
-
1243
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1244
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1245
- await userEvent.click(getByText("Update Value"));
1246
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
1247
- });
1248
-
1249
- it("update-suspense-error-flag: should recover from initial error in async getter function", async () => {
1250
- const Comp: Component = () => {
1251
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
1252
- const $b = state(20);
1253
-
1254
- const $$result = from((t) => $a.get(t) + $b.get(t));
1255
-
1256
- return (
1257
- <div>
1258
- <Switch>
1259
- <Match when={$$result.error}>
1260
- <div>Error</div>
1261
- </Match>
1262
- <Match when={$$result.loading}>
1263
- <div>Loading</div>
1264
- </Match>
1265
- <Match when={$$result()}>
1266
- <div>{$$result()}</div>
1267
- </Match>
1268
- </Switch>
1269
- <button type="button" onClick={() => $a.set(Promise.resolve(11))}>
1270
- Update Value
1271
- </button>
1272
- </div>
1273
- );
1274
- };
1275
-
1276
- const { getByText } = render(() => <Comp />);
1277
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1278
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1279
- await userEvent.click(getByText("Update Value"));
1280
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1281
- });
1282
-
1283
- it("update-suspense-error-flag: should handle error in async getter function", async () => {
1284
- const Comp: Component = () => {
1285
- const $a = stateAsync<number>(Promise.resolve(10));
1286
- const $b = state(20);
1287
-
1288
- const $$result = from((t) => {
1289
- if ($a.get(t) > 10) {
1290
- throw new Error("Test error");
1291
- } else {
1292
- return $a.get(t) + $b.get(t);
1293
- }
1294
- });
1295
-
1296
- return (
1297
- <div>
1298
- <Switch>
1299
- <Match when={$$result.error}>
1300
- <div>Error</div>
1301
- </Match>
1302
- <Match when={$$result.loading}>
1303
- <div>Loading</div>
1304
- </Match>
1305
- <Match when={$$result()}>
1306
- <div>{$$result()}</div>
1307
- </Match>
1308
- </Switch>
1309
- <button type="button" onClick={() => $a.set(async (v) => (v ? v + 1 : 0))}>
1310
- Increment Value
1311
- </button>
1312
- <button type="button" onClick={() => $a.set(async (v) => (v ? v - 1 : 0))}>
1313
- Decrement Value
1314
- </button>
1315
- </div>
1316
- );
1317
- };
1318
-
1319
- const { getByText } = render(() => <Comp />);
1320
-
1321
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1322
- await userEvent.click(getByText("Increment Value"));
1323
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1324
- await userEvent.click(getByText("Decrement Value"));
1325
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1326
- });
1327
- });
1328
-
1329
- describe("component", () => {
1330
- it("update-suspense-error-component: should recover from initial error in AsyncFlowState", async () => {
1331
- const Comp: Component = () => {
1332
- const $stateAsync = stateAsync<number>(Promise.reject(new Error("Test error")));
1333
- const $$stateAsync = from($stateAsync);
1334
-
1335
- return (
1336
- <div>
1337
- <Suspense fallback="Loading">
1338
- <ErrorBoundary fallback="Error">
1339
- <div>{$$stateAsync()}</div>
1340
- </ErrorBoundary>
1341
- </Suspense>
1342
- <button type="button" onClick={() => $stateAsync.set(Promise.resolve(42))}>
1343
- Update Value
1344
- </button>
1345
- </div>
1346
- );
1347
- };
1348
-
1349
- const { getByText } = render(() => <Comp />);
1350
-
1351
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1352
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1353
- await userEvent.click(getByText("Update Value"));
1354
- await vi.waitFor(() => expect.element(getByText("42")).toBeInTheDocument());
1355
- });
1356
-
1357
- it("update-suspense-error-component: should recover from initial error in async getter function", async () => {
1358
- const Comp: Component = () => {
1359
- const $a = stateAsync<number>(Promise.reject(new Error("Test error")));
1360
- const $b = state(20);
1361
-
1362
- const $$result = from((t) => $a.get(t) + $b.get(t));
1363
-
1364
- return (
1365
- <div>
1366
- <Suspense fallback="Loading">
1367
- <ErrorBoundary fallback="Error">
1368
- <div>{$$result()}</div>
1369
- </ErrorBoundary>
1370
- </Suspense>
1371
- <button type="button" onClick={() => $a.set(Promise.resolve(11))}>
1372
- Update Value
1373
- </button>
1374
- </div>
1375
- );
1376
- };
1377
-
1378
- const { getByText } = render(() => <Comp />);
1379
-
1380
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1381
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1382
- await userEvent.click(getByText("Update Value"));
1383
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1384
- });
1385
-
1386
- it("update-suspense-error-component: should handle error in async getter function", async () => {
1387
- const Comp: Component = () => {
1388
- const $a = stateAsync<number>(Promise.resolve(10));
1389
- const $b = state(20);
1390
-
1391
- const $$result = from((t) => {
1392
- if ($a.get(t) > 10) {
1393
- throw new Error("Test error");
1394
- } else {
1395
- return $a.get(t) + $b.get(t);
1396
- }
1397
- });
1398
-
1399
- return (
1400
- <div>
1401
- <Suspense fallback="Loading">
1402
- <ErrorBoundary fallback="Error">
1403
- <div>{$$result()}</div>
1404
- </ErrorBoundary>
1405
- </Suspense>
1406
- <button type="button" onClick={() => $a.set(async (v) => (v ? v + 1 : 0))}>
1407
- Increment Value
1408
- </button>
1409
- <button type="button" onClick={() => $a.set(async (v) => (v ? v - 1 : 0))}>
1410
- Decrement Value
1411
- </button>
1412
- </div>
1413
- );
1414
- };
1415
-
1416
- const { getByText } = render(() => <Comp />);
1417
-
1418
- await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1419
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1420
- await userEvent.click(getByText("Increment Value"));
1421
- await vi.waitFor(() => expect.element(getByText("Error")).toBeInTheDocument());
1422
- await userEvent.click(getByText("Decrement Value"));
1423
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1424
- });
1425
- });
1426
- });
1427
- });
1428
-
1429
- describe("replacement", () => {
1430
- describe("happy path", () => {
1431
- it("replacement-happy-path: should update FlowState via Switch", async () => {
1432
- const CompInner: Component<{ $state: FlowState<number> }> = ({ $state }) => {
1433
- const $$state = from($state);
1434
-
1435
- return (
1436
- <div>
1437
- <div>{$$state()}</div>
1438
- <button type="button" onClick={() => $state.set((v) => (v ? v + 1 : 0))}>
1439
- Increment
1440
- </button>
1441
- </div>
1442
- );
1443
- };
1444
-
1445
- const CompOuter: Component = () => {
1446
- const $stateA = state<number>(20);
1447
- const $stateB = state<number>(30);
1448
- const $selected = state<FlowState<number>>($stateA);
1449
-
1450
- const $$selected = from($selected);
1451
-
1452
- return (
1453
- <div>
1454
- <Switch>
1455
- <Match when={$$selected.loading}>
1456
- <div>Loading</div>
1457
- </Match>
1458
- <Match when={$$selected()}>{(selected) => <CompInner $state={selected()} />}</Match>
1459
- </Switch>
1460
- <button type="button" onClick={() => $selected.set($stateA)}>
1461
- Use A
1462
- </button>
1463
- <button type="button" onClick={() => $selected.set($stateB)}>
1464
- Use B
1465
- </button>
1466
- </div>
1467
- );
1468
- };
1469
-
1470
- const { getByText } = render(() => <CompOuter />);
1471
- await vi.waitFor(() => expect.element(getByText("20")).toBeInTheDocument());
1472
- await userEvent.click(getByText("Increment"));
1473
- await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1474
- await userEvent.click(getByText("Use B"));
1475
- await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1476
- await userEvent.click(getByText("Increment"));
1477
- await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1478
- await userEvent.click(getByText("Use A"));
1479
- await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1480
- });
1481
-
1482
- // it("replacement-happy-path: should update FlowState via Show", async () => {
1483
-
1484
- // const FlowBoundary: Component<{ resource: Resource<FlowState<unknown>>, children: Component<{ $state: FlowState<unknown> }> }> = (props) => {
1485
- // return (
1486
- // <div>
1487
- // <Switch>
1488
- // <Match when={props.resource.loading}>
1489
- // <div>Loading</div>
1490
- // </Match>
1491
- // <Match when={props.resource.error}>
1492
- // <div>Error</div>
1493
- // </Match>
1494
- // <Match when={props.resource()}>
1495
- // {(resource) => props.children({ $state: resource() })}
1496
- // </Match>
1497
- // </Switch>
1498
- // </div >
1499
- // )
1500
- // }
1501
-
1502
- // const CompInner: Component<{ $state: FlowState<number> }> = ({ $state }) => {
1503
-
1504
- // const $$state = from($state);
1505
-
1506
- // return (
1507
- // <div>
1508
- // <div>{$$state()}</div>
1509
- // <button type="button" onClick={() => $state.set((v) => v ? v + 1 : 0)}>Increment</button>
1510
- // </div>
1511
- // )
1512
- // }
1513
-
1514
- // const CompOuter: Component = () => {
1515
- // const $stateA = state<number>(20);
1516
- // const $stateB = state<number>(30);
1517
- // const $selected = state<FlowState<number>>($stateA);
1518
-
1519
- // const $$selected = from($selected);
1520
-
1521
- // return (
1522
- // <div>
1523
- // <FlowBoundary resource={$$selected}>
1524
- // <CompInner/>
1525
- // </FlowBoundary>
1526
- // <button type="button" onClick={() => $selected.set($stateA)}>Use A</button>
1527
- // <button type="button" onClick={() => $selected.set($stateB)}>Use B</button>
1528
- // </div >
1529
- // )
1530
- // }
1531
-
1532
- // const { getByText } = render(() => <CompOuter />);
1533
- // await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1534
- // await vi.waitFor(() => expect.element(getByText("20")).toBeInTheDocument());
1535
- // await userEvent.click(getByText("Increment"));
1536
- // await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1537
- // await userEvent.click(getByText("Use B"));
1538
- // await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1539
- // await userEvent.click(getByText("Increment"));
1540
- // await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1541
- // await userEvent.click(getByText("Use A"));
1542
- // await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1543
- // });
1544
-
1545
- // it("replacement-happy-path: should update FlowState via FlowBoundary", async () => {
1546
-
1547
- // const CompInner: Component<{ $state: FlowState<number> }> = ({ $state }) => {
1548
- // // if (!props.$state) { return undefined }
1549
-
1550
- // const $$state = from($state);
1551
-
1552
- // return (
1553
- // <div>
1554
- // <div>{$$state()}</div>
1555
- // <button type="button" onClick={() => $state.set((v) => v ? v + 1 : 0)}>Increment</button>
1556
- // </div>
1557
- // )
1558
- // }
1559
-
1560
- // const CompOuter: Component = () => {
1561
- // const $stateA = state<number>(20);
1562
- // const $stateB = state<number>(30);
1563
- // const $selected = state<FlowState<number>>($stateA);
1564
-
1565
- // const $$selected = from($selected);
1566
-
1567
- // return (
1568
- // <div>
1569
- // <Suspense fallback="Loading Outer">
1570
- // <CompInner $state={$$selected()} />
1571
- // </Suspense>
1572
- // <button type="button" onClick={() => $selected.set($stateA)}>Use A</button>
1573
- // <button type="button" onClick={() => $selected.set($stateB)}>Use B</button>
1574
- // </div >
1575
- // )
1576
- // }
1577
-
1578
- // const { getByText } = render(() => <CompOuter />);
1579
- // await vi.waitFor(() => expect.element(getByText("Loading")).toBeInTheDocument());
1580
- // await vi.waitFor(() => expect.element(getByText("20")).toBeInTheDocument());
1581
- // await userEvent.click(getByText("Increment"));
1582
- // await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1583
- // await userEvent.click(getByText("Use B"));
1584
- // await vi.waitFor(() => expect.element(getByText("30")).toBeInTheDocument());
1585
- // await userEvent.click(getByText("Increment"));
1586
- // await vi.waitFor(() => expect.element(getByText("31")).toBeInTheDocument());
1587
- // await userEvent.click(getByText("Use A"));
1588
- // await vi.waitFor(() => expect.element(getByText("21")).toBeInTheDocument());
1589
- // });
1590
- });
1591
- });
1592
- });