vitest-pool-assemblyscript 0.2.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.
Files changed (83) hide show
  1. package/BINARYEN_VERSION +1 -0
  2. package/LICENSE +53 -0
  3. package/README.md +607 -0
  4. package/assembly/compare.ts +219 -0
  5. package/assembly/describe.ts +104 -0
  6. package/assembly/expect.ts +335 -0
  7. package/assembly/index.ts +14 -0
  8. package/assembly/options.ts +198 -0
  9. package/assembly/test.ts +147 -0
  10. package/assembly/tsconfig.json +6 -0
  11. package/binding.gyp +62 -0
  12. package/dist/ast-visitor-DC3SuTzs.mjs +310 -0
  13. package/dist/ast-visitor-DC3SuTzs.mjs.map +1 -0
  14. package/dist/compile-runner-8h0dBwG2.mjs +80 -0
  15. package/dist/compile-runner-8h0dBwG2.mjs.map +1 -0
  16. package/dist/compiler/transforms/strip-inline.d.mts +18 -0
  17. package/dist/compiler/transforms/strip-inline.d.mts.map +1 -0
  18. package/dist/compiler/transforms/strip-inline.mjs +38 -0
  19. package/dist/compiler/transforms/strip-inline.mjs.map +1 -0
  20. package/dist/compiler-CN6BRK_N.mjs +295 -0
  21. package/dist/compiler-CN6BRK_N.mjs.map +1 -0
  22. package/dist/config/index-v3.d.mts +111 -0
  23. package/dist/config/index-v3.d.mts.map +1 -0
  24. package/dist/config/index-v3.mjs +11 -0
  25. package/dist/config/index-v3.mjs.map +1 -0
  26. package/dist/config/index.d.mts +4 -0
  27. package/dist/config/index.mjs +8 -0
  28. package/dist/constants-CA50WBdr.mjs +130 -0
  29. package/dist/constants-CA50WBdr.mjs.map +1 -0
  30. package/dist/coverage-merge-0WqdC-dq.mjs +22 -0
  31. package/dist/coverage-merge-0WqdC-dq.mjs.map +1 -0
  32. package/dist/coverage-provider/index.d.mts +15 -0
  33. package/dist/coverage-provider/index.d.mts.map +1 -0
  34. package/dist/coverage-provider/index.mjs +535 -0
  35. package/dist/coverage-provider/index.mjs.map +1 -0
  36. package/dist/custom-provider-options-CF5C1kXb.d.mts +26 -0
  37. package/dist/custom-provider-options-CF5C1kXb.d.mts.map +1 -0
  38. package/dist/debug-IeEHsxy0.mjs +195 -0
  39. package/dist/debug-IeEHsxy0.mjs.map +1 -0
  40. package/dist/index-internal.d.mts +23 -0
  41. package/dist/index-internal.d.mts.map +1 -0
  42. package/dist/index-internal.mjs +4 -0
  43. package/dist/index-v3.d.mts +7 -0
  44. package/dist/index-v3.d.mts.map +1 -0
  45. package/dist/index-v3.mjs +206 -0
  46. package/dist/index-v3.mjs.map +1 -0
  47. package/dist/index.d.mts +3 -0
  48. package/dist/index.mjs +8 -0
  49. package/dist/load-user-imports-J9eaAW0_.mjs +801 -0
  50. package/dist/load-user-imports-J9eaAW0_.mjs.map +1 -0
  51. package/dist/pool-runner-init-CEwLyNI3.d.mts +8 -0
  52. package/dist/pool-runner-init-CEwLyNI3.d.mts.map +1 -0
  53. package/dist/pool-runner-init-d5qScS41.mjs +400 -0
  54. package/dist/pool-runner-init-d5qScS41.mjs.map +1 -0
  55. package/dist/pool-thread/compile-worker-thread.d.mts +7 -0
  56. package/dist/pool-thread/compile-worker-thread.d.mts.map +1 -0
  57. package/dist/pool-thread/compile-worker-thread.mjs +42 -0
  58. package/dist/pool-thread/compile-worker-thread.mjs.map +1 -0
  59. package/dist/pool-thread/test-worker-thread.d.mts +7 -0
  60. package/dist/pool-thread/test-worker-thread.d.mts.map +1 -0
  61. package/dist/pool-thread/test-worker-thread.mjs +39 -0
  62. package/dist/pool-thread/test-worker-thread.mjs.map +1 -0
  63. package/dist/pool-thread/v3-tinypool-thread.d.mts +7 -0
  64. package/dist/pool-thread/v3-tinypool-thread.d.mts.map +1 -0
  65. package/dist/pool-thread/v3-tinypool-thread.mjs +57 -0
  66. package/dist/pool-thread/v3-tinypool-thread.mjs.map +1 -0
  67. package/dist/resolve-config-as1w-Qyz.mjs +65 -0
  68. package/dist/resolve-config-as1w-Qyz.mjs.map +1 -0
  69. package/dist/test-runner-B2BpyPNK.mjs +142 -0
  70. package/dist/test-runner-B2BpyPNK.mjs.map +1 -0
  71. package/dist/types-8KKo9Hbf.d.mts +228 -0
  72. package/dist/types-8KKo9Hbf.d.mts.map +1 -0
  73. package/dist/vitest-file-tasks-BUwzh375.mjs +61 -0
  74. package/dist/vitest-file-tasks-BUwzh375.mjs.map +1 -0
  75. package/dist/vitest-tasks-BKS7689f.mjs +319 -0
  76. package/dist/vitest-tasks-BKS7689f.mjs.map +1 -0
  77. package/dist/worker-rpc-channel-lbhK7Qz8.mjs +25 -0
  78. package/dist/worker-rpc-channel-lbhK7Qz8.mjs.map +1 -0
  79. package/package.json +112 -0
  80. package/prebuilds/linux-x64/vitest-pool-assemblyscript.glibc.node +0 -0
  81. package/scripts/install.js +91 -0
  82. package/scripts/setup-binaryen.js +179 -0
  83. package/src/native-instrumentation/addon.cpp +788 -0
