as-test 1.3.0 → 1.4.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 CHANGED
@@ -1,5 +1,46 @@
1
1
  # Change Log
2
2
 
3
+ ## 2026-05-26 - v1.4.0
4
+
5
+ ### Dependency-free value serialization (json-as removed)
6
+
7
+ - feat: as-test no longer depends on or auto-includes `json-as`. Value serialization for assertion reports, snapshots, and `log()` is now handled by a small in-tree stringifier (`assembly/src/stringify.ts`), so `npm install --save-dev as-test` is all you need — `json-as` is no longer a peer dependency.
8
+ - feat: `stringify<T>` renders a broad set of built-in types as JSON: booleans, integers, floats, strings (RFC 8259 escaping, including UTF-16 surrogate handling), `null`, `Date` (quoted ISO-8601), `ArrayBuffer` (array of unsigned byte values), typed arrays / `ArrayBufferView` (element array), `Array`, `StaticArray`, `Set` (value array), and `Map` (JSON object; non-string keys are coerced to quoted strings). Classes render via a transform-generated `toJSON()` or a `"<TypeName>"` placeholder.
9
+ - chore: removed the json-as peer-advisor (`transform/lib/peer-advisor.js`) and the json-as transform-passthrough integration test. Classes decorated `@json`/`@serializable` are skipped by the toJSON injector, so users who want json-as serialization can still add their own `toJSON()` and wire up `--transform json-as`.
10
+
11
+ ### Structural deep equality for matchers
12
+
13
+ - feat: `.toBe()`, `.toEqual()`, and `.toStrictEqual()` now compare by structure rather than by reference. The EqualsTransform (`transform/lib/equals.js`) synthesises an `__as_test_equals(...)` method for every class that appears as an `expect()`/matcher operand, including nested classes reachable through their fields; the runtime entry point (`assembly/src/reflect.ts`'s `reflectEquals`) handles primitives, nullables, arrays, managed dispatch, and cycle detection, with a strict mode that also checks runtime type ids.
14
+ - feat: hand-written `__as_test_equals` methods are left untouched by the transform, and inheritance chains are supported via a super-call ignore-list pattern.
15
+
16
+ ### Surfacing `log()` output
17
+
18
+ - feat: after a run, as-test now reports how many `log()` lines were captured and writes them to a single aggregated `.as-test/logs/latest.log`, e.g. `19 logs captured → .as-test/logs/latest.log`. The file groups logs by spec and de-duplicates identical output across modes, tagging each block with the modes that produced it: `[LOG] log.spec.ts (node:bindings, node:wasi):`.
19
+ - feat: `ast test --show-logs` (also on `ast run`) prints the captured logs as a clean grouped block at the end of the run instead of pointing at the file. In a normal run logs stay quiet (just the hint line); `--verbose` and non-TTY output still stream them inline as before.
20
+ - fix: the per-spec readable log's `Log:` section was always empty — it read a `value`/`message` field that never existed on log entries (the field is `text`). It now contains the captured logs.
21
+
22
+ ### Suite / test counting
23
+
24
+ - change: every grouping block — `describe`, `test`, `it`, `only` (and their skip variants) — now counts as a **suite**, and each `expect()` assertion counts as a **test**. Previously `test`/`it`/`only` weren't counted as suites and an empty one was tallied as a single test, so an `it()` that contained assertions reported `Suites: 0`. As a result a top-level `it()`/`test()` failure now also appears in the end-of-run failure summary (with its location), instead of only the inline assertion line.
25
+ - fix: nested grouping blocks now actually nest. A `describe`/`it`/`test` declared inside another block is parented to the block whose callback is running (`current_suite`) rather than to a stale depth-indexed stack, so `describe`-in-`describe` no longer flattens (the inner block's children were previously attached to the outer block, leaving the inner one empty). The unused `suites`/`depth` registration globals were removed.
26
+
27
+ ### Scoped beforeEach / afterEach
28
+
29
+ - feat: `beforeEach` and `afterEach` take an optional second argument listing the suite kinds they fire around — `beforeEach(() => {}, ["describe", "test"])`. With no argument the behavior is unchanged: hooks run around test cases (`test` / `it` / `only` and skip variants) and not around grouping blocks like `describe`.
30
+
31
+ ### Watch mode + dependency graph
32
+
33
+ - feat: `ast test --watch` (`-w`) tracks a per-spec dependency graph (`cli/dependency-graph.ts`) built from the files `asc` actually loads during each build, so editing a shared helper re-runs only the specs that depend on it instead of the whole suite. `asc`'s bundled stdlib and the on-disk package are excluded from the graph to keep it small.
34
+ - feat: press `w` in watch mode to toggle auto-run off (manual invocation) and back on. While paused, edits are remembered but not run — invoke runs yourself with `a` (all) or `space` (retry failing); the footer shows how many changes are pending. Resuming re-runs everything if anything changed while paused.
35
+
36
+ ### Breaking
37
+
38
+ - chore: `json-as` is no longer installed or auto-included by as-test. Projects that relied on as-test pulling in `json-as`, or on json-as-shaped serialization output in reports/snapshots, should install `json-as` themselves and add a `toJSON()` to the relevant classes. Existing snapshots whose serialized form changed will need `--overwrite-snapshots` once.
39
+
40
+ ### Tooling
41
+
42
+ - ci: integration tests updated for the new serialization/equality paths (`tests/coverage-points.test.mjs`, new `tests/try-as-dedupe.test.mjs` and `tests/dependency-graph.test.mjs`).
43
+
3
44
  ## 2026-05-22 - v1.3.0
4
45
 
5
46
  ### `features` config array + arbitrary `--enable` passthrough
package/README.md CHANGED
@@ -39,12 +39,9 @@ That gives you a basic config file, a sample test, and optionally a sample fuzze
39
39
  If you already have a project and just want the package:
40
40
 
41
41
  ```bash
42
- npm install --save-dev as-test json-as
42
+ npm install --save-dev as-test
43
43
  ```
44
44
 
45
- `json-as` is a required peer dependency (used for value serialization in
46
- assertions, snapshots, and `log()`)
47
-
48
45
  ## Docs
49
46
 
50
47
  Full documentation lives at:
package/assembly/index.ts CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  sendFileStart,
15
15
  sendReport,
16
16
  } from "./util/wipc";
17
- import { JSON } from "json-as/assembly";
17
+ import { escape, stringify } from "./src/stringify";
18
18
  import { bold, green, red } from "./util/format";
19
19
  import {
20
20
  createFuzzer,
@@ -36,7 +36,8 @@ export {
36
36
  IntegerOptions,
37
37
  StringOptions,
38
38
  } from "./src/fuzz";
39
- export { __as_test_deep_equal } from "./src/expectation";
39
+ export { reflectEquals } from "./src/reflect";
40
+ export { stringify as __as_test_stringify } from "./src/stringify";
40
41
 
41
42
  let entrySuites: Suite[] = [];
42
43
  let entryFuzzers: FuzzerBase[] = [];
@@ -56,10 +57,6 @@ const FILE = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown";
56
57
  string
57
58
  >();
58
59
  // @ts-ignore
59
- @global let suites: Suite[] = [];
60
- // @ts-ignore
61
- @global let depth: i32 = -1;
62
- // @ts-ignore
63
60
  @global let current_suite: Suite | null = null;
64
61
  // @ts-ignore
65
62
  let before_all_callback: (() => void) | null = null;
@@ -68,6 +65,11 @@ let after_all_callback: (() => void) | null = null;
68
65
 
69
66
  export let before_each_callback: (() => void) | null = null;
70
67
  export let after_each_callback: (() => void) | null = null;
68
+ // Suite kinds each hook fires before/after. `null` = the default set (test
69
+ // cases: test / it / only and their skip variants), excluding grouping blocks
70
+ // like `describe`. A caller-supplied list overrides this.
71
+ export let before_each_kinds: string[] | null = null;
72
+ export let after_each_kinds: string[] | null = null;
71
73
  let __test_options!: RunOptions;
72
74
 
73
75
  /**
@@ -240,7 +242,7 @@ export function xexpect<T>(
240
242
  */
241
243
  export function log<T>(data: T): void {
242
244
  if (!__as_test_log_is_enabled()) return;
243
- __as_test_log_serialized(JSON.stringify<T>(data));
245
+ __as_test_log_serialized(stringify<T>(data));
244
246
  }
245
247
 
246
248
  export function __as_test_log_is_enabled(): bool {
@@ -276,21 +278,43 @@ export function afterAll(callback: () => void): void {
276
278
  }
277
279
 
278
280
  /**
279
- * Registers a callback function to be executed before each test case is run.
281
+ * Registers a callback to run before each matching block.
282
+ *
283
+ * By default it runs before each test case (`test` / `it` / `only`, plus their
284
+ * skip variants) and NOT before grouping blocks like `describe`. Pass `kinds`
285
+ * to run before exactly the listed suite kinds instead, e.g.
286
+ * `beforeEach(() => {}, ["describe", "test"])`.
280
287
  *
281
- * @param {() => void} callback - The function to be executed before each test case.
288
+ * @param {() => void} callback - The function to run.
289
+ * @param {string[] | null} kinds - Suite kinds to run before, or `null` for the
290
+ * default test-case kinds.
282
291
  */
283
- export function beforeEach(callback: () => void): void {
292
+ export function beforeEach(
293
+ callback: () => void,
294
+ kinds: string[] | null = null,
295
+ ): void {
284
296
  before_each_callback = callback;
297
+ before_each_kinds = kinds;
285
298
  }
286
299
 
287
300
  /**
288
- * Registers a callback function to be executed after each test case is run.
301
+ * Registers a callback to run after each matching block.
289
302
  *
290
- * @param {() => void} callback - The function to be executed after each test case.
303
+ * By default it runs after each test case (`test` / `it` / `only`, plus their
304
+ * skip variants) and NOT after grouping blocks like `describe`. Pass `kinds`
305
+ * to run after exactly the listed suite kinds instead, e.g.
306
+ * `afterEach(() => {}, ["describe", "test"])`.
307
+ *
308
+ * @param {() => void} callback - The function to run.
309
+ * @param {string[] | null} kinds - Suite kinds to run after, or `null` for the
310
+ * default test-case kinds.
291
311
  */
292
- export function afterEach(callback: () => void): void {
312
+ export function afterEach(
313
+ callback: () => void,
314
+ kinds: string[] | null = null,
315
+ ): void {
293
316
  after_each_callback = callback;
317
+ after_each_kinds = kinds;
294
318
  }
295
319
 
296
320
  /**
@@ -374,10 +398,7 @@ export function run(options: RunOptions = new RunOptions()): void {
374
398
  for (let i = 0; i < entrySuites.length; i++) {
375
399
  // @ts-ignore
376
400
  const suite = unchecked(entrySuites[i]);
377
- suites = [suite];
378
-
379
- current_suite = suite;
380
- depth = -1;
401
+ // @ts-ignore: current_suite is a @global; null between top-level suites
381
402
  current_suite = null;
382
403
 
383
404
  if (hasTopLevelOnly && suite.kind != "only") {
@@ -393,8 +414,7 @@ export function run(options: RunOptions = new RunOptions()): void {
393
414
  fileVerdict = "skip";
394
415
  }
395
416
 
396
- suites = [];
397
- depth = -1;
417
+ // @ts-ignore: current_suite is a @global
398
418
  current_suite = null;
399
419
  }
400
420
  time.end = performance.now();
@@ -402,7 +422,7 @@ export function run(options: RunOptions = new RunOptions()): void {
402
422
  const report = new FileReport();
403
423
  report.suites = entrySuites;
404
424
  report.coverage = collectCoverage();
405
- sendReport(report.serialize());
425
+ sendReport(report.toJSON());
406
426
  }
407
427
 
408
428
  function containsOnlySuites(values: Suite[]): bool {
@@ -422,11 +442,11 @@ class FuzzConfig {
422
442
  class FuzzReport {
423
443
  fuzzers: FuzzerResult[] = [];
424
444
 
425
- serialize(): string {
445
+ toJSON(): string {
426
446
  let out = '{"fuzzers":[';
427
447
  for (let i = 0; i < this.fuzzers.length; i++) {
428
448
  if (i) out += ",";
429
- out += unchecked(this.fuzzers[i]).serialize();
449
+ out += unchecked(this.fuzzers[i]).toJSON();
430
450
  }
431
451
  out += "]}";
432
452
  return out;
@@ -443,7 +463,7 @@ function runFuzzers(): void {
443
463
  const result = fuzzer.run(config.seed, resolveFuzzerRuns(fuzzer, config));
444
464
  report.fuzzers.push(result);
445
465
  }
446
- sendReport(report.serialize());
466
+ sendReport(report.toJSON());
447
467
  }
448
468
 
449
469
  function requestFuzzConfig(): FuzzConfig {
@@ -482,20 +502,19 @@ function registerSuite(
482
502
  kind: string,
483
503
  ): void {
484
504
  const suite = new Suite(description, callback, kind);
485
- if (depth >= 0) {
486
- const _suite = suites[depth];
487
- if (_suite.depth == depth) {
488
- _suite.addSuite(suite);
489
- return;
490
- }
491
- suite.depth = ++depth;
492
- suites.push(suite);
505
+ // Callbacks run lazily during the run phase, and `current_suite` is always
506
+ // the suite whose callback is currently executing (the same reference
507
+ // `expect()`/`log()` resolve against). So a describe/test/it registered from
508
+ // inside another block nests under it — including describe-in-describe.
509
+ // `current_suite` is null only at collection time, i.e. a top-level suite.
510
+ const parent = current_suite;
511
+ if (parent !== null) {
512
+ parent.addSuite(suite);
493
513
  return;
494
514
  }
495
515
 
496
516
  suite.file = FILE;
497
517
  entrySuites.push(suite);
498
- suites.push(suite);
499
518
  }
500
519
 
501
520
  function resolveExpectationSuite(): Suite {
@@ -519,7 +538,7 @@ class CoverageReport {
519
538
  percent: f64 = 100.0;
520
539
  points: CoveragePointReport[] = [];
521
540
 
522
- serialize(): string {
541
+ toJSON(): string {
523
542
  return (
524
543
  '{"total":' +
525
544
  this.total.toString() +
@@ -548,26 +567,26 @@ class CoveragePointReport {
548
567
  scopeName: string = "";
549
568
  depth: i32 = 0;
550
569
 
551
- serialize(): string {
570
+ toJSON(): string {
552
571
  return (
553
572
  '{"hash":' +
554
- JSON.stringify<string>(this.hash) +
573
+ escape(this.hash) +
555
574
  ',"file":' +
556
- JSON.stringify<string>(this.file) +
575
+ escape(this.file) +
557
576
  ',"line":' +
558
577
  this.line.toString() +
559
578
  ',"column":' +
560
579
  this.column.toString() +
561
580
  ',"type":' +
562
- JSON.stringify<string>(this.type) +
581
+ escape(this.type) +
563
582
  ',"executed":' +
564
583
  (this.executed ? "true" : "false") +
565
584
  ',"parentHash":' +
566
- JSON.stringify<string>(this.parentHash) +
585
+ escape(this.parentHash) +
567
586
  ',"scopeKind":' +
568
- JSON.stringify<string>(this.scopeKind) +
587
+ escape(this.scopeKind) +
569
588
  ',"scopeName":' +
570
- JSON.stringify<string>(this.scopeName) +
589
+ escape(this.scopeName) +
571
590
  ',"depth":' +
572
591
  this.depth.toString() +
573
592
  "}"
@@ -579,12 +598,12 @@ class FileReport {
579
598
  suites: Suite[] = [];
580
599
  coverage: CoverageReport = new CoverageReport();
581
600
 
582
- serialize(): string {
601
+ toJSON(): string {
583
602
  return (
584
603
  '{"suites":' +
585
604
  serializeSuites(this.suites) +
586
605
  ',"coverage":' +
587
- this.coverage.serialize() +
606
+ this.coverage.toJSON() +
588
607
  "}"
589
608
  );
590
609
  }
@@ -595,7 +614,7 @@ function serializeSuites(values: Suite[]): string {
595
614
  let out = "[";
596
615
  for (let i = 0; i < values.length; i++) {
597
616
  if (i) out += ",";
598
- out += unchecked(values[i]).serialize();
617
+ out += unchecked(values[i]).toJSON();
599
618
  }
600
619
  out += "]";
601
620
  return out;
@@ -606,7 +625,7 @@ function serializeCoveragePoints(values: CoveragePointReport[]): string {
606
625
  let out = "[";
607
626
  for (let i = 0; i < values.length; i++) {
608
627
  if (i) out += ",";
609
- out += unchecked(values[i]).serialize();
628
+ out += unchecked(values[i]).toJSON();
610
629
  }
611
630
  out += "]";
612
631
  return out;
@@ -690,10 +709,10 @@ export class Result {
690
709
  out += ` ${this.arg1 + this.arg2} total\n`;
691
710
  return out;
692
711
  }
693
- serialize(): string {
712
+ toJSON(): string {
694
713
  return (
695
714
  '{"name":' +
696
- JSON.stringify<string>(this.name) +
715
+ escape(this.name) +
697
716
  ',"arg1":' +
698
717
  this.arg1.toString() +
699
718
  ',"arg2":' +
@@ -710,7 +729,7 @@ export class Time {
710
729
  return formatTime(this.end - this.start);
711
730
  }
712
731
 
713
- serialize(): string {
732
+ toJSON(): string {
714
733
  return (
715
734
  '{"start":' +
716
735
  this.start.toString() +
@@ -1,15 +1,39 @@
1
1
  import { visualize } from "../util/helpers";
2
2
  import { Tests } from "./tests";
3
- import { JSON } from "json-as/assembly";
4
3
  import { namedSnapshotKey, nextUnnamedSnapshotKey } from "..";
5
4
  import {
6
5
  sendAssertionFailure,
7
6
  sendWarning,
8
7
  snapshotAssert,
9
8
  } from "../util/wipc";
10
- import { OBJECT, TOTAL_OVERHEAD } from "~lib/rt/common";
9
+ import { reflectEquals } from "./reflect";
10
+ import { stringify, escape } from "./stringify";
11
11
 
12
12
  let warnedToThrowDisabled = false;
13
+ let warnedSafeStringifyMissing = false;
14
+
15
+ function safeStringify<T>(value: T): string {
16
+ if (
17
+ isManaged<T>() &&
18
+ !warnedSafeStringifyMissing &&
19
+ changetype<usize>(value) != 0 &&
20
+ !isString<T>() &&
21
+ !isArray<T>()
22
+ ) {
23
+ // @ts-expect-error: optional user-supplied serializer
24
+ if (!isDefined(value.toJSON)) {
25
+ sendWarning(
26
+ "Class " +
27
+ nameof<T>() +
28
+ " has no toJSON(): string method. Report values render as a `<" +
29
+ nameof<T>() +
30
+ ">` placeholder. Add toJSON() returning a JSON string to serialize.",
31
+ );
32
+ warnedSafeStringifyMissing = true;
33
+ }
34
+ }
35
+ return stringify<T>(value);
36
+ }
13
37
 
14
38
  export class Expectation<T> extends Tests {
15
39
  public verdict: string = "none";
@@ -18,13 +42,11 @@ export class Expectation<T> extends Tests {
18
42
 
19
43
  private _left: T;
20
44
 
21
- // @ts-ignore
45
+ // @ts-expect-error: used internally
22
46
  private _right: u64 = 0;
23
47
 
24
- // @ts-ignore
25
48
  private _not: boolean = false;
26
49
 
27
- // @ts-ignore
28
50
  private _skip: boolean = false;
29
51
 
30
52
  private _message: string = "";
@@ -492,8 +514,8 @@ export class Expectation<T> extends Tests {
492
514
  this._resolve(
493
515
  passed,
494
516
  "toContain",
495
- JSON.stringify<T>(this._left),
496
- JSON.stringify<valueof<T>>(value),
517
+ safeStringify<T>(this._left),
518
+ safeStringify<valueof<T>>(value),
497
519
  message,
498
520
  );
499
521
  return this;
@@ -519,7 +541,7 @@ export class Expectation<T> extends Tests {
519
541
  ? namedSnapshotKey(this._snapshotKey, name)
520
542
  : nextUnnamedSnapshotKey(this._snapshotKey);
521
543
 
522
- const actual = JSON.stringify<T>(this._left);
544
+ const actual = safeStringify<T>(this._left);
523
545
  const res = snapshotAssert(key, actual);
524
546
  this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
525
547
  return this;
@@ -591,108 +613,44 @@ export class Expectation<T> extends Tests {
591
613
  * Tests for equality
592
614
  */
593
615
  toBe(equals: T, message: string = ""): Expectation<T> {
594
- const passed = this._left === equals;
595
-
616
+ // Deep structural equality for managed values; `===` semantics for
617
+ // primitives and strings (reflectEquals does the dispatch).
618
+ // `toEqual` is kept below as a Jest-familiarity alias.
619
+ const passed = reflectEquals<T>(this._left, equals, [], false);
596
620
  this._resolve(
597
621
  passed,
598
622
  "toBe",
599
- JSON.stringify<T>(this._left),
600
- JSON.stringify<T>(equals),
623
+ safeStringify<T>(this._left),
624
+ safeStringify<T>(equals),
601
625
  message,
602
626
  );
603
627
  return this;
604
628
  }
605
629
 
606
630
  /**
607
- * Tests for deep equality
631
+ * Alias of `toBe` retained for Jest familiarity.
608
632
  */
609
633
  toEqual(equals: T, message: string = ""): Expectation<T> {
610
- const passed = valueEquals<T>(this._left, equals, false);
611
- this._resolve(
612
- passed,
613
- "toEqual",
614
- JSON.stringify<T>(this._left),
615
- JSON.stringify<T>(equals),
616
- message,
617
- );
618
- return this;
634
+ return this.toBe(equals, message);
619
635
  }
620
636
 
621
637
  /**
622
- * Tests for strict deep equality
638
+ * Like `toBe` but also requires the runtime type (rtId) of the
639
+ * operands to match for managed values.
623
640
  */
624
641
  toStrictEqual(equals: T, message: string = ""): Expectation<T> {
625
- const passed = valueEquals<T>(this._left, equals, true);
642
+ const passed = reflectEquals<T>(this._left, equals, [], true);
626
643
  this._resolve(
627
644
  passed,
628
645
  "toStrictEqual",
629
- JSON.stringify<T>(this._left),
630
- JSON.stringify<T>(equals),
646
+ safeStringify<T>(this._left),
647
+ safeStringify<T>(equals),
631
648
  message,
632
649
  );
633
650
  return this;
634
651
  }
635
652
  }
636
653
 
637
- function arrayEquals<T>(a: T[], b: T[], strict: bool): boolean {
638
- if (a.length != b.length) return false;
639
- for (let i = 0; i < a.length; i++) {
640
- if (!valueEquals<T>(unchecked(a[i]), unchecked(b[i]), strict)) {
641
- return false;
642
- }
643
- }
644
- return true;
645
- }
646
-
647
- function valueEquals<T>(left: T, right: T, strict: bool): bool {
648
- if (isBoolean<T>() || isString<T>() || isInteger<T>() || isFloat<T>()) {
649
- return left === right;
650
- }
651
-
652
- if (isNullable<T>()) {
653
- const leftPtr = changetype<usize>(left);
654
- const rightPtr = changetype<usize>(right);
655
- if (leftPtr == 0 || rightPtr == 0) return leftPtr == rightPtr;
656
- }
657
-
658
- if (isArray<T>()) {
659
- return arrayEquals<valueof<T>>(
660
- changetype<valueof<T>[]>(left),
661
- changetype<valueof<T>[]>(right),
662
- strict,
663
- );
664
- }
665
-
666
- if (isManaged<T>()) {
667
- return managedEquals<T>(left, right, strict);
668
- }
669
-
670
- abort(
671
- `Unsupported equality matcher for ${nameof<T>()}. Use toBe() for identity or compare fields explicitly.`,
672
- );
673
- return false;
674
- }
675
-
676
- export function __as_test_deep_equal<T>(left: T, right: T, strict: bool): bool {
677
- return valueEquals<T>(left, right, strict);
678
- }
679
-
680
- function managedEquals<T>(left: T, right: T, strict: bool): bool {
681
- const leftPtr = changetype<usize>(left);
682
- const rightPtr = changetype<usize>(right);
683
- if (leftPtr == rightPtr) return true;
684
- if (leftPtr == 0 || rightPtr == 0) return false;
685
-
686
- if (strict) {
687
- const leftObject = changetype<OBJECT>(leftPtr - TOTAL_OVERHEAD);
688
- const rightObject = changetype<OBJECT>(rightPtr - TOTAL_OVERHEAD);
689
- if (leftObject.rtId != rightObject.rtId) return false;
690
- }
691
-
692
- // @ts-ignore
693
- return left.__as_test_equals(right, strict);
694
- }
695
-
696
654
  function isTruthy<T>(value: T): bool {
697
655
  if (isBoolean<T>()) {
698
656
  return value as bool;
@@ -714,5 +672,5 @@ function isTruthy<T>(value: T): bool {
714
672
  }
715
673
 
716
674
  function q(value: string): string {
717
- return JSON.stringify<string>(value);
675
+ return escape(value);
718
676
  }
@@ -1,7 +1,7 @@
1
- import { JSON } from "json-as/assembly";
1
+ import { escape, stringify } from "./stringify";
2
2
 
3
3
  function quote(s: string): string {
4
- return JSON.stringify<string>(s);
4
+ return escape(s);
5
5
  }
6
6
 
7
7
  export class StringOptions {
@@ -615,7 +615,7 @@ export class FuzzerResult {
615
615
  public failureMessage: string = "";
616
616
  public failures: FuzzFailure[] = [];
617
617
 
618
- serialize(): string {
618
+ toJSON(): string {
619
619
  return (
620
620
  '{"name":"' +
621
621
  this.name +
@@ -653,7 +653,7 @@ export class FuzzFailure {
653
653
  public seed: u64 = 0;
654
654
  public input: string = "";
655
655
 
656
- serialize(): string {
656
+ toJSON(): string {
657
657
  return (
658
658
  '{"run":' +
659
659
  this.run.toString() +
@@ -881,7 +881,7 @@ export class Fuzzer1<A, R> extends FuzzerBase {
881
881
  );
882
882
  } else {
883
883
  this.generator(seed, (a: A): R => {
884
- __as_test_fuzz_input = "[" + JSON.stringify<A>(a) + "]";
884
+ __as_test_fuzz_input = "[" + stringify<A>(a) + "]";
885
885
  return changetype<(a: A) => R>(__fuzz_run1)(a);
886
886
  });
887
887
  }
@@ -952,7 +952,7 @@ export class Fuzzer2<A, B, R> extends FuzzerBase {
952
952
  } else {
953
953
  this.generator(seed, (a: A, b: B): R => {
954
954
  __as_test_fuzz_input =
955
- "[" + JSON.stringify<A>(a) + "," + JSON.stringify<B>(b) + "]";
955
+ "[" + stringify<A>(a) + "," + stringify<B>(b) + "]";
956
956
  return changetype<(a: A, b: B) => R>(__fuzz_run2)(a, b);
957
957
  });
958
958
  }
@@ -1026,11 +1026,11 @@ export class Fuzzer3<A, B, C, R> extends FuzzerBase {
1026
1026
  )(seed, (a: A, b: B, c: C): R => {
1027
1027
  __as_test_fuzz_input =
1028
1028
  "[" +
1029
- JSON.stringify<A>(a) +
1029
+ stringify<A>(a) +
1030
1030
  "," +
1031
- JSON.stringify<B>(b) +
1031
+ stringify<B>(b) +
1032
1032
  "," +
1033
- JSON.stringify<C>(c) +
1033
+ stringify<C>(c) +
1034
1034
  "]";
1035
1035
  return changetype<(a: A, b: B, c: C) => R>(__fuzz_run3)(a, b, c);
1036
1036
  });
@@ -1208,7 +1208,7 @@ function serializeFuzzFailures(values: FuzzFailure[]): string {
1208
1208
  let out = "[";
1209
1209
  for (let i = 0; i < values.length; i++) {
1210
1210
  if (i) out += ",";
1211
- out += unchecked(values[i]).serialize();
1211
+ out += unchecked(values[i]).toJSON();
1212
1212
  }
1213
1213
  out += "]";
1214
1214
  return out;
@@ -1,4 +1,4 @@
1
- import { JSON } from "json-as/assembly";
1
+ import { escape, stringify } from "./stringify";
2
2
 
3
3
  import { sendLog } from "../util/wipc";
4
4
 
@@ -14,14 +14,14 @@ export class Log {
14
14
  sendLog(this.file, this.depth, this.text);
15
15
  }
16
16
 
17
- serialize(): string {
17
+ toJSON(): string {
18
18
  return (
19
19
  '{"order":' +
20
20
  this.order.toString() +
21
21
  ',"depth":' +
22
22
  this.depth.toString() +
23
23
  ',"text":' +
24
- JSON.stringify<string>(this.text) +
24
+ escape(this.text) +
25
25
  "}"
26
26
  );
27
27
  }