as-test 0.5.1 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Change Log
2
2
 
3
+ ## 2026-02-24 - v0.5.2
4
+
5
+ ### Runtime & Serialization
6
+
7
+ - refactor: remove `json-as` dependency by inlining portable serialization and deserialization helpers into the runtime.
8
+
9
+ ### CLI
10
+
11
+ - fix: enforce deterministic alphanumeric test input ordering for `ast build`, `ast run`, and `ast test`.
12
+
13
+ ### Dependencies
14
+
15
+ - chore: remove unused runtime dependencies `as-variant` and `gradient-string`.
16
+
17
+ ## 2026-02-23
18
+
19
+ ### Runtime Matrix & Mode Execution
20
+
21
+ - feat: add `--mode <name[,name...]>` support for `ast build`, `ast run`, and `ast test`, including multi-mode fan-out in one command.
22
+ - feat: add config `modes` map for per-mode overrides (`buildOptions`, `runOptions`, `env`, and optional output/log/coverage/snapshot directories).
23
+ - feat: when running with `--mode`, compile artifacts are emitted as `<name>.<mode>.<type>.wasm` (where `type` is `wasi` or `bindings`).
24
+
25
+ ### Bindings Runner Naming
26
+
27
+ - feat: switch default bindings runner path to `./.as-test/runners/default.bindings.js`.
28
+ - fix: keep backward compatibility with deprecated `./.as-test/runners/default.run.js` and legacy `*.run.js` bindings helper files.
29
+ - feat: add runtime warnings for deprecated bindings runner path usage and automatic fallback to `default.bindings.js` when needed.
30
+
31
+ ### Init, Examples & Docs
32
+
33
+ - feat: `init` now writes both `.as-test/runners/default.wasi.js` and `.as-test/runners/default.bindings.js`.
34
+ - docs: update README runtime examples and mode artifact naming guidance for `--mode`.
35
+ - docs: refresh `examples/` docs/configs for `default.bindings.js` and add a mode matrix example config.
36
+
37
+ ## 2026-02-18
38
+
39
+ ### Reporter & CLI
40
+
41
+ - feat: add built-in TAP v13 reporter (`tap`) for `ast run` and `ast test`.
42
+ - feat: add reporter selection flags `--tap` and `--reporter <name|path>`.
43
+ - feat: when TAP reporter is active, write a single TAP artifact by default to `./.as-test/reports/report.tap`.
44
+ - feat: allow reporter object config (`name`, `options`, `outDir`, `outFile`) for TAP output control, including `single-file` (default) and `per-file`.
45
+ - feat: emit GitHub Actions `::error` annotations for failed TAP assertions (with file/line/col when available).
46
+ - fix: keep TAP stdout clean by routing runtime passthrough output to stderr in TAP mode.
47
+ - fix: ensure reporter flag values are not treated as test selectors in `ast test`.
48
+
49
+ ### Mocking API & Transform
50
+
51
+ - feat: add `unmockFn(oldFn)` and `unmockImport(path)` APIs to complement `mockFn` and `mockImport`.
52
+ - feat: add `snapshotImport(imp, version)` and `restoreImport(imp, version)` to snapshot and restore a single import mock by version.
53
+ - feat: support both import path strings and import functions for `imp`, and `string`/`i32` versions.
54
+ - feat: `snapshotImport` also supports callback form (`snapshotImport(imp, () => ...)`) that snapshots to default version `"default"`.
55
+ - feat: update transform/runtime handling so `unmockFn` stops later function-call rewrites and `unmockImport` clears the active import mock mapping.
56
+
57
+ ### Config & Docs
58
+
59
+ - docs: document built-in TAP usage in README (`--tap`, `--reporter tap`, and config-based usage).
60
+ - docs: update config schema reporter description to include built-in `default` and `tap` values.
61
+ - docs: add README mocking section covering `mockFn`, `unmockFn`, `mockImport`, and `unmockImport`.
62
+
3
63
  ## 2026-02-16 - v0.5.1
