// @vitest-environment jsdom /** * Expanded tests for tool-registry covering edge cases not in the main test. * * Focuses on: DisplayMode type completeness, registry size validation, * livePreview flag consistency, ResultsPanel / PreviewPanel presence, * and verifying the registry aligns with shared TOOLS definitions. */ import { describe, expect, it, vi } from "vitest"; // Mock all lazy-loaded tool settings components vi.mock("@/components/tools/resize-settings", () => ({ ResizeSettings: () => null })); vi.mock("@/components/tools/crop-settings", () => ({ CropSettings: () => null })); vi.mock("@/components/tools/rotate-settings", () => ({ RotateSettings: () => null })); vi.mock("@/components/tools/convert-settings", () => ({ ConvertSettings: () => null })); vi.mock("@/components/tools/compress-settings", () => ({ CompressSettings: () => null })); vi.mock("@/components/tools/optimize-for-web-settings", () => ({ OptimizeForWebSettings: () => null, })); vi.mock("@/components/tools/strip-metadata-settings", () => ({ StripMetadataSettings: () => null, })); vi.mock("@/components/tools/edit-metadata-settings", () => ({ EditMetadataSettings: () => null, })); vi.mock("@/components/tools/color-settings", () => ({ ColorSettings: () => null })); vi.mock("@/components/tools/sharpening-settings", () => ({ SharpeningSettings: () => null })); vi.mock("@/components/tools/watermark-text-settings", () => ({ WatermarkTextSettings: () => null, })); vi.mock("@/components/tools/watermark-image-settings", () => ({ WatermarkImageSettings: () => null, })); vi.mock("@/components/tools/text-overlay-settings", () => ({ TextOverlaySettings: () => null, })); vi.mock("@/components/tools/compose-settings", () => ({ ComposeSettings: () => null })); vi.mock("@/components/tools/info-settings", () => ({ InfoSettings: () => null })); vi.mock("@/components/tools/compare-settings", () => ({ CompareSettings: () => null })); vi.mock("@/components/tools/find-duplicates-settings", () => ({ FindDuplicatesSettings: () => null, })); vi.mock("@/components/tools/find-duplicates-results", () => ({ FindDuplicatesResults: () => null, })); vi.mock("@/components/tools/color-palette-settings", () => ({ ColorPaletteSettings: () => null, })); vi.mock("@/components/tools/qr-generate-settings", () => ({ QrGenerateSettings: () => null, })); vi.mock("@/components/tools/qr-generate-preview", () => ({ QrGeneratePreview: () => null, })); vi.mock("@/components/tools/barcode-read-settings", () => ({ BarcodeReadSettings: () => null, })); vi.mock("@/components/tools/image-to-base64-settings", () => ({ ImageToBase64Settings: () => null, })); vi.mock("@/components/tools/image-to-base64-results", () => ({ ImageToBase64Results: () => null, })); vi.mock("@/components/tools/collage-settings", () => ({ CollageSettings: () => null })); vi.mock("@/components/tools/collage-preview", () => ({ CollagePreview: () => null })); vi.mock("@/components/tools/stitch-settings", () => ({ StitchSettings: () => null })); vi.mock("@/components/tools/split-settings", () => ({ SplitSettings: () => null })); vi.mock("@/components/tools/split-canvas", () => ({ SplitCanvas: () => null })); vi.mock("@/components/tools/border-settings", () => ({ BorderSettings: () => null })); vi.mock("@/components/tools/svg-to-raster-settings", () => ({ SvgToRasterSettings: () => null, })); vi.mock("@/components/tools/vectorize-settings", () => ({ VectorizeSettings: () => null })); vi.mock("@/components/tools/gif-tools-settings", () => ({ GifToolsSettings: () => null })); vi.mock("@/components/tools/bulk-rename-settings", () => ({ BulkRenameSettings: () => null, })); vi.mock("@/components/tools/favicon-settings", () => ({ FaviconSettings: () => null })); vi.mock("@/components/tools/image-to-pdf-settings", () => ({ ImageToPdfSettings: () => null, })); vi.mock("@/components/tools/pdf-to-image-settings", () => ({ PdfToImageSettings: () => null, })); vi.mock("@/components/tools/pdf-to-image-preview", () => ({ PdfToImagePreview: () => null, })); vi.mock("@/components/tools/replace-color-settings", () => ({ ReplaceColorSettings: () => null, })); vi.mock("@/components/tools/remove-bg-settings", () => ({ RemoveBgSettings: () => null })); vi.mock("@/components/tools/upscale-settings", () => ({ UpscaleSettings: () => null })); vi.mock("@/components/tools/ocr-settings", () => ({ OcrSettings: () => null })); vi.mock("@/components/tools/blur-faces-settings", () => ({ BlurFacesSettings: () => null, })); vi.mock("@/components/tools/enhance-faces-settings", () => ({ EnhanceFacesSettings: () => null, })); vi.mock("@/components/tools/erase-object-settings", () => ({ EraseObjectSettings: () => null, })); vi.mock("@/components/tools/smart-crop-settings", () => ({ SmartCropSettings: () => null, })); vi.mock("@/components/tools/image-enhancement-settings", () => ({ ImageEnhancementSettings: () => null, })); vi.mock("@/components/tools/colorize-settings", () => ({ ColorizeSettings: () => null })); vi.mock("@/components/tools/noise-removal-settings", () => ({ NoiseRemovalSettings: () => null, })); vi.mock("@/components/tools/passport-photo-settings", () => ({ PassportPhotoSettings: () => null, PassportPhotoPreview: () => null, })); vi.mock("@/components/tools/red-eye-removal-settings", () => ({ RedEyeRemovalSettings: () => null, })); vi.mock("@/components/tools/restore-photo-settings", () => ({ RestorePhotoSettings: () => null, })); vi.mock("@/components/tools/transparency-fixer-settings", () => ({ TransparencyFixerSettings: () => null, })); vi.mock("@/components/tools/content-aware-resize-settings", () => ({ ContentAwareResizeSettings: () => null, })); vi.mock("@/components/tools/beautify-settings", () => ({ BeautifySettings: () => null, })); vi.mock("@/components/tools/meme-generator-settings", () => ({ MemeGeneratorSettings: () => null, })); vi.mock("@/components/tools/meme-generator-preview", () => ({ MemeGeneratorPreview: () => null, })); vi.mock("@/components/tools/color-blindness-settings", () => ({ ColorBlindnessSettings: () => null, })); vi.mock("@/components/tools/ai-canvas-expand-settings", () => ({ AiCanvasExpandSettings: () => null, })); vi.mock("@/components/tools/merge-pdf-settings", () => ({ MergePdfSettings: () => null, })); vi.mock("@/components/tools/split-pdf-settings", () => ({ SplitPdfSettings: () => null, })); vi.mock("@/components/tools/compress-pdf-settings", () => ({ CompressPdfSettings: () => null, })); vi.mock("@/components/tools/rotate-pdf-settings", () => ({ RotatePdfSettings: () => null, })); vi.mock("@/components/tools/word-to-pdf-settings", () => ({ WordToPdfSettings: () => null, })); vi.mock("@/components/tools/csv-excel-settings", () => ({ CsvExcelSettings: () => null, })); vi.mock("@/components/tools/csv-json-settings", () => ({ CsvJsonSettings: () => null, })); vi.mock("@/components/tools/json-xml-settings", () => ({ JsonXmlSettings: () => null, })); vi.mock("@/components/tools/split-csv-settings", () => ({ SplitCsvSettings: () => null, })); // Wave 5a: Image gap-fill tools vi.mock("@/components/tools/barcode-generate-settings", () => ({ BarcodeGenerateSettings: () => null, })); vi.mock("@/components/tools/chart-maker-settings", () => ({ ChartMakerSettings: () => null, })); vi.mock("@/components/tools/circle-crop-settings", () => ({ CircleCropSettings: () => null, })); vi.mock("@/components/tools/duotone-settings", () => ({ DuotoneSettings: () => null, })); vi.mock("@/components/tools/gif-webp-settings", () => ({ GifWebpSettings: () => null, })); vi.mock("@/components/tools/histogram-settings", () => ({ HistogramSettings: () => null, })); vi.mock("@/components/tools/image-pad-settings", () => ({ ImagePadSettings: () => null, })); vi.mock("@/components/tools/lqip-placeholder-settings", () => ({ LqipPlaceholderSettings: () => null, })); vi.mock("@/components/tools/pixelate-settings", () => ({ PixelateSettings: () => null, })); vi.mock("@/components/tools/sprite-sheet-settings", () => ({ SpriteSheetSettings: () => null, })); vi.mock("@/components/tools/vignette-settings", () => ({ VignetteSettings: () => null, })); import { TOOLS } from "@snapotter/shared"; import type { DisplayMode, ToolRegistryEntry } from "@/lib/tool-registry"; import { getToolRegistryEntry, toolRegistry } from "@/lib/tool-registry"; describe("toolRegistry (expanded)", () => { describe("registry size and completeness", () => { it("has the same number of entries as TOOLS array", () => { expect(toolRegistry.size).toBe(TOOLS.length); }); it("every entry in registry has a matching TOOLS entry", () => { const toolIds = new Set(TOOLS.map((t) => t.id)); for (const registryId of toolRegistry.keys()) { expect(toolIds.has(registryId), `registry has ${registryId} not in TOOLS[]`).toBe(true); } }); }); describe("displayMode distribution", () => { it("has at least one tool per display mode in use", () => { const modesInUse = new Set(); for (const entry of toolRegistry.values()) { modesInUse.add(entry.displayMode); } // These modes should be in use expect(modesInUse.has("side-by-side")).toBe(true); expect(modesInUse.has("before-after")).toBe(true); expect(modesInUse.has("live-preview")).toBe(true); expect(modesInUse.has("interactive-crop")).toBe(true); expect(modesInUse.has("interactive-eraser")).toBe(true); expect(modesInUse.has("no-dropzone")).toBe(true); expect(modesInUse.has("custom-results")).toBe(true); }); }); describe("livePreview flag consistency", () => { it("live-preview display mode tools have livePreview flag set", () => { for (const [toolId, entry] of toolRegistry) { if (entry.displayMode === "live-preview") { expect( entry.livePreview, `${toolId} has live-preview mode but livePreview is falsy`, ).toBe(true); } } }); it("non-live-preview display mode tools do not have livePreview flag", () => { const liveModes: DisplayMode[] = ["live-preview"]; for (const [, entry] of toolRegistry) { if (!liveModes.includes(entry.displayMode) && entry.livePreview) { // Some tools like rotate use livePreview with before-after mode, which is valid // Just verify the flag is a boolean expect(typeof entry.livePreview).toBe("boolean"); } } }); }); describe("ResultsPanel requirements", () => { it("all custom-results tools have a ResultsPanel", () => { const missing: string[] = []; for (const [toolId, entry] of toolRegistry) { if (entry.displayMode === "custom-results" && !entry.ResultsPanel) { missing.push(toolId); } } expect( missing, `Tools with custom-results missing ResultsPanel: ${missing.join(", ")}`, ).toEqual([]); }); it("tools with no-comparison mode do not require ResultsPanel", () => { for (const [, entry] of toolRegistry) { if (entry.displayMode === "no-comparison") { // ResultsPanel is optional for no-comparison mode // Just verify no error occurs expect(entry.Settings).toBeDefined(); } } }); }); describe("PreviewPanel presence", () => { it("tools with PreviewPanel have the correct display modes", () => { const previewAllowedModes: DisplayMode[] = [ "no-dropzone", "custom-results", "interactive-split", ]; for (const [toolId, entry] of toolRegistry) { if ((entry as ToolRegistryEntry & { PreviewPanel?: unknown }).PreviewPanel) { // PreviewPanel is typically on no-dropzone or custom-results tools // Verify the tool has a valid display mode expect( previewAllowedModes.includes(entry.displayMode) || entry.displayMode === "side-by-side" || entry.displayMode === "before-after" || entry.displayMode === "live-preview", `${toolId} has PreviewPanel but unexpected mode ${entry.displayMode}`, ).toBe(true); } } }); }); describe("specific tool configurations", () => { it("watermark-text uses before-after display", () => { const entry = getToolRegistryEntry("watermark-text"); expect(entry?.displayMode).toBe("before-after"); }); it("watermark-image uses before-after display", () => { const entry = getToolRegistryEntry("watermark-image"); expect(entry?.displayMode).toBe("before-after"); }); it("text-overlay uses before-after display", () => { const entry = getToolRegistryEntry("text-overlay"); expect(entry?.displayMode).toBe("before-after"); }); it("replace-color uses before-after display", () => { const entry = getToolRegistryEntry("replace-color"); expect(entry?.displayMode).toBe("before-after"); }); it("sharpening uses before-after display", () => { const entry = getToolRegistryEntry("sharpening"); expect(entry?.displayMode).toBe("before-after"); }); it("strip-metadata uses no-comparison display", () => { const entry = getToolRegistryEntry("strip-metadata"); expect(entry?.displayMode).toBe("no-comparison"); }); it("info uses no-comparison display", () => { const entry = getToolRegistryEntry("info"); expect(entry?.displayMode).toBe("no-comparison"); }); it("compare uses before-after display", () => { const entry = getToolRegistryEntry("compare"); expect(entry?.displayMode).toBe("before-after"); }); it("color-palette uses no-comparison display", () => { const entry = getToolRegistryEntry("color-palette"); expect(entry?.displayMode).toBe("no-comparison"); }); it("barcode-read uses before-after display", () => { const entry = getToolRegistryEntry("barcode-read"); expect(entry?.displayMode).toBe("before-after"); }); it("ocr uses before-after display", () => { const entry = getToolRegistryEntry("ocr"); expect(entry?.displayMode).toBe("before-after"); }); it("vectorize uses before-after display", () => { const entry = getToolRegistryEntry("vectorize"); expect(entry?.displayMode).toBe("before-after"); }); }); describe("getToolRegistryEntry for all TOOLS", () => { it("returns defined entry for every shared TOOLS entry", () => { for (const tool of TOOLS) { const entry = getToolRegistryEntry(tool.id); expect(entry, `getToolRegistryEntry("${tool.id}") returned undefined`).toBeDefined(); } }); it("each returned entry has Settings as a function or object", () => { for (const tool of TOOLS) { const entry = getToolRegistryEntry(tool.id); if (entry) { expect( typeof entry.Settings === "function" || typeof entry.Settings === "object", `${tool.id} Settings is not function/object`, ).toBe(true); } } }); }); });