as-test 1.1.0 → 1.1.2

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.
@@ -62,6 +62,7 @@ export class Expectation<T> extends Tests {
62
62
  instr: string,
63
63
  left: string,
64
64
  right: string,
65
+ message: string = "",
65
66
  ): void {
66
67
  if (this._skip) {
67
68
  this.verdict = "skip";
@@ -77,7 +78,8 @@ export class Expectation<T> extends Tests {
77
78
  this.instr = instr;
78
79
  this.left = left;
79
80
  this.right = right;
80
- this.message = isFail ? this._message : "";
81
+ const resolvedMessage = message.length ? message : this._message;
82
+ this.message = isFail ? resolvedMessage : "";
81
83
  if (isFail) {
82
84
  sendAssertionFailure(this._snapshotKey, instr, left, right, this.message);
83
85
  // @ts-ignore
@@ -103,7 +105,7 @@ export class Expectation<T> extends Tests {
103
105
  /**
104
106
  * Tests if a == null
105
107
  */
106
- toBeNull(): void {
108
+ toBeNull(message: string = ""): void {
107
109
  const passed =
108
110
  (isNullable<T>() && changetype<usize>(this._left) == 0) ||
109
111
  (isInteger<T>() && nameof<T>() == "usize" && this._left == 0);
@@ -117,13 +119,14 @@ export class Expectation<T> extends Tests {
117
119
  visualize<T>(
118
120
  load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
119
121
  ),
122
+ message,
120
123
  );
121
124
  }
122
125
 
123
126
  /**
124
127
  * Tests if a > b
125
128
  */
126
- toBeGreaterThan(value: T): void {
129
+ toBeGreaterThan(value: T, message: string = ""): void {
127
130
  if (!isInteger<T>() && !isFloat<T>())
128
131
  ERROR("toBeGreaterThan() can only be used on number types!");
129
132
 
@@ -139,13 +142,14 @@ export class Expectation<T> extends Tests {
139
142
  visualize<T>(
140
143
  load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
141
144
  ),
145
+ message,
142
146
  );
143
147
  }
144
148
 
145
149
  /**
146
150
  * Tests if a >= b
147
151
  */
148
- toBeGreaterOrEqualTo(value: T): void {
152
+ toBeGreaterOrEqualTo(value: T, message: string = ""): void {
149
153
  if (!isInteger<T>() && !isFloat<T>())
150
154
  ERROR("toBeGreaterOrEqualTo() can only be used on number types!");
151
155
 
@@ -161,13 +165,14 @@ export class Expectation<T> extends Tests {
161
165
  visualize<T>(
162
166
  load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
163
167
  ),
168
+ message,
164
169
  );
165
170
  }
166
171
 
167
172
  /**
168
173
  * Tests if a < b
169
174
  */
170
- toBeLessThan(value: T): void {
175
+ toBeLessThan(value: T, message: string = ""): void {
171
176
  if (!isInteger<T>() && !isFloat<T>())
172
177
  ERROR("toBeLessThan() can only be used on number types!");
173
178
 
@@ -183,13 +188,14 @@ export class Expectation<T> extends Tests {
183
188
  visualize<T>(
184
189
  load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
185
190
  ),
191
+ message,
186
192
  );
187
193
  }
188
194
 
189
195
  /**
190
196
  * Tests if a <= b
191
197
  */
192
- toBeLessThanOrEqualTo(value: T): void {
198
+ toBeLessThanOrEqualTo(value: T, message: string = ""): void {
193
199
  if (!isInteger<T>() && !isFloat<T>())
194
200
  ERROR("toBeLessThanOrEqualTo() can only be used on number types!");
195
201
 
@@ -205,93 +211,127 @@ export class Expectation<T> extends Tests {
205
211
  visualize<T>(
206
212
  load<T>(changetype<usize>(this), offsetof<Expectation<T>>("_right")),
207
213
  ),
214
+ message,
208
215
  );
209
216
  }
210
217
 
211
218
  /**
212
219
  * Tests if a is string
213
220
  */
214
- toBeString(): void {
215
- this._resolve(isString<T>(), "toBeString", q(nameof<T>()), q("string"));
221
+ toBeString(message: string = ""): void {
222
+ this._resolve(
223
+ isString<T>(),
224
+ "toBeString",
225
+ q(nameof<T>()),
226
+ q("string"),
227
+ message,
228
+ );
216
229
  }
217
230
 
218
231
  /**
219
232
  * Tests if a is boolean
220
233
  */
221
- toBeBoolean(): void {
222
- this._resolve(isBoolean<T>(), "toBeBoolean", q(nameof<T>()), q("boolean"));
234
+ toBeBoolean(message: string = ""): void {
235
+ this._resolve(
236
+ isBoolean<T>(),
237
+ "toBeBoolean",
238
+ q(nameof<T>()),
239
+ q("boolean"),
240
+ message,
241
+ );
223
242
  }
224
243
 
225
244
  /**
226
245
  * Tests if a is array
227
246
  */
228
- toBeArray(): void {
229
- this._resolve(isArray<T>(), "toBeArray", q(nameof<T>()), q("Array<any>"));
247
+ toBeArray(message: string = ""): void {
248
+ this._resolve(
249
+ isArray<T>(),
250
+ "toBeArray",
251
+ q(nameof<T>()),
252
+ q("Array<any>"),
253
+ message,
254
+ );
230
255
  }
231
256
 
232
257
  /**
233
258
  * Tests if a is number
234
259
  */
235
- toBeNumber(): void {
260
+ toBeNumber(message: string = ""): void {
236
261
  this._resolve(
237
262
  isFloat<T>() || isInteger<T>(),
238
263
  "toBeNumber",
239
264
  q(nameof<T>()),
240
265
  q("number"),
266
+ message,
241
267
  );
242
268
  }
243
269
 
244
270
  /**
245
271
  * Tests if a is integer
246
272
  */
247
- toBeInteger(): void {
248
- this._resolve(isInteger<T>(), "toBeInteger", q(nameof<T>()), q("integer"));
273
+ toBeInteger(message: string = ""): void {
274
+ this._resolve(
275
+ isInteger<T>(),
276
+ "toBeInteger",
277
+ q(nameof<T>()),
278
+ q("integer"),
279
+ message,
280
+ );
249
281
  }
250
282
 
251
283
  /**
252
284
  * Tests if a is float
253
285
  */
254
- toBeFloat(): void {
255
- this._resolve(isFloat<T>(), "toBeFloat", q(nameof<T>()), q("float"));
286
+ toBeFloat(message: string = ""): void {
287
+ this._resolve(
288
+ isFloat<T>(),
289
+ "toBeFloat",
290
+ q(nameof<T>()),
291
+ q("float"),
292
+ message,
293
+ );
256
294
  }
257
295
 
258
296
  /**
259
297
  * Tests if a is finite
260
298
  */
261
- toBeFinite(): void {
299
+ toBeFinite(message: string = ""): void {
262
300
  // @ts-ignore
263
301
  const passed = (isFloat<T>() || isInteger<T>()) && isFinite(this._left);
264
- this._resolve(passed, "toBeFinite", q("Infinity"), q("Finite"));
302
+ this._resolve(passed, "toBeFinite", q("Infinity"), q("Finite"), message);
265
303
  }
266
304
 
267
305
  /**
268
306
  * Tests if a value is truthy
269
307
  */
270
- toBeTruthy(): void {
308
+ toBeTruthy(message: string = ""): void {
271
309
  this._resolve(
272
310
  isTruthy<T>(this._left),
273
311
  "toBeTruthy",
274
312
  q("falsy"),
275
313
  q("truthy"),
314
+ message,
276
315
  );
277
316
  }
278
317
 
279
318
  /**
280
319
  * Tests if a value is falsy
281
320
  */
282
- toBeFalsy(): void {
321
+ toBeFalsy(message: string = ""): void {
283
322
  this._resolve(
284
323
  !isTruthy<T>(this._left),
285
324
  "toBeFalsy",
286
325
  q("truthy"),
287
326
  q("falsy"),
327
+ message,
288
328
  );
289
329
  }
290
330
 
291
331
  /**
292
332
  * Tests if a floating-point number is close to expected
293
333
  */
294
- toBeCloseTo(expected: T, precision: i32 = 2): void {
334
+ toBeCloseTo(expected: T, precision: i32 = 2, message: string = ""): void {
295
335
  if (!isFloat<T>() && !isInteger<T>())
296
336
  ERROR("toBeCloseTo() can only be used on number types!");
297
337
  const factor = Math.pow(10, precision as f64);
@@ -302,67 +342,80 @@ export class Expectation<T> extends Tests {
302
342
  "toBeCloseTo",
303
343
  visualize<T>(this._left),
304
344
  visualize<T>(expected),
345
+ message,
305
346
  );
306
347
  }
307
348
 
308
349
  /**
309
350
  * Tests if a string contains substring
310
351
  */
311
- toMatch(value: string): void {
352
+ toMatch(value: string, message: string = ""): void {
312
353
  if (!isString<T>()) ERROR("toMatch() can only be used on string types!");
313
354
  // @ts-ignore
314
355
  const passed = this._left.indexOf(value) >= 0;
315
356
  // @ts-ignore
316
- this._resolve(passed, "toMatch", q(this._left as string), q(value));
357
+ this._resolve(
358
+ passed,
359
+ "toMatch",
360
+ q(this._left as string),
361
+ q(value),
362
+ message,
363
+ );
317
364
  }
318
365
 
319
366
  /**
320
367
  * Tests if a string starts with the provided prefix.
321
368
  */
322
- toStartWith(value: string): void {
369
+ toStartWith(value: string, message: string = ""): void {
323
370
  if (!isString<T>())
324
371
  ERROR("toStartWith() can only be used on string types!");
325
372
  // @ts-ignore
326
373
  const left = this._left as string;
327
374
  const passed = left.indexOf(value) == 0;
328
- this._resolve(passed, "toStartWith", q(left), q(value));
375
+ this._resolve(passed, "toStartWith", q(left), q(value), message);
329
376
  }
330
377
 
331
378
  /**
332
379
  * Tests if a string ends with the provided suffix.
333
380
  */
334
- toEndWith(value: string): void {
381
+ toEndWith(value: string, message: string = ""): void {
335
382
  if (!isString<T>()) ERROR("toEndWith() can only be used on string types!");
336
383
  // @ts-ignore
337
384
  const left = this._left as string;
338
385
  const idx = left.lastIndexOf(value);
339
386
  const passed = idx >= 0 && idx + value.length == left.length;
340
- this._resolve(passed, "toEndWith", q(left), q(value));
387
+ this._resolve(passed, "toEndWith", q(left), q(value), message);
341
388
  }
342
389
 
343
390
  /**
344
391
  * Tests if an array has length x
345
392
  */
346
- toHaveLength(value: i32): void {
393
+ toHaveLength(value: i32, message: string = ""): void {
347
394
  // @ts-ignore
348
395
  const leftLen = this._left.length as i32;
349
396
  // @ts-ignore
350
397
  const passed = isArray<T>() && leftLen == value;
351
- this._resolve(passed, "toHaveLength", leftLen.toString(), value.toString());
398
+ this._resolve(
399
+ passed,
400
+ "toHaveLength",
401
+ leftLen.toString(),
402
+ value.toString(),
403
+ message,
404
+ );
352
405
  }
353
406
 
354
407
  /**
355
408
  * Tests if an array or string contains a value
356
409
  */
357
410
  // @ts-ignore
358
- toContain(value: valueof<T>): void {
411
+ toContain(value: valueof<T>, message: string = ""): void {
359
412
  if (isString<T>()) {
360
413
  // @ts-ignore
361
414
  const left = this._left as string;
362
415
  // @ts-ignore
363
416
  const needle = value as string;
364
417
  const passed = left.indexOf(needle) >= 0;
365
- this._resolve(passed, "toContain", q(left), q(needle));
418
+ this._resolve(passed, "toContain", q(left), q(needle), message);
366
419
  return;
367
420
  }
368
421
 
@@ -374,6 +427,7 @@ export class Expectation<T> extends Tests {
374
427
  "toContain",
375
428
  stringifyValue<T>(this._left),
376
429
  stringifyValue<valueof<T>>(value),
430
+ message,
377
431
  );
378
432
  return;
379
433
  }
@@ -385,28 +439,28 @@ export class Expectation<T> extends Tests {
385
439
  * Alias for toContain().
386
440
  */
387
441
  // @ts-ignore
388
- toContains(value: valueof<T>): void {
389
- this.toContain(value);
442
+ toContains(value: valueof<T>, message: string = ""): void {
443
+ this.toContain(value, message);
390
444
  }
391
445
 
392
446
  /**
393
447
  * Tests if serialized value matches stored snapshot.
394
448
  */
395
- toMatchSnapshot(name: string = ""): void {
449
+ toMatchSnapshot(name: string = "", message: string = ""): void {
396
450
  let key = name.length
397
451
  ? namedSnapshotKey(this._snapshotKey, name)
398
452
  : nextUnnamedSnapshotKey(this._snapshotKey);
399
453
 
400
454
  const actual = stringifyValue<T>(this._left);
401
455
  const res = snapshotAssert(key, actual);
402
- this._resolve(res.ok, "toMatchSnapshot", actual, res.expected);
456
+ this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
403
457
  }
404
458
 
405
459
  /**
406
460
  * Delegates throw assertions to try-as when available.
407
461
  * If try-as is unavailable, this matcher is disabled and warns once.
408
462
  */
409
- toThrow(): void {
463
+ toThrow(message: string = ""): void {
410
464
  // @ts-ignore
411
465
  if (!isDefined(AS_TEST_TRY_AS)) {
412
466
  if (!warnedToThrowDisabled) {
@@ -415,7 +469,7 @@ export class Expectation<T> extends Tests {
415
469
  );
416
470
  warnedToThrowDisabled = true;
417
471
  }
418
- this._resolve(true, "toThrow", q("disabled"), q("disabled"));
472
+ this._resolve(true, "toThrow", q("disabled"), q("disabled"), message);
419
473
  return;
420
474
  }
421
475
 
@@ -425,13 +479,13 @@ export class Expectation<T> extends Tests {
425
479
  // @ts-ignore
426
480
  __ExceptionState.Failures--;
427
481
  }
428
- this._resolve(passed, "toThrow", q("throws"), q("throws"));
482
+ this._resolve(passed, "toThrow", q("throws"), q("throws"), message);
429
483
  }
430
484
 
431
485
  /**
432
486
  * Tests for equality
433
487
  */
434
- toBe(equals: T): void {
488
+ toBe(equals: T, message: string = ""): void {
435
489
  const passed = this._left === equals;
436
490
 
437
491
  this._resolve(
@@ -439,32 +493,35 @@ export class Expectation<T> extends Tests {
439
493
  "toBe",
440
494
  stringifyValue<T>(this._left),
441
495
  stringifyValue<T>(equals),
496
+ message,
442
497
  );
443
498
  }
444
499
 
445
500
  /**
446
501
  * Tests for deep equality
447
502
  */
448
- toEqual(equals: T): void {
503
+ toEqual(equals: T, message: string = ""): void {
449
504
  const passed = valueEquals<T>(this._left, equals, false);
450
505
  this._resolve(
451
506
  passed,
452
507
  "toEqual",
453
508
  stringifyValue<T>(this._left),
454
509
  stringifyValue<T>(equals),
510
+ message,
455
511
  );
456
512
  }
457
513
 
458
514
  /**
459
515
  * Tests for strict deep equality
460
516
  */
461
- toStrictEqual(equals: T): void {
517
+ toStrictEqual(equals: T, message: string = ""): void {
462
518
  const passed = valueEquals<T>(this._left, equals, true);
463
519
  this._resolve(
464
520
  passed,
465
521
  "toStrictEqual",
466
522
  stringifyValue<T>(this._left),
467
523
  stringifyValue<T>(equals),
524
+ message,
468
525
  );
469
526
  }
470
527
  }
@@ -79,14 +79,24 @@ export class FuzzSeed {
79
79
 
80
80
  i8(options: IntegerOptions<i8> | null = null): i8 {
81
81
  if (options == null) {
82
- return this.nextI8InRange(i8.MIN_VALUE, i8.MAX_VALUE, EMPTY_I8_EXCLUDE, false);
82
+ return this.nextI8InRange(
83
+ i8.MIN_VALUE,
84
+ i8.MAX_VALUE,
85
+ EMPTY_I8_EXCLUDE,
86
+ false,
87
+ );
83
88
  }
84
89
  return this.nextI8InRange(options.min, options.max, options.exclude, true);
85
90
  }
86
91
 
87
92
  u8(options: IntegerOptions<u8> | null = null): u8 {
88
93
  if (options == null) {
89
- return this.nextU8InRange(u8.MIN_VALUE, u8.MAX_VALUE, EMPTY_U8_EXCLUDE, false);
94
+ return this.nextU8InRange(
95
+ u8.MIN_VALUE,
96
+ u8.MAX_VALUE,
97
+ EMPTY_U8_EXCLUDE,
98
+ false,
99
+ );
90
100
  }
91
101
  return this.nextU8InRange(options.min, options.max, options.exclude, true);
92
102
  }
@@ -186,7 +196,9 @@ export class FuzzSeed {
186
196
  if (!exclude.length) {
187
197
  for (let i = 0; i < length; i++) {
188
198
  unchecked(
189
- (out[i] = <u8>unchecked(include[this.nextRange(0, include.length - 1)])),
199
+ (out[i] = <u8>(
200
+ unchecked(include[this.nextRange(0, include.length - 1)])
201
+ )),
190
202
  );
191
203
  }
192
204
  return out;
@@ -299,7 +311,9 @@ export class FuzzSeed {
299
311
  return <i32>this.nextU32();
300
312
  }
301
313
  if (!exclude.length) {
302
- return max <= min ? min : min + <i32>(this.nextU32() % <u32>(max - min + 1));
314
+ return max <= min
315
+ ? min
316
+ : min + <i32>(this.nextU32() % <u32>(max - min + 1));
303
317
  }
304
318
  for (let attempts = 0; attempts < 1024; attempts++) {
305
319
  const value =
@@ -324,12 +338,16 @@ export class FuzzSeed {
324
338
  const right = <i32>max;
325
339
  if (!exclude.length) {
326
340
  return <i8>(
327
- right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
341
+ (right <= left
342
+ ? left
343
+ : left + <i32>(this.nextU32() % <u32>(right - left + 1)))
328
344
  );
329
345
  }
330
346
  for (let attempts = 0; attempts < 1024; attempts++) {
331
347
  const value = <i8>(
332
- right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
348
+ (right <= left
349
+ ? left
350
+ : left + <i32>(this.nextU32() % <u32>(right - left + 1)))
333
351
  );
334
352
  if (!containsValue<i8>(exclude, value)) return value;
335
353
  }
@@ -350,11 +368,13 @@ export class FuzzSeed {
350
368
  const left = <u32>min;
351
369
  const right = <u32>max;
352
370
  if (!exclude.length) {
353
- return <u8>(right <= left ? left : left + (this.nextU32() % (right - left + 1)));
371
+ return <u8>(
372
+ (right <= left ? left : left + (this.nextU32() % (right - left + 1)))
373
+ );
354
374
  }
355
375
  for (let attempts = 0; attempts < 1024; attempts++) {
356
376
  const value = <u8>(
357
- right <= left ? left : left + (this.nextU32() % (right - left + 1))
377
+ (right <= left ? left : left + (this.nextU32() % (right - left + 1)))
358
378
  );
359
379
  if (!containsValue<u8>(exclude, value)) return value;
360
380
  }
@@ -376,12 +396,16 @@ export class FuzzSeed {
376
396
  const right = <i32>max;
377
397
  if (!exclude.length) {
378
398
  return <i16>(
379
- right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
399
+ (right <= left
400
+ ? left
401
+ : left + <i32>(this.nextU32() % <u32>(right - left + 1)))
380
402
  );
381
403
  }
382
404
  for (let attempts = 0; attempts < 1024; attempts++) {
383
405
  const value = <i16>(
384
- right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
406
+ (right <= left
407
+ ? left
408
+ : left + <i32>(this.nextU32() % <u32>(right - left + 1)))
385
409
  );
386
410
  if (!containsValue<i16>(exclude, value)) return value;
387
411
  }
@@ -402,11 +426,13 @@ export class FuzzSeed {
402
426
  const left = <u32>min;
403
427
  const right = <u32>max;
404
428
  if (!exclude.length) {
405
- return <u16>(right <= left ? left : left + (this.nextU32() % (right - left + 1)));
429
+ return <u16>(
430
+ (right <= left ? left : left + (this.nextU32() % (right - left + 1)))
431
+ );
406
432
  }
407
433
  for (let attempts = 0; attempts < 1024; attempts++) {
408
434
  const value = <u16>(
409
- right <= left ? left : left + (this.nextU32() % (right - left + 1))
435
+ (right <= left ? left : left + (this.nextU32() % (right - left + 1)))
410
436
  );
411
437
  if (!containsValue<u16>(exclude, value)) return value;
412
438
  }
@@ -448,9 +474,7 @@ export class FuzzSeed {
448
474
  const left = this.toOrderedU64(min);
449
475
  const right = this.toOrderedU64(max);
450
476
  if (!exclude.length) {
451
- return this.fromOrderedU64(
452
- left + this.nextU64Offset(left, right),
453
- );
477
+ return this.fromOrderedU64(left + this.nextU64Offset(left, right));
454
478
  }
455
479
  for (let attempts = 0; attempts < 1024; attempts++) {
456
480
  const value = this.fromOrderedU64(left + this.nextU64Offset(left, right));
@@ -529,7 +553,7 @@ export class FuzzSeed {
529
553
  }
530
554
 
531
555
  private toOrderedU64(value: i64): u64 {
532
- return <u64>value ^ I64_SIGN_MASK;
556
+ return (<u64>value) ^ I64_SIGN_MASK;
533
557
  }
534
558
 
535
559
  private fromOrderedU64(value: u64): i64 {
@@ -543,13 +567,11 @@ const DIGIT_ALPHABET: i32[] = rangeChars(48, 57);
543
567
  const HEX_ALPHABET: i32[] = DIGIT_ALPHABET.concat(rangeChars(97, 102));
544
568
  const ALNUM_ALPHABET: i32[] = ALPHA_ALPHABET.concat(DIGIT_ALPHABET);
545
569
  const BASE64_ALPHABET: i32[] = ALPHA_ALPHABET.concat(DIGIT_ALPHABET).concat([
546
- 43,
547
- 47,
548
- 61,
570
+ 43, 47, 61,
549
571
  ]);
550
- const IDENTIFIER_ALPHABET: i32[] = [95].concat(ALPHA_ALPHABET).concat(
551
- DIGIT_ALPHABET,
552
- );
572
+ const IDENTIFIER_ALPHABET: i32[] = [95]
573
+ .concat(ALPHA_ALPHABET)
574
+ .concat(DIGIT_ALPHABET);
553
575
  const WHITESPACE_ALPHABET: i32[] = [9, 10, 13, 32];
554
576
 
555
577
  export abstract class FuzzerBase {
@@ -1113,7 +1135,6 @@ function removeFirst(values: i32[], needle: i32): void {
1113
1135
  if (index >= 0) values.splice(index, 1);
1114
1136
  }
1115
1137
 
1116
-
1117
1138
  function validateLengthRange(label: string, min: i32, max: i32): void {
1118
1139
  if (min < 0 || max < 0) panic();
1119
1140
  if (max < min) panic();
@@ -0,0 +1,113 @@
1
+ export function formatValue<T>(value: T, deep: boolean = false): string {
2
+ if (isNullable<T>() && changetype<usize>(value) == <usize>0) {
3
+ return "null";
4
+ }
5
+
6
+ if (isString<T>()) {
7
+ const text = value as string;
8
+ return deep ? "'" + text + "'" : text;
9
+ }
10
+
11
+ if (isBoolean<T>() || isInteger<T>() || isFloat<T>()) {
12
+ // @ts-expect-error: primitive formatting
13
+ return value.toString();
14
+ }
15
+
16
+ if (isArray<T>()) {
17
+ // @ts-expect-error: array-like handling
18
+ const values = value as valueof<T>[];
19
+ if (!values.length) return "[]";
20
+ let out = "[";
21
+ for (let i = 0; i < values.length; i++) {
22
+ if (i) out += ", ";
23
+ out += formatValue<valueof<T>>(unchecked(values[i]), true);
24
+ }
25
+ out += "]";
26
+ return out;
27
+ }
28
+
29
+ if (value instanceof Map) {
30
+ // @ts-expect-error: generic runtime access
31
+ const keys = value.keys();
32
+ if (!keys.length) return "Map(0) {}";
33
+ // @ts-expect-error: generic runtime access
34
+ const values = value.values();
35
+ let out = "Map(" + keys.length.toString() + ") { ";
36
+ for (let i = 0; i < keys.length; i++) {
37
+ if (i) out += ", ";
38
+ out += formatValue(
39
+ changetype<valueof<typeof keys>>(unchecked(keys[i])),
40
+ true,
41
+ );
42
+ out += " => ";
43
+ out += formatValue(
44
+ changetype<valueof<typeof values>>(unchecked(values[i])),
45
+ true,
46
+ );
47
+ }
48
+ out += " }";
49
+ return out;
50
+ }
51
+
52
+ if (value instanceof Set) {
53
+ // @ts-expect-error: generic runtime access
54
+ const values = value.values();
55
+ if (!values.length) return "Set(0) {}";
56
+ let out = "Set(" + values.length.toString() + ") { ";
57
+ for (let i = 0; i < values.length; i++) {
58
+ if (i) out += ", ";
59
+ out += formatValue(
60
+ changetype<valueof<typeof values>>(unchecked(values[i])),
61
+ true,
62
+ );
63
+ }
64
+ out += " }";
65
+ return out;
66
+ }
67
+
68
+ if (isManaged<T>()) {
69
+ // @ts-expect-error: custom serializer when provided
70
+ if (isDefined(value.__as_test_json)) {
71
+ // @ts-expect-error: dynamic method dispatch
72
+ return value.__as_test_json();
73
+ }
74
+ }
75
+
76
+ return nameof<T>();
77
+ }
78
+
79
+
80
+ @inline
81
+ export function colorText(format: i32[], text: string): string {
82
+ return `\u001b[${format[0].toString()}m${text}\u001b[${format[1].toString()}m`;
83
+ }
84
+
85
+
86
+ @inline
87
+ export function red(text: string): string {
88
+ return colorText([31, 39], text);
89
+ }
90
+
91
+
92
+ @inline
93
+ export function green(text: string): string {
94
+ return colorText([32, 39], text);
95
+ }
96
+
97
+
98
+ @inline
99
+ export function bgRed(text: string): string {
100
+ return colorText([41, 49], text);
101
+ }
102
+
103
+
104
+ @inline
105
+ export function bgGreen(text: string): string {
106
+ return colorText([42, 49], text);
107
+ }
108
+
109
+
110
+ @inline
111
+ export function bold(text: string): string {
112
+ return colorText([1, 22], text);
113
+ }