@metamesh/testkit 0.16.0 → 0.18.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 +191 -0
- package/README.md +122 -88
- 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 +122 -88
- package/dist/docs/manifest.json +175 -35
- 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 -9
- 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 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- 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/expectSerializableDefinition.md +0 -10
- package/dist/docs/reference/index/expectValidParts.md +0 -19
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js.map +0 -1
- package/dist/runner.d.ts +0 -36
- package/dist/runner.d.ts.map +0 -1
- package/dist/runner.js +0 -121
- package/dist/runner.js.map +0 -1
package/dist/assertions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC5F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Numeric contents readable without copying their storage, including immutable model buffers. */
|
|
2
|
+
export interface BufferContent {
|
|
3
|
+
readonly length: number;
|
|
4
|
+
at(index: number): number | undefined;
|
|
5
|
+
}
|
|
6
|
+
/** Ordered manufacturing geometry; placement, appearance and resource identities are separate. */
|
|
7
|
+
export interface MeshContent {
|
|
8
|
+
readonly positions: BufferContent;
|
|
9
|
+
readonly indices: BufferContent;
|
|
10
|
+
}
|
|
11
|
+
/** A difference in valid buffer contents, distinct from malformed inputs or reader failures. */
|
|
12
|
+
export declare class ContentMismatchError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Asserts exact ordered numeric contents through length and at(index), without copying storage.
|
|
17
|
+
* Uses Object.is regardless of storage type: NaN equals NaN; signed zero differs.
|
|
18
|
+
* Throws ContentMismatchError for the first difference, TypeError for malformed inputs, and
|
|
19
|
+
* preserves reader failures. This assertion does not establish valid manufacturing geometry.
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertEqualBuffer(actual: BufferContent, expected: BufferContent): void;
|
|
22
|
+
/**
|
|
23
|
+
* Asserts identical positions and indices with buffer-specific mismatch diagnostics.
|
|
24
|
+
* Ignores metadata. Geometrically equivalent meshes with different triangulation or ordering can fail.
|
|
25
|
+
* Shares assertEqualBuffer's numeric equality, validation and error behavior without copying storage.
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertEqualMesh(actual: MeshContent, expected: MeshContent): void;
|
|
28
|
+
//# sourceMappingURL=content-assertions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-assertions.d.ts","sourceRoot":"","sources":["../src/content-assertions.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,kGAAkG;AAClG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAED,gGAAgG;AAChG,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AA8DD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAMtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GAAG,IAAI,CAKhF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** A difference in valid buffer contents, distinct from malformed inputs or reader failures. */
|
|
2
|
+
export class ContentMismatchError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "ContentMismatchError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function buffer(value, label) {
|
|
9
|
+
if (typeof value !== "object" ||
|
|
10
|
+
value === null ||
|
|
11
|
+
!("length" in value) ||
|
|
12
|
+
typeof value.length !== "number" ||
|
|
13
|
+
!Number.isSafeInteger(value.length) ||
|
|
14
|
+
value.length < 0 ||
|
|
15
|
+
!("at" in value) ||
|
|
16
|
+
typeof value.at !== "function") {
|
|
17
|
+
throw new TypeError(`${label} must provide a nonnegative integer length and at(index).`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function numberAt(value, index, label) {
|
|
22
|
+
const element = value.at(index);
|
|
23
|
+
if (typeof element !== "number") {
|
|
24
|
+
throw new TypeError(`${label}.at(${index}) must return a number within its length.`);
|
|
25
|
+
}
|
|
26
|
+
return element;
|
|
27
|
+
}
|
|
28
|
+
function display(value) {
|
|
29
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
30
|
+
}
|
|
31
|
+
function assertBufferContents(actual, expected, label) {
|
|
32
|
+
if (actual.length !== expected.length) {
|
|
33
|
+
throw new ContentMismatchError(`${label} length: expected ${expected.length}, received ${actual.length}.`);
|
|
34
|
+
}
|
|
35
|
+
for (let index = 0; index < actual.length; index += 1) {
|
|
36
|
+
const received = numberAt(actual, index, `Received ${label}`);
|
|
37
|
+
const wanted = numberAt(expected, index, `Expected ${label}`);
|
|
38
|
+
if (!Object.is(received, wanted)) {
|
|
39
|
+
throw new ContentMismatchError(`${label} differs at index ${index}: expected ${display(wanted)}, received ${display(received)} (length ${actual.length}).`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function mesh(value, label) {
|
|
44
|
+
if (typeof value !== "object" ||
|
|
45
|
+
value === null ||
|
|
46
|
+
!("positions" in value) ||
|
|
47
|
+
!("indices" in value)) {
|
|
48
|
+
throw new TypeError(`${label} must provide positions and indices buffers.`);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
positions: buffer(value.positions, `${label} positions`),
|
|
52
|
+
indices: buffer(value.indices, `${label} indices`),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Asserts exact ordered numeric contents through length and at(index), without copying storage.
|
|
57
|
+
* Uses Object.is regardless of storage type: NaN equals NaN; signed zero differs.
|
|
58
|
+
* Throws ContentMismatchError for the first difference, TypeError for malformed inputs, and
|
|
59
|
+
* preserves reader failures. This assertion does not establish valid manufacturing geometry.
|
|
60
|
+
*/
|
|
61
|
+
export function assertEqualBuffer(actual, expected) {
|
|
62
|
+
assertBufferContents(buffer(actual, "Received buffer"), buffer(expected, "Expected buffer"), "Buffer");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Asserts identical positions and indices with buffer-specific mismatch diagnostics.
|
|
66
|
+
* Ignores metadata. Geometrically equivalent meshes with different triangulation or ordering can fail.
|
|
67
|
+
* Shares assertEqualBuffer's numeric equality, validation and error behavior without copying storage.
|
|
68
|
+
*/
|
|
69
|
+
export function assertEqualMesh(actual, expected) {
|
|
70
|
+
const received = mesh(actual, "Received mesh");
|
|
71
|
+
const wanted = mesh(expected, "Expected mesh");
|
|
72
|
+
assertBufferContents(received.positions, wanted.positions, "Mesh position buffer");
|
|
73
|
+
assertBufferContents(received.indices, wanted.indices, "Mesh index buffer");
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=content-assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-assertions.js","sourceRoot":"","sources":["../src/content-assertions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type BufferContent, type MeshContent } from "./content-assertions.js";
|
|
2
|
+
interface ContentMatcherResult {
|
|
3
|
+
readonly pass: boolean;
|
|
4
|
+
readonly message: () => string;
|
|
5
|
+
}
|
|
6
|
+
/** Optional expect.extend adapters; only content mismatches become failed matcher results. */
|
|
7
|
+
export declare const contentMatchers: {
|
|
8
|
+
toEqualBuffer(received: BufferContent, expected: BufferContent): ContentMatcherResult;
|
|
9
|
+
toEqualMesh(received: MeshContent, expected: MeshContent): ContentMatcherResult;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=content-matchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-matchers.d.ts","sourceRoot":"","sources":["../src/content-matchers.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AAEjC,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC;CAChC;AAYD,8FAA8F;AAC9F,eAAO,MAAM,eAAe;4BACF,aAAa,YAAY,aAAa,GAAG,oBAAoB;0BAM/D,WAAW,YAAY,WAAW,GAAG,oBAAoB;CAMhF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { assertEqualBuffer, assertEqualMesh, ContentMismatchError, } from "./content-assertions.js";
|
|
2
|
+
function match(assertion, equalMessage) {
|
|
3
|
+
try {
|
|
4
|
+
assertion();
|
|
5
|
+
return { pass: true, message: () => equalMessage };
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (!(error instanceof ContentMismatchError))
|
|
9
|
+
throw error;
|
|
10
|
+
return { pass: false, message: () => error.message };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Optional expect.extend adapters; only content mismatches become failed matcher results. */
|
|
14
|
+
export const contentMatchers = {
|
|
15
|
+
toEqualBuffer(received, expected) {
|
|
16
|
+
return match(() => assertEqualBuffer(received, expected), "Expected buffer contents to differ, but they match.");
|
|
17
|
+
},
|
|
18
|
+
toEqualMesh(received, expected) {
|
|
19
|
+
return match(() => assertEqualMesh(received, expected), "Expected mesh contents to differ, but they match.");
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=content-matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-matchers.js","sourceRoot":"","sources":["../src/content-matchers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GAGrB,MAAM,yBAAyB,CAAC;AAOjC,SAAS,KAAK,CAAC,SAAqB,EAAE,YAAoB;IACxD,IAAI,CAAC;QACH,SAAS,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAoB,CAAC;YAAE,MAAM,KAAK,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IACvD,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa,CAAC,QAAuB,EAAE,QAAuB;QAC5D,OAAO,KAAK,CACV,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAC3C,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,WAAW,CAAC,QAAqB,EAAE,QAAqB;QACtD,OAAO,KAAK,CACV,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,EACzC,mDAAmD,CACpD,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -1,120 +1,154 @@
|
|
|
1
1
|
# `@metamesh/testkit`
|
|
2
2
|
|
|
3
|
-
Test MetaMesh models and Generators with
|
|
4
|
-
|
|
3
|
+
Test MetaMesh models and Generators with their production engine. Check definitions, presets,
|
|
4
|
+
geometry, and physical measurements with any test runner.
|
|
5
5
|
|
|
6
6
|
> **Preview:** `0.x` minor releases may break compatibility. Patches remain compatible within the
|
|
7
7
|
> same minor line.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
|
-
Install
|
|
11
|
+
Install matching SDK, Modeling, and Testkit releases. Testkit is a development dependency:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
pnpm add @metamesh/generator-sdk @metamesh/modeling
|
|
15
15
|
pnpm add -D @metamesh/testkit
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
The package publishes ES modules only. Use Node.js 24 or newer. TypeScript consumers need
|
|
19
|
+
TypeScript 6.0 or newer, `strict` checking, and `NodeNext` or `Bundler` module resolution.
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
[
|
|
21
|
+
Testkit has no test-framework, compiler, renderer, or browser dependency. For source checking,
|
|
22
|
+
captured runs, or images, use [CLI](https://github.com/JasonKoolman/metamesh/blob/main/packages/cli/README.md).
|
|
22
23
|
|
|
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
|
-
const result = await runner.execute(getDefaultParameters(definition));
|
|
32
|
-
if (result.type !== "generated") throw new Error("Expected valid Generator output.");
|
|
33
|
-
try {
|
|
34
|
-
await expectValidParts(result.model.parts);
|
|
35
|
-
} finally {
|
|
36
|
-
result.model.dispose();
|
|
37
|
-
}
|
|
38
|
-
} finally {
|
|
39
|
-
runner.dispose();
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
`execute` normalizes values, prepares services, and runs a persistent session. Schema and assertion
|
|
44
|
-
errors return `invalid`; unexpected execution or malformed-output errors reject. Dispose returned
|
|
45
|
-
snapshots separately from the runner; captured snapshots remain valid after runner disposal.
|
|
46
|
-
|
|
47
|
-
Use `runner.validate(parameters, { normalize: false })` to probe deliberately invalid typed values
|
|
48
|
-
without finalizing parts. It checks declarations and reached assertions; observing geometry can
|
|
49
|
-
still initialize the engine. Creating a runner alone loads no engine or assets. Pass `options.engine`
|
|
50
|
-
to reuse a test-owned engine and `options.limits` to lower host ceilings.
|
|
51
|
-
|
|
52
|
-
## Assertions
|
|
53
|
-
|
|
54
|
-
Helpers throw on failure without depending on your test framework:
|
|
55
|
-
|
|
56
|
-
| Helper | Checks |
|
|
57
|
-
| ------------------------------ | ------------------------------------------------------------------ |
|
|
58
|
-
| `expectSerializableDefinition` | Strict parsing and a canonical JSON round trip |
|
|
59
|
-
| `expectValidDefaults` | Strict parameter decoding and agreement with definition defaults |
|
|
60
|
-
| `expectValidPresets` | Serializable preset metadata; use the runner for domain assertions |
|
|
61
|
-
| `expectValidParts` | Strict part metadata, closed topology, and native reconstruction |
|
|
62
|
-
| `expectMeshMeasurements` | Bounds, area, signed volume, and connected-component count |
|
|
63
|
-
|
|
64
|
-
`expectValidParts(parts, engine)` is synchronous and uses that engine's limits. Without an engine,
|
|
65
|
-
it returns a promise using the shared test engine and default limits. It also accepts finalized
|
|
66
|
-
parts, stripping measurement facts before strict validation; other unknown fields reject.
|
|
67
|
-
Snapshot inputs retain shared local mesh buffers and rigid placements in the returned parts.
|
|
24
|
+
## Test a model
|
|
68
25
|
|
|
69
|
-
|
|
26
|
+
Build a plate, check its volume and shell count, then verify native reconstruction:
|
|
70
27
|
|
|
71
28
|
```ts
|
|
72
|
-
import {
|
|
29
|
+
import { box } from "@metamesh/modeling";
|
|
30
|
+
import { assertMeshMeasurements, assertValidParts, runModel } from "@metamesh/testkit";
|
|
73
31
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
32
|
+
const model = await runModel<{ width: number }>(
|
|
33
|
+
({ params, part }) => part("plate", box({ size: [params.width, 20, 4] })),
|
|
34
|
+
{ width: 30 }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
assertMeshMeasurements(
|
|
38
|
+
model.part("plate"),
|
|
39
|
+
{ signedVolume: 2400, shellCount: 1 },
|
|
77
40
|
{ signedVolume: 0.05 }
|
|
78
41
|
);
|
|
42
|
+
await assertValidParts(model.parts);
|
|
79
43
|
```
|
|
80
44
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
45
|
+
Helpers report failures without registering global assertions. `runModel` returns durable,
|
|
46
|
+
immutable parts, measurements, diagnostics, and dependencies; the result needs no cleanup.
|
|
47
|
+
Use `model.part(id)` for a required semantic part or `model.parts` for iteration and export.
|
|
48
|
+
|
|
49
|
+
Always await `assertValidParts(parts, engine)`: it resolves to the same parts or rejects on failure.
|
|
50
|
+
A supplied engine is reused; omission lazily initializes the shared test engine.
|
|
51
|
+
It checks the supplied world-space buffers for closed topology, then requires their positions and
|
|
52
|
+
indices to exactly match local geometry reconstructed with the declared rigid transform.
|
|
53
|
+
A valid local mesh cannot conceal corrupted or inconsistent world-space output.
|
|
54
|
+
|
|
55
|
+
Pass an existing Modeling program directly: `await runModel(program, parameters)` uses the same
|
|
56
|
+
program and parameter types as `engine.run`. A synchronous generation callback is also accepted;
|
|
57
|
+
without parameters, no type argument or wrapper is needed:
|
|
58
|
+
`await runModel(({ part }) => part("cube", box({ size: 2 })))`.
|
|
59
|
+
Parameters retain 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.
|
|
92
63
|
|
|
93
|
-
|
|
94
|
-
checked-in bytes. Install Modeling's optional `fontkit` peer, plus `woff2-encoder` for WOFF2 fixtures.
|
|
95
|
-
Tests need no font downloads or browser cache; retain the fixtures' licenses.
|
|
64
|
+
## Assertions
|
|
96
65
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
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)` directly without a
|
|
84
|
+
test framework. 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 the thin optional adapters 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 module exporting its definition and default runtime, pass
|
|
98
|
+
`runModel({ definition, runtime }, parameters)`. Omit parameters or pass `undefined` to use defaults;
|
|
99
|
+
all other supplied Generator inputs have the definition's inferred types and must form a complete
|
|
100
|
+
canonical map. Use `runner.run(record)` to test unknown keys, missing fields, or wrong value types.
|
|
101
|
+
For one changed value, spread
|
|
102
|
+
`getDefaultParameters(definition)` and then apply the override; the runner does not merge defaults. The overload strictly parses inputs and prepares
|
|
103
|
+
services. Use SDK `normalizeParameters` explicitly when recovering saved values. Plain model
|
|
104
|
+
parameters pass through directly.
|
|
105
|
+
|
|
106
|
+
Generator integration tests should import the actual entry point instead of repeating its input
|
|
107
|
+
resolution and builder wiring. Use a direct callback for isolated construction 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 or cache tests.
|
|
113
|
+
Its `run` method returns a generated result or structured validation failure. `check` probes
|
|
114
|
+
declarations and reached assertions without finalizing parts; geometry observations can still
|
|
115
|
+
require the engine. Dispose the runner in `finally`; returned results remain usable.
|
|
116
|
+
Use `check(parameters, { validateInput: false })` only to probe model assertions with deliberately
|
|
117
|
+
invalid typed values, bypassing the parameter schema.
|
|
118
|
+
|
|
119
|
+
For models using `computed`, include repeated-edit tests with `{ verifyCache: true }` on the runner.
|
|
120
|
+
Change geometry inputs and placement inputs independently. Verification reruns cached calculations
|
|
121
|
+
and rejects changed results without tracked dependencies, including accidentally captured destructured
|
|
122
|
+
parameters. Read parameters inside the computation or list changing outer values in its dependency
|
|
123
|
+
array. Verification is a bounded development probe; unsupported captures or large results can
|
|
124
|
+
require comparison with `{ cache: false }` on a separate runner. It cannot prove arbitrary JavaScript.
|
|
125
|
+
Keep prepared service identity stable for unchanged assets; replacing services creates a new session
|
|
126
|
+
and prevents testing cache reuse. Isolated `runModel` calls do not verify repeated-edit correctness.
|
|
127
|
+
|
|
128
|
+
For plain models, use `engine.createSession(program, options)` for repeated edits or cache tests
|
|
129
|
+
and dispose the session in `finally`. Session results remain usable after later runs and disposal.
|
|
130
|
+
Both `runModel` and Generator runners can reuse a test-owned engine and lower its resource ceilings.
|
|
131
|
+
They accept the same physical `resolution`, `meshValidation`, and `meshTopologyMode` policy.
|
|
132
|
+
|
|
133
|
+
## Fonts and entry points
|
|
134
|
+
|
|
135
|
+
| Import | Responsibility |
|
|
136
|
+
| --------------------------------- | ------------------------------------------------------------------------------ |
|
|
137
|
+
| `@metamesh/testkit` | Model execution, Generator runners, assertions, and optional content matchers. |
|
|
138
|
+
| `@metamesh/testkit/fonts` | Prepared font services from supplied fixture bytes. |
|
|
139
|
+
| `@metamesh/testkit/font-fixtures` | Node-only JSON fixture loading with SHA-256 verification. |
|
|
140
|
+
|
|
141
|
+
Font-dependent Generators receive a `prepareServices` callback before generation. Tests use
|
|
142
|
+
checked-in bytes without CDN access; retain their licenses. Font preparation needs Modeling's
|
|
143
|
+
optional `fontkit` peer, plus `woff2-encoder` for WOFF2 fixtures.
|
|
102
144
|
|
|
103
145
|
## Documentation
|
|
104
146
|
|
|
105
|
-
Resolve `@metamesh/testkit/docs/manifest.json` for
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
[`@metamesh/cli`](https://github.com/JasonKoolman/metamesh/blob/main/packages/cli/README.md).
|
|
111
|
-
Its executable is `metamesh`; reusable Node authoring functions live in `@metamesh/cli/authoring`.
|
|
112
|
-
|
|
113
|
-
- [Generator SDK](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/README.md) — definitions, parameter values, and module authoring.
|
|
114
|
-
- [Modeling](https://github.com/JasonKoolman/metamesh/blob/main/packages/modeling/README.md) — geometry, sessions, and snapshots.
|
|
115
|
-
- [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`.
|
|
116
|
-
|
|
117
|
-
For package development, see [Contributing](https://github.com/JasonKoolman/metamesh/blob/main/CONTRIBUTING.md).
|
|
147
|
+
Resolve `@metamesh/testkit/docs/manifest.json` for exact-version Markdown guidance and annotated
|
|
148
|
+
API references. With CLI installed, read `pnpm exec metamesh docs show testkit/guide/testing`.
|
|
149
|
+
See [Modeling](https://github.com/JasonKoolman/metamesh/blob/main/packages/modeling/README.md)
|
|
150
|
+
for geometry and [Contributing](https://github.com/JasonKoolman/metamesh/blob/main/CONTRIBUTING.md)
|
|
151
|
+
for package development.
|
|
118
152
|
|
|
119
153
|
## License
|
|
120
154
|
|