vitest-pool-assemblyscript 0.2.3 → 0.3.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.
Files changed (38) hide show
  1. package/README.md +111 -192
  2. package/assembly/compare.ts +37 -7
  3. package/assembly/expect.ts +56 -18
  4. package/dist/{ast-visitor-DC3SuTzs.mjs → ast-visitor-lTahoS9R.mjs} +2 -2
  5. package/dist/{ast-visitor-DC3SuTzs.mjs.map → ast-visitor-lTahoS9R.mjs.map} +1 -1
  6. package/dist/{compile-runner-xGvQwgNf.mjs → compile-runner-C2eh_xLp.mjs} +6 -6
  7. package/dist/{compile-runner-xGvQwgNf.mjs.map → compile-runner-C2eh_xLp.mjs.map} +1 -1
  8. package/dist/compiler/transforms/strip-inline.mjs +2 -2
  9. package/dist/{compiler-CN6BRK_N.mjs → compiler-BaNECXMW.mjs} +3 -3
  10. package/dist/{compiler-CN6BRK_N.mjs.map → compiler-BaNECXMW.mjs.map} +1 -1
  11. package/dist/config/index.mjs +5 -4
  12. package/dist/{constants-CA50WBdr.mjs → constants-DX9yo-el.mjs} +11 -2
  13. package/dist/constants-DX9yo-el.mjs.map +1 -0
  14. package/dist/coverage-provider/index.mjs +4 -4
  15. package/dist/{debug-IeEHsxy0.mjs → debug-Cf2jt1kg.mjs} +2 -2
  16. package/dist/{debug-IeEHsxy0.mjs.map → debug-Cf2jt1kg.mjs.map} +1 -1
  17. package/dist/index-internal.mjs +3 -2
  18. package/dist/index-v3.mjs +4 -4
  19. package/dist/index.mjs +5 -4
  20. package/dist/{load-user-imports-Bbmpaciu.mjs → load-user-imports-eGZuxeNp.mjs} +9 -9
  21. package/dist/load-user-imports-eGZuxeNp.mjs.map +1 -0
  22. package/dist/{pool-runner-init-Kuzz61rB.mjs → pool-runner-init-Cdpz_B-F.mjs} +5 -5
  23. package/dist/{pool-runner-init-Kuzz61rB.mjs.map → pool-runner-init-Cdpz_B-F.mjs.map} +1 -1
  24. package/dist/pool-thread/compile-worker-thread.mjs +6 -5
  25. package/dist/pool-thread/compile-worker-thread.mjs.map +1 -1
  26. package/dist/pool-thread/test-worker-thread.mjs +5 -4
  27. package/dist/pool-thread/test-worker-thread.mjs.map +1 -1
  28. package/dist/pool-thread/v3-tinypool-thread.mjs +7 -6
  29. package/dist/pool-thread/v3-tinypool-thread.mjs.map +1 -1
  30. package/dist/{resolve-config-as1w-Qyz.mjs → resolve-config-BKjJQyy5.mjs} +3 -3
  31. package/dist/{resolve-config-as1w-Qyz.mjs.map → resolve-config-BKjJQyy5.mjs.map} +1 -1
  32. package/dist/{test-runner-BR4XyhMA.mjs → test-runner-vGpTcXsw.mjs} +4 -4
  33. package/dist/{test-runner-BR4XyhMA.mjs.map → test-runner-vGpTcXsw.mjs.map} +1 -1
  34. package/dist/{vitest-tasks-BKS7689f.mjs → vitest-tasks-Cbri6MWZ.mjs} +3 -3
  35. package/dist/{vitest-tasks-BKS7689f.mjs.map → vitest-tasks-Cbri6MWZ.mjs.map} +1 -1
  36. package/package.json +1 -1
  37. package/dist/constants-CA50WBdr.mjs.map +0 -1
  38. package/dist/load-user-imports-Bbmpaciu.mjs.map +0 -1
package/README.md CHANGED
@@ -1,26 +1,52 @@
1
1
  # vitest-pool-assemblyscript
2
2
 
3
- AssemblyScript unit testing for your Vitest workflow: Simple, fast, familiar, AS-native.
3
+ <p align="center">
4
+ <img src="docs/images/as-icon.svg" height="50" align="middle">
5
+ &nbsp;&nbsp;&#10133;&nbsp;&nbsp;
6
+ <img src="docs/images/vitest-dark.svg" height="30" align="middle">
7
+ </p>
8
+
9
+ <p align="center">
10
+ AssemblyScript unit testing for your Vitest workflow: Simple, fast, familiar, AS-native.
11
+ <br/>
12
+ <br/>
13
+ <a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat"/></a>
14
+ <a href="https://github.com/themattspiral/vitest-pool-assemblyscript/actions/workflows/release.yml"><img alt="Release Pipeline" src="https://github.com/themattspiral/vitest-pool-assemblyscript/actions/workflows/release.yml/badge.svg"/></a>
15
+ <a href="https://www.npmjs.com/package/vitest-pool-assemblyscript"><img alt="npm package version" src="https://img.shields.io/npm/v/vitest-pool-assemblyscript.svg?style=flat&logo=npm"/></a>
16
+ </p>
4
17
 
