136 lines
6.7 KiB
JavaScript
136 lines
6.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
|
|
const webRoot = path.resolve(import.meta.dirname, "../..");
|
|
const packageJson = JSON.parse(
|
|
fs.readFileSync(path.join(webRoot, "package.json"), "utf8"),
|
|
);
|
|
|
|
test("browser runtime dependencies stay local", () => {
|
|
const runtimePackages = Object.keys(packageJson.dependencies ?? {});
|
|
assert.deepEqual(runtimePackages, ["react", "react-dom"]);
|
|
assert.equal(packageJson.dependencies?.three, undefined);
|
|
assert.equal(packageJson.dependencies?.["@sqlite.org/sqlite-wasm"], undefined);
|
|
for (const name of runtimePackages) {
|
|
assert.ok(
|
|
fs.existsSync(path.join(webRoot, "node_modules", ...name.split("/"))),
|
|
`${name} must be installed in local node_modules`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("required renderer and engine assets are vendored", () => {
|
|
const requiredFiles = [
|
|
"app/src/vendor/three/three.core.js",
|
|
"app/src/vendor/three/three.module.js",
|
|
"app/src/vendor/three/addons/controls/OrbitControls.js",
|
|
"app/src/vendor/blender/web_engine.js",
|
|
"app/src/vendor/blender/web_engine.wasm",
|
|
"app/public/vendor/blender/web_engine.js",
|
|
"app/public/vendor/blender/web_engine.wasm",
|
|
"app/public/vendor/blender/single/web_engine.js",
|
|
"app/public/vendor/blender/single/web_engine.wasm",
|
|
"app/public/vendor/blender/pthread/web_engine.js",
|
|
"app/public/vendor/blender/pthread/web_engine.wasm",
|
|
];
|
|
for (const relativePath of requiredFiles) {
|
|
const filePath = path.join(webRoot, relativePath);
|
|
assert.ok(fs.statSync(filePath).size > 0, `${relativePath} must be non-empty`);
|
|
}
|
|
assert.deepEqual(
|
|
fs.readFileSync(path.join(webRoot, "app/src/vendor/blender/web_engine.wasm")),
|
|
fs.readFileSync(path.join(webRoot, "app/public/vendor/blender/web_engine.wasm")),
|
|
);
|
|
for (const relativePath of [
|
|
"blender-5.2.0/extern/zlib/CMakeLists.txt",
|
|
"blender-5.2.0/extern/zstd/build/cmake/CMakeLists.txt",
|
|
]) {
|
|
assert.ok(fs.statSync(path.join(webRoot, "..", relativePath)).size > 0, `${relativePath} must be vendored`);
|
|
}
|
|
});
|
|
|
|
test("schema v2 engine assets carry a valid release identity", () => {
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "app/public/engine-manifest.json"), "utf8"));
|
|
assert.equal(manifest.schemaVersion, 2);
|
|
assert.match(manifest.releaseId, /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/);
|
|
});
|
|
|
|
test("attribute geometry fixture is reproducible input", () => {
|
|
const fixture = path.join(webRoot, "..", "tests/files/web/attribute_scene.blend");
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "attribute_scene");
|
|
assert.ok(entry, "attribute_scene fixture must be registered");
|
|
assert.equal(fs.statSync(fixture).size, 90108);
|
|
});
|
|
|
|
test("animation fixture is registered with a bounded frame range", () => {
|
|
const fixture = path.join(webRoot, "..", "tests/files/web/animation_scene.blend");
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "animation_scene");
|
|
assert.deepEqual(entry?.frameRange, [1, 10]);
|
|
assert.ok(fs.statSync(fixture).size > 0);
|
|
});
|
|
|
|
test("rigged shape fixture covers modifier, skin and shape-key input", () => {
|
|
const fixture = path.join(webRoot, "..", "tests/files/web/rigged_shape_scene.blend");
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "rigged_shape_scene");
|
|
assert.deepEqual(entry?.features, ["modifier-stack", "vertex-weights", "shape-keys"]);
|
|
assert.equal(entry?.objects, 2);
|
|
assert.deepEqual(entry?.objectNames, ["RiggedArmatureObject", "RiggedShapeObject"]);
|
|
assert.ok(fs.statSync(fixture).size > 0);
|
|
});
|
|
|
|
test("packed image fixture is registered as a local binary asset input", () => {
|
|
const fixture = path.join(webRoot, "..", "tests/files/web/packed_image_scene.blend");
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "packed_image_scene");
|
|
assert.deepEqual(entry?.features, ["packed-image"]);
|
|
assert.equal(entry?.objectNames?.[0], "PackedImageObject");
|
|
assert.ok(fs.statSync(fixture).size > 0);
|
|
});
|
|
|
|
test("topology fixture covers open and non-manifold Collapse input", () => {
|
|
const fixture = path.join(webRoot, "..", "tests/files/web/topology_scene.blend");
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "topology_scene");
|
|
assert.equal(entry?.objects, 3);
|
|
assert.equal(entry?.meshes, 3);
|
|
assert.deepEqual(entry?.features, ["open-boundary-collapse", "open-ngon-collapse", "non-manifold-collapse"]);
|
|
assert.ok(fs.statSync(fixture).size > 0);
|
|
});
|
|
|
|
test("modifier category fixtures and Blender 5.2 goldens are registered", () => {
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const fixtureIds = [
|
|
"modifier_generate_scene",
|
|
"modifier_deform_scene",
|
|
"modifier_deform_curve_scene",
|
|
"modifier_physics_scene",
|
|
"modifier_geometry_nodes_scene",
|
|
"modifier_grease_pencil_scene",
|
|
];
|
|
for (const fixtureId of fixtureIds) {
|
|
const entry = manifest.fixtures.find((item) => item.id === fixtureId);
|
|
assert.ok(entry, `${fixtureId} fixture must be registered`);
|
|
assert.ok(fs.statSync(path.join(webRoot, "..", "tests/files/web", entry.path)).size > 0);
|
|
const goldenPath = path.join(webRoot, "..", "tests/golden/W-075", entry.path.replace(".blend", ".json"));
|
|
const golden = JSON.parse(fs.readFileSync(goldenPath, "utf8"));
|
|
assert.equal(golden.blenderVersion, "5.2.0 LTS");
|
|
assert.equal(golden.fixture, entry.path);
|
|
}
|
|
});
|
|
|
|
test("multi-frame pose and constraint fixture is registered with desktop goldens", () => {
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/files/web/manifest.json"), "utf8"));
|
|
const entry = manifest.fixtures.find((item) => item.id === "pose_constraint_scene");
|
|
assert.ok(entry);
|
|
assert.deepEqual(entry.features, ["pose-animation", "pose-constraint", "multi-frame-depsgraph"]);
|
|
assert.ok(fs.statSync(path.join(webRoot, "..", "tests/files/web", entry.path)).size > 0);
|
|
const golden = JSON.parse(fs.readFileSync(path.join(webRoot, "..", "tests/golden/W-080/pose-constraint-depsgraph.json"), "utf8"));
|
|
assert.equal(golden.blenderVersion, "5.2.0 LTS");
|
|
assert.deepEqual(golden.frames, [1, 5, 10, 15]);
|
|
});
|