test: harden custom widget AI authoring
This commit is contained in:
1 parent
2f0c0a07dd
commit
6eff1da08f
14 files changed
+1056
-23
No files matched your search
+17
-5
@@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import { customWidgetDefinitionSchema } from "@homarr/custom-widgets/core";
|
||||
|
||||
import {
|
||||
applyDefinition,
|
||||
applyCustomWidgetAiResponse,
|
||||
filterSecretsForSourceAuthentication,
|
||||
getChangedSecrets,
|
||||
getCustomWidgetPreviewOptionIssues,
|
||||
@@ -24,9 +24,9 @@ const definition = customWidgetDefinitionSchema.parse({
|
||||
});
|
||||
|
||||
describe("Custom Widget workbench preview options", () => {
|
||||
it("loads a complete pasted widget template into the JSX editor field", () => {
|
||||
it("loads one complete AI response into every editor field, including JSX", () => {
|
||||
const setValues = vi.fn();
|
||||
applyDefinition(
|
||||
const result = applyCustomWidgetAiResponse(
|
||||
{
|
||||
values: {
|
||||
name: "",
|
||||
@@ -40,10 +40,22 @@ describe("Custom Widget workbench preview options", () => {
|
||||
},
|
||||
setValues,
|
||||
} as never,
|
||||
definition,
|
||||
`\`\`\`json\n${JSON.stringify(definition)}\n\`\`\``,
|
||||
);
|
||||
|
||||
expect(setValues).toHaveBeenCalledWith(expect.objectContaining({ template: "<Text>Preview</Text>" }));
|
||||
if (!result.success) throw new Error("Expected the complete AI response to be accepted");
|
||||
expect(setValues).toHaveBeenCalledOnce();
|
||||
const values = setValues.mock.calls[0]?.[0];
|
||||
expect(values).toMatchObject({
|
||||
name: "Preview options",
|
||||
description: "",
|
||||
iconUrl: "",
|
||||
template: "<Text>Preview</Text>",
|
||||
secrets: [],
|
||||
});
|
||||
expect(JSON.parse(values?.sources as string)).toEqual(result.widget.sources);
|
||||
expect(JSON.parse(values?.requests as string)).toEqual(result.widget.requests);
|
||||
expect(JSON.parse(values?.options as string)).toEqual(result.widget.options);
|
||||
});
|
||||
|
||||
it("shows the default source first regardless of manifest key order", () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
customWidgetDefinitionSchema,
|
||||
getCustomWidgetDefaultOptions,
|
||||
getCustomWidgetRequiredSecretKinds,
|
||||
parseCustomWidgetAiResponse,
|
||||
validateCustomWidgetOptions,
|
||||
} from "@homarr/custom-widgets/core";
|
||||
import type { CustomWidgetSource, HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core";
|
||||
@@ -56,6 +57,12 @@ export function applyDefinition(form: CustomWidgetWorkbenchForm, widget: HomarrC
|
||||
});
|
||||
}
|
||||
|
||||
export function applyCustomWidgetAiResponse(form: CustomWidgetWorkbenchForm, response: string) {
|
||||
const result = parseCustomWidgetAiResponse(response);
|
||||
if (result.success) applyDefinition(form, result.widget);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseSources(value: string): Array<CustomWidgetSource & { id: string }> {
|
||||
const parsed = parseJson(value);
|
||||
return isRecord(parsed)
|
||||
|
||||
+3
-8
@@ -6,17 +6,13 @@ import { useRouter } from "next/navigation";
|
||||
import type { UseFormReturnType } from "@mantine/form";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import {
|
||||
customWidgetDefinitionSchema,
|
||||
formatCustomWidgetImportIssues,
|
||||
parseCustomWidgetAiResponse,
|
||||
} from "@homarr/custom-widgets/core";
|
||||
import { formatCustomWidgetImportIssues } from "@homarr/custom-widgets/core";
|
||||
import type { CustomWidgetFormValues } from "@homarr/custom-widgets/workbench";
|
||||
import { showErrorNotification, showSuccessNotification, showWarningNotification } from "@homarr/notifications";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import {
|
||||
applyDefinition,
|
||||
applyCustomWidgetAiResponse,
|
||||
buildDefinition,
|
||||
getChangedSecrets,
|
||||
getDefinitionDefaults,
|
||||
@@ -167,11 +163,10 @@ export function useCustomWidgetFormActions(input: FormActionsInput) {
|
||||
|
||||
const pasteAiResponse = async () => {
|
||||
try {
|
||||
const result = parseCustomWidgetAiResponse(await navigator.clipboard.readText());
|
||||
const result = applyCustomWidgetAiResponse(input.form, await navigator.clipboard.readText());
|
||||
if (!result.success) {
|
||||
throw new Error(formatCustomWidgetImportIssues(result.issues));
|
||||
}
|
||||
applyDefinition(input.form, customWidgetDefinitionSchema.parse(result.widget));
|
||||
input.setOptionsSnapshot(getDefinitionDefaults(result.widget));
|
||||
input.setPreview({ data: {}, status: {}, session: null, outcome: "idle" });
|
||||
showSuccessNotification({
|
||||
|
||||
Reference in new issue
Block a user