4
64
 
5
65
  ### Miscellaneous
package/README.md CHANGED
@@ -5,8 +5,11 @@
5
5
  <details>
6
6
  <summary>Table of Contents</summary>
7
7
 
8
+ - [Why as-test](#why-as-test)
8
9
  - [Installation](#installation)
10
+ - [Examples](#examples)
9
11
  - [Writing Tests](#writing-tests)
12
+ - [Mocking](#mocking)
10
13
  - [Snapshots](#snapshots)
11
14
  - [Coverage](#coverage)
12
15
  - [Custom Reporters](#custom-reporters)
@@ -16,6 +19,20 @@
16
19
 
17
20
  </details>
18
21
 
22
+ ## Why as-test
23
+
24
+ Most AssemblyScript testing tools are tied to a single runtime, usually Node.js. This works for development, but it doesn’t reflect how your code runs in production.
25
+ If you deploy to WASI, Wazero, or a custom runtime, you often end up mocking everything and maintaining parallel logic just for tests.
26
+ as-test solves this by letting you run tests on your actual target runtime, while only mocking what’s necessary.
27
+
28
+ Key benefits
29
+
30
+ - Runtime-agnostic: test on WASI, bindings, or custom runners
31
+ - Minimal mocking: keep real imports when possible
32
+ - Production-like testing: catch runtime-specific issues early
33
+ - Inline mocking and snapshots
34
+ - Custom reporters and coverage
35
+
19
36
  ## Installation
20
37
 
21
38
  The installation script will set everything up for you:
@@ -28,6 +45,17 @@ Alternatively, you can install it manually:
28
45
  npm install as-test --save-dev
29
46
  ```
30
47
 
48
+ ## Examples
49
+
50
+ Full runnable examples live in `examples/`, including:
51
+
52
+ - complete spec files for core features
53
+ - import mocking and import snapshot patterns
54
+ - mode-based runtime matrix config in `examples/as-test.config.json`
55
+ - a dedicated config you can run directly
56
+
57
+ See `examples/README.md` for the walkthrough.
58
+
31
59
  ## Writing Tests
32
60
 
33
61
  Create `assembly/__tests__/math.spec.ts`:
@@ -86,11 +114,67 @@ No test files matched: ...
86
114
  ### Useful flags
87
115
 
88
116
  - `--config <path>`: use another config file
117
+ - `--mode <name[,name...]>`: run one or multiple named config modes
89
118
  - `--update-snapshots`: write snapshot updates
90
119
  - `--no-snapshot`: disable snapshot assertions for the run
91
120
  - `--show-coverage`: print uncovered coverage points
92
121
  - `--verbose`: keep expanded suite/test lines and update running `....` statuses in place
93
122
 
123
+ ## Mocking
124
+
125
+ Use these helpers when you need to replace behavior during tests:
126
+
127
+ - `mockFn(oldFn, newFn)`: rewrites subsequent calls to `oldFn` in the same spec file to use `newFn`
128
+ - `unmockFn(oldFn)`: stops that rewrite for subsequent calls
129
+ - `mockImport("module.field", fn)`: sets the runtime mock for an external import
130
+ - `unmockImport("module.field")`: clears the runtime mock for an external import
131
+ - `snapshotImport<T = Function | string>(imp: T, version: string | i32)`: snapshots a single import mock
132
+ - `snapshotImport<T = Function | string>(imp: T, capture: () => unknown)`: runs `capture` and snapshots using version `"default"`
133
+ - `restoreImport<T = Function | string>(imp: T, version: string | i32)`: restores a single import mock
134
+
135
+ Example:
136
+
137
+ ```ts
138
+ import {
139
+ expect,
140
+ it,
141
+ mockFn,
142
+ mockImport,
143
+ restoreImport,
144
+ run,
145
+ snapshotImport,
146
+ unmockFn,
147
+ unmockImport,
148
+ } from "as-test";
149
+ import { foo } from "./mock";
150
+
151
+ mockImport("mock.foo", (): string => "buz");
152
+ mockFn(foo, (): string => "baz " + foo());
153
+
154
+ it("mocked function", () => {
155
+ expect(foo()).toBe("baz buz");
156
+ });
157
+
158
+ unmockFn(foo);
159
+
160
+ it("function restored", () => {
161
+ expect(foo()).toBe("buz");
162
+ });
163
+
164
+ snapshotImport(foo, 1);
165
+ mockImport("mock.foo", (): string => "temp");
166
+ snapshotImport("mock.foo", "v2");
167
+ restoreImport(foo, 1);
168
+
169
+ snapshotImport("mock.foo", () => foo()); // snapshots to version "default"
170
+ restoreImport("mock.foo", "default");
171
+
172
+ unmockImport("mock.foo");
173
+ mockImport("mock.foo", (): string => "buz");
174
+
175
+ run();
176
+ ```
177
+
94
178
  ## Snapshots
95
179
 
96
180
  Snapshot assertions are enabled by default.
@@ -156,6 +240,7 @@ Example:
156
240
  "args": [],
157
241
  "target": "wasi"
158
242
  },
243
+ "modes": {},
159
244
  "runOptions": {
160
245
  "runtime": {
161
246
  "cmd": "node ./.as-test/runners/default.wasi.js <file>"
@@ -173,10 +258,152 @@ Key fields:
173
258
  - `coverageDir`: coverage output dir or `"none"`
174
259
  - `snapshotDir`: snapshot storage dir
175
260
  - `buildOptions.target`: `wasi` or `bindings`
261
+ - `modes`: named overrides for target/args/runtime/env/artifact directories (selected via `--mode`)
176
262
  - `runOptions.runtime.cmd`: runtime command, supports `<file>` and `<name>`; if its script path is missing, as-test falls back to the default runner for the selected target
177
- - `runOptions.reporter`: optional custom reporter module path
263
+ - `runOptions.reporter`: reporter selection as a string or object
264
+
265
+ Example multi-runtime matrix:
266
+
267
+ ```json
268
+ {
269
+ "modes": {
270
+ "wasi-simd": {
271
+ "buildOptions": {
272
+ "target": "wasi",
273
+ "args": ["--enable", "simd"]
274
+ },
275
+ "runOptions": {
276
+ "runtime": {
277
+ "cmd": "wasmer run <file>"
278
+ }
279
+ }
280
+ },
281
+ "wasi-nosimd": {
282
+ "buildOptions": {
283
+ "target": "wasi"
284
+ },
285
+ "runOptions": {
286
+ "runtime": {
287
+ "cmd": "wasmer run <file>"
288
+ }
289
+ }
290
+ },
291
+ "bindings-node-simd": {
292
+ "buildOptions": {
293
+ "target": "bindings",
294
+ "args": ["--enable", "simd"]
295
+ },
296
+ "runOptions": {
297
+ "runtime": {
298
+ "cmd": "node ./.as-test/runners/default.bindings.js <file>"
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+ ```
305
+
306
+ Run all modes:
307
+
308
+ ```bash
309
+ ast test --mode wasi-simd,wasi-nosimd,bindings-node-simd
310
+ ```
311
+
312
+ When using `--mode`, compiled artifacts are emitted as:
313
+
314
+ ```text
315
+ <test-name>.<mode>.<target>.wasm
316
+ ```
317
+
318
+ Example:
319
+
320
+ ```text
321
+ math.wasi-simd.wasi.wasm
322
+ math.bindings-node-simd.bindings.wasm
323
+ ```
178
324
 
179
- ## Custom Reporter
325
+ Bindings runner naming:
326
+
327
+ - preferred: `./.as-test/runners/default.bindings.js`
328
+ - deprecated but supported: `./.as-test/runners/default.run.js`
329
+
330
+ `ast init` now scaffolds both local runners:
331
+
332
+ - `.as-test/runners/default.wasi.js`
333
+ - `.as-test/runners/default.bindings.js`
334
+
335
+ ## Custom Reporters
336
+
337
+ Built-in TAP reporter (useful for CI, including GitHub Actions):
338
+
339
+ ```bash
340
+ ast run --reporter tap
341
+ ```
342
+
343
+ TAP output is written to `./.as-test/reports/report.tap` by default.
344
+
345
+ Or in config:
346
+
347
+ ```json
348
+ {
349
+ "runOptions": {
350
+ "reporter": "tap"
351
+ }
352
+ }
353
+ ```
354
+
355
+ Or with reporter object config:
356
+
357
+ ```json
358
+ {
359
+ "runOptions": {
360
+ "reporter": {
361
+ "name": "tap",
362
+ "options": ["single-file"],
363
+ "outDir": "./.as-test/reports"
364
+ }
365
+ }
366
+ }
367
+ ```
368
+
369
+ `options` supports `single-file` (default) and `per-file`.
370
+
371
+ Single-file explicit path:
372
+
373
+ ```json
374
+ {
375
+ "runOptions": {
376
+ "reporter": {
377
+ "name": "tap",
378
+ "outFile": "./.as-test/reports/report.tap"
379
+ }
380
+ }
381
+ }
382
+ ```
383
+
384
+ In GitHub Actions, failed TAP points emit `::error` annotations with file and line when available.
385
+
386
+ Example GitHub workflow (Bun + Wasmtime + TAP summary):
387
+
388
+ ```yaml
389
+ name: Run Tests
390
+
391
+ on: [push, pull_request]
392
+
393
+ jobs:
394
+ build:
395
+ runs-on: ubuntu-latest
396
+ steps:
397
+ - uses: actions/checkout@v4
398
+ - uses: jcbhmr/setup-wasmtime@v2
399
+ - uses: oven-sh/setup-bun@v1
400
+ - run: bun install
401
+ - run: bun run test --update-snapshots --tap
402
+ - uses: test-summary/action@v2
403
+ if: always()
404
+ with:
405
+ paths: ".as-test/reports/*.tap"
406
+ ```
180
407
 
181
408
  Set reporter path in config:
182
409
 
@@ -188,6 +415,13 @@ Set reporter path in config:
188
415
  }
189
416
  ```
190
417
 
418
+ It's even possible to use something like [tap-summary](https://github.com/zoubin/tap-summary) to summarize the test results!
419
+
420
+ ```bash
421
+ npm install -g tap-summary
422
+ ast test --reporter tap | tap-summary
423
+ ```
424
+
191
425
  Reporter module should export `createReporter` (named or default):
192
426
 
193
427
  ```js
@@ -205,6 +439,8 @@ export function createReporter(context) {
205
439
  }
206
440
  ```
207
441
 
442
+ With these hooks, you can emit machine-readable output (for example TAP/JSON) while still keeping the default human-readable terminal view for local runs.
443
+
208
444
  ## Assertions
209
445
 
210
446
  Skip helpers:
@@ -102,6 +102,126 @@
102
102
  "target": "wasi"
103
103
  }
104
104
  },
105
+ "modes": {
106
+ "type": "object",
107
+ "description": "Named build/run modes. Each mode can override target/args/runtime/env and artifact directories.",
108
+ "additionalProperties": {
109
+ "type": "object",
110
+ "additionalProperties": false,
111
+ "properties": {
112
+ "outDir": {
113
+ "type": "string",
114
+ "description": "Mode-specific build output directory. If omitted, defaults to <outDir>/<mode-name>."
115
+ },
116
+ "logs": {
117
+ "type": "string",
118
+ "description": "Mode-specific log output directory."
119
+ },
120
+ "coverageDir": {
121
+ "type": "string",
122
+ "description": "Mode-specific coverage output directory."
123
+ },
124
+ "snapshotDir": {
125
+ "type": "string",
126
+ "description": "Mode-specific snapshot directory."
127
+ },
128
+ "config": {
129
+ "type": "string",
130
+ "description": "Mode-specific asconfig path or \"none\"."
131
+ },
132
+ "coverage": {
133
+ "description": "Mode-specific coverage settings.",
134
+ "oneOf": [
135
+ {
136
+ "type": "boolean"
137
+ },
138
+ {
139
+ "type": "object",
140
+ "additionalProperties": true,
141
+ "properties": {
142
+ "enabled": {
143
+ "type": "boolean"
144
+ },
145
+ "includeSpecs": {
146
+ "type": "boolean"
147
+ }
148
+ }
149
+ }
150
+ ]
151
+ },
152
+ "buildOptions": {
153
+ "type": "object",
154
+ "additionalProperties": false,
155
+ "properties": {
156
+ "args": {
157
+ "type": "array",
158
+ "items": {
159
+ "type": "string"
160
+ }
161
+ },
162
+ "target": {
163
+ "type": "string",
164
+ "enum": ["wasi", "bindings"]
165
+ }
166
+ }
167
+ },
168
+ "runOptions": {
169
+ "type": "object",
170
+ "additionalProperties": false,
171
+ "properties": {
172
+ "runtime": {
173
+ "type": "object",
174
+ "additionalProperties": false,
175
+ "properties": {
176
+ "cmd": {
177
+ "type": "string",
178
+ "description": "Mode-specific runtime command."
179
+ }
180
+ }
181
+ },
182
+ "reporter": {
183
+ "description": "Mode-specific reporter config.",
184
+ "oneOf": [
185
+ {
186
+ "type": "string"
187
+ },
188
+ {
189
+ "type": "object",
190
+ "additionalProperties": false,
191
+ "properties": {
192
+ "name": {
193
+ "type": "string"
194
+ },
195
+ "options": {
196
+ "type": "array",
197
+ "items": {
198
+ "type": "string"
199
+ }
200
+ },
201
+ "outDir": {
202
+ "type": "string"
203
+ },
204
+ "outFile": {
205
+ "type": "string"
206
+ }
207
+ },
208
+ "required": ["name"]
209
+ }
210
+ ]
211
+ }
212
+ }
213
+ },
214
+ "env": {
215
+ "type": "object",
216
+ "description": "Environment variables injected when building/running this mode.",
217
+ "additionalProperties": {
218
+ "type": "string"
219
+ }
220
+ }
221
+ }
222
+ },
223
+ "default": {}
224
+ },
105
225
  "runOptions": {
106
226
  "type": "object",
107
227
  "additionalProperties": false,
@@ -121,8 +241,39 @@
121
241
  }
122
242
  },
123
243
  "reporter": {
124
- "type": "string",
125
- "description": "Optional path to a custom reporter module.",
244
+ "description": "Reporter selection. Use a string for built-in/default/custom path, or an object for named reporter configuration.",
245
+ "oneOf": [
246
+ {
247
+ "type": "string",
248
+ "description": "\"\" or \"default\" uses built-in default reporter, \"tap\" uses built-in TAP v13 reporter, otherwise treated as custom module path."
249
+ },
250
+ {
251
+ "type": "object",
252
+ "additionalProperties": false,
253
+ "properties": {
254
+ "name": {
255
+ "type": "string",
256
+ "description": "Reporter name or module path. Built-in values: \"default\", \"tap\"."
257
+ },
258
+ "options": {
259
+ "type": "array",
260
+ "description": "Reporter options. TAP supports \"single-file\" (default) and \"per-file\".",
261
+ "items": {
262
+ "type": "string"
263
+ }
264
+ },
265
+ "outDir": {
266
+ "type": "string",
267
+ "description": "TAP output directory. Defaults to \"./.as-test/reports\"."
268
+ },
269
+ "outFile": {
270
+ "type": "string",
271
+ "description": "TAP output file path for single-file mode. Defaults to \"./.as-test/reports/report.tap\"."
272
+ }
273
+ },
274
+ "required": ["name"]
275
+ }
276
+ ],
126
277
  "default": ""
127
278
  }
128
279
  },