mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-12 20:49:53 +08:00
- index.ts: registerTool() does the session check, try/catch and the
write queue for all 15 tools; fail()/ok(), syncFromBrowser(),
commitDocument() and describePages() replace the copied blocks; drop
pickPageSelector, currentSession.version, the input ?? {} defaults;
add_page reuses prepareNewModel() split out of prepareNewDiagram
- xml-validation.ts: drop the hand-written syntax checks that saxes
already reports, MAX_XML_SIZE and the stale header
- http-server.ts: one pendingExport object, no unread response fields,
no CORS/OPTIONS branch for same-origin requests, fewer exports
- diagram-operations parseNewCell(), addHistory returns void,
normalizeToMxfile without host, foldCells without layer, expandStyles
removed, unused preview.css rule removed, no .d.ts output
- xml-examples.ts styleRules() shares the Styles paragraphs with the
web prompt
- tests: setupFiles installs the DOM polyfill once, server-wiring uses
the SDK client, mcp-core.test.ts drops the cases MCP tests already
cover
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
/**
|
|
* Tests for the diagram history: the versions restore_version can go to.
|
|
*/
|
|
|
|
import { describe, expect, it } from "vitest"
|
|
import {
|
|
addHistory,
|
|
clearHistory,
|
|
getHistory,
|
|
isInHistory,
|
|
otherVersions,
|
|
} from "../src/history.ts"
|
|
|
|
const page = (cellId: string) =>
|
|
`<mxfile><diagram id="p" name="P"><mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="${cellId}" vertex="1" parent="1"/></root></mxGraphModel></diagram></mxfile>`
|
|
|
|
const A = page("a")
|
|
const B = page("b")
|
|
const C = page("c")
|
|
|
|
// draw.io's copy of B: other attribute order, whitespace and viewport
|
|
// attributes, same diagram
|
|
const B_COPY = `<mxfile host="app.diagrams.net">
|
|
<diagram name="P" id="p">
|
|
<mxGraphModel dx="100" dy="200" pageWidth="850">
|
|
<root>
|
|
<mxCell id="0" />
|
|
<mxCell id="1" parent="0" />
|
|
<mxCell id="b" parent="1" vertex="1" />
|
|
</root>
|
|
</mxGraphModel>
|
|
</diagram>
|
|
</mxfile>`
|
|
|
|
const fill = (id: string, versions: string[]) => {
|
|
clearHistory(id)
|
|
for (const xml of versions) addHistory(id, xml)
|
|
}
|
|
|
|
describe("otherVersions", () => {
|
|
it("lists the versions before the canvas, newest first", () => {
|
|
const id = "history-abc"
|
|
fill(id, [A, B, C])
|
|
expect(otherVersions(id, C).map((e) => e.xml)).toEqual([B, A])
|
|
})
|
|
|
|
it("lists each version once after an undo, the left one first", () => {
|
|
const id = "history-abcb"
|
|
fill(id, [A, B, C, B])
|
|
expect(otherVersions(id, B).map((e) => e.xml)).toEqual([C, A])
|
|
})
|
|
|
|
it("lists every version when the canvas is in none of them", () => {
|
|
const id = "history-manual"
|
|
fill(id, [A, B, C])
|
|
expect(otherVersions(id, page("manual")).map((e) => e.xml)).toEqual([
|
|
C,
|
|
B,
|
|
A,
|
|
])
|
|
})
|
|
|
|
it("counts a re-serialised copy as the same version", () => {
|
|
const id = "history-copy"
|
|
fill(id, [A, B, B_COPY])
|
|
expect(otherVersions(id, B).map((e) => e.xml)).toEqual([A])
|
|
expect(otherVersions(id, B_COPY).map((e) => e.xml)).toEqual([A])
|
|
})
|
|
|
|
it("keeps the newest copy of a repeated version", () => {
|
|
const id = "history-newest"
|
|
fill(id, [A, B, A])
|
|
const [newestA] = otherVersions(id, B)
|
|
expect(newestA.id).toBe(getHistory(id)[2].id)
|
|
expect(otherVersions(id, B)).toHaveLength(1)
|
|
})
|
|
|
|
it("matches a bare model from the browser with its named page", () => {
|
|
const id = "history-bare"
|
|
const named = (xml: string) => xml.replace('name="P"', 'name="Arch"')
|
|
fill(id, [named(A), named(B)])
|
|
// The browser's sync sent page B without its <mxfile> wrapper
|
|
const bareB = B.replace(/^.*?(<mxGraphModel>)/, "$1").replace(
|
|
"</diagram></mxfile>",
|
|
"",
|
|
)
|
|
expect(otherVersions(id, bareB).map((e) => e.xml)).toEqual([named(A)])
|
|
expect(isInHistory(id, bareB)).toBe(true)
|
|
expect(isInHistory(id, C)).toBe(false)
|
|
})
|
|
|
|
it("still counts a rename when an unrelated bare model is in History", () => {
|
|
const id = "history-mixed"
|
|
const bareX = page("x")
|
|
.replace(/^.*?(<mxGraphModel>)/, "$1")
|
|
.replace("</diagram></mxfile>", "")
|
|
const arch = B.replace('name="P"', 'name="Arch"')
|
|
const overview = B.replace('name="P"', 'name="Overview"')
|
|
fill(id, [bareX, arch])
|
|
expect(otherVersions(id, overview).map((e) => e.xml)).toEqual([
|
|
arch,
|
|
bareX,
|
|
])
|
|
expect(isInHistory(id, overview)).toBe(false)
|
|
})
|
|
|
|
it("returns nothing for an empty history", () => {
|
|
const id = "history-empty"
|
|
clearHistory(id)
|
|
expect(otherVersions(id, A)).toEqual([])
|
|
})
|
|
})
|