86 lines
6.3 KiB
JavaScript
86 lines
6.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import vm from "node:vm";
|
|
import test from "node:test";
|
|
import ts from "typescript";
|
|
|
|
const modules = new Map();
|
|
const routeIndex = [];
|
|
function load(name) {
|
|
if (name === "./module-registry.generated.js") return { getWebModuleRoute: module => ({ commands: [`${module.toLowerCase()}.pythonFamilyBatch`] }) };
|
|
if (modules.has(name)) return modules.get(name);
|
|
const source = fs.readFileSync(new URL(`../src/${name.replace(/^\.\//, "").replace(/\.js$/, ".ts")}`, import.meta.url), "utf8");
|
|
const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText;
|
|
const module = { exports: {} };
|
|
vm.runInNewContext("(function(exports,require,module){" + code + "})", { AbortController, fetch: async () => ({ ok: true, json: async () => ({ schema: "freecad-web-desktop-command-routes/v1", records: routeIndex }) }) })(module.exports, load, module);
|
|
modules.set(name, module.exports); return module.exports;
|
|
}
|
|
const adapter = load("./techdraw-desktop-adapter.js");
|
|
const { DesktopCommandDispatcher } = load("./desktop-command-dispatcher.js");
|
|
const record = command => ({ id: `TechDraw:desktop:${command}`, command, module: "TechDraw" });
|
|
const prepare = (command, args) => adapter.prepareTechDrawCommand(record(command), args, "Current");
|
|
const plain = value => JSON.parse(JSON.stringify(value));
|
|
|
|
test("section task exclusion, ownership and cancellation contracts are preserved", () => {
|
|
for (const name of ["TechDraw_ComplexSection", "TechDraw_SectionView", "TechDraw_SectionGroup"])
|
|
assert.equal(prepare(name, { activeTask: true }).response.error.code, "task_in_progress");
|
|
assert.equal(prepare("TechDraw_Image", { taskActive: "yes" }).response.error.code, "invalid_request");
|
|
assert.equal(prepare("TechDraw_Image", { document: "Other" }).response.error.code, "invalid_request");
|
|
assert.equal(prepare("TechDraw_Image", { action: "cancel" }).args.document, "Current");
|
|
});
|
|
|
|
test("image, projection and projected subelement selections retain their native inputs", () => {
|
|
const selection = [{ documentId: "Current", object: "View", subelements: ["Face1"] }];
|
|
assert.equal(prepare("TechDraw_Image", { selection }).args.owner, "View");
|
|
assert.deepEqual(plain(prepare("TechDraw_Hatch", { selection }).args.selection), ["Face1"]);
|
|
assert.equal(prepare("TechDraw_Hatch", { selection }).args.view, "View");
|
|
assert.equal(prepare("TechDraw_View", { selection }).response.error.code, "implementation_required");
|
|
assert.deepEqual(plain(prepare("TechDraw_View", { selection: [{ documentId: "Current", object: "Box" }] }).args.sources), ["Box"]);
|
|
assert.equal(prepare("TechDraw_Image", { selection: [{ documentId: "Other", object: "View" }] }).response.error.code, "invalid_request");
|
|
});
|
|
|
|
test("stack selection is deduplicated and command container policy stays native", () => {
|
|
assert.deepEqual(plain(prepare("TechDraw_StackTop", { selection: ["View", "View"] }).args.selection), ["View"]);
|
|
assert.equal(adapter.techDrawRoutePolicy("TechDraw_SectionGroup").familyRoute, true);
|
|
assert.equal(adapter.techDrawRoutePolicy("TechDraw_StackTop").nativeContainer, true);
|
|
assert.equal(adapter.techDrawRoutePolicy("Unrelated").nativeContainer, false);
|
|
});
|
|
|
|
test("native errors and cancellation remain visible while exposed results continue", () => {
|
|
const item = record("TechDraw_Image");
|
|
const response = status => ({ kind: "result", result: { schema: "freecad-web-techdraw/python-family-batch/v1", records: [{ apiId: item.id, status, code: "bad_image", message: "Native error" }] } });
|
|
const cancelled = response("cancelled");
|
|
assert.equal(adapter.validateTechDrawResponse(item, cancelled, item.id), cancelled);
|
|
assert.equal(adapter.validateTechDrawResponse(item, response("error"), item.id).error.code, "bad_image");
|
|
assert.equal(adapter.validateTechDrawResponse(item, response("exposed"), item.id), null);
|
|
});
|
|
|
|
test("the common dispatcher invokes the module adapter without changing other modules", async () => {
|
|
const calls = [];
|
|
const router = { request: async (...args) => { calls.push(args); return { kind: "result", result: { schema: "freecad-web-techdraw/python-family-batch/v1", records: [{ apiId: "TechDraw:desktop:TechDraw_Image", status: "exposed" }] } }; } };
|
|
const dispatcher = new DesktopCommandDispatcher(router);
|
|
await dispatcher.execute(record("TechDraw_Image"), { selection: [{ documentId: "Current", object: "View" }] }, "Current");
|
|
assert.equal(calls[0][2].records[0].args.owner, "View");
|
|
assert.equal(calls[0][2].records[0].args.document, "Current");
|
|
await dispatcher.execute({ id: "Mesh:desktop:Inspect", module: "Mesh", command: "Inspect" }, { value: 4 }, "MeshDoc");
|
|
assert.deepEqual(plain(calls[1][2].records[0].args), { value: 4, documentId: "MeshDoc" });
|
|
});
|
|
|
|
test("Part adapter preserves native selection delivery and original error envelopes", async () => {
|
|
const item = { id: "Part:desktop:Part_BoxSelection", module: "Part", command: "Part_BoxSelection" };
|
|
let native = { apiId: item.id, status: "exposed", result: { documentId: "Current", selection: [{ documentId: "Current", object: "Box" }] } };
|
|
const changes = [], router = { request: async () => ({ kind: "result", result: { records: [native] } }) };
|
|
const dispatcher = new DesktopCommandDispatcher(router);
|
|
const execute = () => dispatcher.execute(item, {}, "Current", (document, selection) => changes.push({ document, selection }));
|
|
assert.equal((await execute()).kind, "result");
|
|
assert.deepEqual(plain(changes), [{ document: "Current", selection: [{ documentId: "Current", object: "Box" }] }]);
|
|
native = { ...native, status: "restricted", code: "native_rejected", message: "Original native failure" };
|
|
assert.equal((await execute()).error.code, "native_rejected"); assert.equal(changes.length, 1);
|
|
native = { ...native, status: "exposed", implementationBoundary: "Missing native operation" };
|
|
assert.equal((await execute()).kind, "error"); assert.equal(changes.length, 1);
|
|
native = { apiId: "Part:desktop:Other", status: "exposed" };
|
|
assert.equal((await execute()).error.code, "native_protocol_error");
|
|
native = { apiId: item.id, status: "exposed", result: { documentId: "Current", selection: [{ documentId: "Other", object: "Box" }] } };
|
|
await execute(); assert.equal(changes.length, 1);
|
|
});
|