as-test 1.1.10 → 1.3.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/CHANGELOG.md +48 -0
- package/as-test.config.schema.json +15 -0
- package/assembly/coverage.ts +22 -26
- package/assembly/index.ts +2 -0
- package/assembly/src/expectation.ts +152 -44
- package/assembly/src/mode.ts +55 -0
- package/bin/commands/build-core.js +190 -65
- package/bin/commands/build.js +3 -1
- package/bin/commands/fuzz-core.js +30 -56
- package/bin/commands/init-core.js +253 -5
- package/bin/commands/run-core.js +38 -119
- package/bin/commands/test.js +1 -1
- package/bin/commands/web-session.js +4 -0
- package/bin/crash-store.js +1 -1
- package/bin/index.js +94 -152
- package/bin/types.js +7 -0
- package/bin/util.js +117 -0
- package/package.json +14 -9
- package/transform/lib/index.js +26 -0
|
@@ -57,6 +57,41 @@ export class Expectation<T> extends Tests {
|
|
|
57
57
|
return this;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Asserts that a custom predicate holds. Accepts either a bool or a
|
|
62
|
+
* `() => bool` lambda. Useful when the verdict isn't expressible via the
|
|
63
|
+
* built-in matchers — e.g. delegating to a hand-written comparator.
|
|
64
|
+
*
|
|
65
|
+
* expect(x).where(x > 0 && x < 10);
|
|
66
|
+
* expect(actual).where((): bool => deepEqual(GLOBAL_A, GLOBAL_B));
|
|
67
|
+
*
|
|
68
|
+
* Chains with other matchers as an independent assertion:
|
|
69
|
+
*
|
|
70
|
+
* expect(7).toBe(7).where((): bool => isFresh());
|
|
71
|
+
*
|
|
72
|
+
* Note: AssemblyScript does not implement closures, so the lambda cannot
|
|
73
|
+
* capture local variables from the enclosing scope. Use the bool form when
|
|
74
|
+
* the predicate references locals, or refer to module-level values from
|
|
75
|
+
* inside the lambda.
|
|
76
|
+
*/
|
|
77
|
+
where<W>(predicate: W, message: string = ""): Expectation<T> {
|
|
78
|
+
let passed: bool;
|
|
79
|
+
if (isFunction<W>()) {
|
|
80
|
+
passed = (predicate as () => bool)();
|
|
81
|
+
} else {
|
|
82
|
+
// @ts-ignore: W is a bool-compatible primitive in this branch
|
|
83
|
+
passed = predicate as bool;
|
|
84
|
+
}
|
|
85
|
+
this._resolve(
|
|
86
|
+
passed,
|
|
87
|
+
"where",
|
|
88
|
+
q(passed ? "true" : "false"),
|
|
89
|
+
q("true"),
|
|
90
|
+
message,
|
|
91
|
+
);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
60
95
|
private _resolve(
|
|
61
96
|
passed: bool,
|
|
62
97
|
instr: string,
|
|
@@ -74,14 +109,26 @@ export class Expectation<T> extends Tests {
|
|
|
74
109
|
return;
|
|
75
110
|
}
|
|
76
111
|
const isFail = this._not ? passed : !passed;
|
|
77
|
-
this.verdict = isFail ? "fail" : "ok";
|
|
78
|
-
this.instr = instr;
|
|
79
|
-
this.left = left;
|
|
80
|
-
this.right = right;
|
|
81
112
|
const resolvedMessage = message.length ? message : this._message;
|
|
82
|
-
|
|
113
|
+
// When matchers chain, later ones must not overwrite an earlier failure's
|
|
114
|
+
// recorded state — otherwise a passing matcher after a failed one would
|
|
115
|
+
// flip the suite's verdict back to "ok". Each matcher still fires its own
|
|
116
|
+
// IPC failure independently below.
|
|
117
|
+
if (this.verdict != "fail") {
|
|
118
|
+
this.verdict = isFail ? "fail" : "ok";
|
|
119
|
+
this.instr = instr;
|
|
120
|
+
this.left = left;
|
|
121
|
+
this.right = right;
|
|
122
|
+
this.message = isFail ? resolvedMessage : "";
|
|
123
|
+
}
|
|
83
124
|
if (isFail) {
|
|
84
|
-
sendAssertionFailure(
|
|
125
|
+
sendAssertionFailure(
|
|
126
|
+
this._snapshotKey,
|
|
127
|
+
instr,
|
|
128
|
+
left,
|
|
129
|
+
right,
|
|
130
|
+
resolvedMessage,
|
|
131
|
+
);
|
|
85
132
|
// @ts-ignore
|
|
86
133
|
if (isDefined(AS_TEST_FUZZ)) {
|
|
87
134
|
// @ts-ignore
|
|
@@ -95,7 +142,7 @@ export class Expectation<T> extends Tests {
|
|
|
95
142
|
// @ts-ignore
|
|
96
143
|
__as_test_fuzz_failure_right = right;
|
|
97
144
|
// @ts-ignore
|
|
98
|
-
__as_test_fuzz_failure_message =
|
|
145
|
+
__as_test_fuzz_failure_message = resolvedMessage;
|
|
99
146
|
}
|
|
100
147
|
}
|
|
101
148
|
}
|
|
@@ -105,7 +152,7 @@ export class Expectation<T> extends Tests {
|
|
|
105
152
|
/**
|
|
106
153
|
* Tests if a == null
|
|
107
154
|
*/
|
|
108
|
-
toBeNull(message: string = ""):
|
|
155
|
+
toBeNull(message: string = ""): Expectation<T> {
|
|
109
156
|
const passed =
|
|
110
157
|
(isNullable<T>() && changetype<usize>(this._left) == 0) ||
|
|
111
158
|
(isInteger<T>() && nameof<T>() == "usize" && this._left == 0);
|
|
@@ -121,12 +168,13 @@ export class Expectation<T> extends Tests {
|
|
|
121
168
|
),
|
|
122
169
|
message,
|
|
123
170
|
);
|
|
171
|
+
return this;
|
|
124
172
|
}
|
|
125
173
|
|
|
126
174
|
/**
|
|
127
175
|
* Tests if a > b
|
|
128
176
|
*/
|
|
129
|
-
toBeGreaterThan(value: T, message: string = ""):
|
|
177
|
+
toBeGreaterThan(value: T, message: string = ""): Expectation<T> {
|
|
130
178
|
if (!isInteger<T>() && !isFloat<T>())
|
|
131
179
|
ERROR("toBeGreaterThan() can only be used on number types!");
|
|
132
180
|
|
|
@@ -144,12 +192,13 @@ export class Expectation<T> extends Tests {
|
|
|
144
192
|
),
|
|
145
193
|
message,
|
|
146
194
|
);
|
|
195
|
+
return this;
|
|
147
196
|
}
|
|
148
197
|
|
|
149
198
|
/**
|
|
150
199
|
* Tests if a >= b
|
|
151
200
|
*/
|
|
152
|
-
toBeGreaterOrEqualTo(value: T, message: string = ""):
|
|
201
|
+
toBeGreaterOrEqualTo(value: T, message: string = ""): Expectation<T> {
|
|
153
202
|
if (!isInteger<T>() && !isFloat<T>())
|
|
154
203
|
ERROR("toBeGreaterOrEqualTo() can only be used on number types!");
|
|
155
204
|
|
|
@@ -167,12 +216,13 @@ export class Expectation<T> extends Tests {
|
|
|
167
216
|
),
|
|
168
217
|
message,
|
|
169
218
|
);
|
|
219
|
+
return this;
|
|
170
220
|
}
|
|
171
221
|
|
|
172
222
|
/**
|
|
173
223
|
* Tests if a < b
|
|
174
224
|
*/
|
|
175
|
-
toBeLessThan(value: T, message: string = ""):
|
|
225
|
+
toBeLessThan(value: T, message: string = ""): Expectation<T> {
|
|
176
226
|
if (!isInteger<T>() && !isFloat<T>())
|
|
177
227
|
ERROR("toBeLessThan() can only be used on number types!");
|
|
178
228
|
|
|
@@ -190,12 +240,13 @@ export class Expectation<T> extends Tests {
|
|
|
190
240
|
),
|
|
191
241
|
message,
|
|
192
242
|
);
|
|
243
|
+
return this;
|
|
193
244
|
}
|
|
194
245
|
|
|
195
246
|
/**
|
|
196
247
|
* Tests if a <= b
|
|
197
248
|
*/
|
|
198
|
-
toBeLessThanOrEqualTo(value: T, message: string = ""):
|
|
249
|
+
toBeLessThanOrEqualTo(value: T, message: string = ""): Expectation<T> {
|
|
199
250
|
if (!isInteger<T>() && !isFloat<T>())
|
|
200
251
|
ERROR("toBeLessThanOrEqualTo() can only be used on number types!");
|
|
201
252
|
|
|
@@ -213,12 +264,13 @@ export class Expectation<T> extends Tests {
|
|
|
213
264
|
),
|
|
214
265
|
message,
|
|
215
266
|
);
|
|
267
|
+
return this;
|
|
216
268
|
}
|
|
217
269
|
|
|
218
270
|
/**
|
|
219
271
|
* Tests if a is string
|
|
220
272
|
*/
|
|
221
|
-
toBeString(message: string = ""):
|
|
273
|
+
toBeString(message: string = ""): Expectation<T> {
|
|
222
274
|
this._resolve(
|
|
223
275
|
isString<T>(),
|
|
224
276
|
"toBeString",
|
|
@@ -226,12 +278,13 @@ export class Expectation<T> extends Tests {
|
|
|
226
278
|
q("string"),
|
|
227
279
|
message,
|
|
228
280
|
);
|
|
281
|
+
return this;
|
|
229
282
|
}
|
|
230
283
|
|
|
231
284
|
/**
|
|
232
285
|
* Tests if a is boolean
|
|
233
286
|
*/
|
|
234
|
-
toBeBoolean(message: string = ""):
|
|
287
|
+
toBeBoolean(message: string = ""): Expectation<T> {
|
|
235
288
|
this._resolve(
|
|
236
289
|
isBoolean<T>(),
|
|
237
290
|
"toBeBoolean",
|
|
@@ -239,12 +292,13 @@ export class Expectation<T> extends Tests {
|
|
|
239
292
|
q("boolean"),
|
|
240
293
|
message,
|
|
241
294
|
);
|
|
295
|
+
return this;
|
|
242
296
|
}
|
|
243
297
|
|
|
244
298
|
/**
|
|
245
299
|
* Tests if a is array
|
|
246
300
|
*/
|
|
247
|
-
toBeArray(message: string = ""):
|
|
301
|
+
toBeArray(message: string = ""): Expectation<T> {
|
|
248
302
|
this._resolve(
|
|
249
303
|
isArray<T>(),
|
|
250
304
|
"toBeArray",
|
|
@@ -252,12 +306,13 @@ export class Expectation<T> extends Tests {
|
|
|
252
306
|
q("Array<any>"),
|
|
253
307
|
message,
|
|
254
308
|
);
|
|
309
|
+
return this;
|
|
255
310
|
}
|
|
256
311
|
|
|
257
312
|
/**
|
|
258
313
|
* Tests if a is number
|
|
259
314
|
*/
|
|
260
|
-
toBeNumber(message: string = ""):
|
|
315
|
+
toBeNumber(message: string = ""): Expectation<T> {
|
|
261
316
|
this._resolve(
|
|
262
317
|
isFloat<T>() || isInteger<T>(),
|
|
263
318
|
"toBeNumber",
|
|
@@ -265,12 +320,13 @@ export class Expectation<T> extends Tests {
|
|
|
265
320
|
q("number"),
|
|
266
321
|
message,
|
|
267
322
|
);
|
|
323
|
+
return this;
|
|
268
324
|
}
|
|
269
325
|
|
|
270
326
|
/**
|
|
271
327
|
* Tests if a is integer
|
|
272
328
|
*/
|
|
273
|
-
toBeInteger(message: string = ""):
|
|
329
|
+
toBeInteger(message: string = ""): Expectation<T> {
|
|
274
330
|
this._resolve(
|
|
275
331
|
isInteger<T>(),
|
|
276
332
|
"toBeInteger",
|
|
@@ -278,12 +334,13 @@ export class Expectation<T> extends Tests {
|
|
|
278
334
|
q("integer"),
|
|
279
335
|
message,
|
|
280
336
|
);
|
|
337
|
+
return this;
|
|
281
338
|
}
|
|
282
339
|
|
|
283
340
|
/**
|
|
284
341
|
* Tests if a is float
|
|
285
342
|
*/
|
|
286
|
-
toBeFloat(message: string = ""):
|
|
343
|
+
toBeFloat(message: string = ""): Expectation<T> {
|
|
287
344
|
this._resolve(
|
|
288
345
|
isFloat<T>(),
|
|
289
346
|
"toBeFloat",
|
|
@@ -291,21 +348,23 @@ export class Expectation<T> extends Tests {
|
|
|
291
348
|
q("float"),
|
|
292
349
|
message,
|
|
293
350
|
);
|
|
351
|
+
return this;
|
|
294
352
|
}
|
|
295
353
|
|
|
296
354
|
/**
|
|
297
355
|
* Tests if a is finite
|
|
298
356
|
*/
|
|
299
|
-
toBeFinite(message: string = ""):
|
|
357
|
+
toBeFinite(message: string = ""): Expectation<T> {
|
|
300
358
|
// @ts-ignore
|
|
301
359
|
const passed = (isFloat<T>() || isInteger<T>()) && isFinite(this._left);
|
|
302
360
|
this._resolve(passed, "toBeFinite", q("Infinity"), q("Finite"), message);
|
|
361
|
+
return this;
|
|
303
362
|
}
|
|
304
363
|
|
|
305
364
|
/**
|
|
306
365
|
* Tests if a value is truthy
|
|
307
366
|
*/
|
|
308
|
-
toBeTruthy(message: string = ""):
|
|
367
|
+
toBeTruthy(message: string = ""): Expectation<T> {
|
|
309
368
|
this._resolve(
|
|
310
369
|
isTruthy<T>(this._left),
|
|
311
370
|
"toBeTruthy",
|
|
@@ -313,12 +372,13 @@ export class Expectation<T> extends Tests {
|
|
|
313
372
|
q("truthy"),
|
|
314
373
|
message,
|
|
315
374
|
);
|
|
375
|
+
return this;
|
|
316
376
|
}
|
|
317
377
|
|
|
318
378
|
/**
|
|
319
379
|
* Tests if a value is falsy
|
|
320
380
|
*/
|
|
321
|
-
toBeFalsy(message: string = ""):
|
|
381
|
+
toBeFalsy(message: string = ""): Expectation<T> {
|
|
322
382
|
this._resolve(
|
|
323
383
|
!isTruthy<T>(this._left),
|
|
324
384
|
"toBeFalsy",
|
|
@@ -326,12 +386,14 @@ export class Expectation<T> extends Tests {
|
|
|
326
386
|
q("falsy"),
|
|
327
387
|
message,
|
|
328
388
|
);
|
|
389
|
+
return this;
|
|
329
390
|
}
|
|
330
391
|
|
|
331
392
|
/**
|
|
332
393
|
* Tests if a floating-point number is close to expected
|
|
333
394
|
*/
|
|
334
|
-
|
|
395
|
+
// prettier-ignore
|
|
396
|
+
toBeCloseTo(expected: T, precision: i32 = 2, message: string = ""): Expectation<T> {
|
|
335
397
|
if (!isFloat<T>() && !isInteger<T>())
|
|
336
398
|
ERROR("toBeCloseTo() can only be used on number types!");
|
|
337
399
|
const factor = Math.pow(10, precision as f64);
|
|
@@ -344,12 +406,13 @@ export class Expectation<T> extends Tests {
|
|
|
344
406
|
visualize<T>(expected),
|
|
345
407
|
message,
|
|
346
408
|
);
|
|
409
|
+
return this;
|
|
347
410
|
}
|
|
348
411
|
|
|
349
412
|
/**
|
|
350
413
|
* Tests if a string contains substring
|
|
351
414
|
*/
|
|
352
|
-
toMatch(value: string, message: string = ""):
|
|
415
|
+
toMatch(value: string, message: string = ""): Expectation<T> {
|
|
353
416
|
if (!isString<T>()) ERROR("toMatch() can only be used on string types!");
|
|
354
417
|
// @ts-ignore
|
|
355
418
|
const passed = this._left.indexOf(value) >= 0;
|
|
@@ -361,36 +424,39 @@ export class Expectation<T> extends Tests {
|
|
|
361
424
|
q(value),
|
|
362
425
|
message,
|
|
363
426
|
);
|
|
427
|
+
return this;
|
|
364
428
|
}
|
|
365
429
|
|
|
366
430
|
/**
|
|
367
431
|
* Tests if a string starts with the provided prefix.
|
|
368
432
|
*/
|
|
369
|
-
toStartWith(value: string, message: string = ""):
|
|
433
|
+
toStartWith(value: string, message: string = ""): Expectation<T> {
|
|
370
434
|
if (!isString<T>())
|
|
371
435
|
ERROR("toStartWith() can only be used on string types!");
|
|
372
436
|
// @ts-ignore
|
|
373
437
|
const left = this._left as string;
|
|
374
438
|
const passed = left.indexOf(value) == 0;
|
|
375
439
|
this._resolve(passed, "toStartWith", q(left), q(value), message);
|
|
440
|
+
return this;
|
|
376
441
|
}
|
|
377
442
|
|
|
378
443
|
/**
|
|
379
444
|
* Tests if a string ends with the provided suffix.
|
|
380
445
|
*/
|
|
381
|
-
toEndWith(value: string, message: string = ""):
|
|
446
|
+
toEndWith(value: string, message: string = ""): Expectation<T> {
|
|
382
447
|
if (!isString<T>()) ERROR("toEndWith() can only be used on string types!");
|
|
383
448
|
// @ts-ignore
|
|
384
449
|
const left = this._left as string;
|
|
385
450
|
const idx = left.lastIndexOf(value);
|
|
386
451
|
const passed = idx >= 0 && idx + value.length == left.length;
|
|
387
452
|
this._resolve(passed, "toEndWith", q(left), q(value), message);
|
|
453
|
+
return this;
|
|
388
454
|
}
|
|
389
455
|
|
|
390
456
|
/**
|
|
391
457
|
* Tests if an array has length x
|
|
392
458
|
*/
|
|
393
|
-
toHaveLength(value: i32, message: string = ""):
|
|
459
|
+
toHaveLength(value: i32, message: string = ""): Expectation<T> {
|
|
394
460
|
// @ts-ignore
|
|
395
461
|
const leftLen = this._left.length as i32;
|
|
396
462
|
// @ts-ignore
|
|
@@ -402,13 +468,14 @@ export class Expectation<T> extends Tests {
|
|
|
402
468
|
value.toString(),
|
|
403
469
|
message,
|
|
404
470
|
);
|
|
471
|
+
return this;
|
|
405
472
|
}
|
|
406
473
|
|
|
407
474
|
/**
|
|
408
475
|
* Tests if an array or string contains a value
|
|
409
476
|
*/
|
|
410
477
|
// @ts-ignore
|
|
411
|
-
toContain(value: valueof<T>, message: string = ""):
|
|
478
|
+
toContain(value: valueof<T>, message: string = ""): Expectation<T> {
|
|
412
479
|
if (isString<T>()) {
|
|
413
480
|
// @ts-ignore
|
|
414
481
|
const left = this._left as string;
|
|
@@ -416,7 +483,7 @@ export class Expectation<T> extends Tests {
|
|
|
416
483
|
const needle = value as string;
|
|
417
484
|
const passed = left.indexOf(needle) >= 0;
|
|
418
485
|
this._resolve(passed, "toContain", q(left), q(needle), message);
|
|
419
|
-
return;
|
|
486
|
+
return this;
|
|
420
487
|
}
|
|
421
488
|
|
|
422
489
|
if (isArray<T>()) {
|
|
@@ -429,24 +496,25 @@ export class Expectation<T> extends Tests {
|
|
|
429
496
|
JSON.stringify<valueof<T>>(value),
|
|
430
497
|
message,
|
|
431
498
|
);
|
|
432
|
-
return;
|
|
499
|
+
return this;
|
|
433
500
|
}
|
|
434
501
|
|
|
435
502
|
ERROR("toContain() can only be used on string and array types!");
|
|
503
|
+
return this;
|
|
436
504
|
}
|
|
437
505
|
|
|
438
506
|
/**
|
|
439
507
|
* Alias for toContain().
|
|
440
508
|
*/
|
|
441
509
|
// @ts-ignore
|
|
442
|
-
toContains(value: valueof<T>, message: string = ""):
|
|
443
|
-
this.toContain(value, message);
|
|
510
|
+
toContains(value: valueof<T>, message: string = ""): Expectation<T> {
|
|
511
|
+
return this.toContain(value, message);
|
|
444
512
|
}
|
|
445
513
|
|
|
446
514
|
/**
|
|
447
515
|
* Tests if serialized value matches stored snapshot.
|
|
448
516
|
*/
|
|
449
|
-
toMatchSnapshot(name: string = "", message: string = ""):
|
|
517
|
+
toMatchSnapshot(name: string = "", message: string = ""): Expectation<T> {
|
|
450
518
|
let key = name.length
|
|
451
519
|
? namedSnapshotKey(this._snapshotKey, name)
|
|
452
520
|
: nextUnnamedSnapshotKey(this._snapshotKey);
|
|
@@ -454,38 +522,75 @@ export class Expectation<T> extends Tests {
|
|
|
454
522
|
const actual = JSON.stringify<T>(this._left);
|
|
455
523
|
const res = snapshotAssert(key, actual);
|
|
456
524
|
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
|
|
525
|
+
return this;
|
|
457
526
|
}
|
|
458
527
|
|
|
459
528
|
/**
|
|
460
|
-
*
|
|
461
|
-
*
|
|
529
|
+
* Invokes the wrapped function inside a try/catch and asserts it threw.
|
|
530
|
+
* Requires the try-as feature (`--enable try-as`).
|
|
531
|
+
*
|
|
532
|
+
* expect((): void => { throw new Error("boom"); }).toThrow();
|
|
533
|
+
*
|
|
534
|
+
* The value passed to `expect()` must be a `() => void` callback — calling
|
|
535
|
+
* `.toThrow()` on a non-function value records a failure that explains the
|
|
536
|
+
* usage.
|
|
462
537
|
*/
|
|
463
|
-
toThrow(message: string = ""):
|
|
538
|
+
toThrow(message: string = ""): Expectation<T> {
|
|
464
539
|
// @ts-ignore
|
|
465
540
|
if (!isDefined(AS_TEST_TRY_AS)) {
|
|
466
541
|
if (!warnedToThrowDisabled) {
|
|
467
542
|
sendWarning(
|
|
468
|
-
|
|
543
|
+
"toThrow() requires the try-as feature. Enable with --enable try-as.",
|
|
469
544
|
);
|
|
470
545
|
warnedToThrowDisabled = true;
|
|
471
546
|
}
|
|
472
547
|
this._resolve(true, "toThrow", q("disabled"), q("disabled"), message);
|
|
473
|
-
return;
|
|
548
|
+
return this;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!isFunction<T>()) {
|
|
552
|
+
this._resolve(
|
|
553
|
+
false,
|
|
554
|
+
"toThrow",
|
|
555
|
+
q("non-function"),
|
|
556
|
+
q("() => void"),
|
|
557
|
+
message.length
|
|
558
|
+
? message
|
|
559
|
+
: "toThrow() requires a function: expect((): void => { ... }).toThrow()",
|
|
560
|
+
);
|
|
561
|
+
return this;
|
|
474
562
|
}
|
|
475
563
|
|
|
564
|
+
// try-as rewrites the throw inside the callback to bump
|
|
565
|
+
// __ExceptionState.Failures and return early from the arrow. We never
|
|
566
|
+
// wrap the call in try/catch here because try-as's source linker does not
|
|
567
|
+
// follow chained method calls (`expect(...).toThrow()`) and so it would
|
|
568
|
+
// not rewrite a `try` placed in this method body. Compare the failure
|
|
569
|
+
// counter before/after instead and consume any failure we observed.
|
|
570
|
+
// @ts-ignore: __ExceptionState is provided by the try-as transform
|
|
571
|
+
const beforeFailures = __ExceptionState.Failures;
|
|
572
|
+
// @ts-ignore: guarded by isFunction<T>() above
|
|
573
|
+
(this._left as () => void)();
|
|
476
574
|
// @ts-ignore
|
|
477
|
-
const
|
|
478
|
-
if (
|
|
575
|
+
const threw = __ExceptionState.Failures > beforeFailures;
|
|
576
|
+
if (threw) {
|
|
479
577
|
// @ts-ignore
|
|
480
|
-
__ExceptionState.Failures
|
|
578
|
+
__ExceptionState.Failures = beforeFailures;
|
|
481
579
|
}
|
|
482
|
-
this._resolve(
|
|
580
|
+
this._resolve(
|
|
581
|
+
threw,
|
|
582
|
+
"toThrow",
|
|
583
|
+
q(threw ? "threw" : "did not throw"),
|
|
584
|
+
q("throws"),
|
|
585
|
+
message,
|
|
586
|
+
);
|
|
587
|
+
return this;
|
|
483
588
|
}
|
|
484
589
|
|
|
485
590
|
/**
|
|
486
591
|
* Tests for equality
|
|
487
592
|
*/
|
|
488
|
-
toBe(equals: T, message: string = ""):
|
|
593
|
+
toBe(equals: T, message: string = ""): Expectation<T> {
|
|
489
594
|
const passed = this._left === equals;
|
|
490
595
|
|
|
491
596
|
this._resolve(
|
|
@@ -495,12 +600,13 @@ export class Expectation<T> extends Tests {
|
|
|
495
600
|
JSON.stringify<T>(equals),
|
|
496
601
|
message,
|
|
497
602
|
);
|
|
603
|
+
return this;
|
|
498
604
|
}
|
|
499
605
|
|
|
500
606
|
/**
|
|
501
607
|
* Tests for deep equality
|
|
502
608
|
*/
|
|
503
|
-
toEqual(equals: T, message: string = ""):
|
|
609
|
+
toEqual(equals: T, message: string = ""): Expectation<T> {
|
|
504
610
|
const passed = valueEquals<T>(this._left, equals, false);
|
|
505
611
|
this._resolve(
|
|
506
612
|
passed,
|
|
@@ -509,12 +615,13 @@ export class Expectation<T> extends Tests {
|
|
|
509
615
|
JSON.stringify<T>(equals),
|
|
510
616
|
message,
|
|
511
617
|
);
|
|
618
|
+
return this;
|
|
512
619
|
}
|
|
513
620
|
|
|
514
621
|
/**
|
|
515
622
|
* Tests for strict deep equality
|
|
516
623
|
*/
|
|
517
|
-
toStrictEqual(equals: T, message: string = ""):
|
|
624
|
+
toStrictEqual(equals: T, message: string = ""): Expectation<T> {
|
|
518
625
|
const passed = valueEquals<T>(this._left, equals, true);
|
|
519
626
|
this._resolve(
|
|
520
627
|
passed,
|
|
@@ -523,6 +630,7 @@ export class Expectation<T> extends Tests {
|
|
|
523
630
|
JSON.stringify<T>(equals),
|
|
524
631
|
message,
|
|
525
632
|
);
|
|
633
|
+
return this;
|
|
526
634
|
}
|
|
527
635
|
}
|
|
528
636
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// The current mode name is patched at build time by as-test's transform
|
|
2
|
+
// (transform/src/index.ts) using the AS_TEST_MODE_NAME env var that
|
|
3
|
+
// build-core.ts injects per-mode build. When unset, defaults to "default".
|
|
4
|
+
export const AS_TEST_MODE_NAME: string = "default";
|
|
5
|
+
|
|
6
|
+
function modeMatches(matchers: string[], current: string): bool {
|
|
7
|
+
if (matchers.length == 0) return false;
|
|
8
|
+
let sawPositive = false;
|
|
9
|
+
let positiveHit = false;
|
|
10
|
+
for (let i = 0; i < matchers.length; i++) {
|
|
11
|
+
const m = matchers[i];
|
|
12
|
+
if (m.length == 0) continue;
|
|
13
|
+
if (m.charCodeAt(0) == 33 /* '!' */) {
|
|
14
|
+
if (m.substring(1) == current) return false;
|
|
15
|
+
} else {
|
|
16
|
+
sawPositive = true;
|
|
17
|
+
if (m == current) positiveHit = true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return sawPositive ? positiveHit : true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gate a block of suite/test registrations on the current execution mode.
|
|
25
|
+
*
|
|
26
|
+
* The current mode is the name under `modes.<name>` in `as-test.config.json`
|
|
27
|
+
* (or `"default"` when running the base config). Comparisons are by exact
|
|
28
|
+
* string match.
|
|
29
|
+
*
|
|
30
|
+
* Matcher semantics:
|
|
31
|
+
* - `["a"]` runs when the current mode equals `"a"`.
|
|
32
|
+
* - `["a", "b"]` runs when the current mode is in `{a, b}` (positive OR).
|
|
33
|
+
* - `["!a"]` runs when the current mode is NOT `"a"`.
|
|
34
|
+
* - `["!a", "!b"]` runs when the current mode is neither `{a, b}` (AND).
|
|
35
|
+
* - Mixed `["a", "!b"]` runs when any positive matches AND no negative does.
|
|
36
|
+
* - `[]` and empty entries are skipped (no-op).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* mode(["simd"], () => {
|
|
41
|
+
* describe("vectorised path", () => { ... });
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* mode(["simd", "swar"], () => {
|
|
45
|
+
* describe("fast paths", () => { ... });
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* mode(["!naive"], () => {
|
|
49
|
+
* describe("anything but naive", () => { ... });
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function mode(matchers: string[], fn: () => void): void {
|
|
54
|
+
if (modeMatches(matchers, AS_TEST_MODE_NAME)) fn();
|
|
55
|
+
}
|