Files
kandev/apps/web/components/shared/mermaid-utils.test.ts
T
2026-06-18 12:00:18 +01:00

109 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { sanitizeMermaidCode } from "./mermaid-utils";
describe("sanitizeMermaidCode", () => {
it("leaves a pre-quoted bracket label with parens inside untouched", () => {
const input = `D --> E["router.push('/github')"]`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("quotes a bracket label containing slashes alongside a pre-quoted neighbour", () => {
const input = `A[plain] --> B[Types 'github' / 'pr' / 'dashboard']\nD --> E["router.push('/github')"]`;
const out = sanitizeMermaidCode(input);
expect(out).toContain(`B["Types 'github' / 'pr' / 'dashboard'"]`);
expect(out).toContain(`E["router.push('/github')"]`);
});
it("leaves an init directive with single quotes untouched", () => {
const input = `%%{init: {'theme': 'neutral'}}%%`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("quotes a bracket label containing parens", () => {
const input = `Action[reorderSidebarViews(activeId, overId)]`;
expect(sanitizeMermaidCode(input)).toBe(`Action["reorderSidebarViews(activeId, overId)"]`);
});
it("quotes a bracket label containing arrow `->`", () => {
const input = `SSR[fetchUserSettings -> mapUserSettingsResponse]`;
expect(sanitizeMermaidCode(input)).toBe(`SSR["fetchUserSettings -> mapUserSettingsResponse"]`);
});
it("quotes a standalone stadium node containing `/`", () => {
const input = `X(/api/v1)`;
expect(sanitizeMermaidCode(input)).toBe(`X("/api/v1")`);
});
it("quotes an edge label containing `/`", () => {
const input = `A -->|/path/to/x| B`;
expect(sanitizeMermaidCode(input)).toBe(`A -->|"/path/to/x"| B`);
});
it("leaves a plain stadium node alone", () => {
const input = `Y(plain text)`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("does not corrupt parens inside a bracket label after the bracket pass quotes it", () => {
// Pass 1 wraps `[fetch(/api/x)]` -> `["fetch(/api/x)"]`. Pass 3 must skip the
// newly-quoted region rather than re-wrapping `(/api/x)` and producing nested quotes.
const input = `Z[fetch(/api/x)]`;
expect(sanitizeMermaidCode(input)).toBe(`Z["fetch(/api/x)"]`);
});
it("renders the full reported case 1 diagram without nested quotes", () => {
const input = [
`%%{init: {'theme': 'neutral'}}%%`,
`flowchart TD`,
` A[User opens Cmd+K panel] --> B[Types 'github' / 'pr' / 'dashboard']`,
` D --> E["router.push('/github')"]`,
].join("\n");
const out = sanitizeMermaidCode(input);
expect(out).not.toContain(`("'`);
expect(out).toContain(`E["router.push('/github')"]`);
expect(out).toContain(`B["Types 'github' / 'pr' / 'dashboard'"]`);
});
it("quotes a stadium node next to a bracket-with-parens on the same line", () => {
// Pass 1 quotes `[fn(x)]` (parens inside bracket). Pass 3 must still quote
// the adjacent stadium `(/api/v1)` — the new quoted range from pass 1 must
// not leak past its actual close and suppress the unrelated stadium node.
const input = `A[fn(x)] --> B(/api/v1)`;
expect(sanitizeMermaidCode(input)).toBe(`A["fn(x)"] --> B("/api/v1")`);
});
it("does not let an unterminated quote leak across lines", () => {
// Line 1 has a stray `"` with no closing pair on the same line. The newline
// guard in findQuotedRanges discards that opener instead of pairing it with
// a `"` on a later line, so the bracket label on line 2 still gets quoted.
const input = `%% stray " comment\nB[/api/x]`;
const out = sanitizeMermaidCode(input);
expect(out).toContain(`B["/api/x"]`);
});
it("does not match an edge label that spans a newline", () => {
const input = `A -->|open\nB | C`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("does not match a bracket label that spans a newline", () => {
const input = `A[open\nB]`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("does not match a parenthesis label that spans a newline", () => {
const input = `A(open\n/path)`;
expect(sanitizeMermaidCode(input)).toBe(input);
});
it("does not quote ER diagram cardinality bars as flowchart edge labels", () => {
const input = [
"erDiagram",
"workspaces ||--o{ workflows : owns",
"workflows ||--o{ workflow_steps : contains",
].join("\n");
expect(sanitizeMermaidCode(input)).toBe(input);
});
});