/** @jest-environment node */ import { createHash } from "node:crypto"; import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; const sceneRoot = join( process.cwd(), "public", "three-dimensional", "zjb", "v29", ); describe("ZJB three-dimensional runtime assets", () => { it("ships every model referenced by the production manifest", () => { const manifest = JSON.parse( readFileSync(join(sceneRoot, "manifest.json"), "utf8"), ) as { packageId: string; renderRevision: number; assets: Array<{ file: string; sha256: string }>; networkModel: { file: string; metadata: string; sha256: string }; sourceCatalogFile?: string; bindingsFile?: string; }; expect(manifest.packageId).toBe("zjb-web-v29"); expect(manifest.renderRevision).toBe(29); expect(manifest.assets).toHaveLength(24); manifest.assets.forEach((asset) => { const assetPath = join(sceneRoot, asset.file); expect(existsSync(assetPath)).toBe(true); expect(createHash("sha256").update(readFileSync(assetPath)).digest("hex")).toBe( asset.sha256, ); }); expect(existsSync(join(sceneRoot, manifest.networkModel.file))).toBe(true); expect(existsSync(join(sceneRoot, manifest.networkModel.metadata))).toBe(true); expect(manifest.sourceCatalogFile).toBeUndefined(); expect(manifest.bindingsFile).toBeUndefined(); const networkModel = readFileSync(join(sceneRoot, manifest.networkModel.file)); expect(createHash("sha256").update(networkModel).digest("hex")).toBe( manifest.networkModel.sha256, ); }); it("includes the offline runtime modules and third-party licenses", () => { [ "preview.html", "preview.mjs", "appearance.mjs", "asset-inspector.mjs", "camera-navigation.mjs", "network-style.mjs", "integration.mjs", "render-effects.mjs", "vendor/meshopt_decoder.module.js", "vendor/meshoptimizer-LICENSE.md", "vendor/three/LICENSE", "vendor/three/three.module.js", "vendor/three/addons/loaders/GLTFLoader.js", ].forEach((relativePath) => { expect(existsSync(join(sceneRoot, relativePath))).toBe(true); }); }); it("keeps the embedded viewer on the versioned same-origin host protocol", () => { const preview = readFileSync(join(sceneRoot, "preview.mjs"), "utf8"); const html = readFileSync(join(sceneRoot, "preview.html"), "utf8"); const inspector = readFileSync( join(sceneRoot, "asset-inspector.mjs"), "utf8", ); expect(preview).toContain("tjwater:zjb-scene"); expect(preview).toContain("HOST_VERSION=2"); expect(preview).toContain("event.origin===window.location.origin"); expect(preview).toContain("event.source===window.parent"); expect(preview).toContain("postHost('ready'"); expect(preview).toContain("data.type==='results'"); expect(preview).toContain("data.type==='clear-results'"); expect(preview).toContain("data.type!=='command'"); expect(preview).toContain("postHost('selection-changed'"); expect(preview).not.toContain("window.zjbNetwork"); expect(preview).not.toContain("resultsFile"); expect(html).not.toContain("绑定运行结果"); expect(html).not.toContain("resultsFile"); expect(inspector).not.toContain("document.getElementById"); }); });