5
- This is a [Vitest](https://vitest.dev) [custom pool](https://vitest.dev/guide/advanced/pool.html) which can compile AssemblyScript to WASM, harness WASM to run tests, and report those results to vitest. It co-exists with existing JavaScript/TypeScript tests, and is designed for incremental adoption.
18
+ ---
19
+
20
+ <br/>
21
+
22
+ <p align="center">
23
+ This <a href="https://vitest.dev/guide/advanced/pool.html">custom pool</a> plugs into <a href="https://vitest.dev">Vitest</a>, giving it the ability to compile AssemblyScript, run isolated WASM tests, and report with Vitest's reporters. It co-exists alongside your existing JavaScript/TypeScript tests and coverage reports, and is designed for simple incremental adoption.
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="#status">Status</a> | <a href="#quick-start">Quick Start</a> | <a href="#features">Features</a> | <a href="#compatibility">Compatibility</a> | <a href="#performance">Performance</a> | <a href="#prior-work">Prior Work</a> | <a href="#license">License</a>
28
+ <br/>
29
+ <a href="#writing-tests">Writing Tests</a> | <a href="docs/matchers-api.md">Matchers API</a> | <a href="docs/configuration-guide.md">Configuration Guide</a> | <a href="docs/providing-wasm-imports.md">Providing WASM Imports</a>
30
+ </p>
31
+
32
+ ---
33
+
34
+ <br/>
35
+ <p align="center">
36
+ <img src="docs/images/example-small.gif" alt="vitest-pool-assemblyscript demo">
37
+ </p>
6
38
 
7
- - [Quick Start](#quick-start)
8
- - [Compatibility](#compatibility)
9
- - [Features](#features)
10
- - [Configuration Guide](docs/configuration-guide.md)
11
- - [Providing WASM Imports](docs/providing-wasm-imports.md)
12
- - [Writing Tests Guide](#writing-tests-guide)
13
- - [Matcher API](#matcher-api)
14
- - [Project Status & Expectations](#project-status--expectations)
15
- - [License](#license)
39
+ ## Status
16
40
 
17
- **Note: 🚧 This project is still early-stage and currently *Under Active Development* 🚧**
18
- - All features listed in the [Features](#features) section are stable and assumed to be bug-free
19
- - Native instrumentation prebuilds are available cross-platform
20
- - Expect matchers are stable (except where noted below), with more coming soon
21
- - See [Project Status & Expectations](#project-status--expectations) to see what's still planned!
41
+ This project is relatively new to the scene, but is being improved every day. Please give it a try!
22
42
 
23
- Please [report a bug / request a feature](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new) if you encounter something you'd like to share!
43
+ - All [listed features](#features) are working and assumed to be bug-free ([report](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new))
44
+ - [`describe()` and `test()` definition APIs](#writing-tests) are stable and not expected to change
45
+ - [`expect()` matchers API](docs/matchers-api.md) is stable and not expected to change. More are coming soon
46
+ - Native instrumentation prebuilds are available [across many platforms](#compatibility)
47
+ - See [Current Limitations & Roadmap](#current-limitations--roadmap) for important limitations to be aware of, and to see what's still planned.
48
+
49
+ Please [report a bug or request a feature](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new) if you have something you'd like to share.
24
50
 
25
51
  ---
26
52
 
@@ -95,25 +121,6 @@ npx vitest run
95
121
 
96
122
  ---
97
123
 
98
- ## Compatibility
99
-
100
- | Dependency | Supported Versions |
101
- |---|---|
102
- | Node.js | 20, 22, 24+ |
103
- | Vitest | 3.2.x, 4.x |
104
- | AssemblyScript | 0.28+ |
105
-
106
- **Platforms with prebuilt native binaries:**
107
-
108
- | | x64 | arm64 |
109
- |---|---|---|
110
- | Linux (glibc) | ✓ | ✓ |
111
- | Linux (musl/Alpine) | ✓ | |
112
- | macOS | ✓ | ✓ |
113
- | Windows | ✓ | ✓ |
114
-
115
- ---
116
-
117
124
  ## Features
118
125
 
119
126
  ### Vitest Integration
@@ -133,33 +140,53 @@ npx vitest run
133
140
  ### Familiar Developer Experience
134
141
  - Suite and test definition using `describe()` and `test()` in AssemblyScript
135
142
  - Inline test option configuration for common vitest options: `timeout`, `retry`, `skip`, `only`, `fails`
136
- - Assertion matching API based on vitest/jest `expect()` API. See [Matcher API](#matcher-api) for the set of supported matchers and differences from JavaScript
143
+ - Assertion matching API based on vitest/jest `expect()` API
144
+ - `.not`, `toBe`, `toBeCloseTo`, `toEqual` (with caveats*), `toStrictEqual`, `toHaveLength`, `toThrowError`, `toBeTruthy`, `toBeFalsy`, `toBeNull`, `toBeNullable`, `toBeNaN` - See [Matchers API](docs/matchers-api.md) for details and differences from JavaScript
137
145
  - Highlighted diffs for assertion and runtime failures, which point to source code
138
- - Source-mapped WASM error stack traces (accurate source `function file:line:column`)
146
+ - Source-mapped WASM error stack traces (accurate AssemblyScript source `function file:line:column`)
139
147
  - AssemblyScript console output captured and provided to vitest for display
140
- - No boilerplate patterns for: `run()`, `endTest()`, `fs.readFile`, `WebAssembly.Instance`, etc
148
+ - AssemblyScript compiler errors output plainly to the console for debugging
149
+ - AssemblyScript source code coverage based on WASM execution, including uncovered source
150
+ - No AssemblyScript boilerplate patterns for: `run()`, `endTest()`, `fs.readFile`, `WebAssembly.Instance`, etc
141
151
 
142
152
  ### Performance & Customization
143
- - Parallel execution thread pool
144
- - Lightweight coverage instrumentation using separate memory
153
+ - Parallel execution thread pools
145
154
  - In-memory binaries and source maps for minimal file I/O
155
+ - Lightweight coverage instrumentation using separate WASM memory (no intermediate JS boundary crossing, no user memory conflicts)
146
156
  - Coverage for inlined (`@inline`) code
147
157
  - Enforced hard timeouts for long-running WASM via thread termination, with intelligent resume
148
158
  - Configurable AssemblyScript compiler options
149
159
  - Configurable test memory size
150
- - Configurable WASM imports with access to memory
160
+ - User-provided WASM imports with access to test memory
151
161
 
152
162
  ---
153
163
 
154
- See also: **[Configuration Guide](docs/configuration-guide.md)** | **[Providing WASM Imports](docs/providing-wasm-imports.md)**
164
+ ## Compatibility
165
+
166
+ | Dependency | Supported Versions |
167
+ |---|---|
168
+ | Node.js | 20, 22, 24+ |
169
+ | Vitest | 3.2.x, 4.x |
170
+ | AssemblyScript | 0.28+ |
171
+
172
+ **Platforms with prebuilt native binaries:**
173
+
174
+ | | x64 | arm64 |
175
+ |---|---|---|
176
+ | Linux (glibc) | ✓ | ✓ |
177
+ | Linux (musl/Alpine) | ✓ | |
178
+ | macOS | ✓ | ✓ |
179
+ | Windows | ✓ | ✓ |
155
180
 
156
181
  ---
157
182
 
158
- ## Writing Tests Guide
183
+ ## Writing Tests
159
184
 
160
- Import `test`, `describe`, `expect` (and `TestOptions` if needed) from `vitest-pool-assemblyscript/assembly`.
185
+ Import `test`, `describe`, `expect` from `vitest-pool-assemblyscript/assembly`. These functions are designed to follow the vitest API as closely as possible, so that everything is familiar and easy to reason about.
161
186
 
162
- `it` is available as an alias for `test`.
187
+ - `it` is available as an alias for `test`
188
+ - `describe` and `test` have inline modifiers to quickly change their status (see below)
189
+ - `TestOptions` allows per-test inline configuration of additional options
163
190
 
164
191
  ```typescript
165
192
  import { test, it, describe, expect, TestOptions } from "vitest-pool-assemblyscript/assembly";
@@ -183,7 +210,7 @@ describe("a suite of math operations", () => {
183
210
  });
184
211
  ```
185
212
 
186
- ### Modifiers: `.skip`, `.only`, `.fails`
213
+ ### Inline Modifiers: `.skip`, `.only`, `.fails`
187
214
 
188
215
  ```typescript
189
216
  test.skip("not ready yet", () => { /* ... */ });
@@ -201,7 +228,9 @@ describe.only("only this suite runs", () => { /* ... */ });
201
228
 
202
229
  ### Inline Test Options
203
230
 
204
- `TestOptions` provides chainable configuration for `timeout`, `retry`, `skip`, `only`, and `fails`. Options can be placed before or after the callback, and suite options are inherited by nested tests and suites.
231
+ `TestOptions` provides chainable configuration for `timeout`, `retry`, `skip`, `only`, and `fails`.
232
+ - Options can be placed before or after the callback
233
+ - Suite options are inherited by nested tests and suites
205
234
 
206
235
  ```typescript
207
236
  // options before callback
@@ -227,152 +256,23 @@ test.fails("expected failure with retry", TestOptions.retry(3), () => {
227
256
  });
228
257
  ```
229
258
 
259
+ See the [Matchers API documentation](docs/matchers-api.md) for details on the available `expect()` methods you can use within your tests.
260
+
230
261
  ### Lifecycle Hooks (Setup & Teardown)
231
262
 
232
263
  Coming Soon!
233
264
 
234
265
  ---
235
266
 
236
- ## Matcher API
237
-
238
- This project aims to follow the [vitest/jest `expect()` API](https://vitest.dev/api/expect.html) as closely as possible, with some necessary differences given AssemblyScript's static-typing.
239
-
240
- The following subset of vitest/jest expect matchers are currently supported:
241
-
242
- ### `.not`
243
- Inverts the matcher that follows. Can be chained.
244
- ```typescript
245
- expect(1).not.toBe(2);
246
- expect("hello").not.toBeNull();
247
- ```
248
-
249
- ### `toBe()`
250
- Checks that a value is what you expect. Primitives and strings are compared directly, and references are checked for reference equality only (including objects and arrays). These comparisons are done using `==`, although it is forgiving of numeric type differences.
251
-
252
- Don't use `toBe` with floating-point numbers - see `toBeCloseTo()` instead.
253
- ```typescript
254
- expect(1 + 1).toBe(2);
255
- expect("hello").toBe("hello");
256
- ```
257
-
258
- > ⚠️ IMPORTANT: this matcher is currently too permissive with type comparison - it deems some numeric values of different types to be the same when they shouldn't actually be. The `toEqual()` matcher is more appropriate for these permissive sementics. Fix coming soon!
267
+ ## Current Limitations & Roadmap
259
268
 
260
- ### `toBeCloseTo()`
261
- Checks if a floating point value is close to what you expect. Using exact equality with floating point numbers often doesn't work correctly, because small internal rounding occurs to be able to represent floats in binary. This rounding means intuitive comparisons will often fail.
269
+ ### Limitations
262
270
 
263
- Comparing strings, integers, or references will fall back to using a `toBe` comparison.
271
+ These are known limitations which are currently being worked on.
264
272
 
265
- Accepts an additional `precision: i32` argument - Number of decimal places that must match for values to be considered close. Defaults to 2 digits, meaning effectively that values must be within 0.005 of each other.
266
- ```typescript
267
- expect(0.1 + 0.2).toBeCloseTo(0.3);
268
- expect(1.005).toBeCloseTo(1.0, 1);
269
- ```
270
-
271
- ### `toEqual()`
272
- Checks that two values have the same value (deep equality). Currently supports checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are compared using `toEqual()` also, while Maps and Sets use their respective rules for membership.
273
-
274
- Primitives, strings, and other object references are compared with `toBe()` rules.
275
-
276
- > ⚠️ IMPORTANT: Does not yet support user-defined object deep equality checking. Coming soon!
277
-
278
- ```typescript
279
- expect([1, 2, 3]).toEqual([1, 2, 3]);
280
- expect(["one", "two", "three"]).toEqual(["one", "two", "three"]);
281
-
282
- // objects use reference equality (deep equality not yet supported)
283
- const a: MyObject = new MyObject();
284
- const b: MyObject = new MyObject();
285
- expect([a, b]).toEqual([a, b]);
286
- ```
287
-
288
- ### `toStrictEqual()`
289
- Alias for `toEqual()`. Currently no differences in AssemblyScript.
290
-
291
- ### `toBeTruthy()` & `toBeFalsey()`
292
- Check that a value is truthy or falsey. Falsey values are `0`, `false`, `""`, and `null`.
293
-
294
- ```typescript
295
- expect(1).toBeTruthy();
296
- expect("hello").toBeTruthy();
297
- expect("").toBeTruthy(); // <-- Empty String is TRUTHY in AS!
298
-
299
- expect(0).toBeFalsey();
300
- expect(NaN).toBeFalsey();
301
- expect(null).toBeFalsey();
302
- ```
303
-
304
- >⚠️ AS Quirk: Unlike in JavaScript, empty string is truthy in AssemblyScript because it is an object reference, not a primitive. An empty string is still an allocated object with a non-zero address, so it evaluates as truthy!
305
-
306
- ### `toBeNull()`
307
- Checks that a value is null (`usize(0)` in AssemblyScript).
308
-
309
- ```typescript
310
- const val: string | null = null;
311
- expect(val).toBeNull();
312
- expect("hello").not.toBeNull();
313
- expect(0).not.toBeNull();
314
- expect(false).not.toBeNull();
315
- ```
316
-
317
- ### `toBeNullable()`
318
- Checks that the type of the value is nullable (can hold `null`). This is a type-level check, not a value check — use `toBeNull()` to check if a value *is* null.
319
-
320
- ```typescript
321
- const val: string | null = null;
322
- expect(val).toBeNullable();
323
- expect("hello").not.toBeNullable();
324
- ```
325
-
326
- ### `toBeNaN()`
327
- Checks that a floating point value is `NaN`.
328
-
329
- ```typescript
330
- expect(NaN).toBeNaN();
331
- expect(1.0).not.toBeNaN();
332
- ```
333
-
334
- ### `toHaveLength()`
335
- Checks that an array or array-like value has the expected length.
336
-
337
- ```typescript
338
- expect([1, 2, 3]).toHaveLength(3);
339
- expect([]).toHaveLength(0);
340
- expect("hello world").toHaveLength(11);
341
- ```
342
-
343
- ### `toThrowError()`
344
- Checks that a function throws an error when called. Optionally checks that the error message matches the provided string. Also available as `toThrow()`.
345
-
346
- ```typescript
347
- expect(() => { throw new Error("boom"); }).toThrowError();
348
- expect(() => { throw new Error("boom"); }).toThrowError("boom");
349
- ```
350
-
351
- >⚠️ AS Quirk: You must provide a callback with a non-inferred type to expect() when using `toThrowError()`, e.g. `expect(() => { returnsNumber(); })` (definitely void) or `expect((): i32 => returnsNumber())` (explicit type)
352
-
353
- >ℹ️ Note: `toThrowError()` does not accept inversion using `expect().not.toThrowError()`
354
-
355
- ### Planned Matchers
356
- `toBeDefined`, `toBeUndefined`, `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan`, `toBeLessThanOrEqual`, `toContain`, `toContainEqual`
357
-
358
- ### Likely Matchers
359
- `toBeOneOf`, `toBeTypeOf`, `toBeInstanceOf`, `toHaveProperty`, `toMatch`
360
-
361
- ---
362
-
363
- ## Project Status & Expectations
364
-
365
- **This is an early-stage project** being developed in the open by an interested individual with a career of experience shipping production code.
366
- - All features listed in the [Features](#features) section are stable and assumed to be bug-free
367
- - Native instrumentation prebuilds are available cross-platform
368
- - Expect matchers are stable (except where noted above), with more coming soon
369
-
370
- Please [report a bug / request a feature](https://github.com/themattspiral/vitest-pool-assemblyscript/issues/new) if you encounter something you'd like to share!
371
-
372
- **⚠️ Known Limitations - Coming Soon:**
373
273
  - **Function-level coverage only**: No statement, branch, or line coverage yet
374
274
  - **No lifecycle hooks**: No setup/teardown hooks yet
375
- - **Watch mode specs only**: Re-runs test files when they are directly changed, but not yet based on changed source files
275
+ - **Watch mode handles specs only**: Re-runs test files when they are directly changed, but not yet based on changed source files
376
276
  - **`toEqual` doesn't reflect**: Doesn't yet support deep inspection of user-defined objects
377
277
 
378
278
  ### Near Future Roadmap
@@ -384,18 +284,18 @@ Please [report a bug / request a feature](https://github.com/themattspiral/vites
384
284
 
385
285
  **Epic: Testing DX**
386
286
  - Lifecycle hooks (`beforeEach`, `afterEach`, `beforeAll`, `afterAll`)
387
- - Watch mode optimization
388
- - `toEqual` reflection
389
- - expect.soft
390
- - Allow delegating JS/TS to istanbul coverage provider
391
- - Per-file compilation setting override?
287
+ - Watch mode: re-run applicable tests on source file changes
288
+ - `toEqual` reflection for deep equality inspection of user objects
289
+ - expect.soft to prevent fail-fast behavior
290
+ - Allow delegating JS/TS to istanbul coverage provider in addition to v8
291
+ - Maybe: Per-file compilation setting override
392
292
 
393
293
  **Epic: Expand expect matcher API**
394
294
  - Planned: `toBeDefined`, `toBeUndefined`, `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan`, `toBeLessThanOrEqual`, `toContain`, `toContainEqual`
395
- - Probably: `toBeOneOf`, `toBeTypeOf`, `toBeInstanceOf`, `toHaveProperty`, `toMatch`
295
+ - Likely: `toBeOneOf`, `toBeTypeOf`, `toBeInstanceOf`, `toHaveProperty`, `toMatch`
396
296
 
397
297
  **Epic: Spy and Mock**
398
- - TBD
298
+ - Intend to support
399
299
 
400
300
  **✖️ Out of Scope (Currently):**
401
301
  - Generic JS-harness testing of any precompiled WASM binary
@@ -406,11 +306,30 @@ Please [report a bug / request a feature](https://github.com/themattspiral/vites
406
306
 
407
307
  ---
408
308
 
309
+ ## Performance
310
+
311
+ Efforts have been made to compile and run tests as quickly as possible:
312
+ - Separate compile and test execution threads for quicker startup and respawn
313
+ - Compile threads tuned to take advantage of significant Node V8 engine warmup time savings on consecutive AssemblyScript compilations
314
+ - In-memory compiled files and source maps to eliminate intermediate disk I/O
315
+ - Enforced hard timeouts for long-running WASM via thread termination, with intelligent resume
316
+
317
+ As such, it is capable of compiling dozens of test files comprising hundreds of tests in a few seconds.
318
+
319
+ <p align="center">
320
+ <img src="docs/images/example-fixtures-suite.gif" alt="vitest-pool-assemblyscript demo">
321
+ </p>
322
+
323
+ ---
324
+
409
325
  ## Prior Work
410
326
 
411
- There are other (standalone) testing frameworks for AssemblyScript testing which have inspired this project. In particular, many thanks are owed to [assemblyscript-unittest-framework](https://github.com/wasm-ecosystem/assemblyscript-unittest-framework) for inspiring parts of our test discovery and instrumentation walking approaches.
327
+ There are several core pieces of software without which this project would not be possible.
412
328
 
413
- See [Built with AssemblyScript - Testing & Benchmarking](https://www.assemblyscript.org/built-with-assemblyscript.html#testing-benchmarking) for other related work.
329
+ - This project makes direct use of the [AssemblyScript language](https://www.assemblyscript.org) and its fantastic [compiler](https://www.assemblyscript.org/compiler.html)
330
+ - Thanks to the [Vitest team](https://github.com/vitest-dev) for creating the framework in the first place and making it pluggable for different runtimes. Their internal pools were used as extensive reference in early phase development.
331
+ - The key component that allows us to perform WASM instrumentation is [Binaryen](https://github.com/WebAssembly/binaryen), a C++ toolchain infrastructure library for WebAssembly.
332
+ - Particular gratitude is owed to [assemblyscript-unittest-framework](https://github.com/wasm-ecosystem/assemblyscript-unittest-framework) for inspiring parts of our test discovery and instrumentation walking approaches.
414
333
 
415
334
  ---
416
335
 
@@ -90,7 +90,6 @@ export function identical<T, U>(actual: T, expected: U): bool {
90
90
  if ( (isBoolean<T>() && !isBoolean<U>()) || (!isBoolean<T>() && isBoolean<U>())
91
91
  ) {
92
92
  // when one is boolean and the other is not, they are not identical
93
- // use toEqual for this comparison
94
93
  return false;
95
94
  } else if (isInteger<T>() && isInteger<U>()) {
96
95
  if (isSigned<T>() && isSigned<U>()) {
@@ -111,6 +110,28 @@ export function identical<T, U>(actual: T, expected: U): bool {
111
110
  } else if (isFloat<T>() && isFloat<U>()) {
112
111
  return f64(actual) === f64(expected);
113
112
  } else if ( (isFloat<T>() && isInteger<U>()) || (isInteger<T>() && isFloat<U>()) ) {
113
+ // Reject combinations where the float's mantissa cannot losslessly represent
114
+ // the integer type's full range. This mirrors AssemblyScript's own == operator,
115
+ // which rejects these same combinations at compile time (e.g. f32 == i32, f64 == i64).
116
+ if (isFloat<T>() && isInteger<U>()) {
117
+ if (sizeof<U>() >= sizeof<T>()) {
118
+ throw new Error(
119
+ "Cannot compare " + nameof<T>() + " with " + nameof<U>()
120
+ + ": float precision is insufficient for the integer type's range."
121
+ + " Cast both values to f64 before comparing, e.g. expect(f64(a)).toBe(f64(b))."
122
+ + " Note: large integer values may lose precision when cast to f64, which could cause false positives."
123
+ );
124
+ }
125
+ } else {
126
+ if (sizeof<T>() >= sizeof<U>()) {
127
+ throw new Error(
128
+ "Cannot compare " + nameof<T>() + " with " + nameof<U>()
129
+ + ": float precision is insufficient for the integer type's range."
130
+ + " Cast both values to f64 before comparing, e.g. expect(f64(a)).toBe(f64(b))."
131
+ + " Note: large integer values may lose precision when cast to f64, which could cause false positives."
132
+ );
133
+ }
134
+ }
114
135
  return f64(actual) === f64(expected);
115
136
  } else if (isVector<T>() && isVector<U>()) {
116
137
  return <v128>actual == <v128>expected;
@@ -121,18 +142,24 @@ export function identical<T, U>(actual: T, expected: U): bool {
121
142
  }
122
143
 
123
144
  export function closeTo<T, U>(actual: T, expected: U, precision: i32 = 2): bool {
124
- const match = equals(actual, expected);
125
- if (match) {
126
- return true;
127
- }
145
+ // Note: closeTo intentionally does NOT delegate to equals()/identical() for its
146
+ // initial exact-match check, because identical() now throws for float/integer
147
+ // combinations the language rejects (e.g. f32 vs i32). closeTo handles these
148
+ // via its own f64 promotion, which is appropriate for approximate comparison.
128
149
 
129
150
  if (isString<T>() && isString<U>()) {
130
- return match;
151
+ return <string>actual == <string>expected;
131
152
  }
132
153
 
133
154
  if ( (isFloat<T>() || isInteger<T>()) && (isFloat<U>() || isInteger<U>()) ) {
134
155
  const actualF64: f64 = f64(actual);
135
156
  const expectedF64: f64 = f64(expected);
157
+
158
+ // exact match shortcut (also handles ±Infinity)
159
+ if (actualF64 === expectedF64) {
160
+ return true;
161
+ }
162
+
136
163
  const expectedDiff: f64 = 10.0 ** -precision / 2.0;
137
164
  const receivedDiff: f64 = Math.abs(expectedF64 - actualF64);
138
165
  return receivedDiff < expectedDiff;
@@ -186,7 +213,10 @@ export function equals<T, U>(actual: T, expected: U): bool {
186
213
  }
187
214
 
188
215
  // TODO value compare
189
- throw new Error("Comparison of user-defined object types not yet implemented");
216
+ throw new Error("Deep equality comparison of user-defined reference types"
217
+ + " is not yet implemented, and these references are not identical."
218
+ + " Use toBe() for reference equality."
219
+ );
190
220
  }
191
221
 
192
222
  export function truthyOrFalsey<T>(actual: T, expected: bool): bool {
@@ -92,13 +92,30 @@ abstract class BaseExpectMatcher<T> {
92
92
  // }
93
93
 
94
94
  /**
95
- * Checks that a value is what you expect. Primitives and strings are compared directly,
96
- * and references are checked for reference equality only (including objects, arrays, etc).
97
- * Don't use `toBe` with floating-point numbers - see `toBeCloseTo` instead.
95
+ * Checks that a value is what you expect using identity comparison. Primitives and strings
96
+ * are compared by value, and references are checked for reference equality only (including
97
+ * objects, arrays, etc).
98
+ *
99
+ * Cross-type numeric comparisons are allowed where AssemblyScript's own `==` operator
100
+ * permits them (e.g. `f64` vs `i32`). Combinations where the float type lacks sufficient
101
+ * mantissa precision for the integer type's range are rejected with an error, mirroring
102
+ * the AS compiler's behavior (e.g. `f32` vs `i32`, `f64` vs `i64`).
103
+ *
104
+ * Don't use `toBe` to compare two floating-point numbers — see `toBeCloseTo` instead.
98
105
  *
99
106
  * @example
100
107
  * expect(1 + 1).toBe(2);
101
108
  * expect("hello").toBe("hello");
109
+ *
110
+ * // cross-type integer comparisons
111
+ * expect(i64(42)).toBe(u8(42));
112
+ *
113
+ * // supported float/integer comparisons (small integer types)
114
+ * expect(f64(42.0)).toBe(i32(42));
115
+ *
116
+ * // unsupported float/integer comparisons throw an error
117
+ * // expect(f32(42.0)).toBe(i32(42)); // Error: float precision insufficient
118
+ * // expect(f64(42.0)).toBe(i64(42)); // Error: float precision insufficient
102
119
  */
103
120
  toBe<U>(val: U): void {
104
121
  this.assertComparison(identical(this.actual, val), this.actual, val, "to be", true);
@@ -108,8 +125,8 @@ abstract class BaseExpectMatcher<T> {
108
125
  * Checks if a value is close to what you expect. Using exact equality with floating point
109
126
  * numbers often doesn't work correctly, because small internal rounding occurs to be able
110
127
  * to represent floats in binary. This rounding means intuitive comparisons will often fail.
111
- *
112
- * Comparing strings, integers, or references will fall back to using a `toBe` comparison.
128
+ *
129
+ * Strings are compared by value equality. Non-numeric, non-string types return false.
113
130
  *
114
131
  * @param precision - Specify an integer representing the number of decimal places
115
132
  * that must match for values to be considered close. Defaults to 2 digits, meaning effectively
@@ -124,12 +141,17 @@ abstract class BaseExpectMatcher<T> {
124
141
  }
125
142
 
126
143
  /**
127
- * Checks that two values have the same value (deep equality). Currently supports
128
- * checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are
129
- * compared using `toEqual()` also, while Maps and Sets use their respective rules
130
- * for membership. Primitives, strings, and other object references are compared with
144
+ * Checks that two values have the same value (deep equality). Currently supports
145
+ * checking equality of Arrays, Sets, Maps, and nulls. Values inside arrays are
146
+ * compared using `toEqual()` also, while Maps and Sets use their respective rules
147
+ * for membership. Primitives, strings, and other object references are compared with
131
148
  * `toBe()` rules.
132
149
  *
150
+ * Like `toBe`, cross-type numeric comparisons follow AssemblyScript's own `==` operator
151
+ * restrictions. Combinations where the float type lacks sufficient mantissa precision
152
+ * for the integer type's range are rejected with an error (e.g. `f32` vs `i32`,
153
+ * `f64` vs `i64`).
154
+ *
133
155
  * Note: Does not yet support user-defined object deep equality checking.
134
156
  *
135
157
  * @example
@@ -156,27 +178,43 @@ abstract class BaseExpectMatcher<T> {
156
178
  }
157
179
 
158
180
  /**
159
- * Checks that a value is truthy (not `0`, `false`, `""`, or `null`).
181
+ * Checks that a value is truthy (not `0`, `false`, `NaN`, or `null`).
182
+ *
183
+ * Unlike in JavaScript, empty string (`""`) is truthy in AssemblyScript because it is
184
+ * an object reference, not a primitive. An empty string is still an allocated object
185
+ * with a non-zero address, so it evaluates as truthy.
160
186
  *
161
187
  * @example
162
188
  * expect(1).toBeTruthy();
163
189
  * expect("hello").toBeTruthy();
190
+ * expect("").toBeTruthy(); // truthy in AS (unlike JS)
164
191
  */
165
192
  toBeTruthy(): void {
166
193
  this.assertComparison(truthyOrFalsey(this.actual, true), this.actual, true, "to be truthy", false);
167
194
  }
168
195
 
169
196
  /**
170
- * Checks that a value is falsey (`0`, `false`, `""`, or `null`).
197
+ * Checks that a value is falsy (`0`, `false`, `NaN`, or `null`).
198
+ *
199
+ * Unlike in JavaScript, empty string (`""`) is NOT falsy in AssemblyScript because it is
200
+ * an object reference, not a primitive. An empty string is still an allocated object
201
+ * with a non-zero address, so it evaluates as truthy.
171
202
  *
172
203
  * @example
173
- * expect(0).toBeFalsey();
174
- * expect("").toBeFalsey();
204
+ * expect(0).toBeFalsy();
205
+ * expect(NaN).toBeFalsy();
206
+ * expect(null).toBeFalsy();
207
+ * expect("").not.toBeFalsy(); // not falsy in AS (unlike JS)
175
208
  */
176
- toBeFalsey(): void {
209
+ toBeFalsy(): void {
177
210
  this.assertComparison(truthyOrFalsey(this.actual, false), this.actual, false, "to be falsey", false);
178
211
  }
179
212
 
213
+ /** @deprecated Use `toBeFalsy()` instead. */
214
+ toBeFalsey(): void {
215
+ this.toBeFalsy();
216
+ }
217
+
180
218
  /**
181
219
  * Checks that a value is null (`usize(0)` in AssemblyScript).
182
220
  *
@@ -239,11 +277,11 @@ abstract class BaseExpectMatcher<T> {
239
277
  const nonNullActual = <NonNullable<T>>this.actual;
240
278
 
241
279
  if (isFloat<U>()) {
242
- // @ts-ignore
243
- this.assertComparison<number, U>(closeTo<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
280
+ // @ts-ignore: .length is i32; use closeTo for float comparison
281
+ this.assertComparison<i32, U>(closeTo<i32, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
244
282
  } else {
245
- // @ts-ignore
246
- this.assertComparison<number, U>(identical<number, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
283
+ // @ts-ignore: .length is i32; compare as integers
284
+ this.assertComparison<i32, U>(identical<i32, U>(nonNullActual.length, length), nonNullActual.length, length, "to have length", true);
247
285
  }
248
286
  } else {
249
287
  this.assertComparison(false, this.actual, length, "to have length", true);
@@ -1,4 +1,4 @@
1
- import { ASNodeKind } from "./constants-CA50WBdr.mjs";
1
+ import { ASNodeKind } from "./constants-DX9yo-el.mjs";
2
2
 
3
3
  //#region src/util/ast-visitor.ts
4
4
  /**
@@ -307,4 +307,4 @@ var ASTVisitor = class {
307
307
 
308
308
  //#endregion
309
309
  export { ASTVisitor };
310
- //# sourceMappingURL=ast-visitor-DC3SuTzs.mjs.map
310
+ //# sourceMappingURL=ast-visitor-lTahoS9R.mjs.map