@@ -0,0 +1,219 @@
1
+ function arrayEquals<T extends ArrayLike<unknown>, U extends ArrayLike<unknown>>(actual: T, expected: U): bool {
2
+ if (actual.length != expected.length) {
3
+ return false;
4
+ }
5
+
6
+ for (let i = 0; i < expected.length; i++) {
7
+ if (!equals(actual[i], expected[i])) {
8
+ return false;
9
+ }
10
+ }
11
+
12
+ return true;
13
+ }
14
+
15
+ function setEquals<T extends Set<V>, U extends Set<V>, V>(actual: T, expected: U): bool {
16
+ if (actual.size != expected.size) {
17
+ return false;
18
+ }
19
+
20
+ const expectedValues: V[] = expected.values();
21
+
22
+ for (let i = 0; i < expectedValues.length; i++) {
23
+ if (!actual.has(expectedValues[i])) {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ return true;
29
+ }
30
+
31
+ function mapEquals<
32
+ T extends Map<V, W>,
33
+ U extends Map<V, W>,
34
+ V, W
35
+ >(actual: T, expected: U): bool {
36
+ if (actual.size != expected.size) {
37
+ return false;
38
+ }
39
+
40
+ const expectedKeys: V[] = expected.keys();
41
+
42
+ for (let i = 0; i < expectedKeys.length; i++) {
43
+ const key: V = expectedKeys[i];
44
+
45
+ if (!actual.has(key)) {
46
+ return false;
47
+ }
48
+
49
+ if (!equals<W, W>(actual.get(key), expected.get(key))) {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ return true;
55
+ }
56
+
57
+ /**
58
+ * Generic primitive / reference equality comparison. Assumes comparable primitive types
59
+ * (or same reference type) for provided values.
60
+ */
61
+ export function identical<T, U>(actual: T, expected: U): bool {
62
+ // both refs
63
+ if (isReference<T>() && isReference<U>()) {
64
+ const actualIsNullable = isNullable<T>();
65
+ const actualIsNull = actualIsNullable && actual == null;
66
+ const expectedIsNullable = isNullable<U>();
67
+ const expectedIsNull = expectedIsNullable && expected == null;
68
+
69
+ // null refs
70
+ if (actualIsNull && expectedIsNull) {
71
+ return true;
72
+ } else if ( (actualIsNull && !expectedIsNull) || (!actualIsNull && expectedIsNull) ) {
73
+ return false;
74
+ }
75
+
76
+ // strings
77
+ if (isString<T>() && isString<U>()) {
78
+ return <string>actual == <string>expected;
79
+ } else {
80
+ // object refs
81
+ return changetype<usize>(actual) == changetype<usize>(expected);
82
+ }
83
+ } else if (isReference<T>() && !isReference<U>()) {
84
+ // actual is null ref, expected bare null
85
+ return changetype<usize>(actual) == usize(0) && expected == usize(0);
86
+ } else if (!isReference<T>() && isReference<U>()) {
87
+ // actual is bare null, expected is null ref
88
+ return actual == usize(0) && changetype<usize>(expected) == usize(0);
89
+ } else { // both primitives
90
+ if ( (isBoolean<T>() && !isBoolean<U>()) || (!isBoolean<T>() && isBoolean<U>())
91
+ ) {
92
+ // when one is boolean and the other is not, they are not identical
93
+ // use toEqual for this comparison
94
+ return false;
95
+ } else if (isInteger<T>() && isInteger<U>()) {
96
+ if (isSigned<T>() && isSigned<U>()) {
97
+ return i64(actual) == i64(expected);
98
+ } else if (isSigned<T>() && !isSigned<U>()) {
99
+ if (i64(actual) < 0) {
100
+ return false;
101
+ }
102
+ return u64(actual) == u64(expected);
103
+ } else if (!isSigned<T>() && isSigned<U>()) {
104
+ if (i64(expected) < 0) {
105
+ return false;
106
+ }
107
+ return u64(actual) == u64(expected);
108
+ } else {
109
+ return u64(actual) == u64(expected);
110
+ }
111
+ } else if (isFloat<T>() && isFloat<U>()) {
112
+ return f64(actual) === f64(expected);
113
+ } else if ( (isFloat<T>() && isInteger<U>()) || (isInteger<T>() && isFloat<U>()) ) {
114
+ return f64(actual) === f64(expected);
115
+ } else if (isVector<T>() && isVector<U>()) {
116
+ return <v128>actual == <v128>expected;
117
+ } else {
118
+ return false;
119
+ }
120
+ }
121
+ }
122
+
123
+ export function closeTo<T, U>(actual: T, expected: U, precision: i32 = 2): bool {
124
+ const match = equals(actual, expected);
125
+ if (match) {
126
+ return true;
127
+ }
128
+
129
+ if (isString<T>() && isString<U>()) {
130
+ return match;
131
+ }
132
+
133
+ if ( (isFloat<T>() || isInteger<T>()) && (isFloat<U>() || isInteger<U>()) ) {
134
+ const actualF64: f64 = f64(actual);
135
+ const expectedF64: f64 = f64(expected);
136
+ const expectedDiff: f64 = 10.0 ** -precision / 2.0;
137
+ const receivedDiff: f64 = Math.abs(expectedF64 - actualF64);
138
+ return receivedDiff < expectedDiff;
139
+ }
140
+
141
+ return false;
142
+ }
143
+
144
+ /**
145
+ * Generic value equality comparison. Assumes comparable types for both values.
146
+ * Does not yet support user-defined types.
147
+ */
148
+ export function equals<T, U>(actual: T, expected: U): bool {
149
+ let exactMatch: bool = false;
150
+
151
+ // allow boolean-to-number comparisons here
152
+ if (isBoolean<T>() && !isBoolean<U>()) {
153
+ exactMatch = identical(u8(actual), expected);
154
+ } else if (!isBoolean<T>() && isBoolean<U>()) {
155
+ exactMatch = identical(actual, u8(expected));
156
+ } else {
157
+ exactMatch = identical(actual, expected);
158
+ }
159
+
160
+ if (!isReference<T>() || isString<T>() || isVector<T>()) {
161
+ // non-bool primitives or strings: return result of comparing
162
+ return exactMatch;
163
+ } else if (exactMatch) {
164
+ // primitive / reference comparison passed already
165
+ return true;
166
+ }
167
+
168
+ if (isNullable<T>()) {
169
+ if (actual == null && expected == null) {
170
+ return true;
171
+ }
172
+
173
+ if ( (actual == null && expected != null) || (actual != null && expected == null) ) {
174
+ return false;
175
+ }
176
+ }
177
+
178
+ if (isArrayLike<T>(actual) && isArrayLike<U>(expected)) {
179
+ return arrayEquals(actual, expected);
180
+ }
181
+ if (actual instanceof Set && expected instanceof Set) {
182
+ return setEquals(actual, expected);
183
+ }
184
+ if (actual instanceof Map && expected instanceof Map) {
185
+ return mapEquals(actual, expected);
186
+ }
187
+
188
+ // TODO value compare
189
+ throw new Error("Comparison of user-defined object types not yet implemented");
190
+ }
191
+
192
+ export function truthyOrFalsey<T>(actual: T, expected: bool): bool {
193
+ return !!actual == expected;
194
+ }
195
+
196
+ export function isNull<T>(value: T): bool {
197
+ if (isReference<T>()) {
198
+ if (isNullable<T>()) {
199
+ return value == null;
200
+ } else {
201
+ return false;
202
+ }
203
+ } else {
204
+ if (isBoolean<T>()) {
205
+ return false;
206
+ } else {
207
+ return nameof<T>(value) == 'usize' && value == 0;
208
+ }
209
+ }
210
+ }
211
+
212
+ export function nan<T>(value: T): bool {
213
+ if (isFloat<T>()) {
214
+ // @ts-ignore
215
+ return isNaN<T>(value);
216
+ } else {
217
+ return false;
218
+ }
219
+ }
@@ -0,0 +1,104 @@
1
+ import { TestCallback } from './test';
2
+ import { TestOptions, DEFAULT_TEST_OPTIONS } from './options';
3
+
4
+ /*
5
+ * @external functions are imported to the
6
+ * WASM execution environment from pool executor
7
+ */
8
+
9
+ // @ts-ignore: top level decorators are supported in AssemblyScript
10
+ @external("__as_pool_env__", "__begin_register_suite")
11
+ declare function __begin_register_suite(
12
+ name: string,
13
+ timeout: i32,
14
+ retry: i32,
15
+ skip: i32,
16
+ only: i32,
17
+ fails: i32
18
+ ): void;
19
+
20
+ // @ts-ignore: top level decorators are supported in AssemblyScript
21
+ @external("__as_pool_env__", "__end_register_suite")
22
+ declare function __end_register_suite(name: string): void;
23
+
24
+
25
+ /**
26
+ * Register a test suite (a collection of tests and suites).
27
+ */
28
+ export function describe<T = TestCallback, U = TestOptions>(
29
+ name: string,
30
+ optionsOrFn: T,
31
+ // @ts-ignore
32
+ fnOrOptions: U = DEFAULT_TEST_OPTIONS // defaults all undefined here, merged with config in JS
33
+ ): void {
34
+ let fn: TestCallback;
35
+ let options: TestOptions;
36
+
37
+ if (isFunction(optionsOrFn) && fnOrOptions instanceof TestOptions) {
38
+ fn = optionsOrFn;
39
+ options = fnOrOptions;
40
+ } else if (optionsOrFn instanceof TestOptions && isFunction(fnOrOptions)) {
41
+ fn = fnOrOptions;
42
+ options = optionsOrFn;
43
+ } else {
44
+ throw new Error("Invalid describe() arguments");
45
+ }
46
+
47
+ __begin_register_suite(
48
+ name,
49
+ options._valueOfTimeout,
50
+ options._valueOfRetry,
51
+ options._valueOfSkip,
52
+ options._valueOfOnly,
53
+ options._valueOfFails
54
+ );
55
+
56
+ fn();
57
+
58
+ __end_register_suite(name);
59
+ }
60
+
61
+ function describeWithMergedOption<T = TestCallback, U = TestOptions>(
62
+ name: string,
63
+ optionToMerge: TestOptions,
64
+ optionsOrFn: T,
65
+ // @ts-ignore
66
+ fnOrOptions: U = DEFAULT_TEST_OPTIONS
67
+ ): void {
68
+ let fn: TestCallback;
69
+ let options: TestOptions;
70
+
71
+ if (isFunction(optionsOrFn) && fnOrOptions instanceof TestOptions) {
72
+ fn = optionsOrFn;
73
+ options = fnOrOptions;
74
+ } else if (optionsOrFn instanceof TestOptions && isFunction(fnOrOptions)) {
75
+ fn = fnOrOptions;
76
+ options = optionsOrFn;
77
+ } else {
78
+ throw new Error("Invalid describe() arguments");
79
+ }
80
+
81
+ const merged = options.__merge(optionToMerge);
82
+
83
+ return describe(name, merged, fn);
84
+ }
85
+
86
+ export namespace describe {
87
+ export function skip<T = TestCallback, U = TestOptions>(
88
+ name: string,
89
+ optionsOrFn: T,
90
+ // @ts-ignore
91
+ fnOrOptions: U = DEFAULT_TEST_OPTIONS
92
+ ): void {
93
+ return describeWithMergedOption(name, TestOptions.skip(), optionsOrFn, fnOrOptions);
94
+ }
95
+
96
+ export function only<T = TestCallback, U = TestOptions>(
97
+ name: string,
98
+ optionsOrFn: T,
99
+ // @ts-ignore
100
+ fnOrOptions: U = DEFAULT_TEST_OPTIONS
101
+ ): void {
102
+ return describeWithMergedOption(name, TestOptions.only(), optionsOrFn, fnOrOptions);
103
+ }
104
+ }
@@ -0,0 +1,335 @@
1
+ import {
2
+ closeTo,
3
+ equals,
4
+ identical,
5
+ isNull,
6
+ nan,
7
+ truthyOrFalsey
8
+ } from './compare';
9
+
10
+ // @external functions are imported to the WASM execution environment from pool code
11
+
12
+ // @ts-ignore: top level decorators are supported in AssemblyScript
13
+ @external("__as_pool_env__", "__assertion_pass")
14
+ declare function __assertion_pass(): void;
15
+
16
+ // @ts-ignore: top level decorators are supported in AssemblyScript
17
+ @external("__as_pool_env__", "__assertion_fail")
18
+ declare function __assertion_fail<T>(
19
+ msg: string,
20
+ typeName: string,
21
+ valuesProvided: bool,
22
+ actual?: T,
23
+ expected?: T
24
+ ): void;
25
+
26
+ // @ts-ignore: top level decorators are supported in AssemblyScript
27
+ @external("__as_pool_env__", "__expect_throw")
28
+ declare function __expect_throw(fnPtr: usize, errorMsg?: string): void;
29
+
30
+ // @ts-ignore: top level decorators are supported in AssemblyScript
31
+ @external("__as_pool_env__", "__end_expect_throw")
32
+ declare function __end_expect_throw(): void;
33
+
34
+
35
+ function itemMessageString<T>(item: T): string {
36
+ if (isNull(item)) return "<null>";
37
+ if (nan(item)) return "NaN";
38
+
39
+ if (isReference<T>()) {
40
+ if (isString<T>()) return `"${item}"`;
41
+ else if (item instanceof ArrayBuffer) return `ArrayBuffer[${item.byteLength}]`;
42
+ else if (isArrayLike<T>(item)) return arrayMessageString(item);
43
+ else if (item instanceof Set || item instanceof Map) return item.toString();
44
+ else return nameof<T>(item);
45
+ } else if (isBoolean<T>()){
46
+ return bool(item).toString();
47
+ } else if (isInteger<T>(item) || isFloat<T>(item)) {
48
+ return item.toString();
49
+ } else {
50
+ return nameof<T>(item);
51
+ }
52
+ }
53
+
54
+ function arrayMessageString<T extends ArrayLike<unknown>>(array: T): string {
55
+ if (isNullable<T>(array) && array == null) {
56
+ return "<null>";
57
+ }
58
+
59
+ let str = "[";
60
+ for (let i = 0; i < array.length; i++) {
61
+ str += itemMessageString(array[i]);
62
+
63
+ if (i < array.length - 1) {
64
+ str += ","
65
+ }
66
+ }
67
+ str += "]";
68
+
69
+ return str;
70
+ }
71
+
72
+ /**
73
+ * Expect matcher
74
+ */
75
+ abstract class BaseExpectMatcher<T> {
76
+ protected isInverted: bool = false;
77
+ protected isSoft: bool = false;
78
+ protected actual: T;
79
+
80
+ constructor(val: T) {
81
+ this.actual = val;
82
+ }
83
+
84
+ /** Inverts the matcher that follows. Can be chained. */
85
+ get not(): InvertedExpectMatcher<T> {
86
+ return new InvertedExpectMatcher(this.actual, !this.isInverted);
87
+ }
88
+
89
+ // get soft(): this {
90
+ // this.isSoft = true;
91
+ // return this;
92
+ // }
93
+
94
+ /**
95
+ * Checks that a value is what you expect. Primitives and strings are compared directly,
96
+ * and references are checked for reference equality only (including objects, arrays, etc).
97
+ * Don't use `toBe` with floating-point numbers - see `toBeCloseTo` instead.
98
+ *
99
+ * @example
100
+ * expect(1 + 1).toBe(2);
101
+ * expect("hello").toBe("hello");
102
+ */
103
+ toBe<U>(val: U): void {
104
+ this.assertComparison(identical(this.actual, val), this.actual, val, "to be", true);
105
+ }
106
+
107
+ /**
108
+ * Checks if a value is close to what you expect. Using exact equality with floating point
109
+ * numbers often doesn't work correctly, because small internal rounding occurs to be able
110
+ * to represent floats in binary. This rounding means intuitive comparisons will often fail.
111
+ *
112
+ * Comparing strings, integers, or references will fall back to using a `toBe` comparison.
113
+ *
114
+ * @param precision - Specify an integer representing the number of decimal places
115
+ * that must match for values to be considered close. Defaults to 2 digits, meaning effectively
116
+ * that values must be within 0.005 of each other.
117
+ *
118
+ * @example
119
+ * expect(0.1 + 0.2).toBeCloseTo(0.3);
120
+ * expect(1.005).toBeCloseTo(1.0, 1);
121
+ */
122
+ toBeCloseTo<U>(val: U, precision: i32 = 2): void {
123
+ this.assertComparison(closeTo(this.actual, val, precision), this.actual, val, "to be close to", true);
124
+ }
125
+
126
+ /**
127
+ * Checks that two values have the same value (deep equality). Currently supports
128
+ * checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are
129
+ * compared using `toEqual()` also, while Maps and Sets use their respective rules
130
+ * for membership. Primitives, strings, and other object references are compared with
131
+ * `toBe()` rules.
132
+ *
133
+ * Note: Does not yet support user-defined object deep equality checking.
134
+ *
135
+ * @example
136
+ * expect([1, 2, 3]).toEqual([1, 2, 3]);
137
+ * expect(["one", "two", "three"]).toEqual(["one", "two", "three"]);
138
+ *
139
+ * // objects use reference equality (deep equality not yet supported)
140
+ * const a: MyObject = new MyObject();
141
+ * const b: MyObject = new MyObject();
142
+ * expect([a, b]).toEqual([a, b]);
143
+ */
144
+ toEqual<U>(val: U): void {
145
+ this.assertComparison(equals(this.actual, val), this.actual, val, "to deeply equal", true);
146
+ }
147
+
148
+ /**
149
+ * Alias for `toEqual`. Currently no differences in AssemblyScript.
150
+ *
151
+ * @example
152
+ * expect([1, 2]).toStrictEqual([1, 2]);
153
+ */
154
+ toStrictEqual<U>(val: U): void {
155
+ this.assertComparison(equals(this.actual, val), this.actual, val, "to strictly equal", true);
156
+ }
157
+
158
+ /**
159
+ * Checks that a value is truthy (not `0`, `false`, `""`, or `null`).
160
+ *
161
+ * @example
162
+ * expect(1).toBeTruthy();
163
+ * expect("hello").toBeTruthy();
164
+ */
165
+ toBeTruthy(): void {
166
+ this.assertComparison(truthyOrFalsey(this.actual, true), this.actual, true, "to be truthy", false);
167
+ }
168
+
169
+ /**
170
+ * Checks that a value is falsey (`0`, `false`, `""`, or `null`).
171
+ *
172
+ * @example
173
+ * expect(0).toBeFalsey();
174
+ * expect("").toBeFalsey();
175
+ */
176
+ toBeFalsey(): void {
177
+ this.assertComparison(truthyOrFalsey(this.actual, false), this.actual, false, "to be falsey", false);
178
+ }
179
+
180
+ /**
181
+ * Checks that a value is null (`usize(0)` in AssemblyScript).
182
+ *
183
+ * @example
184
+ * const val: string | null = null;
185
+ * expect(val).toBeNull();
186
+ * expect("hello").not.toBeNull();
187
+ * expect(0).not.toBeNull();
188
+ * expect(false).not.toBeNull();
189
+ */
190
+ toBeNull(): void {
191
+ this.assertComparison(isNull(this.actual), this.actual, null, "to be null", false);
192
+ }
193
+
194
+ /**
195
+ * Checks that the type of the value is nullable (can hold `null`). This is a type-level
196
+ * check, not a value check — a bare `null` (which is `usize(0)`) is not itself a nullable type.
197
+ * Use `toBeNull()` to check if a value is null.
198
+ *
199
+ * @example
200
+ * const val: string | null = null;
201
+ * expect(val).toBeNullable();
202
+ * expect("hello").not.toBeNullable();
203
+ */
204
+ toBeNullable(): void {
205
+ if (isReference<T>()) {
206
+ this.assertComparison(isNullable<T>(), this.actual, null, "to be nullable", false, false);
207
+ } else {
208
+ this.assertComparison(false, this.actual, null, "to be nullable", false, false);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Checks that a floating point value is `NaN`.
214
+ *
215
+ * @example
216
+ * expect(NaN).toBeNaN();
217
+ * expect(1.0).not.toBeNaN();
218
+ */
219
+ toBeNaN(): void {
220
+ this.assertComparison(nan(this.actual), this.actual, NaN, "to be NaN", false);
221
+ }
222
+
223
+ /**
224
+ * Checks that an array or array-like value has the expected length.
225
+ * Uses `toBeCloseTo` semantics when the expected length is a float.
226
+ *
227
+ * @example
228
+ * expect([1, 2, 3]).toHaveLength(3);
229
+ * expect([]).toHaveLength(0);
230
+ * expect("hello world").toHaveLength(11);
231
+ */
232
+ toHaveLength<U extends number>(length: U): void {
233
+ const actualIsNull = isNull(this.actual);
234
+
235
+ if (actualIsNull) {
236
+ this.assertComparison(false, null, length, "to have length", true);
237
+ } else if (isReference<T>()) {
238
+ if (isArray<T>() || isArrayLike<T>()) {
239
+ const nonNullActual = <NonNullable<T>>this.actual;
240
+
241
+ if (isFloat<U>()) {
242
+ // @ts-ignore
243
+ this.assertComparison<number, U>(closeTo<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
244
+ } else {
245
+ // @ts-ignore
246
+ this.assertComparison<number, U>(identical<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
247
+ }
248
+ } else {
249
+ this.assertComparison(false, this.actual, length, "to have length", true);
250
+ }
251
+ } else {
252
+ this.assertComparison(false, this.actual, length, "to have length", true);
253
+ }
254
+ }
255
+
256
+ protected abortTest(message: string): void {
257
+ if (!this.isSoft) {
258
+ abort(message);
259
+ }
260
+ }
261
+
262
+ protected assertComparison<U, V>(rawCondition: bool, actual: U, expected: V, methodStr: string, printExpected: bool, provideDiff: bool = true): void {
263
+ const condition = this.isInverted ? !rawCondition : rawCondition;
264
+
265
+ if (condition) {
266
+ __assertion_pass();
267
+ } else {
268
+ const notStr = this.isInverted ? "not " : "";
269
+ const actualStr = itemMessageString(actual);
270
+ const expectedStr = itemMessageString(expected);
271
+ const msg = `expected ${actualStr} ${notStr}${methodStr}${printExpected ? ` ${expectedStr}` : ""}`;
272
+
273
+ __assertion_fail<string>(msg, nameof<U>() + " " + nameof<V>(), provideDiff, actualStr, expectedStr);
274
+
275
+ // Abort on failure - terminates WASM execution - must be called from WASM.
276
+ // Imported abort handler will handle this and mark the test as failed.
277
+ this.abortTest(msg);
278
+ }
279
+ }
280
+ }
281
+
282
+ class StandardExpectMatcher<T> extends BaseExpectMatcher<T> {
283
+ constructor(val: T) {
284
+ super(val);
285
+ }
286
+
287
+ /**
288
+ * Checks that a function throws an error when called. Optionally checks that the
289
+ * error message matches the provided string. The callback traps/aborts WASM execution,
290
+ * which is caught and evaluated by the pool. Also available as `toThrow()`.
291
+ *
292
+ * Note: Requires a void callback passed to `expect()`. Does not support `.not` inversion.
293
+ *
294
+ * @param errorMsg - Optional expected error message to match against.
295
+ *
296
+ * @example
297
+ * expect(() => { throw new Error("boom"); }).toThrowError();
298
+ * expect(() => { throw new Error("boom"); }).toThrowError("boom");
299
+ */
300
+ toThrowError(errorMsg: string | null = null): void {
301
+ if (isFunction<T>()) {
302
+ // @ts-ignore
303
+ const fnIndex = this.actual.index;
304
+
305
+ if (errorMsg == null) {
306
+ __expect_throw(fnIndex);
307
+ } else {
308
+ __expect_throw(fnIndex, errorMsg);
309
+ }
310
+
311
+ // if we get here it didn't throw, so this handles the missing error
312
+ __end_expect_throw();
313
+ } else {
314
+ throw new Error("expect() requires a callback function when used with toThrowError() matcher");
315
+ }
316
+ }
317
+
318
+ /** Alias for `toThrowError` */
319
+ toThrow(errorMsg: string | null = null): void {
320
+ this.toThrowError(errorMsg);
321
+ }
322
+ }
323
+
324
+ class InvertedExpectMatcher<T> extends BaseExpectMatcher<T> {
325
+ constructor(val: T, isInverted: bool) {
326
+ super(val);
327
+
328
+ // allow chaining multiple nots if desired
329
+ this.isInverted = isInverted;
330
+ }
331
+ }
332
+
333
+ export function expect<T>(value: T): StandardExpectMatcher<T> {
334
+ return new StandardExpectMatcher<T>(value);
335
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Test framework with per-test crash isolation support
3
+ *
4
+ * Execution flow:
5
+ * 1. Instantiation: Pool creates WASM instance with import callbacks
6
+ * 2. Registration: _start() runs, top-level test() calls invoke __register_test callback
7
+ * 3. Discovery: Pool receives test names + function indices via callbacks
8
+ * 4. Execution: Pool calls table.get(fnIndex)() directly via exported function table
9
+ */
10
+
11
+ export * from './describe';
12
+ export * from './expect';
13
+ export * from './options';
14
+ export * from './test';