as-test 1.0.16 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +45 -4
- package/as-test.config.schema.json +5 -0
- package/assembly/__fuzz__/math.fuzz.ts +19 -0
- package/assembly/__fuzz__/string.fuzz.ts +31 -0
- package/assembly/index.ts +5 -5
- package/assembly/src/expectation.ts +93 -42
- package/assembly/util/format.ts +104 -0
- package/assembly/util/helpers.ts +7 -13
- package/assembly/util/json.ts +2 -2
- package/assembly/util/wipc.ts +15 -5
- package/bin/commands/clean-core.js +135 -0
- package/bin/commands/clean.js +51 -0
- package/bin/commands/init-core.js +33 -225
- package/bin/commands/run-core.js +433 -289
- package/bin/commands/web-runner-source.js +14 -700
- package/bin/commands/web-session.js +1144 -0
- package/bin/index.js +391 -78
- package/bin/types.js +1 -0
- package/bin/util.js +16 -1
- package/bin/wipc.js +7 -2
- package/lib/build/index.d.ts +1 -0
- package/lib/build/index.js +1116 -0
- package/lib/build/web-runner/client.d.ts +1 -0
- package/lib/build/web-runner/client.js +167 -0
- package/lib/build/web-runner/html.d.ts +1 -0
- package/lib/build/web-runner/html.js +201 -0
- package/lib/build/web-runner/worker.d.ts +1 -0
- package/lib/build/web-runner/worker.js +271 -0
- package/lib/src/index.ts +1266 -0
- package/package.json +14 -6
- package/transform/lib/mock.js +50 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## v1.1.1
|
|
4
|
+
|
|
5
|
+
- add `ast clean` command to remove build outputs, coverage outputs, crash reports, and logs.
|
|
6
|
+
- remove deps
|
|
7
|
+
|
|
8
|
+
## v1.1.0
|
|
9
|
+
|
|
10
|
+
### Upgrading to 1.1.0
|
|
11
|
+
|
|
12
|
+
- refresh generated runners with:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
rm -rf .as-test/runners && npx as-test init
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- generated runners now use a single file per target and import `instantiate(...)` from `as-test/lib`
|
|
19
|
+
- new bindings and web runners no longer use `*.hooks.js`
|
|
20
|
+
- named modes now support `default: false` to make a mode manual-only
|
|
21
|
+
- the repo examples and default config now use mode names like `node:wasi`, `node:bindings`, `chromium`, and `chromium:headless`
|
|
22
|
+
|
|
23
|
+
### Runtime & Runners
|
|
24
|
+
|
|
25
|
+
- feat: replace the split bindings/web hooks model with single-file runners that import `instantiate(...)` from `as-test/lib`, keeping bindings, WASI, and web runner syntax aligned.
|
|
26
|
+
- feat: add `as-test/lib` as the shared JS runtime host layer for bindings, WASI, and web targets, with runtime artifact resolution happening out of sight before runner execution.
|
|
27
|
+
- feat: autodetect bindings helper shape at runtime support level (`raw`, `esm`, or `none`) and keep the generated runner surface minimal.
|
|
28
|
+
- fix: make `ast run` rebuild missing artifacts on demand instead of failing when only some selected outputs already exist.
|
|
29
|
+
- fix: report real lazy-build time in `ast run` summaries instead of always printing `0us build`.
|
|
30
|
+
- fix: remove build artifact copy/reuse shortcuts so each selected file/mode compiles directly, avoiding stale output reuse across modes.
|
|
31
|
+
|
|
32
|
+
### Web Runtime
|
|
33
|
+
|
|
34
|
+
- feat: move headful web execution to a persistent single-browser-session architecture that opens one page, runs multiple binaries through it, and keeps browser-side runtime details hidden from the runner file.
|
|
35
|
+
- feat: redesign the non-headless browser page into a minimal macOS-inspired loading surface with light/dark mode support and simpler status messaging.
|
|
36
|
+
- feat: make headful web runs wait for the user to open the local session URL, and expose a browser-side exit control.
|
|
37
|
+
- fix: keep all browser bootstrap, asset, and websocket traffic on one local port.
|
|
38
|
+
- fix: improve browser discovery and launch behavior across Chromium, Firefox, and WebKit, including Playwright cache lookup, macOS app bundle resolution, paths with spaces, and owned-process teardown.
|
|
39
|
+
- fix: fail terminal-side runs when the browser side disconnects unexpectedly, and close the browser side when the websocket is lost.
|
|
40
|
+
|
|
41
|
+
### WASI
|
|
42
|
+
|
|
43
|
+
- fix: make the WASI stdin transport retry only on retryable WASI read errors (`AGAIN` and `INTR`), which resolves intermittent snapshot reply corruption in `node:wasi` runs.
|
|
44
|
+
|
|
45
|
+
### Modes & CLI
|
|
46
|
+
|
|
47
|
+
- feat: add per-mode `default: boolean` selection so modes can be included in implicit runs or kept manual-only.
|
|
48
|
+
- feat: add `ast clean` to remove configured build outputs, coverage outputs, crash reports, and logs.
|
|
49
|
+
- feat: make `ast clean` remove everything by default, prompt with `[Y/n]` before a full clean, and allow `-f` / `--force` to skip that confirmation.
|
|
50
|
+
- fix: restore unnamed root-config execution alongside named default modes when `--mode` is omitted.
|
|
51
|
+
- fix: make `ast clean` ignore mode `default: false` flags and treat an omitted `--mode` as a full clean across every configured mode.
|
|
52
|
+
- fix: make `ast clean --mode ...` stay scoped to the selected mode(s) and skip shared output paths that are still owned by unselected modes instead of deleting them.
|
|
53
|
+
- fix: make full `ast clean` remove the configured output roots directly so stale legacy build, coverage, and log directories are removed too.
|
|
54
|
+
- fix: simplify `ast clean` console output so it only prints removed paths plus a final summary.
|
|
55
|
+
|
|
56
|
+
### Tests
|
|
57
|
+
|
|
58
|
+
- feat: add integration coverage for bindings (`raw`, `esm`, `none`), WASI, and web runtime paths, including browser-resolution regressions and single-origin web runner behavior.
|
|
59
|
+
|
|
3
60
|
## 2026-05-08 - v1.0.16
|
|
4
61
|
|
|
5
62
|
- feat: modes inherit pre-declared properties if not explicitly overriden
|
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
╠═╣ ╚═╗ ══ ║ ╠═ ╚═╗ ║
|
|
3
3
|
╩ ╩ ╚═╝ ╩ ╚═╝ ╚═╝ ╩ </pre></h1>
|
|
4
4
|
|
|
5
|
+
> **Upgrading to 1.1.0**
|
|
6
|
+
>
|
|
7
|
+
> See [CHANGELOG.md](./CHANGELOG.md) for upgrade notes. In most projects, refreshing generated runners is enough:
|
|
8
|
+
>
|
|
9
|
+
> ```bash
|
|
10
|
+
> rm -rf .as-test/runners && npx as-test init
|
|
11
|
+
> ```
|
|
12
|
+
|
|
5
13
|
<details>
|
|
6
14
|
<summary>Table of Contents</summary>
|
|
7
15
|
|
|
@@ -77,7 +85,7 @@ Minimal `as-test.config.json`:
|
|
|
77
85
|
},
|
|
78
86
|
"runOptions": {
|
|
79
87
|
"runtime": {
|
|
80
|
-
"cmd": "node .as-test/runners/default.wasi.js
|
|
88
|
+
"cmd": "node .as-test/runners/default.wasi.js"
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
}
|
|
@@ -323,7 +331,7 @@ For example, a simple WASI setup in `as-test.config.json` can look like this:
|
|
|
323
331
|
},
|
|
324
332
|
"runOptions": {
|
|
325
333
|
"runtime": {
|
|
326
|
-
"cmd": "node ./.as-test/runners/default.wasi.js
|
|
334
|
+
"cmd": "node ./.as-test/runners/default.wasi.js"
|
|
327
335
|
}
|
|
328
336
|
}
|
|
329
337
|
}
|
|
@@ -342,22 +350,41 @@ If you want to keep more than one runtime around, use modes:
|
|
|
342
350
|
"input": ["./assembly/__tests__/*.spec.ts"],
|
|
343
351
|
"modes": {
|
|
344
352
|
"wasi": {
|
|
353
|
+
"default": true,
|
|
345
354
|
"buildOptions": {
|
|
346
355
|
"target": "wasi"
|
|
347
356
|
},
|
|
348
357
|
"runOptions": {
|
|
349
358
|
"runtime": {
|
|
350
|
-
"cmd": "node ./.as-test/runners/default.wasi.js
|
|
359
|
+
"cmd": "node ./.as-test/runners/default.wasi.js"
|
|
351
360
|
}
|
|
352
361
|
}
|
|
353
362
|
},
|
|
354
363
|
"bindings": {
|
|
364
|
+
"default": true,
|
|
355
365
|
"buildOptions": {
|
|
356
366
|
"target": "bindings"
|
|
357
367
|
},
|
|
358
368
|
"runOptions": {
|
|
359
369
|
"runtime": {
|
|
360
|
-
"cmd": "node ./.as-test/runners/default.bindings.js
|
|
370
|
+
"cmd": "node ./.as-test/runners/default.bindings.js"
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Set `"default": false` on a mode when you want to keep it available for explicit `--mode ...` runs without including it in normal runs:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"modes": {
|
|
383
|
+
"web": {
|
|
384
|
+
"default": false,
|
|
385
|
+
"runOptions": {
|
|
386
|
+
"runtime": {
|
|
387
|
+
"browser": "chromium"
|
|
361
388
|
}
|
|
362
389
|
}
|
|
363
390
|
}
|
|
@@ -365,6 +392,20 @@ If you want to keep more than one runtime around, use modes:
|
|
|
365
392
|
}
|
|
366
393
|
```
|
|
367
394
|
|
|
395
|
+
With that setup:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
npx ast test
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
runs the root/default config plus any modes whose `"default"` flag is not `false`, while:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
npx ast test --mode web
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
still runs the `web` mode explicitly.
|
|
408
|
+
|
|
368
409
|
Modes can also be full config objects. That means a mode can override fuzzing, input globs, output aliases, runtime, build flags, and the rest of the normal config surface:
|
|
369
410
|
|
|
370
411
|
```json
|
|
@@ -305,6 +305,11 @@
|
|
|
305
305
|
"type": "object",
|
|
306
306
|
"additionalProperties": false,
|
|
307
307
|
"properties": {
|
|
308
|
+
"default": {
|
|
309
|
+
"type": "boolean",
|
|
310
|
+
"description": "Include this mode when --mode is omitted. Defaults to true.",
|
|
311
|
+
"default": true
|
|
312
|
+
},
|
|
308
313
|
"$schema": {
|
|
309
314
|
"type": "string"
|
|
310
315
|
},
|
|
@@ -7,3 +7,22 @@ fuzz("bounded integer addition", (left: i32, right: i32): bool => {
|
|
|
7
7
|
}).generate((seed: FuzzSeed, run: (left: i32, right: i32) => bool): void => {
|
|
8
8
|
run(seed.i32({ min: -1000, max: 1000 }), seed.i32({ min: -1000, max: 1000 }));
|
|
9
9
|
});
|
|
10
|
+
|
|
11
|
+
fuzz("numeric matchers stay consistent for bounded integers", (value: i32): bool => {
|
|
12
|
+
expect(value).toBeNumber();
|
|
13
|
+
expect(value).toBeInteger();
|
|
14
|
+
expect(value).toBeFinite();
|
|
15
|
+
expect(value).toBe(value);
|
|
16
|
+
expect(value).toBeGreaterOrEqualTo(-1000);
|
|
17
|
+
expect(value).toBeLessThanOrEqualTo(1000);
|
|
18
|
+
|
|
19
|
+
if (value != 0) {
|
|
20
|
+
expect(value).toBeTruthy();
|
|
21
|
+
} else {
|
|
22
|
+
expect(value).toBeFalsy();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return true;
|
|
26
|
+
}).generate((seed: FuzzSeed, run: (value: i32) => bool): void => {
|
|
27
|
+
run(seed.i32({ min: -1000, max: 1000 }));
|
|
28
|
+
}, 250);
|
|
@@ -19,3 +19,34 @@ fuzz(
|
|
|
19
19
|
}),
|
|
20
20
|
);
|
|
21
21
|
}, 250);
|
|
22
|
+
|
|
23
|
+
fuzz(
|
|
24
|
+
"string matchers stay consistent on derived slices",
|
|
25
|
+
(input: string): bool => {
|
|
26
|
+
const split = input.length >> 1;
|
|
27
|
+
const prefix = input.substr(0, split);
|
|
28
|
+
const suffix = input.substr(split);
|
|
29
|
+
|
|
30
|
+
expect(input).toBeString();
|
|
31
|
+
expect(input).toContain(prefix);
|
|
32
|
+
expect(input).toMatch(suffix);
|
|
33
|
+
|
|
34
|
+
if (input.length > 0) {
|
|
35
|
+
expect(input).toBeTruthy();
|
|
36
|
+
expect(input).toContain(input.charAt(input.length - 1));
|
|
37
|
+
} else {
|
|
38
|
+
expect(input).toBeFalsy();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
},
|
|
43
|
+
).generate((seed: FuzzSeed, run: (input: string) => bool): void => {
|
|
44
|
+
run(
|
|
45
|
+
seed.string({
|
|
46
|
+
charset: "ascii",
|
|
47
|
+
min: 0,
|
|
48
|
+
max: 40,
|
|
49
|
+
exclude: [0x00, 0x0a, 0x0d],
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
}, 250);
|
package/assembly/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Suite } from "./src/suite";
|
|
2
2
|
import { Expectation } from "./src/expectation";
|
|
3
|
-
import { stringify } from "as-console/stringify";
|
|
4
3
|
import {
|
|
5
4
|
__COVER,
|
|
6
5
|
__POINTS,
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
sendReport,
|
|
17
16
|
} from "./util/wipc";
|
|
18
17
|
import { quote } from "./util/json";
|
|
18
|
+
import { bold, formatValue, green, red } from "./util/format";
|
|
19
19
|
import {
|
|
20
20
|
createFuzzer,
|
|
21
21
|
FuzzerBase,
|
|
@@ -245,7 +245,7 @@ export function log<T>(data: T): void {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
export function __as_test_log_default<T>(data: T): string {
|
|
248
|
-
return
|
|
248
|
+
return formatValue(data);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
export function __as_test_log_is_enabled(): bool {
|
|
@@ -673,11 +673,11 @@ export class Result {
|
|
|
673
673
|
}
|
|
674
674
|
display(): string {
|
|
675
675
|
let out = "";
|
|
676
|
-
out += `${
|
|
676
|
+
out += `${bold(this.name)} `;
|
|
677
677
|
if (this.arg1) {
|
|
678
|
-
out += `${
|
|
678
|
+
out += `${bold(red(this.arg1.toString() + " failed"))}`;
|
|
679
679
|
} else {
|
|
680
|
-
out += `${
|
|
680
|
+
out += `${bold(green("0 failed"))}`;
|
|
681
681
|
}
|
|
682
682
|
out += ` ${this.arg1 + this.arg2} total\n`;
|
|
683
683
|
return out;
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,74 @@ 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(passed, "toMatch", q(this._left as string), q(value), message);
|
|
317
358
|
}
|
|
318
359
|
|
|
319
360
|
/**
|
|
320
361
|
* Tests if a string starts with the provided prefix.
|
|
321
362
|
*/
|
|
322
|
-
toStartWith(value: string): void {
|
|
363
|
+
toStartWith(value: string, message: string = ""): void {
|
|
323
364
|
if (!isString<T>())
|
|
324
365
|
ERROR("toStartWith() can only be used on string types!");
|
|
325
366
|
// @ts-ignore
|
|
326
367
|
const left = this._left as string;
|
|
327
368
|
const passed = left.indexOf(value) == 0;
|
|
328
|
-
this._resolve(passed, "toStartWith", q(left), q(value));
|
|
369
|
+
this._resolve(passed, "toStartWith", q(left), q(value), message);
|
|
329
370
|
}
|
|
330
371
|
|
|
331
372
|
/**
|
|
332
373
|
* Tests if a string ends with the provided suffix.
|
|
333
374
|
*/
|
|
334
|
-
toEndWith(value: string): void {
|
|
375
|
+
toEndWith(value: string, message: string = ""): void {
|
|
335
376
|
if (!isString<T>()) ERROR("toEndWith() can only be used on string types!");
|
|
336
377
|
// @ts-ignore
|
|
337
378
|
const left = this._left as string;
|
|
338
379
|
const idx = left.lastIndexOf(value);
|
|
339
380
|
const passed = idx >= 0 && idx + value.length == left.length;
|
|
340
|
-
this._resolve(passed, "toEndWith", q(left), q(value));
|
|
381
|
+
this._resolve(passed, "toEndWith", q(left), q(value), message);
|
|
341
382
|
}
|
|
342
383
|
|
|
343
384
|
/**
|
|
344
385
|
* Tests if an array has length x
|
|
345
386
|
*/
|
|
346
|
-
toHaveLength(value: i32): void {
|
|
387
|
+
toHaveLength(value: i32, message: string = ""): void {
|
|
347
388
|
// @ts-ignore
|
|
348
389
|
const leftLen = this._left.length as i32;
|
|
349
390
|
// @ts-ignore
|
|
350
391
|
const passed = isArray<T>() && leftLen == value;
|
|
351
|
-
this._resolve(
|
|
392
|
+
this._resolve(
|
|
393
|
+
passed,
|
|
394
|
+
"toHaveLength",
|
|
395
|
+
leftLen.toString(),
|
|
396
|
+
value.toString(),
|
|
397
|
+
message,
|
|
398
|
+
);
|
|
352
399
|
}
|
|
353
400
|
|
|
354
401
|
/**
|
|
355
402
|
* Tests if an array or string contains a value
|
|
356
403
|
*/
|
|
357
404
|
// @ts-ignore
|
|
358
|
-
toContain(value: valueof<T
|
|
405
|
+
toContain(value: valueof<T>, message: string = ""): void {
|
|
359
406
|
if (isString<T>()) {
|
|
360
407
|
// @ts-ignore
|
|
361
408
|
const left = this._left as string;
|
|
362
409
|
// @ts-ignore
|
|
363
410
|
const needle = value as string;
|
|
364
411
|
const passed = left.indexOf(needle) >= 0;
|
|
365
|
-
this._resolve(passed, "toContain", q(left), q(needle));
|
|
412
|
+
this._resolve(passed, "toContain", q(left), q(needle), message);
|
|
366
413
|
return;
|
|
367
414
|
}
|
|
368
415
|
|
|
@@ -374,6 +421,7 @@ export class Expectation<T> extends Tests {
|
|
|
374
421
|
"toContain",
|
|
375
422
|
stringifyValue<T>(this._left),
|
|
376
423
|
stringifyValue<valueof<T>>(value),
|
|
424
|
+
message,
|
|
377
425
|
);
|
|
378
426
|
return;
|
|
379
427
|
}
|
|
@@ -385,28 +433,28 @@ export class Expectation<T> extends Tests {
|
|
|
385
433
|
* Alias for toContain().
|
|
386
434
|
*/
|
|
387
435
|
// @ts-ignore
|
|
388
|
-
toContains(value: valueof<T
|
|
389
|
-
this.toContain(value);
|
|
436
|
+
toContains(value: valueof<T>, message: string = ""): void {
|
|
437
|
+
this.toContain(value, message);
|
|
390
438
|
}
|
|
391
439
|
|
|
392
440
|
/**
|
|
393
441
|
* Tests if serialized value matches stored snapshot.
|
|
394
442
|
*/
|
|
395
|
-
toMatchSnapshot(name: string = ""): void {
|
|
443
|
+
toMatchSnapshot(name: string = "", message: string = ""): void {
|
|
396
444
|
let key = name.length
|
|
397
445
|
? namedSnapshotKey(this._snapshotKey, name)
|
|
398
446
|
: nextUnnamedSnapshotKey(this._snapshotKey);
|
|
399
447
|
|
|
400
448
|
const actual = stringifyValue<T>(this._left);
|
|
401
449
|
const res = snapshotAssert(key, actual);
|
|
402
|
-
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected);
|
|
450
|
+
this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
|
|
403
451
|
}
|
|
404
452
|
|
|
405
453
|
/**
|
|
406
454
|
* Delegates throw assertions to try-as when available.
|
|
407
455
|
* If try-as is unavailable, this matcher is disabled and warns once.
|
|
408
456
|
*/
|
|
409
|
-
toThrow(): void {
|
|
457
|
+
toThrow(message: string = ""): void {
|
|
410
458
|
// @ts-ignore
|
|
411
459
|
if (!isDefined(AS_TEST_TRY_AS)) {
|
|
412
460
|
if (!warnedToThrowDisabled) {
|
|
@@ -415,7 +463,7 @@ export class Expectation<T> extends Tests {
|
|
|
415
463
|
);
|
|
416
464
|
warnedToThrowDisabled = true;
|
|
417
465
|
}
|
|
418
|
-
this._resolve(true, "toThrow", q("disabled"), q("disabled"));
|
|
466
|
+
this._resolve(true, "toThrow", q("disabled"), q("disabled"), message);
|
|
419
467
|
return;
|
|
420
468
|
}
|
|
421
469
|
|
|
@@ -425,13 +473,13 @@ export class Expectation<T> extends Tests {
|
|
|
425
473
|
// @ts-ignore
|
|
426
474
|
__ExceptionState.Failures--;
|
|
427
475
|
}
|
|
428
|
-
this._resolve(passed, "toThrow", q("throws"), q("throws"));
|
|
476
|
+
this._resolve(passed, "toThrow", q("throws"), q("throws"), message);
|
|
429
477
|
}
|
|
430
478
|
|
|
431
479
|
/**
|
|
432
480
|
* Tests for equality
|
|
433
481
|
*/
|
|
434
|
-
toBe(equals: T): void {
|
|
482
|
+
toBe(equals: T, message: string = ""): void {
|
|
435
483
|
const passed = this._left === equals;
|
|
436
484
|
|
|
437
485
|
this._resolve(
|
|
@@ -439,32 +487,35 @@ export class Expectation<T> extends Tests {
|
|
|
439
487
|
"toBe",
|
|
440
488
|
stringifyValue<T>(this._left),
|
|
441
489
|
stringifyValue<T>(equals),
|
|
490
|
+
message,
|
|
442
491
|
);
|
|
443
492
|
}
|
|
444
493
|
|
|
445
494
|
/**
|
|
446
495
|
* Tests for deep equality
|
|
447
496
|
*/
|
|
448
|
-
toEqual(equals: T): void {
|
|
497
|
+
toEqual(equals: T, message: string = ""): void {
|
|
449
498
|
const passed = valueEquals<T>(this._left, equals, false);
|
|
450
499
|
this._resolve(
|
|
451
500
|
passed,
|
|
452
501
|
"toEqual",
|
|
453
502
|
stringifyValue<T>(this._left),
|
|
454
503
|
stringifyValue<T>(equals),
|
|
504
|
+
message,
|
|
455
505
|
);
|
|
456
506
|
}
|
|
457
507
|
|
|
458
508
|
/**
|
|
459
509
|
* Tests for strict deep equality
|
|
460
510
|
*/
|
|
461
|
-
toStrictEqual(equals: T): void {
|
|
511
|
+
toStrictEqual(equals: T, message: string = ""): void {
|
|
462
512
|
const passed = valueEquals<T>(this._left, equals, true);
|
|
463
513
|
this._resolve(
|
|
464
514
|
passed,
|
|
465
515
|
"toStrictEqual",
|
|
466
516
|
stringifyValue<T>(this._left),
|
|
467
517
|
stringifyValue<T>(equals),
|
|
518
|
+
message,
|
|
468
519
|
);
|
|
469
520
|
}
|
|
470
521
|
}
|