@evolu/common 8.7.0 → 8.8.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/dist/src/Array.d.ts +21 -21
- package/dist/src/Array.js +5 -5
- package/dist/src/Assert.d.ts +478 -141
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +644 -99
- package/dist/src/{Buffer.d.ts → Binary.d.ts} +85 -12
- package/dist/src/Binary.d.ts.map +1 -0
- package/dist/src/{Buffer.js → Binary.js} +206 -24
- package/dist/src/Console.d.ts +2 -10
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +6 -12
- package/dist/src/Crypto.d.ts +5 -5
- package/dist/src/Crypto.js +1 -1
- package/dist/src/Eq.d.ts +35 -40
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +51 -87
- package/dist/src/Function.d.ts +9 -14
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +7 -9
- package/dist/src/Http.d.ts +6 -7
- package/dist/src/Http.d.ts.map +1 -1
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +1 -0
- package/dist/src/LockManager.d.ts +1 -1
- package/dist/src/LockManager.js +1 -1
- package/dist/src/Lookup.d.ts +3 -2
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +4 -3
- package/dist/src/Number.d.ts +1 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +1 -1
- package/dist/src/Object.d.ts +4 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -4
- package/dist/src/Option.d.ts +1 -2
- package/dist/src/Option.d.ts.map +1 -1
- package/dist/src/Option.js +1 -2
- package/dist/src/Platform.js +2 -0
- package/dist/src/Polyfills.js +4 -1
- package/dist/src/Random.d.ts +1 -1
- package/dist/src/Result.d.ts +49 -54
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +17 -22
- package/dist/src/Set.d.ts +6 -6
- package/dist/src/Set.js +2 -2
- package/dist/src/Sqlite.js +1 -1
- package/dist/src/String.js +3 -2
- package/dist/src/Task.d.ts +32 -32
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +20 -22
- package/dist/src/Test.d.ts +31 -3
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +46 -3
- package/dist/src/Time.d.ts +3 -11
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +10 -18
- package/dist/src/Type.d.ts +87 -85
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +70 -63
- package/dist/src/Types.d.ts +12 -12
- package/dist/src/Types.js +1 -1
- package/dist/src/WebSocket.d.ts +4 -7
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/Worker.d.ts +1 -1
- package/dist/src/Worker.js +7 -7
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/intl/_en.d.ts.map +1 -1
- package/dist/src/intl/_en.js +1 -1
- package/dist/src/intl/ar.d.ts.map +1 -1
- package/dist/src/intl/ar.js +1 -1
- package/dist/src/intl/bn.d.ts.map +1 -1
- package/dist/src/intl/bn.js +1 -1
- package/dist/src/intl/ca.d.ts.map +1 -1
- package/dist/src/intl/ca.js +1 -1
- package/dist/src/intl/cs.d.ts.map +1 -1
- package/dist/src/intl/cs.js +1 -1
- package/dist/src/intl/da.d.ts.map +1 -1
- package/dist/src/intl/da.js +1 -1
- package/dist/src/intl/de.d.ts.map +1 -1
- package/dist/src/intl/de.js +1 -1
- package/dist/src/intl/el.d.ts.map +1 -1
- package/dist/src/intl/el.js +1 -1
- package/dist/src/intl/es.d.ts.map +1 -1
- package/dist/src/intl/es.js +1 -1
- package/dist/src/intl/fa.d.ts.map +1 -1
- package/dist/src/intl/fa.js +1 -1
- package/dist/src/intl/fi.d.ts.map +1 -1
- package/dist/src/intl/fi.js +1 -1
- package/dist/src/intl/fil.d.ts.map +1 -1
- package/dist/src/intl/fil.js +1 -1
- package/dist/src/intl/fr.d.ts.map +1 -1
- package/dist/src/intl/fr.js +1 -1
- package/dist/src/intl/he.d.ts.map +1 -1
- package/dist/src/intl/he.js +1 -1
- package/dist/src/intl/hi.d.ts.map +1 -1
- package/dist/src/intl/hi.js +1 -1
- package/dist/src/intl/hr.d.ts.map +1 -1
- package/dist/src/intl/hr.js +1 -1
- package/dist/src/intl/hu.d.ts.map +1 -1
- package/dist/src/intl/hu.js +1 -1
- package/dist/src/intl/id.d.ts.map +1 -1
- package/dist/src/intl/id.js +1 -1
- package/dist/src/intl/it.d.ts.map +1 -1
- package/dist/src/intl/it.js +1 -1
- package/dist/src/intl/ja.d.ts.map +1 -1
- package/dist/src/intl/ja.js +1 -1
- package/dist/src/intl/ko.d.ts.map +1 -1
- package/dist/src/intl/ko.js +1 -1
- package/dist/src/intl/ml.d.ts.map +1 -1
- package/dist/src/intl/ml.js +1 -1
- package/dist/src/intl/mr.d.ts.map +1 -1
- package/dist/src/intl/mr.js +1 -1
- package/dist/src/intl/ms.d.ts.map +1 -1
- package/dist/src/intl/ms.js +1 -1
- package/dist/src/intl/nb.d.ts.map +1 -1
- package/dist/src/intl/nb.js +1 -1
- package/dist/src/intl/nl.d.ts.map +1 -1
- package/dist/src/intl/nl.js +1 -1
- package/dist/src/intl/pa.d.ts.map +1 -1
- package/dist/src/intl/pa.js +1 -1
- package/dist/src/intl/pl.d.ts.map +1 -1
- package/dist/src/intl/pl.js +1 -1
- package/dist/src/intl/pt-BR.d.ts.map +1 -1
- package/dist/src/intl/pt-BR.js +1 -1
- package/dist/src/intl/pt.d.ts.map +1 -1
- package/dist/src/intl/pt.js +1 -1
- package/dist/src/intl/ro.d.ts.map +1 -1
- package/dist/src/intl/ro.js +1 -1
- package/dist/src/intl/sk.d.ts.map +1 -1
- package/dist/src/intl/sk.js +1 -1
- package/dist/src/intl/sl.d.ts.map +1 -1
- package/dist/src/intl/sl.js +1 -1
- package/dist/src/intl/sv.d.ts.map +1 -1
- package/dist/src/intl/sv.js +1 -1
- package/dist/src/intl/sw.d.ts.map +1 -1
- package/dist/src/intl/sw.js +1 -1
- package/dist/src/intl/ta.d.ts.map +1 -1
- package/dist/src/intl/ta.js +1 -1
- package/dist/src/intl/te.d.ts.map +1 -1
- package/dist/src/intl/te.js +1 -1
- package/dist/src/intl/th.d.ts.map +1 -1
- package/dist/src/intl/th.js +1 -1
- package/dist/src/intl/tr.d.ts.map +1 -1
- package/dist/src/intl/tr.js +1 -1
- package/dist/src/intl/uk.d.ts.map +1 -1
- package/dist/src/intl/uk.js +1 -1
- package/dist/src/intl/ur.d.ts.map +1 -1
- package/dist/src/intl/ur.js +1 -1
- package/dist/src/intl/vi.d.ts.map +1 -1
- package/dist/src/intl/vi.js +1 -1
- package/dist/src/intl/zh-CN.d.ts.map +1 -1
- package/dist/src/intl/zh-CN.js +1 -1
- package/dist/src/intl/zh-TW.d.ts.map +1 -1
- package/dist/src/intl/zh-TW.js +1 -1
- package/dist/src/local-first/Evolu.d.ts +12 -12
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +2 -2
- package/dist/src/local-first/Protocol.d.ts +14 -76
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +29 -206
- package/dist/src/local-first/Query.d.ts +11 -11
- package/dist/src/local-first/Query.js +6 -6
- package/dist/src/local-first/Relay.d.ts +2 -2
- package/dist/src/local-first/Schema.d.ts +3 -5
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +2 -4
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +5 -5
- package/dist/src/local-first/Storage.js +4 -4
- package/dist/src/local-first/Timestamp.d.ts +9 -0
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +13 -10
- package/package.json +7 -4
- package/src/Array.test.ts +809 -0
- package/src/Array.ts +21 -21
- package/src/Assert.test.ts +1181 -0
- package/src/Assert.ts +971 -227
- package/src/BigInt.test.ts +28 -0
- package/src/Binary.test.ts +1128 -0
- package/src/{Buffer.ts → Binary.ts} +273 -46
- package/src/Brand.test.ts +90 -0
- package/src/Cache.test.ts +183 -0
- package/src/Callbacks.test.ts +66 -0
- package/src/Console.test.ts +809 -0
- package/src/Console.ts +9 -17
- package/src/Crypto.test.ts +138 -0
- package/src/Crypto.ts +5 -5
- package/src/Eq.test.ts +333 -0
- package/src/Eq.ts +110 -176
- package/src/Error.test.ts +118 -0
- package/src/Function.test.ts +204 -0
- package/src/Function.ts +10 -15
- package/src/Http.test.ts +816 -0
- package/src/Http.ts +6 -7
- package/src/LeakDetector.test.ts +210 -0
- package/src/LeakDetector.ts +1 -0
- package/src/LockManager.ts +2 -2
- package/src/Lookup.test.ts +418 -0
- package/src/Lookup.ts +4 -3
- package/src/Microtask.test.ts +140 -0
- package/src/Number.test.ts +218 -0
- package/src/Number.ts +2 -2
- package/src/Object.test.ts +293 -0
- package/src/Object.ts +5 -4
- package/src/Option.test.ts +104 -0
- package/src/Option.ts +1 -2
- package/src/Order.test.ts +56 -0
- package/src/Platform.ts +2 -0
- package/src/Polyfills.ts +3 -4
- package/src/Random.test.ts +89 -0
- package/src/Random.ts +1 -1
- package/src/Ref.test.ts +139 -0
- package/src/RefCount.test.ts +184 -0
- package/src/Relation.test.ts +505 -0
- package/src/Resource.test.ts +4623 -0
- package/src/Result.test.ts +1829 -0
- package/src/Result.ts +49 -54
- package/src/Schedule.test.ts +1554 -0
- package/src/Set.test.ts +279 -0
- package/src/Set.ts +6 -6
- package/src/Sqlite.ts +1 -1
- package/src/Store.test.ts +288 -0
- package/src/String.test.ts +54 -0
- package/src/String.ts +3 -3
- package/src/Task.test.ts +10820 -0
- package/src/Task.ts +38 -43
- package/src/Test.test.ts +88 -0
- package/src/Test.ts +49 -3
- package/src/Time.test.ts +926 -0
- package/src/Time.ts +13 -21
- package/src/Type.base64.test.ts +88 -0
- package/src/Type.test.ts +20465 -0
- package/src/Type.ts +114 -111
- package/src/Types.test.ts +324 -0
- package/src/Types.ts +13 -13
- package/src/WebSocket.ts +4 -7
- package/src/Worker.test.ts +755 -0
- package/src/Worker.ts +13 -18
- package/src/index.ts +1 -1
- package/src/intl/_en.ts +1 -1
- package/src/intl/ar.ts +1 -1
- package/src/intl/bn.ts +1 -1
- package/src/intl/ca.ts +1 -1
- package/src/intl/cs.ts +1 -1
- package/src/intl/da.ts +1 -1
- package/src/intl/de.ts +1 -1
- package/src/intl/el.ts +1 -1
- package/src/intl/es.ts +1 -1
- package/src/intl/fa.ts +1 -1
- package/src/intl/fi.ts +1 -1
- package/src/intl/fil.ts +1 -1
- package/src/intl/fr.ts +1 -1
- package/src/intl/he.ts +1 -1
- package/src/intl/hi.ts +1 -1
- package/src/intl/hr.ts +1 -1
- package/src/intl/hu.ts +1 -1
- package/src/intl/id.ts +1 -1
- package/src/intl/intl.test.ts +3106 -0
- package/src/intl/it.ts +1 -1
- package/src/intl/ja.ts +1 -1
- package/src/intl/ko.ts +1 -1
- package/src/intl/ml.ts +1 -1
- package/src/intl/mr.ts +1 -1
- package/src/intl/ms.ts +1 -1
- package/src/intl/nb.ts +1 -1
- package/src/intl/nl.ts +1 -1
- package/src/intl/pa.ts +1 -1
- package/src/intl/pl.ts +1 -1
- package/src/intl/pt-BR.ts +1 -1
- package/src/intl/pt.ts +1 -1
- package/src/intl/ro.ts +1 -1
- package/src/intl/sk.ts +1 -1
- package/src/intl/sl.ts +1 -1
- package/src/intl/sv.ts +1 -1
- package/src/intl/sw.ts +1 -1
- package/src/intl/ta.ts +1 -1
- package/src/intl/te.ts +1 -1
- package/src/intl/th.ts +1 -1
- package/src/intl/tr.ts +1 -1
- package/src/intl/uk.ts +1 -1
- package/src/intl/ur.ts +1 -1
- package/src/intl/vi.ts +1 -1
- package/src/intl/zh-CN.ts +1 -1
- package/src/intl/zh-TW.ts +1 -1
- package/src/local-first/Evolu.test.ts +1921 -0
- package/src/local-first/Evolu.ts +18 -14
- package/src/local-first/Owner.test.ts +83 -0
- package/src/local-first/Protocol.test.ts +1037 -0
- package/src/local-first/Protocol.ts +58 -261
- package/src/local-first/Query.test.ts +349 -0
- package/src/local-first/Query.ts +11 -11
- package/src/local-first/Relay.ts +2 -2
- package/src/local-first/Schema.ts +3 -5
- package/src/local-first/Shared.test.ts +1828 -0
- package/src/local-first/Shared.ts +9 -8
- package/src/local-first/Storage.ts +4 -4
- package/src/local-first/Timestamp.test.ts +374 -0
- package/src/local-first/Timestamp.ts +22 -10
- package/dist/src/Buffer.d.ts.map +0 -1
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
import nodeAssert from "node:assert/strict";
|
|
2
|
+
import { describe, it, test } from "node:test";
|
|
3
|
+
import { stripVTControlCharacters } from "node:util";
|
|
4
|
+
import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts";
|
|
5
|
+
import {
|
|
6
|
+
assert,
|
|
7
|
+
assertConditionAfterMicrotasks,
|
|
8
|
+
assertEqual,
|
|
9
|
+
assertEqualBytes,
|
|
10
|
+
assertErr,
|
|
11
|
+
assertFalse,
|
|
12
|
+
assertInstanceOf,
|
|
13
|
+
assertLength,
|
|
14
|
+
assertNonEmptyArray,
|
|
15
|
+
assertNonEmptyReadonlyArray,
|
|
16
|
+
assertNonNullable,
|
|
17
|
+
assertNotDisposed,
|
|
18
|
+
assertNotEqual,
|
|
19
|
+
assertNotNull,
|
|
20
|
+
assertNotSame,
|
|
21
|
+
assertNotUndefined,
|
|
22
|
+
assertOk,
|
|
23
|
+
assertRejects,
|
|
24
|
+
assertRejectsInstanceOf,
|
|
25
|
+
assertRejectsSame,
|
|
26
|
+
assertSame,
|
|
27
|
+
assertThrows,
|
|
28
|
+
assertThrowsInstanceOf,
|
|
29
|
+
assertThrowsSame,
|
|
30
|
+
assertTrue,
|
|
31
|
+
} from "./Assert.ts";
|
|
32
|
+
import type { Brand } from "./Brand.ts";
|
|
33
|
+
import { err, ok, type Err, type Ok, type Result } from "./Result.ts";
|
|
34
|
+
import { assertType, Number } from "./Type.ts";
|
|
35
|
+
|
|
36
|
+
test("assert", () => {
|
|
37
|
+
assert(true, "Should not throw");
|
|
38
|
+
|
|
39
|
+
nodeAssert.throws(() => {
|
|
40
|
+
assert(false, "Condition failed");
|
|
41
|
+
}, /Condition failed/u);
|
|
42
|
+
|
|
43
|
+
const cause = new Error("Cause");
|
|
44
|
+
nodeAssert.throws(
|
|
45
|
+
() => {
|
|
46
|
+
assert(false, "Condition failed", { cause });
|
|
47
|
+
},
|
|
48
|
+
(error: unknown) => {
|
|
49
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
50
|
+
nodeAssert.equal(error.name, "AssertionError");
|
|
51
|
+
nodeAssert.equal(error.code, "ERR_ASSERTION");
|
|
52
|
+
nodeAssert.equal(error.actual, false);
|
|
53
|
+
nodeAssert.equal(error.expected, true);
|
|
54
|
+
nodeAssert.equal(error.generatedMessage, false);
|
|
55
|
+
nodeAssert.equal(error.operator, "==");
|
|
56
|
+
nodeAssert.equal(error.cause, cause);
|
|
57
|
+
nodeAssert.ok(error.stack);
|
|
58
|
+
nodeAssert.equal(
|
|
59
|
+
error.stack.includes("/packages/common/src/Assert.ts:"),
|
|
60
|
+
false,
|
|
61
|
+
);
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const assertString: (value: unknown) => asserts value is string = (value) => {
|
|
67
|
+
assert(typeof value === "string", "Expected a string.", {
|
|
68
|
+
actual: value,
|
|
69
|
+
expected: "string",
|
|
70
|
+
operator: "typeof",
|
|
71
|
+
stackStartFn: assertString,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const value: unknown = "value";
|
|
75
|
+
|
|
76
|
+
assertString(value);
|
|
77
|
+
assertType<typeof value, string>();
|
|
78
|
+
|
|
79
|
+
nodeAssert.throws(
|
|
80
|
+
() => {
|
|
81
|
+
assertString(42);
|
|
82
|
+
},
|
|
83
|
+
(error: unknown) => {
|
|
84
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
85
|
+
nodeAssert.equal(error.actual, 42);
|
|
86
|
+
nodeAssert.equal(error.expected, "string");
|
|
87
|
+
nodeAssert.equal(error.operator, "typeof");
|
|
88
|
+
nodeAssert.ok(error.stack);
|
|
89
|
+
nodeAssert.equal(error.stack.includes("at assertString "), false);
|
|
90
|
+
return true;
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("assert uses a compatible fallback without Node.js", async () => {
|
|
96
|
+
let fallbackAssert: typeof assert = assert;
|
|
97
|
+
let fallbackAssertEqual: typeof assertEqual = assertEqual;
|
|
98
|
+
const nodeProcess = process;
|
|
99
|
+
|
|
100
|
+
if (nodeProcess?.getBuiltinModule !== undefined) {
|
|
101
|
+
const getBuiltinModuleDescriptor = Object.getOwnPropertyDescriptor(
|
|
102
|
+
nodeProcess,
|
|
103
|
+
"getBuiltinModule",
|
|
104
|
+
);
|
|
105
|
+
Object.defineProperty(nodeProcess, "getBuiltinModule", {
|
|
106
|
+
configurable: true,
|
|
107
|
+
value: undefined,
|
|
108
|
+
writable: true,
|
|
109
|
+
});
|
|
110
|
+
try {
|
|
111
|
+
const fallback = (await import(
|
|
112
|
+
`./Assert.ts?fallback=${Date.now()}`
|
|
113
|
+
)) as typeof import("./Assert.ts");
|
|
114
|
+
fallbackAssert = fallback.assert;
|
|
115
|
+
fallbackAssertEqual = fallback.assertEqual;
|
|
116
|
+
} finally {
|
|
117
|
+
if (getBuiltinModuleDescriptor === undefined) {
|
|
118
|
+
delete (nodeProcess as { getBuiltinModule?: unknown }).getBuiltinModule;
|
|
119
|
+
} else {
|
|
120
|
+
Object.defineProperty(
|
|
121
|
+
nodeProcess,
|
|
122
|
+
"getBuiltinModule",
|
|
123
|
+
getBuiltinModuleDescriptor,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const cause = new Error("Cause");
|
|
130
|
+
nodeAssert.throws(
|
|
131
|
+
() => {
|
|
132
|
+
fallbackAssert(false, "Condition failed", { cause });
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "AssertionError",
|
|
136
|
+
code: "ERR_ASSERTION",
|
|
137
|
+
message: "Condition failed",
|
|
138
|
+
actual: false,
|
|
139
|
+
expected: true,
|
|
140
|
+
generatedMessage: false,
|
|
141
|
+
operator: "==",
|
|
142
|
+
cause,
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
nodeAssert.throws(
|
|
147
|
+
() => {
|
|
148
|
+
fallbackAssert(false, "Condition failed");
|
|
149
|
+
},
|
|
150
|
+
(error: unknown) => {
|
|
151
|
+
nodeAssert.ok(error instanceof Error);
|
|
152
|
+
nodeAssert.equal("cause" in error, false);
|
|
153
|
+
return true;
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
nodeAssert.throws(
|
|
158
|
+
() => {
|
|
159
|
+
fallbackAssertEqual({ value: 1 }, { value: 2 });
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
actual: { value: 1 },
|
|
163
|
+
expected: { value: 2 },
|
|
164
|
+
operator: "eqData",
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const assertString: (value: unknown) => asserts value is string = (value) => {
|
|
169
|
+
fallbackAssert(typeof value === "string", "Expected a string.", {
|
|
170
|
+
stackStartFn: assertString,
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
nodeAssert.throws(
|
|
174
|
+
() => assertString(42),
|
|
175
|
+
(error: unknown) => {
|
|
176
|
+
nodeAssert.ok(error instanceof Error);
|
|
177
|
+
nodeAssert.ok(error.stack);
|
|
178
|
+
nodeAssert.equal(error.stack.includes("at assertString "), false);
|
|
179
|
+
nodeAssert.equal(
|
|
180
|
+
error.stack.includes("/packages/common/src/Assert.ts"),
|
|
181
|
+
false,
|
|
182
|
+
);
|
|
183
|
+
return true;
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("assertTrue", () => {
|
|
189
|
+
const value: unknown = true;
|
|
190
|
+
|
|
191
|
+
assertTrue(value);
|
|
192
|
+
assertType<typeof value, true>();
|
|
193
|
+
|
|
194
|
+
nodeAssert.throws(() => assertTrue(1), /Expected true\./u);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("assertTrue narrows named type guard conditions", () => {
|
|
198
|
+
interface Value {
|
|
199
|
+
readonly type: "Value";
|
|
200
|
+
readonly value: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const isValue = (value: unknown): value is Value =>
|
|
204
|
+
typeof value === "object" &&
|
|
205
|
+
value !== null &&
|
|
206
|
+
"type" in value &&
|
|
207
|
+
value.type === "Value" &&
|
|
208
|
+
"value" in value &&
|
|
209
|
+
typeof value.value === "number";
|
|
210
|
+
const value: unknown = { type: "Value", value: 42 };
|
|
211
|
+
|
|
212
|
+
assertTrue(isValue(value));
|
|
213
|
+
|
|
214
|
+
assertType<typeof value, Value>();
|
|
215
|
+
assertEqual(value.value, 42);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("assertFalse", () => {
|
|
219
|
+
const value: unknown = false;
|
|
220
|
+
|
|
221
|
+
assertFalse(value);
|
|
222
|
+
assertType<typeof value, false>();
|
|
223
|
+
|
|
224
|
+
nodeAssert.throws(() => assertFalse(0), /Expected false\./u);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("assertConditionAfterMicrotasks", () => {
|
|
228
|
+
it("resolves when the condition becomes true after the exact count", async () => {
|
|
229
|
+
let callCount = 0;
|
|
230
|
+
|
|
231
|
+
await assertConditionAfterMicrotasks(() => callCount++ === 2, 2);
|
|
232
|
+
|
|
233
|
+
nodeAssert.equal(callCount, 3);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("rejects when the condition becomes true too early", async () => {
|
|
237
|
+
await nodeAssert.rejects(
|
|
238
|
+
assertConditionAfterMicrotasks(() => true, 1),
|
|
239
|
+
/Expected condition to be false after 0 microtasks\./u,
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("rejects when the condition is still false after the exact count", async () => {
|
|
244
|
+
await nodeAssert.rejects(
|
|
245
|
+
assertConditionAfterMicrotasks(() => false, 0),
|
|
246
|
+
/Expected condition to be true after exactly 0 microtasks\./u,
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("assertThrows", () => {
|
|
252
|
+
it("compares the thrown value", () => {
|
|
253
|
+
assertThrows(
|
|
254
|
+
() => {
|
|
255
|
+
// oxlint-disable-next-line eslint/no-throw-literal, typescript/only-throw-error -- JavaScript permits throwing any value, which is part of the assertion contract.
|
|
256
|
+
throw { type: "ExpectedFailure" };
|
|
257
|
+
},
|
|
258
|
+
{ type: "ExpectedFailure" },
|
|
259
|
+
);
|
|
260
|
+
assertThrows(() => {
|
|
261
|
+
// oxlint-disable-next-line eslint/no-throw-literal, typescript/only-throw-error -- JavaScript permits throwing any value, which is part of the assertion contract.
|
|
262
|
+
throw undefined;
|
|
263
|
+
}, undefined);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("runs a custom assertion for the thrown value", () => {
|
|
267
|
+
const error = new Error("Expected failure.");
|
|
268
|
+
|
|
269
|
+
assertThrows(
|
|
270
|
+
() => {
|
|
271
|
+
throw error;
|
|
272
|
+
},
|
|
273
|
+
(thrown) => assertSame(thrown, error),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
nodeAssert.throws(
|
|
277
|
+
() =>
|
|
278
|
+
assertThrows(
|
|
279
|
+
() => {
|
|
280
|
+
throw error;
|
|
281
|
+
},
|
|
282
|
+
// oxlint-disable-next-line typescript/strict-void-return -- Exercise the runtime rejection of predicates that return instead of asserting.
|
|
283
|
+
() => true,
|
|
284
|
+
),
|
|
285
|
+
/Expected the thrown value assertion to return undefined\./u,
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("fails when the thrown value is not equal to the expected value", () => {
|
|
290
|
+
const actual = { value: 1 };
|
|
291
|
+
const expected = { value: 2 };
|
|
292
|
+
|
|
293
|
+
nodeAssert.throws(
|
|
294
|
+
() =>
|
|
295
|
+
assertThrows(() => {
|
|
296
|
+
// oxlint-disable-next-line typescript/only-throw-error -- JavaScript permits throwing any value, which is part of the assertion contract.
|
|
297
|
+
throw actual;
|
|
298
|
+
}, expected),
|
|
299
|
+
(error: unknown) => {
|
|
300
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
301
|
+
nodeAssert.deepEqual(error.actual, actual);
|
|
302
|
+
nodeAssert.deepEqual(error.expected, expected);
|
|
303
|
+
nodeAssert.equal(error.operator, "eqData");
|
|
304
|
+
nodeAssert.match(
|
|
305
|
+
error.message,
|
|
306
|
+
/^Expected the thrown value to equal the expected value\./u,
|
|
307
|
+
);
|
|
308
|
+
nodeAssert.ok(error.stack);
|
|
309
|
+
nodeAssert.equal(error.stack.includes("at assertThrows "), false);
|
|
310
|
+
return true;
|
|
311
|
+
},
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("fails when the function returns", () => {
|
|
316
|
+
nodeAssert.throws(
|
|
317
|
+
() => assertThrows(() => 42, 42),
|
|
318
|
+
(error: unknown) => {
|
|
319
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
320
|
+
nodeAssert.equal(error.actual, 42);
|
|
321
|
+
nodeAssert.equal(error.expected, "throw");
|
|
322
|
+
nodeAssert.equal(error.operator, "throws");
|
|
323
|
+
nodeAssert.equal(error.message, "Expected function to throw.");
|
|
324
|
+
nodeAssert.ok(error.stack);
|
|
325
|
+
nodeAssert.equal(error.stack.includes("at assertThrows "), false);
|
|
326
|
+
return true;
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe("assertThrowsSame", () => {
|
|
333
|
+
it("compares the thrown value using SameValue", () => {
|
|
334
|
+
const expected = () => undefined;
|
|
335
|
+
|
|
336
|
+
assertThrowsSame(() => {
|
|
337
|
+
// oxlint-disable-next-line typescript/only-throw-error -- JavaScript permits throwing any value, which is part of the assertion contract.
|
|
338
|
+
throw expected;
|
|
339
|
+
}, expected);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("fails when the thrown value is not the same", () => {
|
|
343
|
+
nodeAssert.throws(
|
|
344
|
+
() =>
|
|
345
|
+
assertThrowsSame(
|
|
346
|
+
() => {
|
|
347
|
+
// oxlint-disable-next-line eslint/no-throw-literal, typescript/only-throw-error -- JavaScript permits throwing any value, which is part of the assertion contract.
|
|
348
|
+
throw { value: 1 };
|
|
349
|
+
},
|
|
350
|
+
{ value: 1 },
|
|
351
|
+
),
|
|
352
|
+
/Expected the thrown value to be the same as the expected value\./u,
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe("assertThrowsInstanceOf", () => {
|
|
358
|
+
it("returns the narrowed thrown value", () => {
|
|
359
|
+
class TestError extends Error {}
|
|
360
|
+
|
|
361
|
+
const error = assertThrowsInstanceOf(() => {
|
|
362
|
+
throw new TestError("Expected failure.");
|
|
363
|
+
}, TestError);
|
|
364
|
+
|
|
365
|
+
assertType<typeof error, TestError>();
|
|
366
|
+
assertEqual(error.message, "Expected failure.");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("fails when the thrown value is not an instance", () => {
|
|
370
|
+
const AnonymousError = Object.defineProperty(
|
|
371
|
+
class extends Error {},
|
|
372
|
+
"name",
|
|
373
|
+
{
|
|
374
|
+
value: "",
|
|
375
|
+
},
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
nodeAssert.throws(
|
|
379
|
+
() =>
|
|
380
|
+
assertThrowsInstanceOf(() => {
|
|
381
|
+
throw new Error("Actual error.");
|
|
382
|
+
}, AnonymousError),
|
|
383
|
+
/Expected the thrown value to be an instance of the provided constructor\./u,
|
|
384
|
+
);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
describe("assertRejects", () => {
|
|
389
|
+
it("compares the rejection reason", async () => {
|
|
390
|
+
// oxlint-disable-next-line eslint/prefer-promise-reject-errors, typescript/prefer-promise-reject-errors -- JavaScript permits rejecting with any value, which is part of the assertion contract.
|
|
391
|
+
await assertRejects(Promise.reject({ type: "ExpectedFailure" }), {
|
|
392
|
+
type: "ExpectedFailure",
|
|
393
|
+
});
|
|
394
|
+
await assertRejects(
|
|
395
|
+
// oxlint-disable-next-line eslint/prefer-promise-reject-errors, typescript/prefer-promise-reject-errors -- JavaScript permits rejecting with any value, which is part of the assertion contract.
|
|
396
|
+
Promise.reject(undefined),
|
|
397
|
+
undefined,
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("runs a custom assertion for the rejection reason", async () => {
|
|
402
|
+
const error = new Error("Expected failure.");
|
|
403
|
+
|
|
404
|
+
await assertRejects(Promise.reject(error), (reason) =>
|
|
405
|
+
assertSame(reason, error),
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
await nodeAssert.rejects(
|
|
409
|
+
// oxlint-disable-next-line typescript/strict-void-return -- Exercise the runtime rejection of predicates that return instead of asserting.
|
|
410
|
+
assertRejects(Promise.reject(error), () => true),
|
|
411
|
+
/Expected the rejection assertion to return undefined\./u,
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("fails when the rejection reason is not equal to the expected value", async () => {
|
|
416
|
+
const actual = { value: 1 };
|
|
417
|
+
const expected = { value: 2 };
|
|
418
|
+
|
|
419
|
+
await nodeAssert.rejects(
|
|
420
|
+
// oxlint-disable-next-line typescript/prefer-promise-reject-errors -- JavaScript permits rejecting with any value, which is part of the assertion contract.
|
|
421
|
+
assertRejects(Promise.reject(actual), expected),
|
|
422
|
+
(error: unknown) => {
|
|
423
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
424
|
+
nodeAssert.deepEqual(error.actual, actual);
|
|
425
|
+
nodeAssert.deepEqual(error.expected, expected);
|
|
426
|
+
nodeAssert.equal(error.operator, "eqData");
|
|
427
|
+
nodeAssert.match(
|
|
428
|
+
error.message,
|
|
429
|
+
/^Expected the rejection reason to equal the expected value\./u,
|
|
430
|
+
);
|
|
431
|
+
nodeAssert.ok(error.stack);
|
|
432
|
+
nodeAssert.equal(error.stack.includes("at assertRejects "), false);
|
|
433
|
+
return true;
|
|
434
|
+
},
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("fails when the promise resolves", async () => {
|
|
439
|
+
await nodeAssert.rejects(
|
|
440
|
+
assertRejects(Promise.resolve(42), 42),
|
|
441
|
+
(error: unknown) => {
|
|
442
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
443
|
+
nodeAssert.equal(error.actual, 42);
|
|
444
|
+
nodeAssert.equal(error.expected, "rejection");
|
|
445
|
+
nodeAssert.equal(error.operator, "rejects");
|
|
446
|
+
nodeAssert.equal(error.message, "Expected promise to reject.");
|
|
447
|
+
nodeAssert.ok(error.stack);
|
|
448
|
+
nodeAssert.equal(error.stack.includes("at assertRejects "), false);
|
|
449
|
+
return true;
|
|
450
|
+
},
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe("assertRejectsSame", () => {
|
|
456
|
+
it("compares the rejection reason using SameValue", async () => {
|
|
457
|
+
const expected = () => undefined;
|
|
458
|
+
|
|
459
|
+
// oxlint-disable-next-line typescript/prefer-promise-reject-errors -- JavaScript permits rejecting with any value, which is part of the assertion contract.
|
|
460
|
+
await assertRejectsSame(Promise.reject(expected), expected);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("fails when the rejection reason is not the same", async () => {
|
|
464
|
+
await nodeAssert.rejects(
|
|
465
|
+
// oxlint-disable-next-line eslint/prefer-promise-reject-errors, typescript/prefer-promise-reject-errors -- JavaScript permits rejecting with any value, which is part of the assertion contract.
|
|
466
|
+
assertRejectsSame(Promise.reject({ value: 1 }), { value: 1 }),
|
|
467
|
+
/Expected the rejection reason to be the same as the expected value\./u,
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
describe("assertRejectsInstanceOf", () => {
|
|
473
|
+
it("returns the narrowed rejection reason", async () => {
|
|
474
|
+
class TestError extends Error {}
|
|
475
|
+
|
|
476
|
+
const error = await assertRejectsInstanceOf(
|
|
477
|
+
Promise.reject(new TestError("Expected failure.")),
|
|
478
|
+
TestError,
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
assertType<typeof error, TestError>();
|
|
482
|
+
assertEqual(error.message, "Expected failure.");
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("fails when the rejection reason is not an instance", async () => {
|
|
486
|
+
const AnonymousError = Object.defineProperty(
|
|
487
|
+
class extends Error {},
|
|
488
|
+
"name",
|
|
489
|
+
{
|
|
490
|
+
value: "",
|
|
491
|
+
},
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
await nodeAssert.rejects(
|
|
495
|
+
assertRejectsInstanceOf(
|
|
496
|
+
Promise.reject(new Error("Actual error.")),
|
|
497
|
+
AnonymousError,
|
|
498
|
+
),
|
|
499
|
+
/Expected the rejection reason to be an instance of the provided constructor\./u,
|
|
500
|
+
);
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
test("assertInstanceOf", () => {
|
|
505
|
+
class TestError extends Error {}
|
|
506
|
+
|
|
507
|
+
const value: unknown = new TestError("Test error");
|
|
508
|
+
|
|
509
|
+
assertInstanceOf(value, TestError);
|
|
510
|
+
assertType<typeof value, TestError>();
|
|
511
|
+
|
|
512
|
+
const actual = new Error("Actual error");
|
|
513
|
+
nodeAssert.throws(
|
|
514
|
+
() => assertInstanceOf(actual, TestError),
|
|
515
|
+
(error: unknown) => {
|
|
516
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
517
|
+
nodeAssert.equal(error.actual, actual);
|
|
518
|
+
nodeAssert.equal(error.expected, TestError);
|
|
519
|
+
nodeAssert.equal(error.operator, "instanceof");
|
|
520
|
+
nodeAssert.equal(error.message, "Expected an instance of TestError.");
|
|
521
|
+
nodeAssert.ok(error.stack);
|
|
522
|
+
nodeAssert.equal(error.stack.includes("at assertInstanceOf "), false);
|
|
523
|
+
return true;
|
|
524
|
+
},
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
const AnonymousError = Object.defineProperty(class extends Error {}, "name", {
|
|
528
|
+
value: "",
|
|
529
|
+
});
|
|
530
|
+
nodeAssert.throws(
|
|
531
|
+
() => assertInstanceOf(actual, AnonymousError),
|
|
532
|
+
/Expected an instance of the provided constructor\./u,
|
|
533
|
+
);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test("assertNonNullable", () => {
|
|
537
|
+
const value = "value" as string | null | undefined;
|
|
538
|
+
assertNonNullable(value);
|
|
539
|
+
assertType<typeof value, string>();
|
|
540
|
+
nodeAssert.equal(value, "value");
|
|
541
|
+
|
|
542
|
+
nodeAssert.throws(() => assertNonNullable(null), {
|
|
543
|
+
actual: null,
|
|
544
|
+
expected: null,
|
|
545
|
+
operator: "!=",
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
nodeAssert.throws(() => {
|
|
549
|
+
assertNonNullable(undefined, "Custom error");
|
|
550
|
+
}, /Custom error/u);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("assertNotNull", () => {
|
|
554
|
+
const value = undefined as string | null | undefined;
|
|
555
|
+
assertNotNull(value);
|
|
556
|
+
assertType<typeof value, string | undefined>();
|
|
557
|
+
nodeAssert.equal(value, undefined);
|
|
558
|
+
|
|
559
|
+
const unknownValue: unknown = undefined;
|
|
560
|
+
assertNotNull(unknownValue);
|
|
561
|
+
assertType<typeof unknownValue, {} | undefined>();
|
|
562
|
+
nodeAssert.equal(unknownValue, undefined);
|
|
563
|
+
|
|
564
|
+
nodeAssert.throws(() => assertNotNull(null), {
|
|
565
|
+
actual: null,
|
|
566
|
+
expected: null,
|
|
567
|
+
operator: "notStrictEqual",
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
nodeAssert.throws(() => {
|
|
571
|
+
assertNotNull(null, "Custom error");
|
|
572
|
+
}, /Custom error/u);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test("assertNotUndefined", () => {
|
|
576
|
+
const value = null as string | null | undefined;
|
|
577
|
+
assertNotUndefined(value);
|
|
578
|
+
assertType<typeof value, string | null>();
|
|
579
|
+
nodeAssert.equal(value, null);
|
|
580
|
+
|
|
581
|
+
const unknownValue: unknown = null;
|
|
582
|
+
assertNotUndefined(unknownValue);
|
|
583
|
+
assertType<typeof unknownValue, {} | null>();
|
|
584
|
+
nodeAssert.equal(unknownValue, null);
|
|
585
|
+
|
|
586
|
+
nodeAssert.throws(() => assertNotUndefined(undefined), {
|
|
587
|
+
actual: undefined,
|
|
588
|
+
expected: undefined,
|
|
589
|
+
operator: "notStrictEqual",
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
nodeAssert.throws(() => {
|
|
593
|
+
assertNotUndefined(undefined, "Custom error");
|
|
594
|
+
}, /Custom error/u);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test("assertLength", () => {
|
|
598
|
+
const values: ReadonlyArray<string> = ["Ada", "Grace"];
|
|
599
|
+
|
|
600
|
+
assertLength(values, 2);
|
|
601
|
+
assertType<typeof values.length, 2>();
|
|
602
|
+
|
|
603
|
+
nodeAssert.throws(
|
|
604
|
+
() => assertLength(values, 1),
|
|
605
|
+
(error: unknown) => {
|
|
606
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
607
|
+
nodeAssert.equal(error.actual, 2);
|
|
608
|
+
nodeAssert.equal(error.expected, 1);
|
|
609
|
+
nodeAssert.equal(error.operator, "strictEqual");
|
|
610
|
+
nodeAssert.match(error.message, /^Expected value to have length 1\./u);
|
|
611
|
+
nodeAssert.ok(error.stack);
|
|
612
|
+
nodeAssert.equal(error.stack.includes("at assertLength "), false);
|
|
613
|
+
return true;
|
|
614
|
+
},
|
|
615
|
+
);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
test("assertNonEmptyArray", () => {
|
|
619
|
+
// Valid non-empty array
|
|
620
|
+
const arr = [1, 2, 3];
|
|
621
|
+
assertNonEmptyArray(arr);
|
|
622
|
+
assertType<typeof arr, NonEmptyArray<number>>();
|
|
623
|
+
// No type change, just validation
|
|
624
|
+
nodeAssert.deepEqual(arr, [1, 2, 3]);
|
|
625
|
+
|
|
626
|
+
// Empty array should throw
|
|
627
|
+
nodeAssert.throws(() => assertNonEmptyArray([]), {
|
|
628
|
+
actual: 0,
|
|
629
|
+
expected: 0,
|
|
630
|
+
operator: ">",
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Custom error message
|
|
634
|
+
nodeAssert.throws(() => {
|
|
635
|
+
assertNonEmptyArray([], "Custom error");
|
|
636
|
+
}, /Custom error/u);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
test("assertNonEmptyReadonlyArray", () => {
|
|
640
|
+
// Valid non-empty readonly array
|
|
641
|
+
const arr: ReadonlyArray<number> = [1, 2, 3];
|
|
642
|
+
assertNonEmptyReadonlyArray(arr);
|
|
643
|
+
assertType<typeof arr, NonEmptyReadonlyArray<number>>();
|
|
644
|
+
// Ensures no changes
|
|
645
|
+
nodeAssert.deepEqual(arr, [1, 2, 3]);
|
|
646
|
+
|
|
647
|
+
// Empty readonly array should throw
|
|
648
|
+
nodeAssert.throws(() => assertNonEmptyReadonlyArray([]), {
|
|
649
|
+
actual: 0,
|
|
650
|
+
expected: 0,
|
|
651
|
+
operator: ">",
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// Custom error message
|
|
655
|
+
nodeAssert.throws(() => {
|
|
656
|
+
assertNonEmptyReadonlyArray([], "Custom error");
|
|
657
|
+
}, /Custom error/u);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test("assertNotDisposed", async () => {
|
|
661
|
+
const stack = new AsyncDisposableStack();
|
|
662
|
+
|
|
663
|
+
assertNotDisposed(stack);
|
|
664
|
+
|
|
665
|
+
await stack.disposeAsync();
|
|
666
|
+
|
|
667
|
+
nodeAssert.throws(() => assertNotDisposed(stack), {
|
|
668
|
+
actual: true,
|
|
669
|
+
expected: false,
|
|
670
|
+
operator: "==",
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test("assertion helpers omit their implementation from Node.js stack traces", () => {
|
|
675
|
+
if (globalThis.process?.getBuiltinModule === undefined) return;
|
|
676
|
+
|
|
677
|
+
const failures: ReadonlyArray<readonly [string, () => void]> = [
|
|
678
|
+
["assertNonNullable", () => assertNonNullable(null)],
|
|
679
|
+
["assertNotNull", () => assertNotNull(null)],
|
|
680
|
+
["assertNotUndefined", () => assertNotUndefined(undefined)],
|
|
681
|
+
["assertLength", () => assertLength([], 1)],
|
|
682
|
+
["assertNonEmptyArray", () => assertNonEmptyArray([])],
|
|
683
|
+
["assertNonEmptyReadonlyArray", () => assertNonEmptyReadonlyArray([])],
|
|
684
|
+
["assertNotDisposed", () => assertNotDisposed({ disposed: true })],
|
|
685
|
+
["assertInstanceOf", () => assertInstanceOf({}, Error)],
|
|
686
|
+
];
|
|
687
|
+
|
|
688
|
+
for (const [name, run] of failures) {
|
|
689
|
+
nodeAssert.throws(run, (error: unknown) => {
|
|
690
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
691
|
+
nodeAssert.ok(error.stack);
|
|
692
|
+
nodeAssert.equal(error.stack.includes(`at ${name} `), false);
|
|
693
|
+
return true;
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test("assertSame", () => {
|
|
699
|
+
interface User {
|
|
700
|
+
readonly name: string;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const user: User = { name: "Ada" };
|
|
704
|
+
const value: unknown = user;
|
|
705
|
+
|
|
706
|
+
assertSame(value, user);
|
|
707
|
+
assertType<typeof value, User>();
|
|
708
|
+
assertSame(NaN, NaN);
|
|
709
|
+
|
|
710
|
+
const otherUser = { name: "Ada" };
|
|
711
|
+
nodeAssert.throws(
|
|
712
|
+
() => assertSame(user, otherUser),
|
|
713
|
+
(error: unknown) => {
|
|
714
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
715
|
+
nodeAssert.equal(error.actual, user);
|
|
716
|
+
nodeAssert.equal(error.expected, otherUser);
|
|
717
|
+
nodeAssert.equal(error.operator, "strictEqual");
|
|
718
|
+
nodeAssert.match(error.message, /Expected values to be the same\./u);
|
|
719
|
+
return true;
|
|
720
|
+
},
|
|
721
|
+
);
|
|
722
|
+
nodeAssert.throws(
|
|
723
|
+
() => assertSame(0, -0),
|
|
724
|
+
/Expected values to be the same\./u,
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test("assertNotSame", () => {
|
|
729
|
+
const first = { name: "Ada" };
|
|
730
|
+
const second = { name: "Ada" };
|
|
731
|
+
|
|
732
|
+
assertNotSame(first, second);
|
|
733
|
+
assertNotSame(0, -0);
|
|
734
|
+
|
|
735
|
+
nodeAssert.throws(
|
|
736
|
+
() => assertNotSame(first, first),
|
|
737
|
+
(error: unknown) => {
|
|
738
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
739
|
+
nodeAssert.equal(error.actual, first);
|
|
740
|
+
nodeAssert.equal(error.expected, first);
|
|
741
|
+
nodeAssert.equal(error.operator, "notStrictEqual");
|
|
742
|
+
nodeAssert.equal(error.generatedMessage, false);
|
|
743
|
+
nodeAssert.equal(error.message, "Expected values not to be the same.");
|
|
744
|
+
return true;
|
|
745
|
+
},
|
|
746
|
+
);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
test("assertEqual", () => {
|
|
750
|
+
interface User {
|
|
751
|
+
readonly name: string;
|
|
752
|
+
readonly roles: ReadonlySet<string>;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const actual: User = {
|
|
756
|
+
name: "Ada",
|
|
757
|
+
roles: new Set(["admin", "author"]),
|
|
758
|
+
};
|
|
759
|
+
const expected: User = {
|
|
760
|
+
name: "Ada",
|
|
761
|
+
roles: new Set(["author", "admin"]),
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
assertEqual(actual, expected);
|
|
765
|
+
const unknownActual: unknown = ["fmt", "msg"];
|
|
766
|
+
const unknownExpected: unknown = ["fmt", "msg"];
|
|
767
|
+
assertEqual(unknownActual, unknownExpected);
|
|
768
|
+
|
|
769
|
+
const opaque = (): void => undefined;
|
|
770
|
+
assertEqual(opaque, opaque);
|
|
771
|
+
assertEqual({ opaque }, { opaque });
|
|
772
|
+
nodeAssert.throws(
|
|
773
|
+
() =>
|
|
774
|
+
assertEqual(
|
|
775
|
+
() => undefined,
|
|
776
|
+
() => undefined,
|
|
777
|
+
),
|
|
778
|
+
/Expected values to be equal\./u,
|
|
779
|
+
);
|
|
780
|
+
nodeAssert.throws(() => assertEqual(0, -0), /Expected values to be equal\./u);
|
|
781
|
+
|
|
782
|
+
const unexpected = { ...expected, name: "Grace" };
|
|
783
|
+
nodeAssert.throws(
|
|
784
|
+
() => assertEqual(actual, unexpected),
|
|
785
|
+
(error: unknown) => {
|
|
786
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
787
|
+
nodeAssert.equal(error.actual, actual);
|
|
788
|
+
nodeAssert.equal(error.expected, unexpected);
|
|
789
|
+
nodeAssert.equal(error.operator, "eqData");
|
|
790
|
+
nodeAssert.match(error.message, /Expected values to be equal\./u);
|
|
791
|
+
nodeAssert.match(
|
|
792
|
+
stripVTControlCharacters(error.message),
|
|
793
|
+
/\+ actual - expected/u,
|
|
794
|
+
);
|
|
795
|
+
return true;
|
|
796
|
+
},
|
|
797
|
+
);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
test("assertEqualBytes", () => {
|
|
801
|
+
assertEqualBytes(new Uint8Array([1, 5, 39, 254]), [1, 5, 39, 254]);
|
|
802
|
+
|
|
803
|
+
const compileTimeAssertions = () => {
|
|
804
|
+
// @ts-expect-error assertEqualBytes actual value must be a Uint8Array.
|
|
805
|
+
assertEqualBytes([1, 5, 39, 254], [1, 5, 39, 254]);
|
|
806
|
+
};
|
|
807
|
+
assertEqual(typeof compileTimeAssertions, "function");
|
|
808
|
+
|
|
809
|
+
const actual = new Uint8Array([0]);
|
|
810
|
+
const expected = [1];
|
|
811
|
+
|
|
812
|
+
nodeAssert.throws(
|
|
813
|
+
() => assertEqualBytes(actual, expected),
|
|
814
|
+
(error: unknown) => {
|
|
815
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
816
|
+
nodeAssert.equal(error.actual, actual);
|
|
817
|
+
nodeAssert.equal(error.expected, expected);
|
|
818
|
+
nodeAssert.equal(error.operator, "eqArrayNumber");
|
|
819
|
+
nodeAssert.match(error.message, /Expected bytes to be equal\./u);
|
|
820
|
+
nodeAssert.ok(error.stack);
|
|
821
|
+
nodeAssert.equal(error.stack.includes("at assertEqualBytes "), false);
|
|
822
|
+
return true;
|
|
823
|
+
},
|
|
824
|
+
);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
test("assertNotEqual", () => {
|
|
828
|
+
const actual = { name: "Ada", roles: new Set(["admin", "author"]) };
|
|
829
|
+
const expected = { name: "Ada", roles: new Set(["author", "admin"]) };
|
|
830
|
+
|
|
831
|
+
assertNotEqual(actual, { ...expected, name: "Grace" });
|
|
832
|
+
assertNotEqual(0, -0);
|
|
833
|
+
assertNotEqual(
|
|
834
|
+
() => undefined,
|
|
835
|
+
() => undefined,
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
nodeAssert.throws(
|
|
839
|
+
() => assertNotEqual(actual, expected),
|
|
840
|
+
(error: unknown) => {
|
|
841
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
842
|
+
nodeAssert.equal(error.actual, actual);
|
|
843
|
+
nodeAssert.equal(error.expected, expected);
|
|
844
|
+
nodeAssert.equal(error.operator, "notEqData");
|
|
845
|
+
nodeAssert.equal(error.message, "Expected values not to be equal.");
|
|
846
|
+
return true;
|
|
847
|
+
},
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
const opaque = (): void => undefined;
|
|
851
|
+
nodeAssert.throws(
|
|
852
|
+
() => assertNotEqual(opaque, opaque),
|
|
853
|
+
/Expected values not to be equal\./u,
|
|
854
|
+
);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
describe("assertOk and assertErr", () => {
|
|
858
|
+
interface TestError {
|
|
859
|
+
readonly type: "TestError";
|
|
860
|
+
readonly code: number;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
it("asserts and narrows an Ok without comparing its value", () => {
|
|
864
|
+
interface Service {
|
|
865
|
+
readonly run: () => void;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
const createResult = (): Result<Service, TestError> =>
|
|
869
|
+
ok({ run: () => undefined });
|
|
870
|
+
const result = createResult();
|
|
871
|
+
|
|
872
|
+
assertOk(result);
|
|
873
|
+
|
|
874
|
+
assertType<typeof result, Ok<Service>>();
|
|
875
|
+
nodeAssert.equal(typeof result.value.run, "function");
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it("asserts and narrows an Ok with a deeply equal value", () => {
|
|
879
|
+
const result: Result<{ readonly value: number }, TestError> = ok({
|
|
880
|
+
value: 42,
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
assertOk(result, { value: 42 });
|
|
884
|
+
|
|
885
|
+
assertType<typeof result, Ok<{ readonly value: number }>>();
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
it("compares an Ok with an independently typed Data value", () => {
|
|
889
|
+
type Answer = number & Brand<"Answer">;
|
|
890
|
+
const result: Result<Answer, TestError> = ok(42 as Answer);
|
|
891
|
+
|
|
892
|
+
assertOk(result, 42);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it("asserts a void Ok", () => {
|
|
896
|
+
const result: Result<void, TestError> = ok();
|
|
897
|
+
|
|
898
|
+
assertOk(result, undefined);
|
|
899
|
+
|
|
900
|
+
assertType<typeof result, Ok<void>>();
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
it("compares an explicitly provided undefined value", () => {
|
|
904
|
+
const result: Result<number | undefined, TestError> = ok(42);
|
|
905
|
+
|
|
906
|
+
nodeAssert.throws(
|
|
907
|
+
() => assertOk(result, undefined),
|
|
908
|
+
/Expected the value to equal the expected value\./u,
|
|
909
|
+
);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it("rejects an Err when asserting an Ok", () => {
|
|
913
|
+
const result: Result<number, TestError> = err({
|
|
914
|
+
type: "TestError",
|
|
915
|
+
code: 1,
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
nodeAssert.throws(() => assertOk(result, 42), /Expected an Ok result\./u);
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
it("rejects an unexpected Ok value", () => {
|
|
922
|
+
const result: Result<{ readonly value: number }, TestError> = ok({
|
|
923
|
+
value: 42,
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
nodeAssert.throws(
|
|
927
|
+
() => assertOk(result, { value: 41 }),
|
|
928
|
+
/Expected the value to equal the expected value\./u,
|
|
929
|
+
);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
it("accepts a custom Eq for an Ok value", () => {
|
|
933
|
+
const result: Result<number, TestError> = ok(42);
|
|
934
|
+
|
|
935
|
+
assertOk(result, 40, (x, y) => Math.abs(x - y) <= 2);
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it("does not format a failed custom Ok Eq as deep equality", () => {
|
|
939
|
+
const actual = { value: 42 };
|
|
940
|
+
const expected = { value: 42 };
|
|
941
|
+
const result: Result<typeof actual, TestError> = ok(actual);
|
|
942
|
+
|
|
943
|
+
nodeAssert.throws(
|
|
944
|
+
() => {
|
|
945
|
+
assertOk(result, expected, Object.is);
|
|
946
|
+
},
|
|
947
|
+
(error: unknown) => {
|
|
948
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
949
|
+
nodeAssert.equal(error.actual, actual);
|
|
950
|
+
nodeAssert.equal(error.expected, expected);
|
|
951
|
+
nodeAssert.equal(error.operator, "Eq");
|
|
952
|
+
nodeAssert.equal(
|
|
953
|
+
error.message,
|
|
954
|
+
"Expected the value to equal the expected value.",
|
|
955
|
+
);
|
|
956
|
+
return true;
|
|
957
|
+
},
|
|
958
|
+
);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it("compares a non-Data Ok value by identity", () => {
|
|
962
|
+
interface Service {
|
|
963
|
+
readonly run: () => void;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const service: Service = { run: () => undefined };
|
|
967
|
+
const result: Result<Service, TestError> = ok(service);
|
|
968
|
+
|
|
969
|
+
assertOk(result, service);
|
|
970
|
+
nodeAssert.throws(
|
|
971
|
+
() => assertOk(result, { run: () => undefined }),
|
|
972
|
+
/Expected the value to equal the expected value\./u,
|
|
973
|
+
);
|
|
974
|
+
assertOk(result, service, (x, y) => x === y);
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
it("compares a broad object Ok value by identity", () => {
|
|
978
|
+
const value: NonNullable<unknown> = new WeakMap();
|
|
979
|
+
const result: Result<NonNullable<unknown>, TestError> = ok(value);
|
|
980
|
+
|
|
981
|
+
assertOk(result, value);
|
|
982
|
+
nodeAssert.throws(
|
|
983
|
+
() => assertOk(result, new WeakMap()),
|
|
984
|
+
/Expected the value to equal the expected value\./u,
|
|
985
|
+
);
|
|
986
|
+
assertOk(result, value, (x, y) => x === y);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
it("accepts an untyped expected value in a default Ok comparison", () => {
|
|
990
|
+
const value = JSON.parse("null");
|
|
991
|
+
const result = ok(42);
|
|
992
|
+
|
|
993
|
+
nodeAssert.throws(
|
|
994
|
+
() => assertOk(result, value),
|
|
995
|
+
/Expected the value to equal the expected value\./u,
|
|
996
|
+
);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
it("accepts an untyped Result value in a default Ok comparison", () => {
|
|
1000
|
+
const result = ok(JSON.parse("null"));
|
|
1001
|
+
|
|
1002
|
+
assertOk(result, null);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
it("asserts and narrows an Err with a deeply equal error", () => {
|
|
1006
|
+
const result: Result<number, TestError> = err({
|
|
1007
|
+
type: "TestError",
|
|
1008
|
+
code: 1,
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
assertErr(result, { code: 1, type: "TestError" });
|
|
1012
|
+
|
|
1013
|
+
assertType<typeof result, Err<TestError>>();
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
it("compares an Err with an independently typed Data value", () => {
|
|
1017
|
+
type ErrorCode = number & Brand<"ErrorCode">;
|
|
1018
|
+
interface BrandedTestError {
|
|
1019
|
+
readonly type: "TestError";
|
|
1020
|
+
readonly code: ErrorCode;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const result: Result<number, BrandedTestError> = err({
|
|
1024
|
+
type: "TestError",
|
|
1025
|
+
code: 1 as ErrorCode,
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
assertErr(result, { type: "TestError", code: 1 });
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
it("asserts and narrows an Err without comparing its error", () => {
|
|
1032
|
+
interface ServiceError {
|
|
1033
|
+
readonly type: "ServiceError";
|
|
1034
|
+
readonly retry: () => void;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const createResult = (): Result<number, ServiceError> =>
|
|
1038
|
+
err({ type: "ServiceError", retry: () => undefined });
|
|
1039
|
+
const result = createResult();
|
|
1040
|
+
|
|
1041
|
+
assertErr(result);
|
|
1042
|
+
|
|
1043
|
+
assertType<typeof result, Err<ServiceError>>();
|
|
1044
|
+
nodeAssert.equal(typeof result.error.retry, "function");
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
it("compares an explicitly provided undefined error", () => {
|
|
1048
|
+
const result: Result<number, TestError | undefined> = err({
|
|
1049
|
+
type: "TestError",
|
|
1050
|
+
code: 1,
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
nodeAssert.throws(
|
|
1054
|
+
() => assertErr(result, undefined),
|
|
1055
|
+
/Expected the error to equal the expected error\./u,
|
|
1056
|
+
);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
it("rejects an Ok when asserting an Err", () => {
|
|
1060
|
+
const result: Result<number, TestError> = ok(42);
|
|
1061
|
+
|
|
1062
|
+
nodeAssert.throws(
|
|
1063
|
+
() => assertErr(result, { type: "TestError", code: 1 }),
|
|
1064
|
+
/Expected an Err result\./u,
|
|
1065
|
+
);
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
it("rejects an unexpected Err error", () => {
|
|
1069
|
+
const result: Result<number, TestError> = err({
|
|
1070
|
+
type: "TestError",
|
|
1071
|
+
code: 1,
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
nodeAssert.throws(
|
|
1075
|
+
() => assertErr(result, { type: "TestError", code: 2 }),
|
|
1076
|
+
/Expected the error to equal the expected error\./u,
|
|
1077
|
+
);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it("accepts a custom Eq for an Err error", () => {
|
|
1081
|
+
const result: Result<number, TestError> = err({
|
|
1082
|
+
type: "TestError",
|
|
1083
|
+
code: 2,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
assertErr(
|
|
1087
|
+
result,
|
|
1088
|
+
{ type: "TestError", code: 1 },
|
|
1089
|
+
(x, y) => x.type === y.type,
|
|
1090
|
+
);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
it("does not format a failed custom Err Eq as deep equality", () => {
|
|
1094
|
+
const actual: TestError = { type: "TestError", code: 1 };
|
|
1095
|
+
const expected: TestError = { type: "TestError", code: 1 };
|
|
1096
|
+
const result: Result<number, TestError> = err(actual);
|
|
1097
|
+
|
|
1098
|
+
nodeAssert.throws(
|
|
1099
|
+
() => {
|
|
1100
|
+
assertErr(result, expected, Object.is);
|
|
1101
|
+
},
|
|
1102
|
+
(error: unknown) => {
|
|
1103
|
+
nodeAssert.ok(error instanceof nodeAssert.AssertionError);
|
|
1104
|
+
nodeAssert.equal(error.actual, actual);
|
|
1105
|
+
nodeAssert.equal(error.expected, expected);
|
|
1106
|
+
nodeAssert.equal(error.operator, "Eq");
|
|
1107
|
+
nodeAssert.equal(
|
|
1108
|
+
error.message,
|
|
1109
|
+
"Expected the error to equal the expected error.",
|
|
1110
|
+
);
|
|
1111
|
+
return true;
|
|
1112
|
+
},
|
|
1113
|
+
);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
it("compares a non-Data Err error by identity", () => {
|
|
1117
|
+
interface ServiceError {
|
|
1118
|
+
readonly type: "ServiceError";
|
|
1119
|
+
readonly retry: () => void;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const serviceError: ServiceError = {
|
|
1123
|
+
type: "ServiceError",
|
|
1124
|
+
retry: () => undefined,
|
|
1125
|
+
};
|
|
1126
|
+
const result: Result<number, ServiceError> = err(serviceError);
|
|
1127
|
+
|
|
1128
|
+
assertErr(result, serviceError);
|
|
1129
|
+
nodeAssert.throws(
|
|
1130
|
+
() =>
|
|
1131
|
+
assertErr(result, {
|
|
1132
|
+
type: "ServiceError",
|
|
1133
|
+
retry: () => undefined,
|
|
1134
|
+
}),
|
|
1135
|
+
/Expected the error to equal the expected error\./u,
|
|
1136
|
+
);
|
|
1137
|
+
assertErr(result, serviceError, (x, y) => x === y);
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
it("narrows heterogeneous Result unions", () => {
|
|
1141
|
+
const createOkResult = ():
|
|
1142
|
+
Result<number, "NumberError"> | Result<string, "StringError"> => ok(42);
|
|
1143
|
+
const createErrResult = ():
|
|
1144
|
+
Result<number, "NumberError"> | Result<string, "StringError"> =>
|
|
1145
|
+
err("NumberError");
|
|
1146
|
+
const okResult = createOkResult();
|
|
1147
|
+
const errResult = createErrResult();
|
|
1148
|
+
|
|
1149
|
+
assertOk(okResult, 42);
|
|
1150
|
+
assertErr(errResult, "NumberError");
|
|
1151
|
+
|
|
1152
|
+
assertType<typeof okResult, Ok<number> | Ok<string>>();
|
|
1153
|
+
assertType<typeof errResult, Err<"NumberError"> | Err<"StringError">>();
|
|
1154
|
+
});
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
describe("design decisions", () => {
|
|
1158
|
+
/**
|
|
1159
|
+
* Evolu cannot expose `SomeType.assert(value)` because assertion methods do
|
|
1160
|
+
* not narrow when the Type is inferred from a factory. TypeScript requires
|
|
1161
|
+
* every name in an assertion method's call target to have an explicit type
|
|
1162
|
+
* annotation. See the [TypeScript design
|
|
1163
|
+
* limitation](https://github.com/microsoft/TypeScript/issues/36931).
|
|
1164
|
+
*/
|
|
1165
|
+
it("does not expose Type.assert because inferred assertion methods cannot narrow", () => {
|
|
1166
|
+
const createTypeWithAssert = () => ({
|
|
1167
|
+
assert: (value: unknown): asserts value is string => {
|
|
1168
|
+
nodeAssert.equal(typeof value, "string");
|
|
1169
|
+
},
|
|
1170
|
+
});
|
|
1171
|
+
const InferredType = createTypeWithAssert();
|
|
1172
|
+
const inferredValue: unknown = "value";
|
|
1173
|
+
|
|
1174
|
+
// @ts-expect-error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
|
|
1175
|
+
InferredType.assert(inferredValue);
|
|
1176
|
+
|
|
1177
|
+
const value: unknown = 42;
|
|
1178
|
+
assertType(Number, value);
|
|
1179
|
+
assertType<typeof value, number>();
|
|
1180
|
+
});
|
|
1181
|
+
});
|