import { describe, expect, it } from "vitest"; import { defaultSvgLimits } from "../../src/app/limits"; import { parseSvgSource } from "../../src/document/source-parser"; import { applySourcePatches, patchAttribute, } from "../../src/document/source-patcher"; const source = ` `; describe("source-faithful SVG parsing", () => { it("retains canonical source and maps unknown elements to exact ranges", () => { const result = parseSvgSource(source, 7); expect(result.valid).toBe(true); expect(result.semantic?.source).toBe(source); expect(result.semantic?.revision).toBe(7); const vendor = [...result.semantic!.nodes.values()].find( (node) => node.name === "vendor:thing", )!; expect( source.slice(vendor.sourceRange.full.from, vendor.sourceRange.full.to), ).toContain("vendor:thing"); expect(vendor.attributes.unusual).toBe("yes"); }); it("rejects malformed XML without constructing a semantic model", () => { const result = parseSvgSource( '', 2, ); expect(result.valid).toBe(false); expect(result.semantic).toBeNull(); expect(result.diagnostics.some((item) => item.severity === "error")).toBe( true, ); expect(result.diagnostics).toContainEqual( expect.objectContaining({ code: "xml-unclosed-element" }), ); }); it("preserves but refuses entity declarations before invoking the XML parser", () => { const entitySource = `]>&local;`; const result = parseSvgSource(entitySource, 8); expect(result.valid).toBe(false); expect(result.semantic).toBeNull(); expect(result.source).toBe(entitySource); expect(result.diagnostics).toContainEqual( expect.objectContaining({ code: "entity-declaration-blocked", severity: "error", }), ); }); it("preserves a simple doctype while excluding it from the editing parser", () => { const doctypeSource = ``; const result = parseSvgSource(doctypeSource, 9); expect(result.valid).toBe(true); expect(result.semantic?.source).toBe(doctypeSource); expect(result.diagnostics).toContainEqual( expect.objectContaining({ code: "doctype-preserved", severity: "warning", }), ); }); it("treats duplicate IDs as editable diagnostics, not XML invalidity", () => { const result = parseSvgSource( '', 3, ); expect(result.valid).toBe(true); expect(result.semantic).not.toBeNull(); expect(result.diagnostics).toContainEqual( expect.objectContaining({ code: "duplicate-id", severity: "warning" }), ); }); it("patches one attribute while preserving comments, quote style and unknown content", () => { const result = parseSvgSource(source, 1); const dot = [...result.semantic!.nodes.values()].find( (node) => node.id === "dot", )!; const patch = patchAttribute(source, dot, "cx", "42", result.preferences)!; const next = applySourcePatches(source, [patch]); expect(next).toBe(source.replace('cx="10"', 'cx="42"')); expect(next).toContain(""); expect(next).toContain("vendor:thing"); }); it("rejects overlapping or stale patches", () => { expect(() => applySourcePatches("abcdef", [ { from: 1, to: 4, insert: "x", label: "first" }, { from: 3, to: 5, insert: "y", label: "second" }, ]), ).toThrow(/overlapping/u); }); it("enforces each configurable structural resource limit", () => { const constrained = { ...defaultSvgLimits, maximumAttributeLength: 12, maximumPathCommandsPerPath: 2, maximumPathCommandsTotal: 2, maximumCssRules: 1, maximumReferences: 0, maximumAnimations: 0, maximumFilterPrimitives: 0, maximumTextLength: 4, maximumEmbeddedResourceBytes: 8, }; const result = parseSvgSource( ` longer text `, 9, constrained, ); const codes = result.diagnostics.map((item) => item.code); expect(result.valid).toBe(false); expect(codes).toEqual( expect.arrayContaining([ "attribute-length-limit", "embedded-resource-limit", "path-command-per-element-limit", "path-command-limit", "css-rule-limit", "reference-limit", "animation-limit", "filter-primitive-limit", "text-length-limit", "total-text-limit", ]), ); }); it("counts SVG path commands without mistaking exponent notation for commands", () => { const result = parseSvgSource( '', 4, ); expect(result.semantic?.metrics.pathCommandCount).toBe(2); }); it("counts implicit path groups and packed arc flags as resolved commands", () => { const result = parseSvgSource( '', 5, { ...defaultSvgLimits, maximumPathCommandsPerPath: 3 }, ); expect(result.semantic?.metrics.pathCommandCount).toBe(4); expect(result.diagnostics).toContainEqual( expect.objectContaining({ code: "path-command-per-element-limit" }), ); }); });