as-test 0.5.1 → 0.5.3
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 +101 -0
- package/README.md +268 -3
- package/as-test.config.schema.json +171 -2
- package/assembly/coverage.ts +20 -0
- package/assembly/index.ts +196 -12
- package/assembly/src/expectation.ts +15 -29
- package/assembly/src/log.ts +13 -1
- package/assembly/src/suite.ts +53 -9
- package/assembly/src/tests.ts +25 -5
- package/assembly/util/helpers.ts +0 -1
- package/assembly/util/json.ts +78 -0
- package/bin/build.js +118 -33
- package/bin/index.js +524 -35
- package/bin/init.js +35 -10
- package/bin/reporters/default.js +26 -9
- package/bin/reporters/tap.js +294 -0
- package/bin/run.js +368 -44
- package/bin/types.js +18 -0
- package/bin/util.js +229 -1
- package/package.json +40 -50
- package/transform/lib/coverage.js +135 -124
- package/transform/lib/index.js +57 -23
- package/transform/lib/log.js +2 -39
- package/transform/lib/mock.js +42 -22
- package/transform/lib/builder.js.map +0 -1
- package/transform/lib/coverage.js.map +0 -1
- package/transform/lib/index.js.map +0 -1
- package/transform/lib/linker.js.map +0 -1
- package/transform/lib/location.js.map +0 -1
- package/transform/lib/log.js.map +0 -1
- package/transform/lib/mock.js.map +0 -1
- package/transform/lib/range.js.map +0 -1
- package/transform/lib/types.js.map +0 -1
- package/transform/lib/util.js.map +0 -1
- package/transform/lib/visitor.js.map +0 -1
package/assembly/index.ts
CHANGED
|
@@ -8,20 +8,38 @@ import {
|
|
|
8
8
|
__ALL_POINTS,
|
|
9
9
|
CoverPoint,
|
|
10
10
|
} from "as-test/assembly/coverage";
|
|
11
|
-
import { JSON } from "json-as";
|
|
12
11
|
import { Log } from "./src/log";
|
|
13
12
|
import { sendFileEnd, sendFileStart, sendReport } from "./util/wipc";
|
|
13
|
+
import { quote } from "./util/json";
|
|
14
14
|
|
|
15
15
|
let entrySuites: Suite[] = [];
|
|
16
16
|
|
|
17
17
|
// @ts-ignore
|
|
18
18
|
const FILE = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown";
|
|
19
|
+
|
|
20
|
+
class ImportSnapshot {
|
|
21
|
+
hasValue: bool = false;
|
|
22
|
+
value: u32 = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_IMPORT_SNAPSHOT_VERSION = "default";
|
|
26
|
+
|
|
19
27
|
// Globals
|
|
20
28
|
// @ts-ignore
|
|
21
29
|
@global let __mock_global: Map<string, u32> = new Map<string, u32>();
|
|
22
30
|
// @ts-ignore
|
|
23
31
|
@global let __mock_import: Map<string, u32> = new Map<string, u32>();
|
|
24
32
|
// @ts-ignore
|
|
33
|
+
@global let __mock_import_snapshots: Map<string, ImportSnapshot> = new Map<
|
|
34
|
+
string,
|
|
35
|
+
ImportSnapshot
|
|
36
|
+
>();
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
@global let __mock_import_target_by_index: Map<u32, string> = new Map<
|
|
39
|
+
u32,
|
|
40
|
+
string
|
|
41
|
+
>();
|
|
42
|
+
// @ts-ignore
|
|
25
43
|
@global let suites: Suite[] = [];
|
|
26
44
|
// @ts-ignore
|
|
27
45
|
@global let depth: i32 = -1;
|
|
@@ -231,13 +249,63 @@ export function afterEach(callback: () => void): void {
|
|
|
231
249
|
* @param {Function} oldFn - name of function to mock
|
|
232
250
|
* @param {Function} newFn - the function to substitute it with
|
|
233
251
|
*/
|
|
234
|
-
export function mockFn
|
|
252
|
+
export function mockFn<T extends Function, U extends Function>(
|
|
253
|
+
oldFn: T,
|
|
254
|
+
newFn: U,
|
|
255
|
+
): void {}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Restore references previously mocked with `mockFn`.
|
|
259
|
+
* This applies to calls that appear after `unmockFn` in source order.
|
|
260
|
+
*/
|
|
261
|
+
export function unmockFn<T extends Function>(oldFn: T): void {}
|
|
235
262
|
|
|
236
263
|
export function mockImport<T extends Function>(oldFn: string, newFn: T): void {
|
|
237
264
|
__mock_import.set(oldFn, newFn.index);
|
|
238
265
|
// mocks.set(oldFn, new MockFn(oldFn, newFn).enable());
|
|
239
266
|
}
|
|
240
267
|
|
|
268
|
+
export function unmockImport(oldFn: string): void {
|
|
269
|
+
__mock_import.delete(oldFn);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Save a single import mock value for the given version.
|
|
274
|
+
*
|
|
275
|
+
* Accepts either:
|
|
276
|
+
* - `snapshotImport(importOrPath, version)`
|
|
277
|
+
* - `snapshotImport(importOrPath, () => { ... })` (uses default version)
|
|
278
|
+
*
|
|
279
|
+
* `imp` accepts either a string import path (e.g. "mock.foo") or the imported function.
|
|
280
|
+
*/
|
|
281
|
+
export function snapshotImport<T, V>(imp: T, versionOrCapture: V): void {
|
|
282
|
+
const importKey = resolveImportKey<T>(imp);
|
|
283
|
+
if (isFunction<V>(versionOrCapture)) {
|
|
284
|
+
// @ts-ignore
|
|
285
|
+
versionOrCapture();
|
|
286
|
+
saveImportSnapshot(importKey, DEFAULT_IMPORT_SNAPSHOT_VERSION);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
saveImportSnapshot(importKey, versionKey<V>(versionOrCapture));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Restore a single import mock value for the given version.
|
|
294
|
+
*
|
|
295
|
+
* Accepts either a string import path (e.g. "mock.foo") or the imported function.
|
|
296
|
+
*/
|
|
297
|
+
export function restoreImport<T, V>(imp: T, version: V): void {
|
|
298
|
+
const importKey = resolveImportKey<T>(imp);
|
|
299
|
+
const snapshotKey = importSnapshotKey(importKey, versionKey<V>(version));
|
|
300
|
+
if (!__mock_import_snapshots.has(snapshotKey)) return;
|
|
301
|
+
const snapshot = __mock_import_snapshots.get(snapshotKey);
|
|
302
|
+
if (snapshot.hasValue) {
|
|
303
|
+
__mock_import.set(importKey, snapshot.value);
|
|
304
|
+
} else {
|
|
305
|
+
__mock_import.delete(importKey);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
241
309
|
/**
|
|
242
310
|
* Class defining options that can be passed to the `run` function.
|
|
243
311
|
*
|
|
@@ -303,7 +371,7 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
303
371
|
const report = new FileReport();
|
|
304
372
|
report.suites = entrySuites;
|
|
305
373
|
report.coverage = collectCoverage();
|
|
306
|
-
sendReport(
|
|
374
|
+
sendReport(report.serialize());
|
|
307
375
|
}
|
|
308
376
|
|
|
309
377
|
function registerSuite(
|
|
@@ -328,18 +396,30 @@ function registerSuite(
|
|
|
328
396
|
suites.push(suite);
|
|
329
397
|
}
|
|
330
398
|
|
|
331
|
-
|
|
332
|
-
@json
|
|
333
399
|
class CoverageReport {
|
|
334
400
|
total: i32 = 0;
|
|
335
401
|
covered: i32 = 0;
|
|
336
402
|
uncovered: i32 = 0;
|
|
337
403
|
percent: f64 = 100.0;
|
|
338
404
|
points: CoveragePointReport[] = [];
|
|
339
|
-
}
|
|
340
405
|
|
|
406
|
+
serialize(): string {
|
|
407
|
+
return (
|
|
408
|
+
'{"total":' +
|
|
409
|
+
this.total.toString() +
|
|
410
|
+
',"covered":' +
|
|
411
|
+
this.covered.toString() +
|
|
412
|
+
',"uncovered":' +
|
|
413
|
+
this.uncovered.toString() +
|
|
414
|
+
',"percent":' +
|
|
415
|
+
this.percent.toString() +
|
|
416
|
+
',"points":' +
|
|
417
|
+
serializeCoveragePoints(this.points) +
|
|
418
|
+
"}"
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
341
422
|
|
|
342
|
-
@json
|
|
343
423
|
class CoveragePointReport {
|
|
344
424
|
hash: string = "";
|
|
345
425
|
file: string = "";
|
|
@@ -347,13 +427,61 @@ class CoveragePointReport {
|
|
|
347
427
|
column: i32 = 0;
|
|
348
428
|
type: string = "";
|
|
349
429
|
executed: bool = false;
|
|
350
|
-
}
|
|
351
430
|
|
|
431
|
+
serialize(): string {
|
|
432
|
+
return (
|
|
433
|
+
'{"hash":' +
|
|
434
|
+
quote(this.hash) +
|
|
435
|
+
',"file":' +
|
|
436
|
+
quote(this.file) +
|
|
437
|
+
',"line":' +
|
|
438
|
+
this.line.toString() +
|
|
439
|
+
',"column":' +
|
|
440
|
+
this.column.toString() +
|
|
441
|
+
',"type":' +
|
|
442
|
+
quote(this.type) +
|
|
443
|
+
',"executed":' +
|
|
444
|
+
(this.executed ? "true" : "false") +
|
|
445
|
+
"}"
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
352
449
|
|
|
353
|
-
@json
|
|
354
450
|
class FileReport {
|
|
355
451
|
suites: Suite[] = [];
|
|
356
452
|
coverage: CoverageReport = new CoverageReport();
|
|
453
|
+
|
|
454
|
+
serialize(): string {
|
|
455
|
+
return (
|
|
456
|
+
'{"suites":' +
|
|
457
|
+
serializeSuites(this.suites) +
|
|
458
|
+
',"coverage":' +
|
|
459
|
+
this.coverage.serialize() +
|
|
460
|
+
"}"
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function serializeSuites(values: Suite[]): string {
|
|
466
|
+
if (!values.length) return "[]";
|
|
467
|
+
let out = "[";
|
|
468
|
+
for (let i = 0; i < values.length; i++) {
|
|
469
|
+
if (i) out += ",";
|
|
470
|
+
out += unchecked(values[i]).serialize();
|
|
471
|
+
}
|
|
472
|
+
out += "]";
|
|
473
|
+
return out;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function serializeCoveragePoints(values: CoveragePointReport[]): string {
|
|
477
|
+
if (!values.length) return "[]";
|
|
478
|
+
let out = "[";
|
|
479
|
+
for (let i = 0; i < values.length; i++) {
|
|
480
|
+
if (i) out += ",";
|
|
481
|
+
out += unchecked(values[i]).serialize();
|
|
482
|
+
}
|
|
483
|
+
out += "]";
|
|
484
|
+
return out;
|
|
357
485
|
}
|
|
358
486
|
|
|
359
487
|
function collectCoverage(): CoverageReport {
|
|
@@ -399,6 +527,46 @@ function snapshotKey(): string {
|
|
|
399
527
|
return FILE + "::" + path + "::" + suite.tests.length.toString();
|
|
400
528
|
}
|
|
401
529
|
|
|
530
|
+
function resolveImportKey<T>(imp: T): string {
|
|
531
|
+
if (isString<T>()) {
|
|
532
|
+
// @ts-ignore
|
|
533
|
+
return imp as string;
|
|
534
|
+
}
|
|
535
|
+
// @ts-ignore
|
|
536
|
+
const index = imp.index as u32;
|
|
537
|
+
if (__mock_import_target_by_index.has(index)) {
|
|
538
|
+
return __mock_import_target_by_index.get(index);
|
|
539
|
+
}
|
|
540
|
+
return index.toString();
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function importSnapshotKey(importKey: string, version: string): string {
|
|
544
|
+
return importKey + "::" + version;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function versionKey<V>(version: V): string {
|
|
548
|
+
if (isString<V>()) {
|
|
549
|
+
// @ts-ignore
|
|
550
|
+
return version as string;
|
|
551
|
+
}
|
|
552
|
+
if (isInteger<V>()) {
|
|
553
|
+
// @ts-ignore
|
|
554
|
+
return (<i64>version).toString();
|
|
555
|
+
}
|
|
556
|
+
ERROR("snapshot/restore version must be string or integer");
|
|
557
|
+
return "";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function saveImportSnapshot(importKey: string, version: string): void {
|
|
561
|
+
const snapshotKey = importSnapshotKey(importKey, version);
|
|
562
|
+
const snapshot = new ImportSnapshot();
|
|
563
|
+
if (__mock_import.has(importKey)) {
|
|
564
|
+
snapshot.hasValue = true;
|
|
565
|
+
snapshot.value = __mock_import.get(importKey);
|
|
566
|
+
}
|
|
567
|
+
__mock_import_snapshots.set(snapshotKey, snapshot);
|
|
568
|
+
}
|
|
569
|
+
|
|
402
570
|
export class Result {
|
|
403
571
|
public name: string;
|
|
404
572
|
public arg1: i32;
|
|
@@ -420,18 +588,34 @@ export class Result {
|
|
|
420
588
|
return out;
|
|
421
589
|
}
|
|
422
590
|
serialize(): string {
|
|
423
|
-
return
|
|
591
|
+
return (
|
|
592
|
+
'{"name":' +
|
|
593
|
+
quote(this.name) +
|
|
594
|
+
',"arg1":' +
|
|
595
|
+
this.arg1.toString() +
|
|
596
|
+
',"arg2":' +
|
|
597
|
+
this.arg2.toString() +
|
|
598
|
+
"}"
|
|
599
|
+
);
|
|
424
600
|
}
|
|
425
601
|
}
|
|
426
602
|
|
|
427
|
-
|
|
428
|
-
@json
|
|
429
603
|
export class Time {
|
|
430
604
|
start: f64 = 0;
|
|
431
605
|
end: f64 = 0;
|
|
432
606
|
format(): string {
|
|
433
607
|
return formatTime(this.end - this.start);
|
|
434
608
|
}
|
|
609
|
+
|
|
610
|
+
serialize(): string {
|
|
611
|
+
return (
|
|
612
|
+
'{"start":' +
|
|
613
|
+
this.start.toString() +
|
|
614
|
+
',"end":' +
|
|
615
|
+
this.end.toString() +
|
|
616
|
+
"}"
|
|
617
|
+
);
|
|
618
|
+
}
|
|
435
619
|
}
|
|
436
620
|
|
|
437
621
|
class Unit {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { visualize } from "../util/helpers";
|
|
2
2
|
import { Tests } from "./tests";
|
|
3
|
-
import {
|
|
3
|
+
import { quote, stringifyValue } from "../util/json";
|
|
4
4
|
import {
|
|
5
5
|
sendAssertionFailure,
|
|
6
6
|
sendWarning,
|
|
@@ -9,41 +9,26 @@ import {
|
|
|
9
9
|
|
|
10
10
|
let warnedToThrowDisabled = false;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
@json
|
|
14
12
|
export class Expectation<T> extends Tests {
|
|
15
13
|
public verdict: string = "none";
|
|
16
|
-
public right:
|
|
17
|
-
public left:
|
|
18
|
-
|
|
14
|
+
public right: string = "null";
|
|
15
|
+
public left: string = "null";
|
|
19
16
|
|
|
20
|
-
@omit
|
|
21
17
|
private _left: T;
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
@omit
|
|
25
19
|
// @ts-ignore
|
|
26
20
|
private _right: u64 = 0;
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
@omit
|
|
30
22
|
// @ts-ignore
|
|
31
23
|
private _not: boolean = false;
|
|
32
24
|
|
|
33
|
-
@omit
|
|
34
25
|
// @ts-ignore
|
|
35
26
|
private _skip: boolean = false;
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
@omit
|
|
39
28
|
private _message: string = "";
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
@omit
|
|
43
30
|
private _snapshotKey: string = "";
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
@omit
|
|
47
32
|
private _location: string = "";
|
|
48
33
|
|
|
49
34
|
constructor(
|
|
@@ -79,8 +64,8 @@ export class Expectation<T> extends Tests {
|
|
|
79
64
|
if (this._skip) {
|
|
80
65
|
this.verdict = "skip";
|
|
81
66
|
this.instr = instr;
|
|
82
|
-
this.left
|
|
83
|
-
this.right
|
|
67
|
+
this.left = left;
|
|
68
|
+
this.right = right;
|
|
84
69
|
this.message = "";
|
|
85
70
|
this._not = false;
|
|
86
71
|
return;
|
|
@@ -88,8 +73,8 @@ export class Expectation<T> extends Tests {
|
|
|
88
73
|
const isFail = this._not ? passed : !passed;
|
|
89
74
|
this.verdict = isFail ? "fail" : "ok";
|
|
90
75
|
this.instr = instr;
|
|
91
|
-
this.left
|
|
92
|
-
this.right
|
|
76
|
+
this.left = left;
|
|
77
|
+
this.right = right;
|
|
93
78
|
this.message = isFail ? this._message : "";
|
|
94
79
|
if (isFail) {
|
|
95
80
|
sendAssertionFailure(this._snapshotKey, instr, left, right, this.message);
|
|
@@ -317,7 +302,8 @@ export class Expectation<T> extends Tests {
|
|
|
317
302
|
* Tests if a string starts with the provided prefix.
|
|
318
303
|
*/
|
|
319
304
|
toStartWith(value: string): void {
|
|
320
|
-
if (!isString<T>())
|
|
305
|
+
if (!isString<T>())
|
|
306
|
+
ERROR("toStartWith() can only be used on string types!");
|
|
321
307
|
// @ts-ignore
|
|
322
308
|
const left = this._left as string;
|
|
323
309
|
const passed = left.indexOf(value) == 0;
|
|
@@ -369,7 +355,7 @@ export class Expectation<T> extends Tests {
|
|
|
369
355
|
let key = this._snapshotKey;
|
|
370
356
|
if (name.length) key += "::" + name;
|
|
371
357
|
|
|
372
|
-
const actual =
|
|
358
|
+
const actual = stringifyValue<T>(this._left);
|
|
373
359
|
const res = snapshotAssert(key, actual);
|
|
374
360
|
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected);
|
|
375
361
|
}
|
|
@@ -417,21 +403,21 @@ export class Expectation<T> extends Tests {
|
|
|
417
403
|
passed = this._left === equals;
|
|
418
404
|
} else {
|
|
419
405
|
// Fallback for reference/value types where strict equality is not enough.
|
|
420
|
-
passed =
|
|
406
|
+
passed = stringifyValue<T>(this._left) == stringifyValue<T>(equals);
|
|
421
407
|
}
|
|
422
408
|
|
|
423
409
|
this._resolve(
|
|
424
410
|
passed,
|
|
425
411
|
"toBe",
|
|
426
|
-
|
|
427
|
-
|
|
412
|
+
stringifyValue<T>(this._left),
|
|
413
|
+
stringifyValue<T>(equals),
|
|
428
414
|
);
|
|
429
415
|
}
|
|
430
416
|
}
|
|
431
417
|
|
|
432
418
|
function arrayEquals<T extends any[]>(a: T, b: T): boolean {
|
|
433
419
|
if (a.length != b.length) return false;
|
|
434
|
-
return
|
|
420
|
+
return stringifyValue(a) == stringifyValue(b);
|
|
435
421
|
}
|
|
436
422
|
|
|
437
423
|
function isTruthy<T>(value: T): bool {
|
|
@@ -455,5 +441,5 @@ function isTruthy<T>(value: T): bool {
|
|
|
455
441
|
}
|
|
456
442
|
|
|
457
443
|
function q(value: string): string {
|
|
458
|
-
return
|
|
444
|
+
return quote(value);
|
|
459
445
|
}
|
package/assembly/src/log.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { quote } from "../util/json";
|
|
1
2
|
|
|
2
|
-
@json
|
|
3
3
|
export class Log {
|
|
4
4
|
public order: i32 = 0;
|
|
5
5
|
public depth: i32 = 0;
|
|
@@ -8,4 +8,16 @@ export class Log {
|
|
|
8
8
|
this.text = text;
|
|
9
9
|
}
|
|
10
10
|
display(): void {}
|
|
11
|
+
|
|
12
|
+
serialize(): string {
|
|
13
|
+
return (
|
|
14
|
+
'{"order":' +
|
|
15
|
+
this.order.toString() +
|
|
16
|
+
',"depth":' +
|
|
17
|
+
this.depth.toString() +
|
|
18
|
+
',"text":' +
|
|
19
|
+
quote(this.text) +
|
|
20
|
+
"}"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
11
23
|
}
|
package/assembly/src/suite.ts
CHANGED
|
@@ -4,12 +4,9 @@ import { Tests } from "./tests";
|
|
|
4
4
|
import { Log } from "./log";
|
|
5
5
|
import { after_each_callback, before_each_callback } from "..";
|
|
6
6
|
import { sendSuiteEnd, sendSuiteStart } from "../util/wipc";
|
|
7
|
+
import { quote } from "../util/json";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
@json
|
|
10
9
|
export class Suite {
|
|
11
|
-
|
|
12
|
-
@omitif((self: Suite) => self.depth > 0)
|
|
13
10
|
public file: string = "unknown";
|
|
14
11
|
public order: i32 = 0;
|
|
15
12
|
public time: Time = new Time();
|
|
@@ -20,8 +17,6 @@ export class Suite {
|
|
|
20
17
|
public logs: Log[] = [];
|
|
21
18
|
public kind: string;
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
@omit
|
|
25
20
|
public parent: Suite | null = null;
|
|
26
21
|
|
|
27
22
|
public verdict: string = "none";
|
|
@@ -59,9 +54,7 @@ export class Suite {
|
|
|
59
54
|
this.time.start = performance.now();
|
|
60
55
|
sendSuiteStart(this.file, this.depth, this.kind, this.description);
|
|
61
56
|
const isSkippedCase =
|
|
62
|
-
this.kind == "xdescribe" ||
|
|
63
|
-
this.kind == "xtest" ||
|
|
64
|
-
this.kind == "xit";
|
|
57
|
+
this.kind == "xdescribe" || this.kind == "xtest" || this.kind == "xit";
|
|
65
58
|
const isTestCase =
|
|
66
59
|
this.kind == "test" ||
|
|
67
60
|
this.kind == "it" ||
|
|
@@ -134,4 +127,55 @@ export class Suite {
|
|
|
134
127
|
this.verdict,
|
|
135
128
|
);
|
|
136
129
|
}
|
|
130
|
+
|
|
131
|
+
serialize(): string {
|
|
132
|
+
let out = "{";
|
|
133
|
+
if (this.depth <= 0) {
|
|
134
|
+
out += '"file":' + quote(this.file) + ",";
|
|
135
|
+
}
|
|
136
|
+
out += '"order":' + this.order.toString();
|
|
137
|
+
out += ',"time":' + this.time.serialize();
|
|
138
|
+
out += ',"description":' + quote(this.description);
|
|
139
|
+
out += ',"depth":' + this.depth.toString();
|
|
140
|
+
out += ',"suites":' + serializeSuites(this.suites);
|
|
141
|
+
out += ',"tests":' + serializeTests(this.tests);
|
|
142
|
+
out += ',"logs":' + serializeLogs(this.logs);
|
|
143
|
+
out += ',"kind":' + quote(this.kind);
|
|
144
|
+
out += ',"verdict":' + quote(this.verdict);
|
|
145
|
+
out += "}";
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function serializeSuites(values: Suite[]): string {
|
|
151
|
+
if (!values.length) return "[]";
|
|
152
|
+
let out = "[";
|
|
153
|
+
for (let i = 0; i < values.length; i++) {
|
|
154
|
+
if (i) out += ",";
|
|
155
|
+
out += unchecked(values[i]).serialize();
|
|
156
|
+
}
|
|
157
|
+
out += "]";
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function serializeTests(values: Tests[]): string {
|
|
162
|
+
if (!values.length) return "[]";
|
|
163
|
+
let out = "[";
|
|
164
|
+
for (let i = 0; i < values.length; i++) {
|
|
165
|
+
if (i) out += ",";
|
|
166
|
+
out += unchecked(values[i]).serialize();
|
|
167
|
+
}
|
|
168
|
+
out += "]";
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function serializeLogs(values: Log[]): string {
|
|
173
|
+
if (!values.length) return "[]";
|
|
174
|
+
let out = "[";
|
|
175
|
+
for (let i = 0; i < values.length; i++) {
|
|
176
|
+
if (i) out += ",";
|
|
177
|
+
out += unchecked(values[i]).serialize();
|
|
178
|
+
}
|
|
179
|
+
out += "]";
|
|
180
|
+
return out;
|
|
137
181
|
}
|
package/assembly/src/tests.ts
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { quote, rawOrNull } from "../util/json";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
@json
|
|
5
3
|
export class Tests {
|
|
6
4
|
public order: i32 = 0;
|
|
7
5
|
public type: string = "";
|
|
8
6
|
public verdict: string = "none";
|
|
9
|
-
public left:
|
|
10
|
-
public right:
|
|
7
|
+
public left: string = "null";
|
|
8
|
+
public right: string = "null";
|
|
11
9
|
public instr: string = "";
|
|
12
10
|
public message: string = "";
|
|
13
11
|
public location: string = "";
|
|
12
|
+
|
|
13
|
+
serialize(): string {
|
|
14
|
+
return (
|
|
15
|
+
'{"order":' +
|
|
16
|
+
this.order.toString() +
|
|
17
|
+
',"type":' +
|
|
18
|
+
quote(this.type) +
|
|
19
|
+
',"verdict":' +
|
|
20
|
+
quote(this.verdict) +
|
|
21
|
+
',"left":' +
|
|
22
|
+
rawOrNull(this.left) +
|
|
23
|
+
',"right":' +
|
|
24
|
+
rawOrNull(this.right) +
|
|
25
|
+
',"instr":' +
|
|
26
|
+
quote(this.instr) +
|
|
27
|
+
',"message":' +
|
|
28
|
+
quote(this.message) +
|
|
29
|
+
',"location":' +
|
|
30
|
+
quote(this.location) +
|
|
31
|
+
"}"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
14
34
|
}
|
package/assembly/util/helpers.ts
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { stringify } from "as-console/stringify";
|
|
2
|
+
|
|
3
|
+
export function quote(value: string): string {
|
|
4
|
+
return '"' + escape(value) + '"';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function rawOrNull(value: string): string {
|
|
8
|
+
return value.length ? value : "null";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function stringifyValue<T>(value: T): string {
|
|
12
|
+
if (isNullable<T>() && changetype<usize>(value) == <usize>0) {
|
|
13
|
+
return "null";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (isBoolean<T>()) {
|
|
17
|
+
return (value as bool) ? "true" : "false";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isInteger<T>() || isFloat<T>()) {
|
|
21
|
+
// @ts-expect-error: type
|
|
22
|
+
return value.toString();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isString<T>()) {
|
|
26
|
+
return quote(value as string);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (isArray<T>()) {
|
|
30
|
+
// @ts-expect-error: type
|
|
31
|
+
return stringifyArray<valueof<T>>(value as valueof<T>[]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const formatted = stringify<T>(value);
|
|
35
|
+
if (formatted != "none") {
|
|
36
|
+
return quote(formatted);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return quote(nameof<T>());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function stringifyArray<T>(values: T[]): string {
|
|
43
|
+
if (!values.length) return "[]";
|
|
44
|
+
|
|
45
|
+
let out = "[";
|
|
46
|
+
for (let i = 0; i < values.length; i++) {
|
|
47
|
+
if (i) out += ",";
|
|
48
|
+
out += stringifyValue<T>(unchecked(values[i]));
|
|
49
|
+
}
|
|
50
|
+
out += "]";
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function escape(value: string): string {
|
|
55
|
+
let out = "";
|
|
56
|
+
for (let i = 0; i < value.length; i++) {
|
|
57
|
+
const ch = value.charCodeAt(i);
|
|
58
|
+
if (ch == 34) {
|
|
59
|
+
out += '\\"';
|
|
60
|
+
} else if (ch == 92) {
|
|
61
|
+
out += "\\\\";
|
|
62
|
+
} else if (ch == 10) {
|
|
63
|
+
out += "\\n";
|
|
64
|
+
} else if (ch == 13) {
|
|
65
|
+
out += "\\r";
|
|
66
|
+
} else if (ch == 9) {
|
|
67
|
+
out += "\\t";
|
|
68
|
+
} else if (ch < 32) {
|
|
69
|
+
out += "\\u00";
|
|
70
|
+
const hex = ch.toString(16);
|
|
71
|
+
if (hex.length < 2) out += "0";
|
|
72
|
+
out += hex;
|
|
73
|
+
} else {
|
|
74
|
+
out += value.charAt(i);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|