import { describe, expect, it } from "vitest"
import {
applyDiagramOperations,
autoFixXml,
cn,
extractCompleteMxCells,
isMxCellXmlComplete,
validateAndFixXml,
validateMxCellStructure,
wrapWithMxFile,
} from "@/lib/utils"
describe("isMxCellXmlComplete", () => {
it("returns false for empty/null input", () => {
expect(isMxCellXmlComplete("")).toBe(false)
expect(isMxCellXmlComplete(null)).toBe(false)
expect(isMxCellXmlComplete(undefined)).toBe(false)
})
it("returns true for self-closing mxCell", () => {
const xml =
''
expect(isMxCellXmlComplete(xml)).toBe(true)
})
it("returns true for mxCell with closing tag", () => {
const xml = `
`
expect(isMxCellXmlComplete(xml)).toBe(true)
})
it("returns false for truncated mxCell", () => {
const xml =
' {
const xml = `
{
const xml = `
`
expect(isMxCellXmlComplete(xml)).toBe(false)
})
it("returns false when output stops after of an open mxCell", () => {
const xml = `
`
expect(isMxCellXmlComplete(xml)).toBe(false)
})
it("returns true for a self-closing last mxCell with > in its value", () => {
const xml = `
`
expect(isMxCellXmlComplete(xml)).toBe(true)
})
it("returns true for multiple complete mxCells", () => {
const xml = `
`
expect(isMxCellXmlComplete(xml)).toBe(true)
})
})
describe("wrapWithMxFile", () => {
it("wraps empty string with default structure", () => {
const result = wrapWithMxFile("")
expect(result).toContain("")
expect(result).toContain("")
expect(result).toContain('')
expect(result).toContain('')
})
it("wraps raw mxCell content", () => {
const xml = ''
const result = wrapWithMxFile(xml)
expect(result).toContain("")
expect(result).toContain(xml)
expect(result).toContain("")
})
it("returns full mxfile unchanged", () => {
const fullXml =
''
const result = wrapWithMxFile(fullXml)
expect(result).toBe(fullXml)
})
it("handles whitespace in input", () => {
const result = wrapWithMxFile(" ")
expect(result).toContain("")
})
})
describe("cn (class name utility)", () => {
it("merges class names", () => {
expect(cn("foo", "bar")).toBe("foo bar")
})
it("handles conditional classes", () => {
expect(cn("foo", false && "bar", "baz")).toBe("foo baz")
})
it("merges tailwind classes correctly", () => {
expect(cn("px-2", "px-4")).toBe("px-4")
expect(cn("text-red-500", "text-blue-500")).toBe("text-blue-500")
})
})
describe("extractCompleteMxCells", () => {
it("keeps the cell right after self-closing root cells", () => {
const xml = ``
const ids = [
...extractCompleteMxCells(xml).matchAll(/ m[1])
expect(ids).toEqual(["0", "1", "2", "3"])
})
it("drops an incomplete trailing cell", () => {
const xml = ``
expect(extractCompleteMxCells(xml)).toBe(
'',
)
})
})
const page = (id: string, cells: string) =>
`${cells}`
describe("duplicate ids in multi-page documents", () => {
const shape = (id: string, value = "Box") =>
``
it("accepts the same ids on different pages", () => {
const xml = `${page("p1", shape("2"))}${page("p2", shape("2"))}`
expect(validateMxCellStructure(xml)).toBeNull()
})
it("still reports duplicate ids within one page", () => {
const xml = `${page("p1", shape("2") + shape("2"))}${page("p2", "")}`
expect(validateMxCellStructure(xml)).toContain("duplicate ID")
})
it("does not rename the root cells of other pages when fixing", () => {
const xml = `${page("p1", shape("2", "R&D"))}${page("p2", shape("3"))}`
const result = validateAndFixXml(xml)
expect(result.valid).toBe(true)
expect(result.fixed).not.toContain("_dup")
expect(result.fixed).toContain("R&D")
})
it("renames a duplicate id within a page", () => {
const xml = `${page("p1", shape("d") + shape("d"))}`
const { fixed } = autoFixXml(xml)
expect(fixed).toContain(' {
it("does not insert a space at the start of style values", () => {
const xml = ``
const { fixed } = autoFixXml(xml)
expect(fixed).toContain('style="rounded=1;whiteSpace=wrap;"')
})
it("adds a missing space between attributes", () => {
const xml = ``
expect(autoFixXml(xml).fixed).toContain('vertex="1" parent="1"')
})
it("keeps " inside rich text labels", () => {
const label = "<font color="#ff0000">Hello</font>"
const xml = ``
const result = validateAndFixXml(xml)
expect(result.valid).toBe(true)
expect(result.fixed).toContain(`value="${label}"`)
})
it("fixes an attribute delimited by "", () => {
const xml = ``
expect(autoFixXml(xml).fixed).toContain('dashPattern="1 1;"')
})
it("keeps cells written on one line next to multi-line cells", () => {
const xml = `
`
const result = validateAndFixXml(xml)
expect(result.valid).toBe(true)
for (const id of ["2", "e1", "3"]) {
expect(result.fixed).toContain(` {
const xml = ``
const result = validateAndFixXml(xml)
expect(result.valid).toBe(true)
expect(result.fixed).toContain(' {
const xml = ``
it("deletes a wrapped cell and its edges", () => {
const { result, errors } = applyDiagramOperations(xml, [
{ operation: "delete", cell_id: "5" },
{ operation: "delete", cell_id: "e1" },
])
expect(errors).toEqual([])
expect(result).not.toContain("UserObject")
expect(result).not.toContain('id="e1"')
expect(result).toContain('id="6"')
})
it("rejects adding a cell with the id of a wrapped cell", () => {
const { errors } = applyDiagramOperations(xml, [
{
operation: "add",
cell_id: "5",
new_xml: '',
},
])
expect(errors[0]?.message).toContain("already exists")
})
it("updates a wrapped cell", () => {
const { result, errors } = applyDiagramOperations(xml, [
{
operation: "update",
cell_id: "5",
new_xml:
'',
},
])
expect(errors).toEqual([])
expect(result).toContain('label="New"')
expect(result).not.toContain('label="Docs"')
})
it("reports deleting a cell that does not exist", () => {
const { errors } = applyDiagramOperations(xml, [
{ operation: "delete", cell_id: "missing" },
])
expect(errors[0]?.message).toContain("not found")
})
})