import { describe, expect, it } from "vitest"
import { extractCompleteMxCells, isMxCellXmlComplete } from "@/lib/utils"
import {
expandCompactCells,
foldCells,
} from "@/packages/mcp-server/src/compact-cells.ts"
import { editDiagram } from "@/packages/mcp-server/src/edit-diagram.ts"
import { prepareNewDiagram } from "@/packages/mcp-server/src/new-diagram.ts"
import {
normalizeToMxfile,
wrapCellsInModel,
} from "@/packages/mcp-server/src/pages.ts"
const shape =
''
const shapeLong =
''
const edge = ''
const edgeLong =
''
/** Cells compared by what they mean, not by attribute order */
function canonical(xml: string): string {
const doc = new DOMParser().parseFromString(
`${xml}`,
"text/xml",
)
const describe = (el: Element): string => {
const attrs = Array.from(el.attributes)
.map((a) => `${a.name}=${a.value}`)
.sort()
.join(" ")
const children = Array.from(el.children).map(describe).join("")
return `<${el.tagName} ${attrs}>${children}${el.tagName}>`
}
return Array.from(doc.documentElement.children).map(describe).join("\n")
}
describe("expandCompactCells", () => {
it("gives a compact shape its flags, parent and geometry", () => {
expect(expandCompactCells(shape)).toBe(shapeLong)
})
it("gives a compact edge its flags, parent and relative geometry", () => {
expect(expandCompactCells(edge)).toBe(edgeLong)
})
it("keeps an explicit parent and accepts width/height as names", () => {
const inLane =
''
expect(expandCompactCells(inLane)).toBe(
'',
)
})
it("fills in a default position and size when some are missing", () => {
expect(expandCompactCells('')).toBe(
'',
)
})
it("lets an explicit geometry win over compact attributes", () => {
// The stray attributes stay as written; the geometry is the one used
const both =
''
expect(expandCompactCells(both)).toBe(
'',
)
})
it("adds the relative geometry to a long-form edge that has none", () => {
const noGeometry =
''
expect(expandCompactCells(noGeometry)).toBe(edgeLong)
})
it("leaves root cells, long-form cells and edge labels alone", () => {
const roots = ''
const label =
''
expect(expandCompactCells(roots + shapeLong + edgeLong + label)).toBe(
roots + shapeLong + edgeLong + label,
)
})
it("works on a cell inside a UserObject wrapper", () => {
const wrapped =
''
expect(expandCompactCells(wrapped)).toBe(
'',
)
})
})
describe("foldCells", () => {
it("writes plain shapes and edges compactly", () => {
expect(foldCells(shapeLong)).toBe(shape)
expect(foldCells(edgeLong)).toBe(edge)
})
it("keeps a parent other than 1 and leaves special cells as written", () => {
const inLane =
''
expect(foldCells(inLane)).toBe(
'',
)
const waypoints =
''
const label =
''
const sourcePoint =
''
const roots = ''
const wrongAs =
''
for (const xml of [waypoints, label, sourcePoint, roots, wrongAs]) {
expect(foldCells(xml)).toBe(xml)
}
})
it("round-trips through expandCompactCells", () => {
const page = `${shapeLong}${edgeLong}`
const folded = foldCells(page)
expect(folded).not.toContain("vertex=")
expect(canonical(expandCompactCells(folded))).toBe(canonical(page))
})
})
describe("compact cells through the pipeline", () => {
const geometryOf = (xml: string, id: string) =>
xml.match(
new RegExp(`]*>(]*/>)`),
)?.[1]
it("prepareNewDiagram expands them and still adds the defaults", () => {
const prepared = prepareNewDiagram(`${shape}\n${edge}`)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain(
'style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"',
)
expect(geometryOf(prepared.xml, "2")).toBe(
'',
)
expect(prepared.xml).toContain(
'',
)
})
it("editDiagram accepts a compact cell in new_xml", () => {
const file =
normalizeToMxfile(wrapCellsInModel(shapeLong), {
pageId: "p1",
pageName: "Page-1",
}) ?? ""
const outcome = editDiagram(
file,
[
{
operation: "add",
cell_id: "3",
new_xml:
'',
},
{
operation: "update",
cell_id: "2",
new_xml:
'',
},
],
{},
)
expect(outcome.ok).toBe(true)
if (!outcome.ok) return
expect(outcome.xml).toContain('value="Next"')
expect(outcome.xml).toContain('value="Begin"')
expect(geometryOf(outcome.xml, "3")).toBe(
'',
)
})
})
describe("a compact cell with a slip", () => {
it("repairs a missing closing quote before the slash", () => {
const prepared = prepareNewDiagram(
'',
)
})
})
describe("review round: cells the model may write", () => {
it("escapes a double quote from a single-quoted value", () => {
const out = expandCompactCells(
``,
)
expect(out).toContain('value="Say "hi""')
const prepared = prepareNewDiagram(
``,
)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain('value="a" visible="0"')
expect(prepared.xml).not.toContain(' visible="0"')
const folded = foldCells(
``,
)
expect(folded).toBe(
'',
)
})
it("accepts labels with > in compact self-closing cells", () => {
const prepared = prepareNewDiagram(
`
`,
)
expect(prepared.ok).toBe(true)
expect(
isMxCellXmlComplete(
'',
),
).toBe(true)
expect(
extractCompleteMxCells(
'\n')
})
it("infers a vertex for an edge label with a geometry and an edge for a connection with a size", () => {
const label =
''
expect(expandCompactCells(label)).toBe(
'',
)
expect(
expandCompactCells(
'',
),
).toBe(
'',
)
// A dangling edge has only one end
expect(
expandCompactCells(''),
).toBe(
'',
)
})
it("does not take a geometry inside custom data for the cell's own", () => {
const out = expandCompactCells(
'',
)
expect(out).toBe(
'',
)
})
it("keeps stray size attributes when the cell has its own geometry", () => {
const xml =
''
expect(expandCompactCells(xml)).toBe(xml)
expect(foldCells(xml)).toBe(xml)
})
it("ignores prototype names and folds only cells that name their parent", () => {
expect(expandCompactCells('')).toBe(
'',
)
const noParent =
''
expect(foldCells(noParent)).toBe(noParent)
})
it("puts a compact cell on the page's first layer when editing", () => {
const file = `${shapeLong.replace('parent="1"', 'parent="L1"')}`
const outcome = editDiagram(
file,
[
{
operation: "add",
cell_id: "3",
new_xml:
'',
},
],
{},
)
expect(outcome.ok).toBe(true)
if (!outcome.ok) return
expect(outcome.xml).toContain(
'',
)
})
it("refuses a compact shape with a root cell id instead of dropping it", () => {
const prepared = prepareNewDiagram(
'',
)
expect(prepared.ok).toBe(false)
if (!prepared.ok) expect(prepared.error).toContain("root cells")
})
it("repairs the quote slip only on a cell's last numeric attribute", () => {
expect(
isMxCellXmlComplete('',
)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain('value="5/>"')
expect(prepared.xml).toContain(
'',
)
})
it("closes a compact cell written without the slash", () => {
const prepared = prepareNewDiagram(
'\n',
)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain('value="A"')
expect(prepared.xml).toContain('value="B"')
expect(prepared.fixes.join()).toContain("without the slash")
})
})
describe("a long-form cell missing its closing tag next to compact cells", () => {
it("is closed before the compact cell that follows", () => {
const prepared = prepareNewDiagram(
`
`,
)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain('')
expect(prepared.xml).toContain('value="C"')
expect((prepared.xml.match(/ {
const prepared = prepareNewDiagram(
`
`,
)
expect(prepared.ok).toBe(true)
if (!prepared.ok) return
expect(prepared.xml).toContain('value="A"')
expect(prepared.xml).toContain('value="B"')
expect((prepared.xml.match(/