Files
homarr/packages/workshop/src/schema.spec.ts
T
2026-07-13 19:24:31 +02:00

68 lines
2.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
CSS_SCHEMA_VERSION,
WIDGET_SCHEMA_VERSION,
schemaVersionForType,
validateWorkshopContent,
workshopExportFilename,
workshopScreenshotsSchema,
workshopSubmissionInputSchema,
} from "./schema";
const validWidget = {
$schema: WIDGET_SCHEMA_VERSION,
name: "Status",
url: "https://example.com/api/status",
authType: "none",
method: "GET",
displayType: "singleValue",
displayConfig: {
type: "singleValue",
jsonPath: "$.value",
label: "Status",
unit: "",
valueSize: "md",
labelPosition: "above",
},
};
describe("Workshop contracts", () => {
it("accepts the canonical custom widget export", () => {
expect(validateWorkshopContent("widget", JSON.stringify(validWidget)).success).toBe(true);
});
it("rejects missing and mismatched widget schemas", () => {
const { $schema: _, ...missing } = validWidget;
expect(validateWorkshopContent("widget", JSON.stringify(missing)).success).toBe(false);
expect(
validateWorkshopContent("widget", JSON.stringify({ ...validWidget, displayConfig: { type: "raw" } })).success,
).toBe(false);
});
it("enforces CSS and input limits", () => {
expect(validateWorkshopContent("css", " ").success).toBe(false);
expect(
workshopSubmissionInputSchema.safeParse({
type: "css",
title: "Theme",
description: "",
content: "body { color: red; }",
changelog: "",
}).success,
).toBe(true);
});
it("returns stable schema versions and safe filenames", () => {
expect(schemaVersionForType("widget")).toBe(WIDGET_SCHEMA_VERSION);
expect(schemaVersionForType("css")).toBe(CSS_SCHEMA_VERSION);
expect(workshopExportFilename("My / Theme!", "css")).toBe("my-theme.css");
});
it("validates screenshot count, MIME type, and size", () => {
expect(workshopScreenshotsSchema.safeParse([{ size: 1024, type: "image/png" }]).success).toBe(true);
expect(workshopScreenshotsSchema.safeParse([{ size: 1024, type: "image/svg+xml" }]).success).toBe(false);
expect(workshopScreenshotsSchema.safeParse([{ size: 6 * 1024 * 1024, type: "image/webp" }]).success).toBe(false);
});
});