test: address custom widget review findings

This commit is contained in:
ajnart committed 2026-07-30 11:34:24 +02:00
1 parent 6eff1da08f
commit 45dd20f4f7
6 files changed
+96 -16

No files matched your search

+2
View File
@@ -414,6 +414,7 @@ describe("Custom JSX v2 workbench", () => {
await page.reload();
const migratedCard = page
.getByRole("main")
.getByText(legacyWidgetName, { exact: true })
.locator("xpath=ancestor::*[contains(@class, 'mantine-Card-root')]");
await expect(migratedCard.getByText("Migration required", { exact: true })).not.toBeVisible();
@@ -441,6 +442,7 @@ describe("Custom JSX v2 workbench", () => {
await page.reload();
const resurfacedLegacyCard = page
.getByRole("main")
.getByText(legacyWidgetName, { exact: true })
.locator("xpath=ancestor::*[contains(@class, 'mantine-Card-root')]");
await expect(resurfacedLegacyCard.getByText("Migration required", { exact: true })).toBeVisible();
@@ -47,6 +47,7 @@ const program = ts.createProgram({ rootNames: [...config.fileNames, import.meta.
const checker = program.getTypeChecker();
const source = program.getSourceFile(import.meta.filename);
if (!source) throw new Error("Unable to inspect the component catalog generator");
const sourceNode: ts.Node = source;
const moduleSymbols = new Map<string, ts.Symbol>();
for (const declaration of source.statements) {
@@ -163,7 +164,7 @@ function inspectMantineComponent(component: CatalogSourceComponent): InspectedCo
const rootName = component.name.split(".")[0];
let symbol =
moduleSymbol && checker.getExportsOfModule(moduleSymbol).find((candidate) => candidate.name === rootName);
let location = symbol?.valueDeclaration ?? symbol?.declarations?.[0] ?? source;
let location: ts.Node = symbol?.valueDeclaration ?? symbol?.declarations?.[0] ?? sourceNode;
let type = symbol && checker.getTypeOfSymbolAtLocation(symbol, location);
for (const segment of component.name.split(".").slice(1)) {
symbol = type?.getProperty(segment);
@@ -311,7 +312,7 @@ function mergeGlobalProp(name: string, candidates: UninternedPropDescriptor[]):
const fallback = createFallbackProp(name, "global");
const type = mostFrequent(candidates.map((candidate) => candidate.type)) ?? fallback.type;
const descriptions = candidates.flatMap(({ description }) => (description ? [description] : []));
const literalValues = uniqueLiterals(candidates.flatMap(({ literalValues }) => literalValues ?? []));
const literalValues = uniqueLiterals(candidates.flatMap((candidate) => candidate.literalValues ?? []));
return {
name,
type,
+9 -6
View File
@@ -23,7 +23,7 @@ export const customWidgetMcpAgentResultSchema = z.strictObject({
status: z.enum(["pass", "needs-user-configuration", "fail"]),
summary: z.string().min(1).max(1_000),
definitionId: z.string().min(1).nullable(),
previewUrl: z.string().url().nullable(),
previewUrl: z.url().nullable(),
iterations: z.number().int().min(1).max(20),
evidence: z
.array(
@@ -168,7 +168,8 @@ export function getCustomWidgetMcpWorkflowIssues(args: {
issues.push("customWidget_get must run before validating an edit");
}
const previewCreate = firstSuccessfulIndex("customWidget_previewCreate");
if (finalValidation >= 0 && previewCreate >= 0 && finalValidation > previewCreate) {
const finalPreviewCreate = lastSuccessfulIndex("customWidget_previewCreate");
if (finalValidation >= 0 && finalPreviewCreate >= 0 && finalValidation > finalPreviewCreate) {
issues.push("The final validation must run before customWidget_previewCreate");
}
const previewChecks = [
@@ -194,10 +195,12 @@ export function getCustomWidgetMcpWorkflowIssues(args: {
}
}
const finalJournal = lastSuccessfulIndex("customWidget_previewJournal");
for (const tool of ["customWidget_previewQuery", "customWidget_previewAction"]) {
if (lastSuccessfulIndex(tool) > finalJournal) {
issues.push("customWidget_previewJournal must run after all preview requests");
break;
if (finalJournal >= 0) {
for (const tool of ["customWidget_previewQuery", "customWidget_previewAction"]) {
if (lastSuccessfulIndex(tool) > finalJournal) {
issues.push("customWidget_previewJournal must run after all preview requests");
break;
}
}
}
}
@@ -69,4 +69,18 @@ describe("AI prompt", () => {
expect(prompt).toContain('"X-Auth": "[REDACTED]"');
expect(prompt).toContain('"X-Feature-Key": "dashboard-layout"');
});
it("redacts credentials embedded directly in the free-form request", () => {
const prompt = buildCustomWidgetAiPrompt(
undefined,
null,
null,
"Use Authorization: Bearer sk-secret-123456 and token=ghp_abcdefghijklmnopqrstuvwxyz123456",
);
expect(prompt).not.toContain("sk-secret-123456");
expect(prompt).not.toContain("ghp_abcdefghijklmnopqrstuvwxyz123456");
expect(prompt).toContain("Authorization: Bearer [REDACTED]");
expect(prompt).toContain("token=[REDACTED]");
});
});
@@ -248,6 +248,72 @@ describe("Custom Widget MCP agent", () => {
);
});
it("accepts a repaired preview followed by final validation and a fresh preview", () => {
const toolsUsed = [
"customWidget_getAuthoringPrompt",
"customWidget_getSkill",
"customWidget_schema",
"customWidget_validate",
"customWidget_previewCreate",
"customWidget_validate",
"customWidget_previewCreate",
"customWidget_previewQuery",
"customWidget_previewJournal",
];
expect(
getCustomWidgetMcpWorkflowIssues({
output: {
status: "pass",
summary: "The repaired widget passed a fresh preview.",
definitionId: null,
previewUrl: "http://localhost:3000/manage/custom-widgets/preview/session-repaired",
iterations: 2,
evidence: [...new Set(toolsUsed)].map((name) => ({
tool: name,
outcome: name === "customWidget_validate" ? ("repaired" as const) : ("passed" as const),
detail: `${name} completed.`,
})),
remainingIssues: [],
},
toolExecutions: successfulExecutions(toolsUsed),
persist: false,
}),
).toEqual([]);
});
it("does not report journal ordering when the journal was never run", () => {
const toolsUsed = [
"customWidget_getAuthoringPrompt",
"customWidget_getSkill",
"customWidget_schema",
"customWidget_validate",
"customWidget_previewCreate",
"customWidget_previewQuery",
];
const issues = getCustomWidgetMcpWorkflowIssues({
output: {
status: "pass",
summary: "The agent incorrectly skipped the journal.",
definitionId: null,
previewUrl: "http://localhost:3000/manage/custom-widgets/preview/session-no-journal",
iterations: 1,
evidence: toolsUsed.map((name) => ({
tool: name,
outcome: "passed" as const,
detail: `${name} completed.`,
})),
remainingIssues: [],
},
toolExecutions: successfulExecutions(toolsUsed),
persist: false,
});
expect(issues).toContain("Required tool was not used: customWidget_previewJournal");
expect(issues).not.toContain("customWidget_previewJournal must run after all preview requests");
});
it("allows secure preview configuration to pause before authenticated requests can run", () => {
const toolsUsed = [
"customWidget_getAuthoringPrompt",
@@ -3,12 +3,6 @@
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/scripts-tsbuildinfo.json"
},
"include": [
"scripts/ai-evaluation-cases.ts",
"scripts/ai-evaluation.ts",
"scripts/evaluate-ai-authoring.ts",
"scripts/evaluate-mcp-agent.ts",
"scripts/mcp-agent.ts"
],
"exclude": ["node_modules"]
"include": ["scripts/**/*.ts"],
"exclude": ["node_modules", "scripts/verify-reference-apis.ts"]
}