90 lines
4.2 KiB
TypeScript
90 lines
4.2 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
getCustomWidgetComponent,
|
|
getCustomWidgetComponentCatalog,
|
|
getCustomWidgetExample,
|
|
getCustomWidgetSharedProps,
|
|
getCustomWidgetSkill,
|
|
getCustomWidgetSkillContent,
|
|
} from "../core/authoring-resources";
|
|
|
|
const skillSourceDirectory = path.resolve(import.meta.dirname, "../../../../.agents/skills/homarr-custom-widget");
|
|
|
|
describe("authoring resources", () => {
|
|
it("returns the complete portable skill and keeps every bundled source file in sync", () => {
|
|
const skill = getCustomWidgetSkill();
|
|
expect(skill).toMatchObject({ name: "homarr-custom-widget", version: "2.3.0" });
|
|
expect(skill.skillsShUrl).toContain("skills.sh/homarr-labs/homarr");
|
|
expect(skill.sourceUrl).toContain("/tree/HEAD/.agents/skills/homarr-custom-widget");
|
|
expect(skill.installCommand).toContain("--skill homarr-custom-widget");
|
|
expect(skill).not.toHaveProperty("componentCatalog");
|
|
expect(skill).not.toHaveProperty("entrypointMd");
|
|
expect(skill).not.toHaveProperty("bundledMarkdown");
|
|
expect(skill.skillMd.trim()).toBe(readFileSync(path.join(skillSourceDirectory, "SKILL.md"), "utf8").trim());
|
|
const bundledContent = getCustomWidgetSkillContent();
|
|
for (const [file, content] of Object.entries(skill.references)) {
|
|
expect(content.trim()).toBe(readFileSync(path.join(skillSourceDirectory, file), "utf8").trim());
|
|
expect(bundledContent).toContain(`# Bundled file: ${file}`);
|
|
expect(bundledContent).toContain(content.trim());
|
|
}
|
|
expect(bundledContent).toContain(skill.skillMd.trim());
|
|
expect(Buffer.byteLength(JSON.stringify(skill), "utf8")).toBeLessThan(12_000);
|
|
});
|
|
|
|
it("keeps catalog discovery compact and component details lazy", () => {
|
|
const catalog = getCustomWidgetComponentCatalog();
|
|
expect(catalog.components.length).toBeGreaterThan(100);
|
|
expect(catalog.components).toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ name: "Stack", category: expect.any(String) })]),
|
|
);
|
|
expect(catalog.examples).toEqual(expect.arrayContaining([expect.objectContaining({ id: "service-dashboard" })]));
|
|
expect(catalog.examples).toEqual(expect.arrayContaining([expect.objectContaining({ id: "pokedex" })]));
|
|
const pokedex = getCustomWidgetExample("pokedex");
|
|
expect(pokedex).toMatchObject({
|
|
id: "pokedex",
|
|
widget: {
|
|
$schema: "homarr-custom-widget-v2",
|
|
name: "Pokédex",
|
|
sources: { default: { baseUrl: "https://pokeapi.co" } },
|
|
},
|
|
});
|
|
expect(Buffer.byteLength(JSON.stringify(pokedex), "utf8")).toBeLessThan(16_000);
|
|
expect(catalog.sharedProps).toMatchObject({
|
|
count: expect.any(Number),
|
|
fetchTool: "customWidget_getSharedProps",
|
|
maxPerRequest: 64,
|
|
});
|
|
expect(catalog.sharedProps.names).toContain("p");
|
|
expect(Buffer.byteLength(JSON.stringify(catalog), "utf8")).toBeLessThan(45_000);
|
|
|
|
const stack = getCustomWidgetComponent("Stack");
|
|
expect(stack).toMatchObject({
|
|
name: "Stack",
|
|
sharedProps: { fetchTool: "customWidget_getSharedProps" },
|
|
});
|
|
expect(stack?.props.some(({ source }) => source === "global")).toBe(false);
|
|
expect(Buffer.byteLength(JSON.stringify(stack), "utf8")).toBeLessThan(20_000);
|
|
expect(getCustomWidgetComponent("Icon")?.knownValues?.name.length).toBeGreaterThan(100);
|
|
|
|
const largestComponentPayload = Math.max(
|
|
...catalog.components.map(({ name }) =>
|
|
Buffer.byteLength(JSON.stringify(getCustomWidgetComponent(name)), "utf8"),
|
|
),
|
|
);
|
|
expect(largestComponentPayload).toBeLessThan(20_000);
|
|
});
|
|
|
|
it("returns only requested shared prop documentation in one bounded payload", () => {
|
|
const result = getCustomWidgetSharedProps(["p", "m", "not-a-shared-prop", "p"]);
|
|
expect(result.props.map(({ name }) => name)).toEqual(["p", "m"]);
|
|
expect(result.notFound).toEqual(["not-a-shared-prop"]);
|
|
expect(Buffer.byteLength(JSON.stringify(result), "utf8")).toBeLessThan(4_000);
|
|
|
|
const boundedBatch = getCustomWidgetSharedProps(getCustomWidgetComponentCatalog().sharedProps.names.slice(0, 64));
|
|
expect(Buffer.byteLength(JSON.stringify(boundedBatch), "utf8")).toBeLessThan(15_000);
|
|
});
|
|
});
|