@metamesh/testkit 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +199 -0
- package/README.md +121 -110
- package/dist/assertions.d.ts +8 -15
- package/dist/assertions.d.ts.map +1 -1
- package/dist/assertions.js +24 -70
- package/dist/assertions.js.map +1 -1
- package/dist/content-assertions.d.ts +28 -0
- package/dist/content-assertions.d.ts.map +1 -0
- package/dist/content-assertions.js +75 -0
- package/dist/content-assertions.js.map +1 -0
- package/dist/content-matchers.d.ts +12 -0
- package/dist/content-matchers.d.ts.map +1 -0
- package/dist/content-matchers.js +22 -0
- package/dist/content-matchers.js.map +1 -0
- package/dist/docs/guides/package.md +121 -110
- package/dist/docs/manifest.json +157 -45
- package/dist/docs/reference/font-fixtures/LoadedFontFixtures.md +1 -1
- package/dist/docs/reference/font-fixtures/loadFontFixtureServices.md +1 -1
- package/dist/docs/reference/font-fixtures/loadFontFixtures.md +1 -1
- package/dist/docs/reference/fonts/FontTestFixture.md +1 -1
- package/dist/docs/reference/fonts/prepareFontTestServices.md +1 -1
- package/dist/docs/reference/index/BufferContent.md +13 -0
- package/dist/docs/reference/index/ContentMismatchError.md +12 -0
- package/dist/docs/reference/index/GeneratorModelTestInput.md +13 -0
- package/dist/docs/reference/index/GeneratorModelTestOptions.md +10 -0
- package/dist/docs/reference/index/GeneratorTestResult.md +18 -0
- package/dist/docs/reference/index/GeneratorTestRunner.md +9 -11
- package/dist/docs/reference/index/GeneratorTestRunnerOptions.md +2 -3
- package/dist/docs/reference/index/MeshContent.md +13 -0
- package/dist/docs/reference/index/MeshMeasurementExpectation.md +3 -3
- package/dist/docs/reference/index/MeshMeasurementTolerances.md +1 -1
- package/dist/docs/reference/index/RunModelOptions.md +13 -0
- package/dist/docs/reference/index/assertEqualBuffer.md +15 -0
- package/dist/docs/reference/index/assertEqualMesh.md +14 -0
- package/dist/docs/reference/index/{expectMeshMeasurements.md → assertMeshMeasurements.md} +3 -3
- package/dist/docs/reference/index/{expectValidDefaults.md → assertValidDefaults.md} +3 -3
- package/dist/docs/reference/index/assertValidDefinition.md +10 -0
- package/dist/docs/reference/index/assertValidParts.md +12 -0
- package/dist/docs/reference/index/{expectValidPresets.md → assertValidPresets.md} +3 -3
- package/dist/docs/reference/index/contentMatchers.md +12 -0
- package/dist/docs/reference/index/createGeneratorTestRunner.md +2 -4
- package/dist/docs/reference/index/getTestEngine.md +1 -1
- package/dist/docs/reference/index/runModel.md +13 -0
- package/dist/generator-execution.d.ts +6 -0
- package/dist/generator-execution.d.ts.map +1 -0
- package/dist/generator-execution.js +20 -0
- package/dist/generator-execution.js.map +1 -0
- package/dist/generator-test-runner.d.ts +32 -0
- package/dist/generator-test-runner.d.ts.map +1 -0
- package/dist/generator-test-runner.js +109 -0
- package/dist/generator-test-runner.js.map +1 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/measurements.d.ts +4 -4
- package/dist/measurements.d.ts.map +1 -1
- package/dist/measurements.js +10 -9
- package/dist/measurements.js.map +1 -1
- package/dist/run-model.d.ts +21 -0
- package/dist/run-model.d.ts.map +1 -0
- package/dist/run-model.js +39 -0
- package/dist/run-model.js.map +1 -0
- package/dist/{engine.d.ts → test-engine.d.ts} +1 -1
- package/dist/test-engine.d.ts.map +1 -0
- package/dist/{engine.js → test-engine.js} +1 -1
- package/dist/test-engine.js.map +1 -0
- package/package.json +8 -5
- package/dist/docs/reference/index/GeneratorTestExecution.md +0 -19
- package/dist/docs/reference/index/ModelTestOptions.md +0 -13
- package/dist/docs/reference/index/expectSerializableDefinition.md +0 -10
- package/dist/docs/reference/index/expectValidParts.md +0 -19
- package/dist/docs/reference/index/withModel.md +0 -17
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js.map +0 -1
- package/dist/model.d.ts +0 -17
- package/dist/model.d.ts.map +0 -1
- package/dist/model.js +0 -22
- package/dist/model.js.map +0 -1
- package/dist/runner.d.ts +0 -38
- package/dist/runner.d.ts.map +0 -1
- package/dist/runner.js +0 -130
- package/dist/runner.js.map +0 -1
- package/dist/snapshot-scope.d.ts +0 -4
- package/dist/snapshot-scope.d.ts.map +0 -1
- package/dist/snapshot-scope.js +0 -10
- package/dist/snapshot-scope.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,204 @@
|
|
|
1
1
|
# @metamesh/testkit
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c9de510: Extend straight extrusion end finishing to concave outlines, holes, and disconnected regions.
|
|
8
|
+
Add independent-solid `join` with a feature datum and a smooth (default) or straight width/height
|
|
9
|
+
transition against validated planar, angled, and curved sampled support patches. Both profiles use
|
|
10
|
+
the same support, collar, and whole-volume containment proofs.
|
|
11
|
+
|
|
12
|
+
Breaking: compiled Generators require Modeling runtime major 12. Rebuild packages for the new
|
|
13
|
+
host contract; old compiled runtimes are rejected. Joining preserves the supplied operands and
|
|
14
|
+
does not promise a circular fillet radius or perform automatic placement.
|
|
15
|
+
|
|
16
|
+
Replace named lazy `computed` readers with eager `memo(fn, ...args)` values. Stable pure helpers
|
|
17
|
+
reuse immutable arguments and tracked current params/context reads, with bounded recent histories
|
|
18
|
+
and native geometry retention. Update Studio diagnostics to warn about fresh function identities.
|
|
19
|
+
All authored and compiled callers must use the new API; there is no compatibility surface.
|
|
20
|
+
|
|
21
|
+
- de3d234: Add runtime-neutral STL, 3MF, and GLB export under `@metamesh/modeling/export`, with isolated
|
|
22
|
+
format entry points and backpressure-aware streaming writers. All formats consume captured local
|
|
23
|
+
geometry, placement, and appearance, with independent limits, cancellation, and structured
|
|
24
|
+
information-loss diagnostics. Standard 3MF color groups carry appearance, while base materials
|
|
25
|
+
and multiproperties retain explicit material identities independently of RGB values. These
|
|
26
|
+
properties do not assign slicer filaments or physical device trays. Add `mesh.render()` for
|
|
27
|
+
shared crease-aware display buffers that preserve exact triangle coordinates.
|
|
28
|
+
|
|
29
|
+
Breaking: remove `export3mf` and export types from `/engine`, remove SDK export request protocols
|
|
30
|
+
and `toWorldMeshes`, and return owned `Uint8Array` bytes with format metadata from the shared
|
|
31
|
+
exporters. Studio uses one export worker for every format. CLI captures use format version 3;
|
|
32
|
+
replace `run --export-3mf` with `metamesh export RUN_DIRECTORY --format 3mf --output model.3mf`.
|
|
33
|
+
Captured-run export also supports STL and GLB without rerunning source. No legacy export paths
|
|
34
|
+
or capture readers are retained.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [c9de510]
|
|
39
|
+
- Updated dependencies [de3d234]
|
|
40
|
+
- @metamesh/modeling@0.19.0
|
|
41
|
+
- @metamesh/generator-sdk@0.19.0
|
|
42
|
+
|
|
43
|
+
## 0.18.0
|
|
44
|
+
|
|
45
|
+
### Minor Changes
|
|
46
|
+
|
|
47
|
+
- 2eea694: Consolidate model authoring, execution, and testing around fluent shapes and durable immutable
|
|
48
|
+
results. Upgrade all four packages together; these Preview API changes have no compatibility
|
|
49
|
+
aliases. Rebuild compiled Generators for module format 5 and Modeling runtime major 11. Older
|
|
50
|
+
module formats and incompatible runtime versions are rejected before execution.
|
|
51
|
+
|
|
52
|
+
### Modeling imports and operations
|
|
53
|
+
|
|
54
|
+
| Previous API | 0.18 API |
|
|
55
|
+
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| `body.subtract([cutter])` for a single operand | `body.subtract(cutter)`; fluent `union`, `intersect`, and `subtract` accept one shape or a readonly array |
|
|
57
|
+
| `compose(shapes)` | `union(shapes)` |
|
|
58
|
+
| Free shape inspection, emptiness, component, and anchor queries | `shape.inspect()`, `shape.isEmpty()`, `shape.components()`, and `shape.anchor(name)` |
|
|
59
|
+
| Free Solid distance/segment queries and splitting functions | `solid.distance(other)`, `solid.castSegment(...)`, `solid.split(cutter)`, and `solid.splitByPlane(plane)` |
|
|
60
|
+
| `solid.toTriangleMesh()` | `solid.toMesh()` |
|
|
61
|
+
| Named `mesh` import from `/geometry`, or standalone triangle-mesh functions | `import * as mesh from "@metamesh/modeling/geometry/mesh"` |
|
|
62
|
+
| Standalone `immutableMesh(...)` | `mesh.immutable(...)` |
|
|
63
|
+
| `helicalThreadRidgeMesh`, `helicalThreadSolidMesh`, and `preflightHelicalThreadSolidMesh` | `mesh.threadRidge`, `mesh.threadSolid`, and `mesh.preflightThreadSolid` |
|
|
64
|
+
| `HelicalThreadRidgeMeshOptions`, `HelicalThreadSolidMeshOptions`, and `HelicalThreadSolidMeshPreflight` | `ThreadRidgeOptions`, `ThreadSolidOptions`, and `ThreadSolidPreflight`, exported from the mesh module |
|
|
65
|
+
| Mesh `connectedComponentCount` | `shellCount`; native inspection `componentCount` still counts material components |
|
|
66
|
+
| Inspection extrema on the inspection result | Immutable `inspection.bounds.min` and `inspection.bounds.max` |
|
|
67
|
+
| `starContour(outerRadius, points, innerRadius, limits)` | `starContour({ outerRadius, innerRadius, points, limits })` |
|
|
68
|
+
|
|
69
|
+
Free `union(shapes)` and `intersect(shapes)` remain for collections of peers and reject empty
|
|
70
|
+
collections. Fluent Boolean methods with an empty array return the receiver.
|
|
71
|
+
|
|
72
|
+
Use `section.extrude(options)` in fluent modeling, `extrude(profile, options)` for generic
|
|
73
|
+
`Section | CorrespondingProfile` inputs, and `mesh.extrude(contour, options)` for pure triangle
|
|
74
|
+
buffers. The same fluent/generic distinction applies to revolution and sweeping. Pure extrusion
|
|
75
|
+
uses `ExtrudeMeshOptions` and rejects `ends`; use Section extrusion for native end treatments.
|
|
76
|
+
Construction, transforms, and splitting remain deferred recipes; observations and `toMesh()`
|
|
77
|
+
require active model execution.
|
|
78
|
+
|
|
79
|
+
Point-loop and arbitrary Section lofts default to direct correspondence when the method is
|
|
80
|
+
omitted; known primitive landmarks remain preserved. Dimension inputs accept fluent vectors.
|
|
81
|
+
Tube sizing accepts complete inner/outer radius or diameter pairs. Both star factories require
|
|
82
|
+
an explicit inner radius and support degree rotation; use `innerRadius: outerRadius / 2` to retain
|
|
83
|
+
the contour helper's former default.
|
|
84
|
+
|
|
85
|
+
Rod and internal-cutter `bevelBottom`, `bevelTop`, and `bevelSize` become `bevel`. The shared
|
|
86
|
+
`Bevel` contract accepts a nonnegative size, `"auto"`, or a readonly `[bottom, top]` pair; zero
|
|
87
|
+
disables an end and omission preserves factory defaults. Complete-thread mesh builders use the
|
|
88
|
+
same contract, with thread termination under `ends`. Nuts replace bevel selection, `chamferSize`,
|
|
89
|
+
and `flushBuildPlateBottom` with independent `innerBevel` and `outerBevel`. For a flat outside
|
|
90
|
+
bottom that retains its inner opening bevel, use `outerBevel: [0, "auto"]`. Nut preflight exposes
|
|
91
|
+
resolved inner/outer pairs and checks face walls pairwise; each end's reservation and automatic
|
|
92
|
+
sizing are resolved independently, including internal-cutter axial extension.
|
|
93
|
+
|
|
94
|
+
Primitive contours and their points are immutable and can be explicit `computed` dependencies.
|
|
95
|
+
Copy the outer array and any points before editing. Geometry errors are available from the
|
|
96
|
+
Modeling root; operation option/result types are exported beside their owning operations.
|
|
97
|
+
Topology analysis with `includeTopology: true`, finalized parts, and local geometry resources
|
|
98
|
+
include required `shellCount` and `edgeCount`. Bounds construction options describe inputs only;
|
|
99
|
+
existing fluent bounds remain accepted.
|
|
100
|
+
|
|
101
|
+
### Part placement and result ownership
|
|
102
|
+
|
|
103
|
+
Replace `part(id, { geometry, position, rotation })` with
|
|
104
|
+
`part(id, { geometry, transform })`. The optional transform accepts a fluent `transform3()` value,
|
|
105
|
+
a row-major 16-number matrix, or a computed reader; omission means identity. Pattern and
|
|
106
|
+
attachment matrices pass directly to `part()`. Only rigid placement is supported: scale, shear,
|
|
107
|
+
reflection, perspective, and non-finite values are rejected. Geometry-only and geometry-or-options
|
|
108
|
+
union inputs remain accepted without casts.
|
|
109
|
+
|
|
110
|
+
Use `PartDeclaration` for authored parts. Replace `ModelSnapshot` / `FinalizedModel` with
|
|
111
|
+
`ModelResult`, and `SnapshotPart` / `FinalizedPart` with `ModelPart`. `engine.run()`, `session.run()`,
|
|
112
|
+
and Testkit's `runModel()` return durable immutable results. Dispose sessions and runners to release their
|
|
113
|
+
caches; completed results remain valid and need no disposal. `model.part(id)` returns a required
|
|
114
|
+
semantic part or throws with available IDs; `model.parts` preserves output order.
|
|
115
|
+
|
|
116
|
+
Finalized buffers expose stable, iterable `ImmutableBuffer` values. Use `.copy()` for mutable
|
|
117
|
+
storage. A `ModelPart`'s top-level positions and indices are already in world space; apply its
|
|
118
|
+
immutable `transform` only to the reusable local buffers under `geometry`. Worker deliveries also
|
|
119
|
+
nest local resources under `geometry`. Host integrations use `ModelDeliveryEncoder`,
|
|
120
|
+
`PartDeliveryDecoder`, and `toWorldMeshes` for owned transfer and manufacturing output.
|
|
121
|
+
Replace `engine.export3mf(...)` with standalone `export3mf(parts, options)` from `/engine`, which
|
|
122
|
+
accepts completed meshes with explicit resource limits, independently of engine initialization.
|
|
123
|
+
|
|
124
|
+
Statistics rename `volumeMm3` → `signedVolume`, `finalizationMs` → `finalizeMs`, and
|
|
125
|
+
`generationMs` → `generateMs`; `buildMs` measures declarations.
|
|
126
|
+
`ModelResult.stats.byteCount` means bytes occupied by finalized world-space position and index buffers,
|
|
127
|
+
counting shared buffers once. Worker statistics preserve engine `generateMs`; host elapsed time
|
|
128
|
+
is `timings.totalMs`, with `paramsMs` for parameter work. Other host timing stages are optional:
|
|
129
|
+
an omitted stage is unmeasured.
|
|
130
|
+
|
|
131
|
+
Model parameter roots must be object maps. Captured parameters and computed results expose
|
|
132
|
+
deeply readonly `ImmutableValue<T>` views; supported mutable caller input is still accepted and
|
|
133
|
+
recaptured on every revision, including mutable children inside frozen objects. Deeply immutable
|
|
134
|
+
data retains identity reuse. Assertions and warnings accept bounded immutable nested `path`
|
|
135
|
+
metadata. Replace `validateModel` with `checkModel` and session/runner `.validate()` with `.check()`.
|
|
136
|
+
These check declarations and reached assertions without finalizing geometry; unexpected execution
|
|
137
|
+
failures still throw.
|
|
138
|
+
|
|
139
|
+
### Generator SDK and Testkit
|
|
140
|
+
|
|
141
|
+
| Previous API | 0.18 API |
|
|
142
|
+
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| Nested compiled Generator module | Flat format-5 model program; bind an existing program with `defineGeneratorModule({ definition, program })` |
|
|
144
|
+
| `isGeneratorModule(value)` | `parseGeneratorModule(value, definition)` |
|
|
145
|
+
| `executeGenerator({ ..., parsedParameters })` | `executeGenerator({ ..., parameters })`; current-definition validation always runs |
|
|
146
|
+
| Callback-scoped `withModel`, `ModelTestOptions`, or `runner.withModel` | `await runModel(programOrCallback, ...)` for one-off results; explicit sessions or Generator runners for repeated edits |
|
|
147
|
+
| Generator test `runner.execute(...)` | `runner.run(...)`, returning `GeneratorTestResult` |
|
|
148
|
+
| `expectSerializableDefinition` | `assertValidDefinition` |
|
|
149
|
+
| `expectValidDefaults`, `expectValidPresets`, and `expectValidParts` | `assertValidDefaults`, `assertValidPresets`, and `assertValidParts` |
|
|
150
|
+
| `expectMeshMeasurements` | `assertMeshMeasurements` |
|
|
151
|
+
| `canonicalizeParameters(definition, parameters)` from `/parameters` | `canonicalizeParameters(parameters)` from `/runtime` |
|
|
152
|
+
| `GeneratedPartsDecoder`, `parseGeneratedParts`, and `GeneratedPartsLimits` | `PartDeliveryDecoder`, `parseModelParts`, and `PartDeliveryLimits` |
|
|
153
|
+
| `GeneratedPart` and `GeneratedModel` | `ModelPartData` and `ModelData` |
|
|
154
|
+
| `GeneratedPartDelivery` and `GeneratedModelDelivery` | `PartDelivery` and `ModelDelivery` |
|
|
155
|
+
|
|
156
|
+
Testkit `runModel` accepts existing `ModelProgram` values and synchronous inline callbacks;
|
|
157
|
+
typed callbacks use `runModel<Parameters, Services>`. Its definition-bound Generator overload
|
|
158
|
+
validates inputs and prepares services. The SDK Modeling adapter adds definition-inferred
|
|
159
|
+
`GeneratorModel` and `parseGeneratorModel`. Ordinary model tests preserve definition-inferred
|
|
160
|
+
input types. Reusable Generator runners accept physical generation policy plus `cache` and
|
|
161
|
+
`verifyCache` for edit sequences; dispose the runner after use.
|
|
162
|
+
|
|
163
|
+
Generator execution and ordinary tests strictly require complete canonical parameter maps.
|
|
164
|
+
Use the parameter codec for strict input and `normalizeParameters` for forgiving recovery;
|
|
165
|
+
replace `prepareParameters`, `validateParameters`, and `PreparedParameters` with
|
|
166
|
+
`createParamCodec(definition).parse(...)` or `.safeParse(...)`. Schema adapters remain private.
|
|
167
|
+
Codec failures provide structured issues without a substitute parameter map. For deliberate
|
|
168
|
+
malformed-input assertion probes, replace `runner.validate(parameters, { normalize: false })`
|
|
169
|
+
with `runner.check(parameters, { validateInput: false })`.
|
|
170
|
+
Defaults are selected only by omission or `undefined` where supported; a `null` parameter map is
|
|
171
|
+
rejected. Parameterless model calls can omit the empty map. Definition and preset helpers catch
|
|
172
|
+
nested key typos and validate immediately while retaining literal inference.
|
|
173
|
+
|
|
174
|
+
`assertValidParts` checks the supplied world-space buffers, closed topology, and exact agreement
|
|
175
|
+
with local geometry under `transform`; it no longer hides corrupted output by reconstructing it.
|
|
176
|
+
Always await `assertValidParts(parts, engine)` (or omit the engine): failures reject, and supplying
|
|
177
|
+
an engine changes resource reuse only.
|
|
178
|
+
`assertValidDefinition` also checks the canonical JSON round trip. New `assertEqualBuffer` and
|
|
179
|
+
`assertEqualMesh` compare exact ordered contents without copying; equivalent shapes with different
|
|
180
|
+
triangulations can differ. `ContentMismatchError` identifies the first differing element or
|
|
181
|
+
length, while malformed input and reader failures retain their errors. Optional `contentMatchers`
|
|
182
|
+
support `expect.extend`, including negation, without a test-framework dependency.
|
|
183
|
+
|
|
184
|
+
### CLI and package requirements
|
|
185
|
+
|
|
186
|
+
Rename the type-only CLI command `metamesh check` to `metamesh typecheck`; no alias is retained.
|
|
187
|
+
Node authoring APIs and Modeling/Testkit declaration-check methods retain their names. Generator
|
|
188
|
+
runs use `overrides` / `--overrides`, merged with defaults before strict parsing; standalone models
|
|
189
|
+
use `params` / `--params`. Supplying the other entry kind's input option is rejected.
|
|
190
|
+
|
|
191
|
+
Captured runs use format 2 with transform-only placement. The bundled capture renderer resolves
|
|
192
|
+
from the installed CLI package so published installations can render captures.
|
|
193
|
+
Public packages require Node.js 24 or newer, ESM imports, and TypeScript 6.0 or newer with strict
|
|
194
|
+
checking for TypeScript consumers.
|
|
195
|
+
|
|
196
|
+
### Patch Changes
|
|
197
|
+
|
|
198
|
+
- Updated dependencies [2eea694]
|
|
199
|
+
- @metamesh/modeling@0.18.0
|
|
200
|
+
- @metamesh/generator-sdk@0.18.0
|
|
201
|
+
|
|
3
202
|
## 0.17.0
|
|
4
203
|
|
|
5
204
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,146 +1,157 @@
|
|
|
1
1
|
# `@metamesh/testkit`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Check that your MetaMesh models produce the geometry you expect. Run models and Generators with
|
|
4
|
+
the production engine, check their measurements, and test definitions and presets with your
|
|
5
|
+
preferred test runner.
|
|
5
6
|
|
|
6
7
|
> **Preview:** `0.x` minor releases may break compatibility. Patches remain compatible within the
|
|
7
8
|
> same minor line.
|
|
8
9
|
|
|
9
10
|
## Installation
|
|
10
11
|
|
|
11
|
-
Install
|
|
12
|
+
Install matching SDK, Modeling, and Testkit releases, with Testkit as a development dependency:
|
|
12
13
|
|
|
13
14
|
```bash
|
|
14
15
|
pnpm add @metamesh/generator-sdk @metamesh/modeling
|
|
15
16
|
pnpm add -D @metamesh/testkit
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
The package publishes ES modules only. Use Node.js 24 or newer. TypeScript consumers need
|
|
20
|
+
TypeScript 6.0 or newer, `strict` checking, and `NodeNext` or `Bundler` module resolution.
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
[
|
|
22
|
+
The assertions work without a test framework and don't register globals. Testkit has no compiler,
|
|
23
|
+
renderer, or browser dependency. Use the [CLI](https://github.com/JasonKoolman/metamesh/blob/main/packages/cli/README.md)
|
|
24
|
+
to check source files, save captured runs, or render images.
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
import { getDefaultParameters } from "@metamesh/generator-sdk/parameters";
|
|
25
|
-
import { createGeneratorTestRunner, expectValidParts } from "@metamesh/testkit";
|
|
26
|
-
|
|
27
|
-
import runtime, { definition } from "./generator.js";
|
|
28
|
-
|
|
29
|
-
const runner = createGeneratorTestRunner(definition, runtime);
|
|
30
|
-
try {
|
|
31
|
-
await runner.withModel(getDefaultParameters(definition), async (model) => {
|
|
32
|
-
await expectValidParts(model.parts);
|
|
33
|
-
});
|
|
34
|
-
} finally {
|
|
35
|
-
runner.dispose();
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
`withModel` normalizes values, prepares services, and runs the persistent session. It awaits the
|
|
40
|
-
callback, returns its result, and disposes the snapshot even when assertions throw or reject.
|
|
41
|
-
Schema or generation assertion failures throw `GenerationError` with their structured diagnostics.
|
|
42
|
-
Unexpected execution and malformed-output errors retain their original failures. The runner's
|
|
43
|
-
session and caches remain reusable until `runner.dispose()`.
|
|
26
|
+
## Test a model
|
|
44
27
|
|
|
45
|
-
|
|
28
|
+
Build a plate, check its volume and shell count, then check that its meshes reconstruct correctly:
|
|
46
29
|
|
|
47
30
|
```ts
|
|
48
|
-
import { box
|
|
49
|
-
import {
|
|
31
|
+
import { box } from "@metamesh/modeling";
|
|
32
|
+
import { assertMeshMeasurements, assertValidParts, runModel } from "@metamesh/testkit";
|
|
50
33
|
|
|
51
|
-
const
|
|
52
|
-
part("plate", box({ size: [params.width, 20, 4] }))
|
|
34
|
+
const result = await runModel<{ width: number }>(
|
|
35
|
+
({ params, part }) => part("plate", box({ size: [params.width, 20, 4] })),
|
|
36
|
+
{ width: 30 }
|
|
53
37
|
);
|
|
54
|
-
await withModel(model, { width: 30 }, async (snapshot) => {
|
|
55
|
-
await expectValidParts(snapshot.parts);
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
The plain-model helper also disposes its temporary session. Its optional fourth argument accepts
|
|
60
|
-
session policy, prepared `services`, and a test-owned `engine`; declared services are required.
|
|
61
|
-
It passes model parameters directly without Generator normalization and never disposes the engine.
|
|
62
|
-
Previously obtained parts remain usable after snapshot disposal; returning parts does not eagerly
|
|
63
|
-
copy their buffers.
|
|
64
|
-
|
|
65
|
-
Snapshot parts include session-specific geometry resource identities. Compare manufacturing
|
|
66
|
-
fields such as world positions, indices, measurements, IDs and colors for equality across runs.
|
|
67
|
-
Each positions or indices read copies its buffer; capture the buffer once before a dense loop.
|
|
68
|
-
|
|
69
|
-
Use `runner.execute` for explicit snapshot ownership or invalid-result assertions. It returns
|
|
70
|
-
`invalid` for schema or generation assertion errors. Dispose successful snapshots separately from
|
|
71
|
-
the runner; captured snapshots remain valid after runner disposal.
|
|
72
|
-
|
|
73
|
-
Use `runner.validate(parameters, { normalize: false })` to probe deliberately invalid typed values
|
|
74
|
-
without finalizing parts. It checks declarations and reached assertions; observing geometry can
|
|
75
|
-
still initialize the engine. Creating a runner alone loads no engine or assets. Pass `options.engine`
|
|
76
|
-
to reuse a test-owned engine and `options.limits` to lower host ceilings.
|
|
77
|
-
|
|
78
|
-
## Assertions
|
|
79
38
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
| ------------------------------ | ------------------------------------------------------------------ |
|
|
84
|
-
| `expectSerializableDefinition` | Strict parsing and a canonical JSON round trip |
|
|
85
|
-
| `expectValidDefaults` | Strict parameter decoding and agreement with definition defaults |
|
|
86
|
-
| `expectValidPresets` | Serializable preset metadata; use the runner for domain assertions |
|
|
87
|
-
| `expectValidParts` | Strict part metadata, closed topology, and native reconstruction |
|
|
88
|
-
| `expectMeshMeasurements` | Bounds, area, signed volume, and connected-component count |
|
|
89
|
-
|
|
90
|
-
`expectValidParts(parts, engine)` is synchronous and uses that engine's limits. Without an engine,
|
|
91
|
-
it returns a promise using the shared test engine and default limits. It also accepts finalized
|
|
92
|
-
parts, stripping measurement facts before strict validation; other unknown fields reject.
|
|
93
|
-
Snapshot inputs retain shared local mesh buffers and rigid placements in the returned parts.
|
|
94
|
-
|
|
95
|
-
Compare physical measurements with explicit tolerances. For a 30 × 20 × 4 mm box mesh:
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
import { expectMeshMeasurements } from "@metamesh/testkit";
|
|
99
|
-
|
|
100
|
-
expectMeshMeasurements(
|
|
101
|
-
mesh,
|
|
102
|
-
{ signedVolume: 30 * 20 * 4, connectedComponentCount: 1 },
|
|
39
|
+
assertMeshMeasurements(
|
|
40
|
+
result.part("plate"),
|
|
41
|
+
{ signedVolume: 2400, shellCount: 1 },
|
|
103
42
|
{ signedVolume: 0.05 }
|
|
104
43
|
);
|
|
44
|
+
await assertValidParts(result.parts);
|
|
105
45
|
```
|
|
106
46
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
measurements alone do not establish geometric equivalence or exclude self-intersections; use
|
|
110
|
-
`expectValidParts` for native reconstruction.
|
|
47
|
+
`runModel` returns immutable parts, measurements, diagnostics, and dependencies. Results need no
|
|
48
|
+
cleanup. Use `result.part(id)` to find a required part by its ID, or iterate over `result.parts`.
|
|
111
49
|
|
|
112
|
-
|
|
50
|
+
Always await `assertValidParts(parts)`. It checks the world-space buffers for closed topology,
|
|
51
|
+
then reconstructs each part from its local geometry and rigid transform and compares the positions
|
|
52
|
+
and indices exactly. It resolves to the same parts or rejects on failure. Pass an engine as the
|
|
53
|
+
second argument to reuse one you own; otherwise, the shared test engine initializes when needed.
|
|
113
54
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
55
|
+
You can pass an existing Modeling program: `await runModel(program, parameters)` uses the same
|
|
56
|
+
program and parameter types as `engine.run`. For a small test, pass a synchronous callback
|
|
57
|
+
directly. Without parameters, no type argument or wrapper is needed:
|
|
58
|
+
`await runModel(({ part }) => part("cube", box({ size: 2 })))`.
|
|
59
|
+
Parameters keep the program or callback's declared type. Use
|
|
60
|
+
`GenerateCallback<Parameters, Services>` for a reusable callback or supply
|
|
61
|
+
`runModel<Parameters, Services>` inline. Options stay in the third argument; use `undefined` for
|
|
62
|
+
omitted parameters when supplying an engine, limits, or prepared services.
|
|
118
63
|
|
|
119
|
-
|
|
120
|
-
checked-in bytes. Install Modeling's optional `fontkit` peer, plus `woff2-encoder` for WOFF2 fixtures.
|
|
121
|
-
Tests need no font downloads or browser cache; retain the fixtures' licenses.
|
|
64
|
+
## Assertions
|
|
122
65
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
`
|
|
127
|
-
|
|
66
|
+
| Helper | Checks |
|
|
67
|
+
| ------------------------ | -------------------------------------------------------------- |
|
|
68
|
+
| `assertValidDefinition` | Strict parsing and a canonical JSON round trip. |
|
|
69
|
+
| `assertValidDefaults` | Strict decoding and agreement with definition defaults. |
|
|
70
|
+
| `assertValidPresets` | Complete, normalized preset metadata. |
|
|
71
|
+
| `assertValidParts` | Part metadata, closed topology, and native reconstruction. |
|
|
72
|
+
| `assertMeshMeasurements` | Bounds, surface area, signed volume, and surface shell count. |
|
|
73
|
+
| `assertEqualBuffer` | Exact ordered numeric buffer contents without copying storage. |
|
|
74
|
+
| `assertEqualMesh` | Exact positions and indices, independently of mesh metadata. |
|
|
75
|
+
|
|
76
|
+
`assertValidDefinition` requires strict SDK parsing and a canonical JSON round trip; it does not
|
|
77
|
+
execute the Generator. Check model assertions and domain constraints through the Generator runner.
|
|
78
|
+
|
|
79
|
+
Measurement tolerances are absolute: millimetres for bounds, mm² for area, and mm³ for volume.
|
|
80
|
+
They default to zero; shell counts are exact. Matching measurements do not prove geometric
|
|
81
|
+
equivalence or rule out self-intersections. Use native reconstruction checks alongside them.
|
|
82
|
+
|
|
83
|
+
Use `assertEqualBuffer(actual, expected)` or `assertEqualMesh(actual, expected)` for exact content
|
|
84
|
+
comparisons. They read `length` and `at(index)` without copying storage and throw
|
|
85
|
+
`ContentMismatchError` with the first differing index or length. Malformed representations throw
|
|
86
|
+
`TypeError`; reader failures propagate. Numeric equality uses `Object.is`: `NaN` equals `NaN` and
|
|
87
|
+
signed zero differs. Mesh comparisons ignore metadata and require identical position and index
|
|
88
|
+
contents. Geometrically equivalent meshes with different triangulation or ordering can fail.
|
|
89
|
+
|
|
90
|
+
Register `contentMatchers` with your runner's `expect.extend(contentMatchers)` to use
|
|
91
|
+
`toEqualBuffer` and `toEqualMesh`, including `.not`. Only `ContentMismatchError` becomes a failed
|
|
92
|
+
matcher result; invalid inputs and unexpected errors still throw. Follow your runner's
|
|
93
|
+
[matcher extension guidance](https://vitest.dev/guide/extending-matchers) for TypeScript declarations.
|
|
94
|
+
|
|
95
|
+
## Test Generators and repeated edits
|
|
96
|
+
|
|
97
|
+
For a Generator, import its definition and default runtime, then call
|
|
98
|
+
`runModel({ definition, runtime }, parameters)`. This validates the inputs and prepares services
|
|
99
|
+
before generation. Omit parameters or pass `undefined` to use defaults. Supplied inputs use the
|
|
100
|
+
definition's inferred types and must form a complete canonical map.
|
|
101
|
+
|
|
102
|
+
To change one value, spread `getDefaultParameters(definition)` and apply your override. The runner
|
|
103
|
+
doesn't merge defaults into supplied values. Use SDK `normalizeParameters` when recovering saved
|
|
104
|
+
values. Plain model parameters pass through directly.
|
|
105
|
+
|
|
106
|
+
Import the actual Generator entry point in integration tests so they exercise its input handling
|
|
107
|
+
and model together. Direct callbacks are useful for testing construction helpers or mesh reconstruction.
|
|
108
|
+
For a deliberate schema bypass, `runModel(runtime, parameters, { services })` executes
|
|
109
|
+
the Generator's model behavior without claiming those values pass its parameter schema.
|
|
110
|
+
See the [SDK example](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/README.md#define-controls).
|
|
111
|
+
|
|
112
|
+
Use `createGeneratorTestRunner(definition, runtime)` for repeated parameter cases. Its `run` method
|
|
113
|
+
returns a generated result or a structured validation failure. Call `runner.run(record)` to test
|
|
114
|
+
unknown keys, missing fields, or wrong value types. Use `check` to test declarations and reached
|
|
115
|
+
assertions without finalizing parts; geometry observations can still require the engine.
|
|
116
|
+
Dispose the runner in `finally`; returned results remain usable.
|
|
117
|
+
Use `check(parameters, { validateInput: false })` only to probe model assertions with deliberately
|
|
118
|
+
invalid typed values, bypassing the parameter schema.
|
|
119
|
+
|
|
120
|
+
### Check cache reuse
|
|
121
|
+
|
|
122
|
+
For models using `memo`, include repeated-edit tests with `{ verifyCache: true }` on the runner.
|
|
123
|
+
Change geometry inputs and placement inputs independently. Verification reruns cached calculations
|
|
124
|
+
and rejects changed results for unchanged inputs. Keep pure helpers stable and pass changing values
|
|
125
|
+
as arguments; pass the current params/context to track fields read inside the helper.
|
|
126
|
+
Verification has bounded comparison work. For outputs it can't compare, use `{ cache: false }`
|
|
127
|
+
on a separate runner and compare the resulting geometry. Neither check proves arbitrary JavaScript pure.
|
|
128
|
+
Keep prepared service identity stable for unchanged assets; replacing services creates a new session
|
|
129
|
+
and prevents testing cache reuse. Isolated `runModel` calls do not verify repeated-edit correctness.
|
|
130
|
+
|
|
131
|
+
For plain models, use `engine.createSession(program, options)` for repeated edits or cache tests
|
|
132
|
+
and dispose the session in `finally`. Session results remain usable after later runs and disposal.
|
|
133
|
+
Both `runModel` and Generator runners can reuse a test-owned engine and lower its resource ceilings.
|
|
134
|
+
They accept the same physical `resolution`, `meshValidation`, and `meshTopologyMode` policy.
|
|
135
|
+
|
|
136
|
+
## Fonts and entry points
|
|
137
|
+
|
|
138
|
+
| Import | Responsibility |
|
|
139
|
+
| --------------------------------- | ------------------------------------------------------------------------------ |
|
|
140
|
+
| `@metamesh/testkit` | Model execution, Generator runners, assertions, and optional content matchers. |
|
|
141
|
+
| `@metamesh/testkit/fonts` | Prepared font services from supplied fixture bytes. |
|
|
142
|
+
| `@metamesh/testkit/font-fixtures` | Node-only JSON fixture loading with SHA-256 verification. |
|
|
143
|
+
|
|
144
|
+
For a Generator with Font parameters, supply `prepareServices` to prepare its font services before
|
|
145
|
+
generation. Tests use checked-in bytes without CDN access; retain their licenses. Font preparation
|
|
146
|
+
needs Modeling's optional `fontkit` peer, plus `woff2-encoder` for WOFF2 fixtures.
|
|
128
147
|
|
|
129
148
|
## Documentation
|
|
130
149
|
|
|
131
|
-
Resolve `@metamesh/testkit/docs/manifest.json` for
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
[`@metamesh/cli`](https://github.com/JasonKoolman/metamesh/blob/main/packages/cli/README.md).
|
|
137
|
-
Its executable is `metamesh`; reusable Node authoring functions live in `@metamesh/cli/authoring`.
|
|
138
|
-
|
|
139
|
-
- [Generator SDK](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/README.md) — definitions, parameter values, and module authoring.
|
|
140
|
-
- [Modeling](https://github.com/JasonKoolman/metamesh/blob/main/packages/modeling/README.md) — geometry, sessions, and snapshots.
|
|
141
|
-
- [API source](https://github.com/JasonKoolman/metamesh/blob/main/packages/testkit/src/index.ts) — runner and assertion exports; [font helpers](https://github.com/JasonKoolman/metamesh/blob/main/packages/testkit/src/fonts.ts) live in `/fonts`.
|
|
142
|
-
|
|
143
|
-
For package development, see [Contributing](https://github.com/JasonKoolman/metamesh/blob/main/CONTRIBUTING.md).
|
|
150
|
+
Resolve `@metamesh/testkit/docs/manifest.json` for exact-version Markdown guidance and annotated
|
|
151
|
+
API references. With CLI installed, read `pnpm exec metamesh docs show testkit/guide/testing`.
|
|
152
|
+
See [Modeling](https://github.com/JasonKoolman/metamesh/blob/main/packages/modeling/README.md)
|
|
153
|
+
for geometry and [Contributing](https://github.com/JasonKoolman/metamesh/blob/main/CONTRIBUTING.md)
|
|
154
|
+
for package development.
|
|
144
155
|
|
|
145
156
|
## License
|
|
146
157
|
|
package/dist/assertions.d.ts
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import type { GeneratorDefinition, GeneratorParams, GeneratorPreset } from "@metamesh/generator-sdk";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/** Throws unless a definition already has the canonical package-serializable representation. */
|
|
6
|
-
export declare function expectSerializableDefinition(definition: GeneratorDefinition): void;
|
|
2
|
+
import type { ModelEngine, ModelPart } from "@metamesh/modeling/engine";
|
|
3
|
+
/** Strictly parses a definition and requires its JSON round trip to retain the canonical package representation. */
|
|
4
|
+
export declare function assertValidDefinition(definition: GeneratorDefinition): void;
|
|
7
5
|
/** Throws unless defaults strictly decode and equal the defaults derived from the definition. */
|
|
8
|
-
export declare function
|
|
6
|
+
export declare function assertValidDefaults<const D extends GeneratorDefinition>(definition: D, defaults: GeneratorParams<D>): void;
|
|
9
7
|
/** Checks preset metadata. Validate domain assertions through the generation test runner. */
|
|
10
|
-
export declare function
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
* Runs mesh reconstruction/finalization synchronously using the supplied engine.
|
|
8
|
+
export declare function assertValidPresets<const D extends GeneratorDefinition>(definition: D, presets: readonly GeneratorPreset<D>[]): void;
|
|
9
|
+
/** Validates supplied world meshes and their exact agreement with reconstructed local geometry and placement.
|
|
10
|
+
* Resolves to the same parts or rejects on failure. A supplied engine is reused; otherwise the shared engine initializes lazily.
|
|
14
11
|
*/
|
|
15
|
-
export declare function
|
|
16
|
-
/** Strictly checks unknown wire data and reconstructs its meshes using the supplied engine. */
|
|
17
|
-
export declare function expectValidParts(value: unknown, engine: ModelEngine): readonly GeneratedPart[];
|
|
18
|
-
/** Initializes the shared test engine lazily, then applies the same wire and mesh checks. */
|
|
19
|
-
export declare function expectValidParts(value: unknown): Promise<readonly GeneratedPart[]>;
|
|
12
|
+
export declare function assertValidParts(parts: readonly ModelPart[], engine?: ModelEngine): Promise<readonly ModelPart[]>;
|
|
20
13
|
//# sourceMappingURL=assertions.d.ts.map
|
package/dist/assertions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,eAAe,EAChB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AA2BxE,oHAAoH;AACpH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAU3E;AACD,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,EACrE,UAAU,EAAE,CAAC,EACb,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,GAC3B,IAAI,CAQN;AACD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,EACpE,UAAU,EAAE,CAAC,EACb,OAAO,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,GACrC,IAAI,CAEN;AACD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CA6B/B"}
|