import { describe, expect, test } from "vitest"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { validateCustomJsxTemplate } from "../jsx/analyzer"; import { createCustomJsxBindings, CUSTOM_JSX_DATA_LIMITS } from "../jsx/bindings"; import { renderSafeJsx, sanitizeCustomJsxProps } from "../jsx/interpreter"; import { CUSTOM_JSX_BLOCKED_PROPERTIES, CUSTOM_JSX_BLOCKED_PROPS, CUSTOM_JSX_LIMITS } from "../jsx/policy"; const components = { Text: () => null }; const bindings = { data: Object.create(null) as Record }; describe("shared Custom JSX policy", () => { test.each(['status.list === "loading"', '"success" === status.list', 'status.list !== "error"'])( "rejects string request-status comparisons in %s", (expression) => { expect(validateCustomJsxTemplate(`{${expression}}`)).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("INVALID_STATUS_COMPARISON: status.list is an object"), }), ]), ); }, ); test("accepts request-status object fields", () => { const diagnostics = validateCustomJsxTemplate( "{status.list?.loading ? 'Loading' : status.list?.ok === false ? status.list.error : 'Ready'}", ); expect(diagnostics.filter(({ severity }) => severity === "error")).toEqual([]); }); test("accepts canonical Mantine compound component names", () => { const diagnostics = validateCustomJsxTemplate( '', ); expect(diagnostics.filter(({ severity }) => severity === "error")).toEqual([]); }); test("accepts the concise Icon alias used by the AI prompt", () => { expect(validateCustomJsxTemplate('')).toEqual([]); }); test("rejects TablerIcon without its required name prop", () => { expect(validateCustomJsxTemplate('')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("MISSING_REQUIRED_PROP: 'name' on TablerIcon"), }), expect.objectContaining({ severity: "error", message: expect.stringContaining("UNKNOWN_COMPONENT_PROP: 'icon' on TablerIcon"), }), ]), ); }); test("accepts common bounded formatting and collection operations", () => { expect( validateCustomJsxTemplate( '{[1, 2].concat([3]).flatMap(value => [value]).map(value => value.toLocaleString()).join(", ")}', ), ).toEqual([]); expect(validateCustomJsxTemplate('{"b".localeCompare("a")}')).toEqual([]); }); test.each([...CUSTOM_JSX_BLOCKED_PROPERTIES])( "blocks reflective property %s in diagnostics and runtime", (property) => { const template = `{data[${JSON.stringify(property)}]}`; expect(validateCustomJsxTemplate(template).some(({ severity }) => severity === "error")).toBe(true); expect(() => renderSafeJsx({ template, components, bindings })).toThrow(/reflective property/u); }, ); test.each([...CUSTOM_JSX_BLOCKED_PROPS])("removes blocked prop %s at the renderer boundary", (property) => { const safe = sanitizeCustomJsxProps({ [property]: "unsafe", title: "safe" }, "Text"); expect(safe).not.toHaveProperty(property); expect(safe).toHaveProperty("title", "safe"); }); test("uses the same depth and operation budgets for analysis and runtime", () => { const template = `{${"!".repeat(CUSTOM_JSX_LIMITS.astDepth + 1)}data}`; expect(validateCustomJsxTemplate(template).some(({ message }) => message.includes("depth limit"))).toBe(true); }); test("passes unknown ordinary props through as advisories and blocks capabilities explicitly", () => { const diagnostics = validateCustomJsxTemplate('Safe'); expect(diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "warning", message: expect.stringContaining("UNKNOWN_MANTINE_PROP") }), ]), ); expect(sanitizeCustomJsxProps({ futureMantineProp: "yes" }, "Text")).toHaveProperty("futureMantineProp", "yes"); expect(validateCustomJsxTemplate(" null}>Unsafe")).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("BLOCKED_CAPABILITY") }), ]), ); expect(validateCustomJsxTemplate(" value}>Unsafe")).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("Callback prop") }), ]), ); expect(validateCustomJsxTemplate('Unsafe')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("INVALID_PROP_VALUE") }), ]), ); expect(validateCustomJsxTemplate('Unsafe')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("BLOCKED_CAPABILITY") }), ]), ); expect(sanitizeCustomJsxProps({ pos: "fixed", top: 0, unstyled: true })).toEqual({ unstyled: true }); expect(sanitizeCustomJsxProps({ position: "top", unstyled: true }, "Tooltip")).toEqual({ position: "top", unstyled: true, }); }); test("rejects unsupported date-time multiple mode in analysis and at the renderer boundary", () => { expect(validateCustomJsxTemplate('')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("INVALID_PROP_VALUE") }), ]), ); expect(sanitizeCustomJsxProps({ type: "multiple", label: "Date" }, "DateTimePicker")).toEqual({ label: "Date", }); const rendered = renderSafeJsx({ template: "", components: { ...components, DateTimePicker: (() => null) as never }, bindings: { data: { props: { type: "multiple", label: "Date" } } }, }); expect(rendered.warnings).toEqual([expect.stringContaining("INVALID_PROP_VALUE")]); }); test("reports and removes component-specific blocked props from explicit and spread JSX", () => { expect(validateCustomJsxTemplate('Unsafe')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("BLOCKED_CAPABILITY") }), ]), ); expect(sanitizeCustomJsxProps({ target: "#outside", label: "Safe" }, "Tooltip")).toEqual({ label: "Safe" }); const rendered = renderSafeJsx({ template: "Safe", components: { ...components, Tooltip: (() => null) as never }, bindings: { data: { props: { target: "#outside", label: "Safe" } } }, }); expect(rendered.warnings).toEqual([ expect.stringContaining("BLOCKED_CAPABILITY: Prop 'Tooltip.target' is not allowed"), ]); }); test("preserves interpreted React elements in safe ReactNode props", () => { const rendered = renderSafeJsx({ template: '', components: { Button: ((props: { children?: unknown; leftSection?: unknown }) => createElement("button", null, props.leftSection as never, props.children as never)) as never, TablerIcon: ((props: { name?: string }) => createElement("span", null, props.name)) as never, }, bindings, }); expect(rendered.warnings).toEqual([]); expect(renderToStaticMarkup(rendered.node)).toContain("plusAdd"); }); test.each(["|", "&", "^", "in", "instanceof"])( "rejects unsupported binary operator %s in analysis and runtime", (operator) => { const right = operator === "in" ? "data" : operator === "instanceof" ? "data.Type" : "1"; const template = `{1 ${operator} ${right}}`; expect(validateCustomJsxTemplate(template)).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("Binary operator") }), ]), ); expect(() => renderSafeJsx({ template, components, bindings: { data: { Type: Object } } })).toThrow( "Unsupported binary operator", ); }, ); test.each([ ["data.fn()", "CALL_TARGET_NOT_ALLOWED"], ["data.value.unknownMethod()", "CALL_TARGET_NOT_ALLOWED"], ["data.items.map()", "CALLBACK_VALUE_NOT_ALLOWED"], ["data.items.map(data.fn)", "CALLBACK_VALUE_NOT_ALLOWED"], ["data.items.sort(Number)", "CALLBACK_VALUE_NOT_ALLOWED"], ["data.items.reduce(Number, 0)", "CALLBACK_VALUE_NOT_ALLOWED"], ])("rejects analyzer/runtime call mismatch %s", (expression, diagnostic) => { const template = `{${expression}}`; expect(validateCustomJsxTemplate(template)).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining(diagnostic) }), ]), ); expect(() => renderSafeJsx({ template, components, bindings: { data: { fn: () => 1, items: [1, 2], value: {}, Type: Object } }, }), ).toThrow(); }); test.each([ "data.items.map(Number).join(',')", "data.items.filter(Boolean).join(',')", "String(data.value)", "data.label.toUpperCase()", ])("accepts documented safe call %s", (expression) => { const template = `{${expression}}`; expect(validateCustomJsxTemplate(template).filter(({ severity }) => severity === "error")).toEqual([]); expect(() => renderSafeJsx({ template, components, bindings: createCustomJsxBindings({ items: [0, 2], label: "safe", value: 2 }), }), ).not.toThrow(); }); test("blocks refs, native popovers, and editable DOM capabilities without blocking declarative bind", () => { const diagnostics = validateCustomJsxTemplate( '', ); expect(diagnostics.filter(({ severity }) => severity === "error")).toHaveLength(3); expect(diagnostics.some(({ message }) => message.includes("Prop 'bind'"))).toBe(false); }); test("validates explicit bind attributes through the JSX AST", () => { expect( validateCustomJsxTemplate('').filter(({ severity }) => severity === "error"), ).toEqual([]); expect(validateCustomJsxTemplate("")).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("literal input name") }), ]), ); expect(validateCustomJsxTemplate('')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("Invalid bind input name") }), ]), ); expect( validateCustomJsxTemplate('{""}').some(({ message }) => message.includes("literal input name"), ), ).toBe(false); }); test("rejects bind values introduced by JSX spreads at the emitter boundary", () => { expect(() => renderSafeJsx({ template: "", components: { TextInput: (() => null) as never }, bindings: { data: { props: { bind: "remote-name", label: "Unsafe" } } }, }), ).toThrow("BIND_SPREAD_NOT_ALLOWED"); }); test("provides closest component and binding diagnostics", () => { expect(validateCustomJsxTemplate("")[0]?.message).toMatch(/UNKNOWN_COMPONENT.*Stack/u); expect(validateCustomJsxTemplate('')).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "warning", message: expect.stringContaining("BINDING_UNAVAILABLE") }), ]), ); expect(() => renderSafeJsx({ template: '', components, bindings })).not.toThrow(); }); test("reports denied component families as blocked capabilities", () => { expect(validateCustomJsxTemplate("")[0]?.message).toMatch(/BLOCKED_CAPABILITY/u); }); test("reports blocked capabilities introduced through spreads at runtime", () => { const rendered = renderSafeJsx({ template: "Safe", components, bindings: { data: { props: { onClick: "unsafe", href: "javascript:alert(1)" } } }, }); expect(rendered.warnings).toEqual( expect.arrayContaining([ expect.stringContaining("BLOCKED_CAPABILITY"), expect.stringContaining("INVALID_PROP_VALUE"), ]), ); }); test("sanitizes and diagnoses blocked capabilities inside nested Mantine props", () => { const props = sanitizeCustomJsxProps({ tooltipProps: { label: "Safe", onClick: "unsafe", href: "javascript:alert(1)" }, data: [{ image: "metric-name", onClick: "api-field" }], }); expect(props).toEqual({ tooltipProps: { label: "Safe" }, data: [{ image: "metric-name", onClick: "api-field" }], }); expect(sanitizeCustomJsxProps({ values: ["safe", () => "unsafe", { nested: () => "unsafe" }] })).toEqual({ values: ["safe", undefined, { nested: undefined }], }); expect(validateCustomJsxTemplate(" null }}>Unsafe")).toEqual( expect.arrayContaining([ expect.objectContaining({ severity: "error", message: expect.stringContaining("Callback prop") }), ]), ); expect( validateCustomJsxTemplate(" item)} />").some(({ message }) => message.includes("Callback prop"), ), ).toBe(false); expect(validateCustomJsxTemplate("