import { describe, expect, it } from "vitest"; import type { ToolcraftControlSchema } from "../schema/types"; import type { ToolcraftModelAsset } from "../state/types"; import { createToolcraftSourceAssetPresentation } from "./source-asset-presentation"; const MODEL_CONTROL: ToolcraftControlSchema = { assetKind: "model", target: "source.model", type: "fileDrop", }; function createModelAsset(lifecycle: ToolcraftModelAsset["lifecycle"]): ToolcraftModelAsset { const repairPlanRef = lifecycle === "repairable" ? "toolcraft:model-repair-plan:sha256:plan:sha256:envelope" : undefined; return { activeDocumentRef: "toolcraft:model-document:active", analysis: { boundaryEdges: 0, diagnostics: [], disconnectedComponents: 1, nonManifoldEdges: 0, outcome: lifecycle === "repairable" ? "repairable" : "clean", ...(repairPlanRef ? { repairPlanRef } : {}), triangles: 1, vertices: 3, }, assetKind: "model", fileName: "scene.glb", id: "model-1", layerId: "layer-model-1", lifecycle, mimeType: "model/gltf-binary", originalAnalysis: { boundaryEdges: 0, diagnostics: [], disconnectedComponents: 1, nonManifoldEdges: 0, outcome: lifecycle === "repairable" ? "repairable" : "clean", ...(repairPlanRef ? { repairPlanRef } : {}), triangles: 1, vertices: 3, }, originalDocumentRef: "toolcraft:model-document:original", position: { x: 0, y: 0 }, size: { height: 512, unit: "px", width: 512 }, sourceBundleDigest: "sha256:source", sourceBundleRef: "toolcraft:model-source-bundle:source", sourceTarget: MODEL_CONTROL.target, topologyProfile: "realtime-mesh", }; } describe("model source asset presentation", () => { it("offers one half-width-compatible Fix model action only while repairable", () => { const presentation = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [createModelAsset("repairable")], { phase: "idle", target: MODEL_CONTROL.target }, ); expect(presentation.secondaryActions).toEqual([ expect.objectContaining({ label: "Fix model", state: undefined, value: "model.fix", }), ]); expect(presentation.allowsFileBatch).toBe(true); expect(presentation.allowsFolderSelection).toBe(true); expect( createToolcraftSourceAssetPresentation("model", MODEL_CONTROL, [createModelAsset("fixed")], { phase: "idle", target: MODEL_CONTROL.target, }).secondaryActions, ).toEqual([]); }); it("keeps folder selection disabled for ordinary file controls", () => { const presentation = createToolcraftSourceAssetPresentation( "file", { assetKind: "file", target: "source.files", type: "fileDrop", }, [], { phase: "idle", target: "source.files" }, ); expect(presentation.allowsFolderSelection).toBe(false); }); it("uses the standard loading button state while repair runs", () => { const presentation = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [createModelAsset("repairable")], { phase: "repairing", progress: 0.4, target: MODEL_CONTROL.target }, ); expect(presentation.secondaryActions[0]).toMatchObject({ state: { disabled: true, loading: true, loadingAriaLabel: "Fixing model", }, }); }); it("presents persisted restoring and unavailable states through the standard uploader", () => { const restoring = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [createModelAsset("restoring")], { phase: "idle", target: MODEL_CONTROL.target }, ); const unavailableAsset = { ...createModelAsset("unavailable"), lastRepairError: { category: "resource-unavailable" as const, code: "model-resource-unavailable", message: "The saved model resources are unavailable.", }, }; const unavailable = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [unavailableAsset], { phase: "idle", target: MODEL_CONTROL.target }, ); expect(restoring.status).toEqual({ label: "Restoring model", phase: "analyzing", }); expect(unavailable.feedback).toEqual(unavailableAsset.lastRepairError); }); it("shows presentation feedback without treating it as topology repair", () => { const feedback = { category: "resource-unavailable" as const, code: "model-presentation-unavailable", message: "The model presentation is unavailable.", }; const presentation = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [createModelAsset("clean")], { phase: "idle", presentationFeedback: feedback, target: MODEL_CONTROL.target, }, ); expect(presentation.feedback).toEqual(feedback); expect(presentation.secondaryActions).toEqual([]); }); it("keeps a committed model warning visible without offering topology repair", () => { const asset = createModelAsset("clean"); const warningAsset: ToolcraftModelAsset = { ...asset, analysis: { ...asset.analysis, diagnostics: [ { affectedCount: 1, code: "missing-appearance-resource", explanation: "The referenced texture is absent; other material channels remain available.", severity: "warning", }, ], }, }; const presentation = createToolcraftSourceAssetPresentation( "model", MODEL_CONTROL, [warningAsset], { phase: "idle", target: MODEL_CONTROL.target }, ); expect(presentation.feedback).toEqual({ category: "format", code: "missing-appearance-resource", message: "The referenced texture is absent; other material channels remain available.", }); expect(presentation.secondaryActions).toEqual([]); }); });