@typed/async-data 0.13.0 → 1.0.0-beta.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 +159 -0
- package/dist/index.d.ts +92 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -5
- package/package.json +18 -28
- package/src/AsyncData.test.ts +417 -62
- package/src/index.test.ts +497 -0
- package/src/index.ts +291 -4
- package/tsconfig.json +5 -26
- package/.nvmrc +0 -1
- package/biome.json +0 -36
- package/dist/AsyncData.d.ts +0 -196
- package/dist/AsyncData.js +0 -285
- package/dist/AsyncData.js.map +0 -1
- package/dist/LazyRef.d.ts +0 -21
- package/dist/LazyRef.js +0 -27
- package/dist/LazyRef.js.map +0 -1
- package/dist/Progress.d.ts +0 -25
- package/dist/Progress.js +0 -21
- package/dist/Progress.js.map +0 -1
- package/dist/TypeId.d.ts +0 -11
- package/dist/TypeId.js +0 -8
- package/dist/TypeId.js.map +0 -1
- package/dist/_internal.d.ts +0 -16
- package/dist/_internal.js +0 -53
- package/dist/_internal.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/readme.md +0 -218
- package/src/AsyncData.ts +0 -654
- package/src/LazyRef.ts +0 -87
- package/src/Progress.ts +0 -33
- package/src/TypeId.ts +0 -13
- package/src/_internal.ts +0 -114
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Exit from "effect/Exit";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Result from "effect/Result";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import * as AsyncData from "./index.js";
|
|
7
|
+
|
|
8
|
+
function assert<T, U extends T>(value: T, guard: (v: T) => v is U): asserts value is U {
|
|
9
|
+
if (!guard(value)) throw new Error("Assertion failed");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("AsyncData", () => {
|
|
13
|
+
describe("constructors", () => {
|
|
14
|
+
it("NoData", () => {
|
|
15
|
+
const data = AsyncData.NoData;
|
|
16
|
+
assert(data, AsyncData.isNoData);
|
|
17
|
+
expect(data._tag).toBe("NoData");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("loading without progress", () => {
|
|
21
|
+
const data = AsyncData.loading();
|
|
22
|
+
assert(data, AsyncData.isLoading);
|
|
23
|
+
expect(data._tag).toBe("Loading");
|
|
24
|
+
expect(data.progress).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("loading with progress", () => {
|
|
28
|
+
const progress = { loaded: 50, total: 100 };
|
|
29
|
+
const data = AsyncData.loading(progress);
|
|
30
|
+
assert(data, AsyncData.isLoading);
|
|
31
|
+
expect(data.progress?.loaded).toBe(50);
|
|
32
|
+
expect(data.progress?.total).toBe(100);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("success without progress", () => {
|
|
36
|
+
const data = AsyncData.success(42);
|
|
37
|
+
assert(data, AsyncData.isSuccess);
|
|
38
|
+
expect(data._tag).toBe("Success");
|
|
39
|
+
expect(data.value).toBe(42);
|
|
40
|
+
expect(data.progress).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("success with progress", () => {
|
|
44
|
+
const progress = { loaded: 75, total: 100 };
|
|
45
|
+
const data = AsyncData.success(42, progress);
|
|
46
|
+
assert(data, AsyncData.isSuccess);
|
|
47
|
+
expect(data.value).toBe(42);
|
|
48
|
+
expect(data.progress?.loaded).toBe(75);
|
|
49
|
+
expect(data.progress?.total).toBe(100);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("failure without progress", () => {
|
|
53
|
+
const cause = Cause.fail("error");
|
|
54
|
+
const data = AsyncData.failure(cause);
|
|
55
|
+
assert(data, AsyncData.isFailure);
|
|
56
|
+
expect(data._tag).toBe("Failure");
|
|
57
|
+
expect(data.cause).toBe(cause);
|
|
58
|
+
expect(data.progress).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("failure with progress", () => {
|
|
62
|
+
const cause = Cause.fail("error");
|
|
63
|
+
const progress = { loaded: 25 };
|
|
64
|
+
const data = AsyncData.failure(cause, progress);
|
|
65
|
+
assert(data, AsyncData.isFailure);
|
|
66
|
+
expect(data.progress?.loaded).toBe(25);
|
|
67
|
+
expect(data.progress?.total).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("optimistic", () => {
|
|
71
|
+
const previous = AsyncData.success(10);
|
|
72
|
+
const data = AsyncData.optimistic(previous, 20);
|
|
73
|
+
assert(data, AsyncData.isOptimistic);
|
|
74
|
+
expect(data._tag).toBe("Optimistic");
|
|
75
|
+
expect(data.value).toBe(20);
|
|
76
|
+
expect(data.previous).toBe(previous);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("type guards", () => {
|
|
81
|
+
it("isNoData", () => {
|
|
82
|
+
const noData = AsyncData.NoData;
|
|
83
|
+
assert(noData, AsyncData.isNoData);
|
|
84
|
+
expect(noData._tag).toBe("NoData");
|
|
85
|
+
expect(AsyncData.isNoData(AsyncData.loading())).toBe(false);
|
|
86
|
+
expect(AsyncData.isNoData(AsyncData.success(1))).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("isLoading", () => {
|
|
90
|
+
const loadingData = AsyncData.loading();
|
|
91
|
+
assert(loadingData, AsyncData.isLoading);
|
|
92
|
+
expect(loadingData._tag).toBe("Loading");
|
|
93
|
+
expect(AsyncData.isLoading(AsyncData.NoData)).toBe(false);
|
|
94
|
+
expect(AsyncData.isLoading(AsyncData.success(1))).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("isSuccess", () => {
|
|
98
|
+
const successData = AsyncData.success(1);
|
|
99
|
+
assert(successData, AsyncData.isSuccess);
|
|
100
|
+
expect(successData.value).toBe(1);
|
|
101
|
+
expect(AsyncData.isSuccess(AsyncData.NoData)).toBe(false);
|
|
102
|
+
expect(AsyncData.isSuccess(AsyncData.loading())).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("isFailure", () => {
|
|
106
|
+
const cause = Cause.fail("error");
|
|
107
|
+
const failureData = AsyncData.failure(cause);
|
|
108
|
+
assert(failureData, AsyncData.isFailure);
|
|
109
|
+
expect(failureData.cause).toBe(cause);
|
|
110
|
+
expect(AsyncData.isFailure(AsyncData.NoData)).toBe(false);
|
|
111
|
+
expect(AsyncData.isFailure(AsyncData.success(1))).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("isOptimistic", () => {
|
|
115
|
+
const previous = AsyncData.success(10);
|
|
116
|
+
const data = AsyncData.optimistic(previous, 20);
|
|
117
|
+
assert(data, AsyncData.isOptimistic);
|
|
118
|
+
expect(data.value).toBe(20);
|
|
119
|
+
expect(data.previous).toBe(previous);
|
|
120
|
+
expect(AsyncData.isOptimistic(AsyncData.NoData)).toBe(false);
|
|
121
|
+
expect(AsyncData.isOptimistic(AsyncData.success(1))).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("isAsyncData", () => {
|
|
125
|
+
expect(AsyncData.isAsyncData(AsyncData.NoData)).toBe(true);
|
|
126
|
+
expect(AsyncData.isAsyncData(AsyncData.loading())).toBe(true);
|
|
127
|
+
expect(AsyncData.isAsyncData(AsyncData.success(1))).toBe(true);
|
|
128
|
+
expect(AsyncData.isAsyncData(AsyncData.failure(Cause.fail("error")))).toBe(true);
|
|
129
|
+
expect(AsyncData.isAsyncData(null)).toBe(false);
|
|
130
|
+
expect(AsyncData.isAsyncData({})).toBe(false);
|
|
131
|
+
expect(AsyncData.isAsyncData({ _tag: "Invalid" })).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("isRefreshing", () => {
|
|
135
|
+
const progress = { loaded: 50 };
|
|
136
|
+
const successWithProgress = AsyncData.success(1, progress);
|
|
137
|
+
const failureWithProgress = AsyncData.failure(Cause.fail("error"), progress);
|
|
138
|
+
assert(successWithProgress, AsyncData.isSuccess);
|
|
139
|
+
expect(AsyncData.isRefreshing(successWithProgress)).toBe(true);
|
|
140
|
+
expect(successWithProgress.progress?.loaded).toBe(50);
|
|
141
|
+
assert(failureWithProgress, AsyncData.isFailure);
|
|
142
|
+
expect(AsyncData.isRefreshing(failureWithProgress)).toBe(true);
|
|
143
|
+
expect(AsyncData.isRefreshing(AsyncData.success(1))).toBe(false);
|
|
144
|
+
expect(AsyncData.isRefreshing(AsyncData.loading())).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("isPending", () => {
|
|
148
|
+
const loadingData = AsyncData.loading();
|
|
149
|
+
assert(loadingData, AsyncData.isLoading);
|
|
150
|
+
expect(AsyncData.isPending(loadingData)).toBe(true);
|
|
151
|
+
const progress = { loaded: 50 };
|
|
152
|
+
const successRefreshing = AsyncData.success(1, progress);
|
|
153
|
+
assert(successRefreshing, AsyncData.isSuccess);
|
|
154
|
+
expect(AsyncData.isPending(successRefreshing)).toBe(true);
|
|
155
|
+
const failureRefreshing = AsyncData.failure(Cause.fail("error"), progress);
|
|
156
|
+
assert(failureRefreshing, AsyncData.isFailure);
|
|
157
|
+
expect(AsyncData.isPending(failureRefreshing)).toBe(true);
|
|
158
|
+
expect(AsyncData.isPending(AsyncData.NoData)).toBe(false);
|
|
159
|
+
expect(AsyncData.isPending(AsyncData.success(1))).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("extractors", () => {
|
|
164
|
+
it("getSuccess", () => {
|
|
165
|
+
const successData = AsyncData.success(42);
|
|
166
|
+
const successOption = AsyncData.getSuccess(successData);
|
|
167
|
+
assert(successOption, Option.isSome);
|
|
168
|
+
expect(successOption.value).toBe(42);
|
|
169
|
+
const none1 = AsyncData.getSuccess(AsyncData.NoData);
|
|
170
|
+
assert(none1, Option.isNone);
|
|
171
|
+
const none2 = AsyncData.getSuccess(AsyncData.loading());
|
|
172
|
+
assert(none2, Option.isNone);
|
|
173
|
+
const previous = AsyncData.success(10);
|
|
174
|
+
const opt = AsyncData.optimistic(previous, 20);
|
|
175
|
+
const optimisticOption = AsyncData.getSuccess(opt);
|
|
176
|
+
assert(optimisticOption, Option.isSome);
|
|
177
|
+
expect(optimisticOption.value).toBe(20);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("getCause", () => {
|
|
181
|
+
const cause = Cause.fail("error");
|
|
182
|
+
const failureData = AsyncData.failure(cause);
|
|
183
|
+
const causeOption = AsyncData.getCause(failureData);
|
|
184
|
+
assert(causeOption, Option.isSome);
|
|
185
|
+
expect(causeOption.value).toBe(cause);
|
|
186
|
+
assert(AsyncData.getCause(AsyncData.NoData), Option.isNone);
|
|
187
|
+
assert(AsyncData.getCause(AsyncData.loading()), Option.isNone);
|
|
188
|
+
assert(AsyncData.getCause(AsyncData.success(1)), Option.isNone);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("getError", () => {
|
|
192
|
+
const error = "error";
|
|
193
|
+
const cause = Cause.fail(error);
|
|
194
|
+
const failureData = AsyncData.failure(cause);
|
|
195
|
+
const errorOption = AsyncData.getError(failureData);
|
|
196
|
+
assert(errorOption, Option.isSome);
|
|
197
|
+
expect(errorOption.value).toBe(error);
|
|
198
|
+
assert(AsyncData.getError(AsyncData.NoData), Option.isNone);
|
|
199
|
+
assert(AsyncData.getError(AsyncData.loading()), Option.isNone);
|
|
200
|
+
assert(AsyncData.getError(AsyncData.success(1)), Option.isNone);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("match", () => {
|
|
205
|
+
it("invokes correct branch for each variant", () => {
|
|
206
|
+
const noDataResult = AsyncData.match(AsyncData.NoData, {
|
|
207
|
+
NoData: () => "no-data",
|
|
208
|
+
Loading: () => "loading",
|
|
209
|
+
Failure: () => "failure",
|
|
210
|
+
Success: () => "success",
|
|
211
|
+
Optimistic: () => "optimistic",
|
|
212
|
+
});
|
|
213
|
+
expect(noDataResult).toBe("no-data");
|
|
214
|
+
|
|
215
|
+
const loadingResult = AsyncData.match(AsyncData.loading(), {
|
|
216
|
+
NoData: () => "no-data",
|
|
217
|
+
Loading: () => "loading",
|
|
218
|
+
Failure: () => "failure",
|
|
219
|
+
Success: () => "success",
|
|
220
|
+
Optimistic: () => "optimistic",
|
|
221
|
+
});
|
|
222
|
+
expect(loadingResult).toBe("loading");
|
|
223
|
+
|
|
224
|
+
const successResult = AsyncData.match(AsyncData.success(42), {
|
|
225
|
+
NoData: () => "no-data",
|
|
226
|
+
Loading: () => "loading",
|
|
227
|
+
Failure: () => "failure",
|
|
228
|
+
Success: (value) => `success-${value}`,
|
|
229
|
+
Optimistic: () => "optimistic",
|
|
230
|
+
});
|
|
231
|
+
expect(successResult).toBe("success-42");
|
|
232
|
+
|
|
233
|
+
const cause = Cause.fail("error");
|
|
234
|
+
const failureResult = AsyncData.match(AsyncData.failure(cause), {
|
|
235
|
+
NoData: () => "no-data",
|
|
236
|
+
Loading: () => "loading",
|
|
237
|
+
Failure: (c) => `failure-${String(Cause.squash(c))}`,
|
|
238
|
+
Success: () => "success",
|
|
239
|
+
Optimistic: () => "optimistic",
|
|
240
|
+
});
|
|
241
|
+
expect(failureResult).toBe("failure-error");
|
|
242
|
+
|
|
243
|
+
const previous = AsyncData.success(10);
|
|
244
|
+
const optimisticResult = AsyncData.match(AsyncData.optimistic(previous, 20), {
|
|
245
|
+
NoData: () => "no-data",
|
|
246
|
+
Loading: () => "loading",
|
|
247
|
+
Failure: () => "failure",
|
|
248
|
+
Success: () => "success",
|
|
249
|
+
Optimistic: (value) => `optimistic-${value}`,
|
|
250
|
+
});
|
|
251
|
+
expect(optimisticResult).toBe("optimistic-20");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("supports data-first (curried) form", () => {
|
|
255
|
+
const matchers = {
|
|
256
|
+
NoData: () => "n",
|
|
257
|
+
Loading: () => "l",
|
|
258
|
+
Failure: () => "f",
|
|
259
|
+
Success: (v: number) => `s-${v}`,
|
|
260
|
+
Optimistic: (v: number) => `o-${v}`,
|
|
261
|
+
};
|
|
262
|
+
const fn = AsyncData.match(matchers);
|
|
263
|
+
expect(fn(AsyncData.NoData)).toBe("n");
|
|
264
|
+
expect(fn(AsyncData.success(7))).toBe("s-7");
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe("map", () => {
|
|
269
|
+
it("maps Success value", () => {
|
|
270
|
+
const data = AsyncData.success(5);
|
|
271
|
+
const mapped = AsyncData.map(data, (n) => n * 2);
|
|
272
|
+
assert(mapped, AsyncData.isSuccess);
|
|
273
|
+
expect(mapped.value).toBe(10);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("preserves NoData and Loading", () => {
|
|
277
|
+
const mappedNoData = AsyncData.map(AsyncData.NoData, (n: number) => n * 2);
|
|
278
|
+
assert(mappedNoData, AsyncData.isNoData);
|
|
279
|
+
const mappedLoading = AsyncData.map(AsyncData.loading(), (n: number) => n * 2);
|
|
280
|
+
assert(mappedLoading, AsyncData.isLoading);
|
|
281
|
+
const cause = Cause.fail("err");
|
|
282
|
+
const mappedFailure = AsyncData.map(AsyncData.failure(cause), (n: number) => n * 2);
|
|
283
|
+
assert(mappedFailure, AsyncData.isFailure);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("maps Optimistic value and previous", () => {
|
|
287
|
+
const optimisticData = AsyncData.optimistic(AsyncData.success(5), 10);
|
|
288
|
+
const mapped = AsyncData.map(optimisticData, (n) => n * 2);
|
|
289
|
+
assert(mapped, AsyncData.isOptimistic);
|
|
290
|
+
expect(mapped.value).toBe(20);
|
|
291
|
+
assert(mapped.previous, AsyncData.isSuccess);
|
|
292
|
+
expect(mapped.previous.value).toBe(10);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("preserves progress on Success", () => {
|
|
296
|
+
const progress = { loaded: 50, total: 100 };
|
|
297
|
+
const data = AsyncData.success(5, progress);
|
|
298
|
+
const mapped = AsyncData.map(data, (n) => n * 2);
|
|
299
|
+
assert(mapped, AsyncData.isSuccess);
|
|
300
|
+
expect(mapped.progress?.loaded).toBe(50);
|
|
301
|
+
expect(mapped.progress?.total).toBe(100);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("supports data-last (curried) form", () => {
|
|
305
|
+
const double = AsyncData.map((n: number) => n * 2);
|
|
306
|
+
const result = double(AsyncData.success(3));
|
|
307
|
+
assert(result, AsyncData.isSuccess);
|
|
308
|
+
expect(result.value).toBe(6);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("flatMap", () => {
|
|
313
|
+
it("flatMaps Success", () => {
|
|
314
|
+
const data = AsyncData.success(5);
|
|
315
|
+
const flatMapped = AsyncData.flatMap(data, (n) => AsyncData.success(n * 2));
|
|
316
|
+
assert(flatMapped, AsyncData.isSuccess);
|
|
317
|
+
expect(flatMapped.value).toBe(10);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("preserves NoData, Loading, Failure", () => {
|
|
321
|
+
const f = (n: number) => AsyncData.success(n * 2);
|
|
322
|
+
const flatNoData = AsyncData.flatMap(AsyncData.NoData, f);
|
|
323
|
+
assert(flatNoData, AsyncData.isNoData);
|
|
324
|
+
const flatLoading = AsyncData.flatMap(AsyncData.loading(), f);
|
|
325
|
+
assert(flatLoading, AsyncData.isLoading);
|
|
326
|
+
const cause = Cause.fail("err");
|
|
327
|
+
const flatFailure = AsyncData.flatMap(AsyncData.failure(cause), f);
|
|
328
|
+
assert(flatFailure, AsyncData.isFailure);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("flatMaps Optimistic and can return Optimistic", () => {
|
|
332
|
+
const optimisticData = AsyncData.optimistic(AsyncData.success(5), 10);
|
|
333
|
+
const flatMapped = AsyncData.flatMap(optimisticData, (n) => AsyncData.success(n * 2));
|
|
334
|
+
assert(flatMapped, AsyncData.isSuccess);
|
|
335
|
+
expect(flatMapped.value).toBe(20);
|
|
336
|
+
const flatMappedOpt = AsyncData.flatMap(optimisticData, (n, prev) =>
|
|
337
|
+
AsyncData.optimistic(prev, n * 2),
|
|
338
|
+
);
|
|
339
|
+
assert(flatMappedOpt, AsyncData.isOptimistic);
|
|
340
|
+
expect(flatMappedOpt.value).toBe(20);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("mapError", () => {
|
|
345
|
+
it("maps Failure cause", () => {
|
|
346
|
+
const cause = Cause.fail("error");
|
|
347
|
+
const data = AsyncData.failure(cause);
|
|
348
|
+
const mapped = AsyncData.mapError(data, (e) => `mapped: ${e}`);
|
|
349
|
+
assert(mapped, AsyncData.isFailure);
|
|
350
|
+
const err = AsyncData.getError(mapped);
|
|
351
|
+
assert(err, Option.isSome);
|
|
352
|
+
expect(err.value).toBe("mapped: error");
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("preserves NoData, Loading, Success", () => {
|
|
356
|
+
const f = (e: string) => `mapped: ${e}`;
|
|
357
|
+
const mappedNoData = AsyncData.mapError(AsyncData.NoData, f);
|
|
358
|
+
assert(mappedNoData, AsyncData.isNoData);
|
|
359
|
+
const mappedLoading = AsyncData.mapError(AsyncData.loading(), f);
|
|
360
|
+
assert(mappedLoading, AsyncData.isLoading);
|
|
361
|
+
const mappedSuccess = AsyncData.mapError(AsyncData.success(1), f);
|
|
362
|
+
assert(mappedSuccess, AsyncData.isSuccess);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("maps Optimistic previous failure", () => {
|
|
366
|
+
const cause = Cause.fail("error");
|
|
367
|
+
const optimisticData = AsyncData.optimistic(AsyncData.failure(cause), 10);
|
|
368
|
+
const mapped = AsyncData.mapError(optimisticData, (e) => `mapped: ${e}`);
|
|
369
|
+
assert(mapped, AsyncData.isOptimistic);
|
|
370
|
+
assert(mapped.previous, AsyncData.isFailure);
|
|
371
|
+
const prevErr = AsyncData.getError(mapped.previous);
|
|
372
|
+
assert(prevErr, Option.isSome);
|
|
373
|
+
expect(prevErr.value).toBe("mapped: error");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("preserves progress on Failure", () => {
|
|
377
|
+
const progress = { loaded: 50 };
|
|
378
|
+
const cause = Cause.fail("error");
|
|
379
|
+
const data = AsyncData.failure(cause, progress);
|
|
380
|
+
const mapped = AsyncData.mapError(data, (e) => `mapped: ${e}`);
|
|
381
|
+
assert(mapped, AsyncData.isFailure);
|
|
382
|
+
expect(mapped.progress?.loaded).toBe(50);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("startLoading", () => {
|
|
387
|
+
it("from NoData returns Loading", () => {
|
|
388
|
+
const started = AsyncData.startLoading(AsyncData.NoData);
|
|
389
|
+
assert(started, AsyncData.isLoading);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("from Success keeps value and adds progress", () => {
|
|
393
|
+
const data = AsyncData.success(42);
|
|
394
|
+
const progress = { loaded: 50 };
|
|
395
|
+
const started = AsyncData.startLoading(data, progress);
|
|
396
|
+
assert(started, AsyncData.isSuccess);
|
|
397
|
+
expect(started.value).toBe(42);
|
|
398
|
+
expect(started.progress?.loaded).toBe(50);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("from Failure keeps cause and adds progress", () => {
|
|
402
|
+
const cause = Cause.fail("error");
|
|
403
|
+
const data = AsyncData.failure(cause);
|
|
404
|
+
const progress = { loaded: 50 };
|
|
405
|
+
const started = AsyncData.startLoading(data, progress);
|
|
406
|
+
assert(started, AsyncData.isFailure);
|
|
407
|
+
expect(started.cause).toBe(cause);
|
|
408
|
+
expect(started.progress?.loaded).toBe(50);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("from Optimistic adds progress to previous", () => {
|
|
412
|
+
const previous = AsyncData.success(10);
|
|
413
|
+
const data = AsyncData.optimistic(previous, 20);
|
|
414
|
+
const progress = { loaded: 50 };
|
|
415
|
+
const started = AsyncData.startLoading(data, progress);
|
|
416
|
+
assert(started, AsyncData.isOptimistic);
|
|
417
|
+
expect(started.value).toBe(20);
|
|
418
|
+
assert(started.previous, AsyncData.isSuccess);
|
|
419
|
+
expect(started.previous.progress?.loaded).toBe(50);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe("stopLoading", () => {
|
|
424
|
+
it("removes progress from Success", () => {
|
|
425
|
+
const progress = { loaded: 50 };
|
|
426
|
+
const data = AsyncData.success(42, progress);
|
|
427
|
+
const stopped = AsyncData.stopLoading(data);
|
|
428
|
+
assert(stopped, AsyncData.isSuccess);
|
|
429
|
+
expect(stopped.value).toBe(42);
|
|
430
|
+
expect(stopped.progress).toBeUndefined();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it("removes progress from Failure", () => {
|
|
434
|
+
const cause = Cause.fail("error");
|
|
435
|
+
const progress = { loaded: 50 };
|
|
436
|
+
const data = AsyncData.failure(cause, progress);
|
|
437
|
+
const stopped = AsyncData.stopLoading(data);
|
|
438
|
+
assert(stopped, AsyncData.isFailure);
|
|
439
|
+
expect(stopped.cause).toBe(cause);
|
|
440
|
+
expect(stopped.progress).toBeUndefined();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("removes progress from Optimistic previous", () => {
|
|
444
|
+
const previous = AsyncData.success(10, { loaded: 50 });
|
|
445
|
+
const data = AsyncData.optimistic(previous, 20);
|
|
446
|
+
const stopped = AsyncData.stopLoading(data);
|
|
447
|
+
assert(stopped, AsyncData.isOptimistic);
|
|
448
|
+
expect(stopped.value).toBe(20);
|
|
449
|
+
assert(stopped.previous, AsyncData.isSuccess);
|
|
450
|
+
expect(stopped.previous.progress).toBeUndefined();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("leaves NoData and Loading unchanged", () => {
|
|
454
|
+
const stoppedNoData = AsyncData.stopLoading(AsyncData.NoData);
|
|
455
|
+
assert(stoppedNoData, AsyncData.isNoData);
|
|
456
|
+
const loadingData = AsyncData.loading();
|
|
457
|
+
const stoppedLoading = AsyncData.stopLoading(loadingData);
|
|
458
|
+
assert(stoppedLoading, AsyncData.isLoading);
|
|
459
|
+
expect(stoppedLoading).toBe(loadingData);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
describe("fromExit", () => {
|
|
464
|
+
it("Success exit -> Success", () => {
|
|
465
|
+
const exit = Exit.succeed(42);
|
|
466
|
+
const data = AsyncData.fromExit(exit);
|
|
467
|
+
assert(data, AsyncData.isSuccess);
|
|
468
|
+
expect(data.value).toBe(42);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("Failure exit -> Failure", () => {
|
|
472
|
+
const cause = Cause.fail("error");
|
|
473
|
+
const exit = Exit.failCause(cause);
|
|
474
|
+
const data = AsyncData.fromExit(exit);
|
|
475
|
+
assert(data, AsyncData.isFailure);
|
|
476
|
+
expect(data.cause).toBe(cause);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe("fromResult", () => {
|
|
481
|
+
it("Success result -> Success", () => {
|
|
482
|
+
const result = Result.succeed(42);
|
|
483
|
+
const data = AsyncData.fromResult(result);
|
|
484
|
+
assert(data, AsyncData.isSuccess);
|
|
485
|
+
expect(data.value).toBe(42);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("Failure result -> Failure", () => {
|
|
489
|
+
const result = Result.fail("error");
|
|
490
|
+
const data = AsyncData.fromResult(result);
|
|
491
|
+
assert(data, AsyncData.isFailure);
|
|
492
|
+
const err = AsyncData.getError(data);
|
|
493
|
+
assert(err, Option.isSome);
|
|
494
|
+
expect(err.value).toBe("error");
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|