@as-pect/assembly 6.1.0 → 7.0.1

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.
@@ -1,481 +1,544 @@
1
- import { toIncludeComparison } from "./comparison/toIncludeComparison";
2
- import { toIncludeEqualComparison } from "./comparison/toIncludeEqualComparison";
3
- import { Actual } from "./Actual";
4
- import { Expected } from "./Expected";
5
- import { assert } from "./assert";
6
-
7
- // @ts-ignore: Decorators *are* valid here
8
- @external("__aspect", "tryCall")
9
- declare function tryCall(func: () => void): bool;
10
-
11
- // @ts-ignore: Decorators *are* valid here
12
- @global
13
- export class Expectation<T> {
14
- /**
15
- * This i32 is set to 1 if the expectation is negated. Using the _not (xor) condition assertion
16
- * makes assertions very easy to write and understand.
17
- */
18
- _not: i32 = 0;
19
-
20
- actual: T;
21
-
22
- constructor(actual: T) {
23
- this.actual = actual;
24
- }
25
-
26
- public get not(): Expectation<T> {
27
- this._not = 1;
28
- return this;
29
- }
30
-
31
- public toBe(expected: T, message: string = ""): void {
32
- let actual = this.actual;
33
- let equals = i32(actual == expected);
34
- let negated = this._not;
35
-
36
- Actual.report(actual);
37
-
38
- if (isReference(actual) && !isFunction(actual)) {
39
- if (
40
- !negated &&
41
- changetype<usize>(actual) !== 0 &&
42
- changetype<usize>(expected) !== 0 &&
43
- Reflect.equals(actual, expected) == Reflect.SUCCESSFUL_MATCH
44
- ) {
45
- Expected.report("Serializes to same value.", 0);
46
- } else {
47
- Expected.report(expected, negated);
48
- }
49
- } else {
50
- Expected.report(expected, negated);
51
- }
52
-
53
- // The assertion is either the items equal, or the expectation is negated
54
- assert(equals ^ negated, message);
55
- Actual.clear();
56
- Expected.clear();
57
- }
58
-
59
- public toStrictEqual(expected: T, message: string = ""): void {
60
- let result = Reflect.FAILED_MATCH;
61
- result = Reflect.equals(this.actual, expected);
62
-
63
- let equals = i32(result == Reflect.SUCCESSFUL_MATCH);
64
- Actual.report(this.actual);
65
- Expected.report(expected);
66
-
67
- assert(equals ^ this._not, message);
68
-
69
- Actual.clear();
70
- Expected.clear();
71
- }
72
-
73
- public toBlockEqual(expected: T, message: string = ""): void {
74
- WARNING(
75
- "toBlockEqual has been deprecated and results in a toStrictEqual call.",
76
- );
77
- this.toStrictEqual(expected, message);
78
- }
79
-
80
- public toBeTruthy(message: string = ""): void {
81
- let actual = this.actual;
82
- Actual.report(actual);
83
- let negated = this._not;
84
- Expected.reportTruthy(negated);
85
-
86
- if (isReference(actual)) {
87
- if (actual instanceof String) {
88
- let truthy = i32(
89
- changetype<usize>(actual) != 0 &&
90
- changetype<string>(actual).length > 0,
91
- );
92
- assert(truthy ^ negated, message);
93
- } else {
94
- let truthy = i32(changetype<usize>(actual) != 0);
95
- assert(truthy ^ negated, message);
96
- }
97
- } else {
98
- if (isFloat(actual)) {
99
- let truthy = i32(!isNaN(actual) && actual != 0.0);
100
- assert(truthy ^ negated, message);
101
- } else if (isInteger(actual)) {
102
- let truthy = i32(actual != 0);
103
- assert(truthy ^ negated, message);
104
- }
105
- }
106
-
107
- Actual.clear();
108
- Expected.clear();
109
- }
110
-
111
- public toBeFalsy(message: string = ""): void {
112
- let actual = this.actual;
113
- Actual.report(actual);
114
- let negated = this._not;
115
- Expected.reportFalsy(negated);
116
-
117
- if (isReference(actual)) {
118
- // strings require an extra length check
119
- if (actual instanceof String) {
120
- let falsy = i32(
121
- changetype<usize>(actual) == 0 ||
122
- changetype<string>(actual).length == 0,
123
- );
124
- assert(falsy ^ negated, message);
125
- } else {
126
- let falsy = i32(changetype<usize>(actual) == 0);
127
- assert(falsy ^ negated, message);
128
- }
129
- } else {
130
- if (isFloat(actual)) {
131
- // @ts-ignore: actual is a float value
132
- let falsy = i32(isNaN(actual) || actual == 0.0);
133
- assert(falsy ^ negated, message);
134
- } else if (isInteger(actual)) {
135
- let falsy = i32(actual == 0);
136
- assert(falsy ^ negated, message);
137
- }
138
- }
139
-
140
- Actual.clear();
141
- Expected.clear();
142
- }
143
-
144
- public toThrow(message: string = ""): void {
145
- let actual = this.actual;
146
- let negated = this._not;
147
-
148
- if (!isFunction(this.actual))
149
- ERROR(
150
- "Expectation#toThrow assertion called on actual T where T is not a function reference",
151
- );
152
- if (idof<T>() != idof<() => void>())
153
- ERROR(
154
- "Expectation#toThrow assertion called on actual T where T is not a function reference with signature () => void",
155
- );
156
-
157
- // @ts-ignore: safe tryCall
158
- let throws = i32(!tryCall(actual));
159
- Actual.report(throws ? "Throws" : "Not Throws");
160
- Expected.report("Throws", negated);
161
- assert(negated ^ throws, message);
162
- Actual.clear();
163
- Expected.clear();
164
- }
165
-
166
- public toBeGreaterThan(expected: T, message: string = ""): void {
167
- let actual = this.actual;
168
- let negated = this._not;
169
- Actual.report(actual);
170
- Expected.report(expected, negated);
171
-
172
- if (!isDefined(actual > expected))
173
- ERROR(
174
- "Invalid call to toBeGreaterThan. Generic type T must have an operator implemented for the greaterThan (>) operation.",
175
- );
176
-
177
- if (isReference(actual)) {
178
- // Perform reference type null checks
179
- assert(
180
- i32(changetype<usize>(expected) != 0),
181
- "Value comparison fails, expected value is null.",
182
- );
183
- assert(
184
- i32(changetype<usize>(actual) != 0),
185
- "Value comparison fails, actual value is null.",
186
- );
187
- }
188
-
189
- // Compare float types
190
- if (isFloat(actual)) {
191
- assert(
192
- i32(!isNaN(expected)),
193
- "Value comparison fails, expected value is NaN.",
194
- );
195
- assert(
196
- i32(!isNaN(actual)),
197
- "Value comparison fails, actual value is NaN.",
198
- );
199
- }
200
-
201
- // do actual greater than comparison
202
- assert(negated ^ i32(actual > expected), message);
203
- Actual.clear();
204
- Expected.clear();
205
- }
206
-
207
- public toBeGreaterThanOrEqual(expected: T, message: string = ""): void {
208
- let actual = this.actual;
209
- let negated = this._not;
210
-
211
- Actual.report(actual);
212
- Expected.report(expected, negated);
213
-
214
- if (!isDefined(actual >= expected))
215
- ERROR(
216
- "Invalid call to toBeGreaterThanOrEqual. Generic type T must have an operator implemented for the greaterThanOrEqual (>=) operation.",
217
- );
218
-
219
- // null checks
220
- if (isReference(actual)) {
221
- assert(
222
- i32(changetype<usize>(expected) != 0),
223
- "Value comparison fails, expected value is null.",
224
- );
225
- assert(
226
- i32(changetype<usize>(actual) != 0),
227
- "Value comparison fails, actual value is null.",
228
- );
229
- }
230
-
231
- // Compare float types
232
- if (isFloat(actual)) {
233
- assert(
234
- i32(!isNaN(expected)),
235
- "Value comparison fails, expected value is NaN.",
236
- );
237
- assert(
238
- i32(!isNaN(actual)),
239
- "Value comparison fails, actual value is NaN.",
240
- );
241
- }
242
-
243
- // do actual greater than comparison
244
- assert(negated ^ i32(actual >= expected), message);
245
- Actual.clear();
246
- Expected.clear();
247
- }
248
-
249
- public toBeLessThan(expected: T, message: string = ""): void {
250
- let actual = this.actual;
251
- let negated = this._not;
252
- Actual.report(actual);
253
- Expected.report(expected, negated);
254
-
255
- if (!isDefined(actual < expected))
256
- ERROR(
257
- "Invalid call to toBeLessThan. Generic type T must have an operator implemented for the lessThan (<) operation.",
258
- );
259
-
260
- // null checks
261
- if (isReference(actual)) {
262
- assert(
263
- i32(changetype<usize>(expected) != 0),
264
- "Value comparison fails, expected value is null.",
265
- );
266
- assert(
267
- i32(changetype<usize>(actual) != 0),
268
- "Value comparison fails, actual value is null.",
269
- );
270
- } else if (isFloat(actual)) {
271
- assert(
272
- i32(!isNaN(expected)),
273
- "Value comparison fails, expected value is NaN.",
274
- );
275
- assert(
276
- i32(!isNaN(actual)),
277
- "Value comparison fails, actual value is NaN.",
278
- );
279
- }
280
-
281
- // do actual less than comparison
282
- assert(negated ^ i32(actual < expected), message);
283
- Actual.clear();
284
- Expected.clear();
285
- }
286
-
287
- public toBeLessThanOrEqual(expected: T, message: string = ""): void {
288
- let actual = this.actual;
289
- let negated = this._not;
290
- Actual.report(actual);
291
- Expected.report(expected, negated);
292
-
293
- if (!isDefined(actual > expected))
294
- ERROR(
295
- "Invalid call to toBeLessThanOrEqual. Generic type T must have an operator implemented for the lessThanOrEqual (<=) operation.",
296
- );
297
-
298
- // null checks
299
- if (isReference(actual)) {
300
- assert(
301
- i32(changetype<usize>(expected) != 0),
302
- "Value comparison fails, expected value is null.",
303
- );
304
- assert(
305
- i32(changetype<usize>(actual) != 0),
306
- "Value comparison fails, actual value is null.",
307
- );
308
- }
309
-
310
- if (isFloat(actual)) {
311
- assert(
312
- i32(!isNaN(expected)),
313
- "Value comparison fails, expected value is NaN.",
314
- );
315
- assert(
316
- i32(!isNaN(actual)),
317
- "Value comparison fails, actual value is NaN.",
318
- );
319
- }
320
-
321
- // do actual less than comparison
322
- assert(negated ^ i32(actual <= expected), message);
323
- Actual.clear();
324
- Expected.clear();
325
- }
326
-
327
- public toBeNull(message: string = ""): void {
328
- let negated = this._not;
329
- let actual = this.actual;
330
-
331
- if (actual instanceof usize) {
332
- Actual.report(actual);
333
- Expected.report(<usize>0, negated);
334
- // @ts-ignore: actual is instanceof number type
335
- assert(negated ^ i32(actual == 0), message);
336
- Actual.clear();
337
- Expected.clear();
338
- } else if (isReference(actual)) {
339
- Actual.report(actual);
340
-
341
- Expected.report(changetype<T>(0), negated);
342
- assert(negated ^ i32(changetype<usize>(actual) == 0), message);
343
- Actual.clear();
344
- Expected.clear();
345
- } else {
346
- ERROR(
347
- "toBeNull assertion must be called with a reference type T or usize.",
348
- );
349
- }
350
- }
351
-
352
- public toBeCloseTo(
353
- expected: T,
354
- decimalPlaces: i32 = 2,
355
- message: string = "",
356
- ): void {
357
- let actual = this.actual;
358
- let negated = this._not;
359
-
360
- // must be called on a float T
361
- if (!isFloat(actual))
362
- ERROR("toBeCloseTo must be called with a Float value type T.");
363
- Actual.report(actual);
364
- Expected.report(expected, negated);
365
-
366
- // both actual and expected values must be finite
367
- assert(
368
- i32(isFinite(actual)),
369
- "toBeCloseTo assertion fails because a actual value is not finite",
370
- );
371
- assert(
372
- i32(isFinite(expected)),
373
- "toBeCloseTo assertion fails because expected value is not finite.",
374
- );
375
-
376
- // calculated: `|expected - actual| < 1 / numberOfDigits`.
377
- // @ts-ignore tooling errors because T does not extend a numeric value type. This compiles just fine.
378
- let isClose = i32(abs(expected - actual) < Math.pow(0.1, decimalPlaces));
379
- assert(negated ^ isClose, message);
380
- Actual.clear();
381
- Expected.clear();
382
- }
383
-
384
- public toBeNaN(message: string = ""): void {
385
- let actual = this.actual;
386
- let negated = this._not;
387
-
388
- // must be called on a float T
389
- if (!isFloat(actual))
390
- ERROR("toBeNaN must be called with a Float value type T.");
391
- Actual.report(actual);
392
-
393
- // @ts-ignore: The compiler should pass bit count (64/32 bit float to the report function)
394
- Expected.report<T>(NaN, negated);
395
-
396
- let isNaNValue = i32(isNaN(actual));
397
- assert(isNaNValue ^ negated, message);
398
- Actual.clear();
399
- Expected.clear();
400
- }
401
-
402
- public toBeFinite(message: string = ""): void {
403
- let actual = this.actual;
404
- let negated = this._not;
405
-
406
- // must be called on a float T
407
- if (!isFloat(actual))
408
- ERROR("toBeNaN must be called with a Float value type T.");
409
- Actual.report(actual);
410
- Expected.reportFinite(negated);
411
-
412
- let isFiniteValue = i32(isFinite(actual));
413
- assert(isFiniteValue ^ negated, message);
414
- Actual.clear();
415
- Expected.clear();
416
- }
417
-
418
- public toHaveLength(expected: i32, message: string = ""): void {
419
- let actual = this.actual;
420
- let negated = this._not;
421
- let length = 0;
422
- if (actual instanceof ArrayBuffer) {
423
- length = actual.byteLength;
424
- } else {
425
- // @ts-ignore: This results in a compile time check for a length property with a better error message
426
- if (!isDefined(actual.length))
427
- ERROR(
428
- "toHaveLength cannot be called on type T where T.length is not defined.",
429
- );
430
- // @ts-ignore: This results in a compile time check for a length property with a better error message
431
- length = <i32>actual.length;
432
- }
433
-
434
- Actual.report(length);
435
- Expected.report(expected, negated);
436
-
437
- let lengthsEqual = i32(length == expected);
438
- assert(lengthsEqual ^ negated, message);
439
- Actual.clear();
440
- Expected.clear();
441
- }
442
-
443
- public toInclude<U>(expected: U, message: string = ""): void {
444
- toIncludeComparison<T, U>(this.actual, expected, this._not, message);
445
- Actual.clear();
446
- Expected.clear();
447
- }
448
-
449
- // @ts-ignore: valueof<T> requires that T extends something with an @operator("[]")
450
- public toContain(expected: valueof<T>, message: string = ""): void {
451
- this.toInclude(expected, message);
452
- }
453
-
454
- public toIncludeEqual<U>(expected: U, message: string = ""): void {
455
- toIncludeEqualComparison<T, U>(this.actual, expected, this._not, message);
456
- Actual.clear();
457
- Expected.clear();
458
- }
459
-
460
- public toContainEqual<U>(expected: U, message: string = ""): void {
461
- this.toIncludeEqual(expected, message);
462
- }
463
-
464
- public toMatchSnapshot(name: string | null = null): void {
465
- assert(i32(!this._not), "Snapshots cannot be negated.");
466
- Expected.reportSnapshot(this.actual, name);
467
- }
468
- }
469
-
470
- // @ts-ignore: decorators *are* valid here
471
- @global
472
- export function expect<T>(actual: T): Expectation<T> {
473
- return new Expectation(actual);
474
- }
475
-
476
- // @ts-ignore: decorators *are* valid here
477
- @global
478
- export function expectFn(cb: () => void): Expectation<() => void> {
479
- WARNING("expectFn() has been deprecated. Use expect() instead.");
480
- return new Expectation(cb);
481
- }
1
+ import { toIncludeComparison } from "./comparison/toIncludeComparison";
2
+ import { toIncludeEqualComparison } from "./comparison/toIncludeEqualComparison";
3
+ import { Actual } from "./Actual";
4
+ import { Expected } from "./Expected";
5
+ import { assert } from "./assert";
6
+
7
+ // @ts-ignore: Decorators *are* valid here
8
+ @external("__aspect", "tryCall")
9
+ declare function tryCall(func: () => void): bool;
10
+
11
+ // @ts-ignore: Decorators *are* valid here
12
+ @global
13
+ export class Expectation<T> {
14
+ /**
15
+ * This i32 is set to 1 if the expectation is negated. Using the _not (xor) condition assertion
16
+ * makes assertions very easy to write and understand.
17
+ */
18
+ _not: i32 = 0;
19
+
20
+ actual: T;
21
+
22
+ constructor(actual: T) {
23
+ this.actual = actual;
24
+ }
25
+
26
+ public get not(): Expectation<T> {
27
+ this._not = 1;
28
+ return this;
29
+ }
30
+
31
+ public toBe(expected: T, message: string = ""): void {
32
+ let actual = this.actual;
33
+ let equals = 0;
34
+
35
+ if (isReference<T>()) {
36
+ if (isNullable<T>()) {
37
+ if (changetype<usize>(actual) == 0 && changetype<usize>(expected) == 0) {
38
+ equals = 1;
39
+ } else {
40
+ if (
41
+ i32(changetype<usize>(actual) == 0)
42
+ ^ i32(changetype<usize>(expected) == 0)
43
+ ) {
44
+ equals = 0;
45
+ } else {
46
+ equals = i32(actual! == expected!);
47
+ }
48
+ }
49
+ } else {
50
+ equals = i32(actual == expected);
51
+ }
52
+ } else {
53
+ equals = i32(actual == expected);
54
+ }
55
+
56
+ let negated = this._not;
57
+
58
+ Actual.report(actual);
59
+
60
+ if (isReference(actual) && !isFunction(actual)) {
61
+ if (
62
+ !negated &&
63
+ changetype<usize>(actual) !== 0 &&
64
+ changetype<usize>(expected) !== 0 &&
65
+ Reflect.equals(actual, expected) == Reflect.SUCCESSFUL_MATCH
66
+ ) {
67
+ Expected.report("Serializes to same value.", 0);
68
+ } else {
69
+ Expected.report(expected, negated);
70
+ }
71
+ } else {
72
+ Expected.report(expected, negated);
73
+ }
74
+
75
+ // The assertion is either the items equal, or the expectation is negated
76
+ assert(equals ^ negated, message);
77
+ Actual.clear();
78
+ Expected.clear();
79
+ }
80
+
81
+ public toStrictEqual(expected: T, message: string = ""): void {
82
+ let result = Reflect.FAILED_MATCH;
83
+ result = Reflect.equals(this.actual, expected);
84
+
85
+ let equals = i32(result == Reflect.SUCCESSFUL_MATCH);
86
+ Actual.report(this.actual);
87
+ Expected.report(expected);
88
+
89
+ assert(equals ^ this._not, message);
90
+
91
+ Actual.clear();
92
+ Expected.clear();
93
+ }
94
+
95
+ // @as-covers: ignore because this function is deprecated
96
+ public toBlockEqual(expected: T, message: string = ""): void {
97
+ WARNING(
98
+ "toBlockEqual has been deprecated and results in a toStrictEqual call.",
99
+ );
100
+ this.toStrictEqual(expected, message);
101
+ }
102
+
103
+ public toBeTruthy(message: string = ""): void {
104
+ let actual = this.actual;
105
+ Actual.report(actual);
106
+ let negated = this._not;
107
+ Expected.reportTruthy(negated);
108
+
109
+ if (isReference(actual)) {
110
+ if (actual instanceof String) {
111
+ let truthy = i32(
112
+ changetype<usize>(actual) != 0 &&
113
+ changetype<string>(actual).length > 0,
114
+ );
115
+ assert(truthy ^ negated, message);
116
+ } else {
117
+ let truthy = i32(changetype<usize>(actual) != 0);
118
+ assert(truthy ^ negated, message);
119
+ }
120
+ } else {
121
+ if (isFloat(actual)) {
122
+ let truthy = i32(!isNaN(actual) && actual != 0.0);
123
+ assert(truthy ^ negated, message);
124
+ } else if (isInteger(actual)) {
125
+ let truthy = i32(actual != 0);
126
+ assert(truthy ^ negated, message);
127
+ }
128
+ }
129
+
130
+ Actual.clear();
131
+ Expected.clear();
132
+ }
133
+
134
+ public toBeFalsy(message: string = ""): void {
135
+ let actual = this.actual;
136
+ Actual.report(actual);
137
+ let negated = this._not;
138
+ Expected.reportFalsy(negated);
139
+
140
+ if (isReference(actual)) {
141
+ // strings require an extra length check
142
+ if (actual instanceof String) {
143
+ let falsy = i32(
144
+ changetype<usize>(actual) == 0 ||
145
+ changetype<string>(actual).length == 0,
146
+ );
147
+ assert(falsy ^ negated, message);
148
+ } else {
149
+ let falsy = i32(changetype<usize>(actual) == 0);
150
+ assert(falsy ^ negated, message);
151
+ }
152
+ } else {
153
+ if (isFloat(actual)) {
154
+ // @ts-ignore: actual is a float value
155
+ let falsy = i32(isNaN(actual) || actual == 0.0);
156
+ assert(falsy ^ negated, message);
157
+ } else if (isInteger(actual)) {
158
+ let falsy = i32(actual == 0);
159
+ assert(falsy ^ negated, message);
160
+ }
161
+ }
162
+
163
+ Actual.clear();
164
+ Expected.clear();
165
+ }
166
+
167
+ public toThrow(message: string = ""): void {
168
+ let actual = this.actual;
169
+ let negated = this._not;
170
+
171
+ if (!isFunction(actual)) {
172
+ // @as-covers: ignore because this is a compile time error
173
+ ERROR(nameof<T>());
174
+ ERROR(
175
+ "Expectation#toThrow assertion called on actual T where T is not a function reference",
176
+ );
177
+ }
178
+ if (idof<T>() != idof<() => void>())
179
+ // @as-covers: ignore because this is a compile time error
180
+ ERROR(
181
+ "Expectation#toThrow assertion called on actual T where T is not a function reference with signature () => void",
182
+ );
183
+
184
+ // @ts-ignore: safe tryCall
185
+ let throws = i32(!tryCall(actual));
186
+ Actual.report(throws ? "Throws" : "Not Throws");
187
+ Expected.report("Throws", negated);
188
+ assert(negated ^ throws, message);
189
+ Actual.clear();
190
+ Expected.clear();
191
+ }
192
+
193
+ public toBeGreaterThan(expected: T, message: string = ""): void {
194
+ let actual = this.actual;
195
+ let negated = this._not;
196
+ Actual.report(actual);
197
+ Expected.report(expected, negated);
198
+
199
+ if (!isDefined(actual > expected))
200
+ // @as-covers: ignore because this is a compile time error
201
+ ERROR(
202
+ "Invalid call to toBeGreaterThan. Generic type T must have an operator implemented for the greaterThan (>) operation.",
203
+ );
204
+
205
+ if (isReference(actual)) {
206
+ // Perform reference type null checks
207
+ assert(
208
+ i32(changetype<usize>(expected) != 0),
209
+ "Value comparison fails, expected value is null.",
210
+ );
211
+ assert(
212
+ i32(changetype<usize>(actual) != 0),
213
+ "Value comparison fails, actual value is null.",
214
+ );
215
+ }
216
+
217
+ // Compare float types
218
+ if (isFloat(actual)) {
219
+ assert(
220
+ i32(!isNaN(expected)),
221
+ "Value comparison fails, expected value is NaN.",
222
+ );
223
+ assert(
224
+ i32(!isNaN(actual)),
225
+ "Value comparison fails, actual value is NaN.",
226
+ );
227
+ }
228
+
229
+ // do actual greater than comparison
230
+ if (isNullable(actual)) {
231
+ assert(negated ^ i32(actual! > expected!), message);
232
+ } else {
233
+ assert(negated ^ i32(actual > expected), message);
234
+ }
235
+
236
+ Actual.clear();
237
+ Expected.clear();
238
+ }
239
+
240
+ public toBeGreaterThanOrEqual(expected: T, message: string = ""): void {
241
+ let actual = this.actual;
242
+ let negated = this._not;
243
+
244
+ Actual.report(actual);
245
+ Expected.report(expected, negated);
246
+
247
+ if (!isDefined(actual >= expected))
248
+ // @as-covers: ignore because this is a compile time error
249
+ ERROR(
250
+ "Invalid call to toBeGreaterThanOrEqual. Generic type T must have an operator implemented for the greaterThanOrEqual (>=) operation.",
251
+ );
252
+
253
+ // null checks
254
+ if (isReference(actual)) {
255
+ assert(
256
+ i32(changetype<usize>(expected) != 0),
257
+ "Value comparison fails, expected value is null.",
258
+ );
259
+ assert(
260
+ i32(changetype<usize>(actual) != 0),
261
+ "Value comparison fails, actual value is null.",
262
+ );
263
+ }
264
+
265
+ // Compare float types
266
+ if (isFloat(actual)) {
267
+ assert(
268
+ i32(!isNaN(expected)),
269
+ "Value comparison fails, expected value is NaN.",
270
+ );
271
+ assert(
272
+ i32(!isNaN(actual)),
273
+ "Value comparison fails, actual value is NaN.",
274
+ );
275
+ }
276
+
277
+ // do actual greater than comparison
278
+ if (isNullable(actual)) {
279
+ assert(negated ^ i32(actual! >= expected!), message);
280
+ } else {
281
+ assert(negated ^ i32(actual >= expected), message);
282
+ }
283
+
284
+ Actual.clear();
285
+ Expected.clear();
286
+ }
287
+
288
+ public toBeLessThan(expected: T, message: string = ""): void {
289
+ let actual = this.actual;
290
+ let negated = this._not;
291
+ Actual.report(actual);
292
+ Expected.report(expected, negated);
293
+
294
+ if (!isDefined(actual < expected))
295
+ // @as-covers: ignore because this is a compile time error
296
+ ERROR(
297
+ "Invalid call to toBeLessThan. Generic type T must have an operator implemented for the lessThan (<) operation.",
298
+ );
299
+
300
+ // null checks
301
+ if (isReference(actual)) {
302
+ assert(
303
+ i32(changetype<usize>(expected) != 0),
304
+ "Value comparison fails, expected value is null.",
305
+ );
306
+ assert(
307
+ i32(changetype<usize>(actual) != 0),
308
+ "Value comparison fails, actual value is null.",
309
+ );
310
+ } else if (isFloat(actual)) {
311
+ assert(
312
+ i32(!isNaN(expected)),
313
+ "Value comparison fails, expected value is NaN.",
314
+ );
315
+ assert(
316
+ i32(!isNaN(actual)),
317
+ "Value comparison fails, actual value is NaN.",
318
+ );
319
+ }
320
+
321
+ // do actual less than comparison
322
+ if (isNullable(actual)) {
323
+ assert(negated ^ i32(actual! < expected!), message);
324
+ } else {
325
+ assert(negated ^ i32(actual < expected), message);
326
+ }
327
+
328
+ Actual.clear();
329
+ Expected.clear();
330
+ }
331
+
332
+ public toBeLessThanOrEqual(expected: T, message: string = ""): void {
333
+ let actual = this.actual;
334
+ let negated = this._not;
335
+ Actual.report(actual);
336
+ Expected.report(expected, negated);
337
+
338
+ if (!isDefined(actual <= expected))
339
+ // @as-covers: ignore because this is a compile time error
340
+ ERROR(
341
+ "Invalid call to toBeLessThanOrEqual. Generic type T must have an operator implemented for the lessThanOrEqual (<=) operation.",
342
+ );
343
+
344
+ // null checks
345
+ if (isReference(actual)) {
346
+ assert(
347
+ i32(changetype<usize>(expected) != 0),
348
+ "Value comparison fails, expected value is null.",
349
+ );
350
+ assert(
351
+ i32(changetype<usize>(actual) != 0),
352
+ "Value comparison fails, actual value is null.",
353
+ );
354
+ }
355
+
356
+ if (isFloat(actual)) {
357
+ assert(
358
+ i32(!isNaN(expected)),
359
+ "Value comparison fails, expected value is NaN.",
360
+ );
361
+ assert(
362
+ i32(!isNaN(actual)),
363
+ "Value comparison fails, actual value is NaN.",
364
+ );
365
+ }
366
+
367
+ // do actual less than comparison
368
+ if (isNullable(actual)) {
369
+ assert(negated ^ i32(actual! <= expected!), message);
370
+ } else {
371
+ assert(negated ^ i32(actual <= expected), message);
372
+ }
373
+
374
+ Actual.clear();
375
+ Expected.clear();
376
+ }
377
+
378
+ public toBeNull(message: string = ""): void {
379
+ let negated = this._not;
380
+ let actual = this.actual;
381
+
382
+ if (actual instanceof usize) {
383
+ Actual.report(actual);
384
+ Expected.report(<usize>0, negated);
385
+ // @ts-ignore: actual is instanceof number type
386
+ assert(negated ^ i32(actual == 0), message);
387
+ Actual.clear();
388
+ Expected.clear();
389
+ } else if (isReference(actual)) {
390
+ Actual.report(actual);
391
+
392
+ Expected.report(changetype<T>(0), negated);
393
+ assert(negated ^ i32(changetype<usize>(actual) == 0), message);
394
+ Actual.clear();
395
+ Expected.clear();
396
+ // @as-covers: ignore because this is a compile time error
397
+ } else {
398
+ ERROR(
399
+ "toBeNull assertion must be called with a reference type T or usize.",
400
+ );
401
+ }
402
+ }
403
+
404
+ public toBeCloseTo(
405
+ expected: T,
406
+ decimalPlaces: i32 = 2,
407
+ message: string = "",
408
+ ): void {
409
+ let actual = this.actual;
410
+ let negated = this._not;
411
+
412
+ // must be called on a float T
413
+ if (!isFloat(actual))
414
+ // @as-covers: ignore because this is a compile time error
415
+ ERROR("toBeCloseTo must be called with a Float value type T.");
416
+ Actual.report(actual);
417
+ Expected.report(expected, negated);
418
+
419
+ // both actual and expected values must be finite
420
+ assert(
421
+ i32(isFinite(actual)),
422
+ "toBeCloseTo assertion fails because a actual value is not finite",
423
+ );
424
+ assert(
425
+ i32(isFinite(expected)),
426
+ "toBeCloseTo assertion fails because expected value is not finite.",
427
+ );
428
+
429
+ // calculated: `|expected - actual| < 1 / numberOfDigits`.
430
+ // @ts-ignore tooling errors because T does not extend a numeric value type. This compiles just fine.
431
+ let isClose = i32(abs(expected - actual) < Math.pow(10, -decimalPlaces));
432
+ assert(negated ^ isClose, message);
433
+ Actual.clear();
434
+ Expected.clear();
435
+ }
436
+
437
+ public toBeNaN(message: string = ""): void {
438
+ let actual = this.actual;
439
+ let negated = this._not;
440
+
441
+ // must be called on a float T
442
+ if (!isFloat(actual))
443
+ // @as-covers: ignore because this is a compile time error
444
+ ERROR("toBeNaN must be called with a Float value type T.");
445
+ Actual.report(actual);
446
+
447
+ // @ts-ignore: The compiler should pass bit count (64/32 bit float to the report function)
448
+ Expected.report<T>(NaN, negated);
449
+
450
+ let isNaNValue = i32(isNaN(actual));
451
+ assert(isNaNValue ^ negated, message);
452
+ Actual.clear();
453
+ Expected.clear();
454
+ }
455
+
456
+ public toBeFinite(message: string = ""): void {
457
+ let actual = this.actual;
458
+ let negated = this._not;
459
+
460
+ // must be called on a float T
461
+ if (!isFloat(actual))
462
+ // @as-covers: ignore because this is a compile time error
463
+ ERROR("toBeNaN must be called with a Float value type T.");
464
+ Actual.report(actual);
465
+ Expected.reportFinite(negated);
466
+
467
+ let isFiniteValue = i32(isFinite(actual));
468
+ assert(isFiniteValue ^ negated, message);
469
+ Actual.clear();
470
+ Expected.clear();
471
+ }
472
+
473
+ public toHaveLength(expected: i32, message: string = ""): void {
474
+ let actual = this.actual;
475
+ let negated = this._not;
476
+ let length = 0;
477
+ if (actual instanceof ArrayBuffer) {
478
+ length = actual.byteLength;
479
+ } else {
480
+ // @ts-ignore: This results in a compile time check for a length property with a better error message
481
+ if (!isDefined(actual.length))
482
+ // @as-covers: ignore because this is a compile time error
483
+ ERROR(
484
+ "toHaveLength cannot be called on type T where T.length is not defined.",
485
+ );
486
+ if (isNullable(actual)) {
487
+ // @ts-ignore: This results in a compile time check for a length property with a better error message
488
+ length = <i32>actual!.length;
489
+ } else {
490
+ // @ts-ignore: This results in a compile time check for a length property with a better error message
491
+ length = <i32>actual.length;
492
+ }
493
+ }
494
+
495
+ Actual.report(length);
496
+ Expected.report(expected, negated);
497
+
498
+ let lengthsEqual = i32(length == expected);
499
+ assert(lengthsEqual ^ negated, message);
500
+ Actual.clear();
501
+ Expected.clear();
502
+ }
503
+
504
+ public toInclude<U>(expected: U, message: string = ""): void {
505
+ toIncludeComparison<T, U>(this.actual, expected, this._not, message);
506
+ Actual.clear();
507
+ Expected.clear();
508
+ }
509
+
510
+ // @ts-ignore: valueof<T> requires that T extends something with an @operator("[]")
511
+ // @as-covers: ignore because this is just an alias function
512
+ public toContain(expected: valueof<T>, message: string = ""): void {
513
+ this.toInclude(expected, message);
514
+ }
515
+
516
+ public toIncludeEqual<U>(expected: U, message: string = ""): void {
517
+ toIncludeEqualComparison<T, U>(this.actual, expected, this._not, message);
518
+ Actual.clear();
519
+ Expected.clear();
520
+ }
521
+
522
+ // @as-covers: ignore because this is an alias
523
+ public toContainEqual<U>(expected: U, message: string = ""): void {
524
+ this.toIncludeEqual(expected, message);
525
+ }
526
+
527
+ public toMatchSnapshot(name: string | null = null): void {
528
+ assert(i32(!this._not), "Snapshots cannot be negated.");
529
+ Expected.reportSnapshot(this.actual, name);
530
+ }
531
+ }
532
+
533
+ // @ts-ignore: decorators *are* valid here
534
+ @global
535
+ export function expect<T>(actual: T): Expectation<T> {
536
+ return new Expectation(actual);
537
+ }
538
+
539
+ // @ts-ignore: decorators *are* valid here
540
+ // @as-covers: ignore because this is deprecated
541
+ @global export function expectFn(cb: () => void): Expectation<() => void> {
542
+ WARNING("expectFn() has been deprecated. Use expect() instead.");
543
+ return new Expectation(cb);
544
+ }