diff --git a/src/components/MermaidToExcalidraw.tsx b/src/components/MermaidToExcalidraw.tsx index 6c88ebebc..9f0fcb2ce 100644 --- a/src/components/MermaidToExcalidraw.tsx +++ b/src/components/MermaidToExcalidraw.tsx @@ -15,7 +15,7 @@ import Spinner from "./Spinner"; import "./MermaidToExcalidraw.scss"; import { MermaidToExcalidrawResult } from "@excalidraw/mermaid-to-excalidraw/dist/interfaces"; -import { MermaidOptions } from "@excalidraw/mermaid-to-excalidraw"; +import type { MermaidOptions } from "@excalidraw/mermaid-to-excalidraw"; import { t } from "../i18n"; import Trans from "./Trans"; @@ -49,6 +49,7 @@ const importMermaidDataFromStorage = () => { const ErrorComp = ({ error }: { error: string }) => { return (
{ return ( diff --git a/src/tests/MermaidToExcalidraw.test.tsx b/src/tests/MermaidToExcalidraw.test.tsx new file mode 100644 index 000000000..fc6e556d5 --- /dev/null +++ b/src/tests/MermaidToExcalidraw.test.tsx @@ -0,0 +1,178 @@ +import { + act, + fireEvent, + getTextEditor, + render, + updateTextEditor, +} from "./test-utils"; +import { Excalidraw } from "../packages/excalidraw/index"; +import React from "react"; +import { expect, vi } from "vitest"; +import * as MermaidToExcalidraw from "@excalidraw/mermaid-to-excalidraw"; + +vi.mock("@excalidraw/mermaid-to-excalidraw", async (importActual) => { + const module = (await importActual()) as any; + + return { + __esModule: true, + ...module, + }; +}); +const parseMermaidToExcalidrawSpy = vi.spyOn( + MermaidToExcalidraw, + "parseMermaidToExcalidraw", +); + +parseMermaidToExcalidrawSpy.mockImplementation( + async ( + definition: string, + options?: MermaidToExcalidraw.MermaidOptions | undefined, + ) => { + const firstLine = definition.split("\n")[0]; + return new Promise((resolve, reject) => { + if (firstLine === "flowchart TD") { + resolve({ + elements: [ + { + id: "Start", + type: "rectangle", + groupIds: [], + x: 0, + y: 0, + width: 69.703125, + height: 44, + strokeWidth: 2, + label: { + groupIds: [], + text: "Start", + fontSize: 20, + }, + link: null, + }, + { + id: "Stop", + type: "rectangle", + groupIds: [], + x: 2.7109375, + y: 94, + width: 64.28125, + height: 44, + strokeWidth: 2, + label: { + groupIds: [], + text: "Stop", + fontSize: 20, + }, + link: null, + }, + { + id: "Start_Stop", + type: "arrow", + groupIds: [], + x: 34.852, + y: 44, + strokeWidth: 2, + points: [ + [0, 0], + [0, 50], + ], + roundness: { + type: 2, + }, + start: { + id: "Start", + }, + end: { + id: "Stop", + }, + }, + ], + }); + } else { + reject(new Error("ERROR")); + } + }); + }, +); + +vi.spyOn(React, "useRef").mockReturnValue({ + current: { + parseMermaidToExcalidraw: parseMermaidToExcalidrawSpy, + }, +}); + +describe("Test ", () => { + beforeEach(async () => { + await render( + , + ); + }); + + it("should open mermaid popup when active tool is mermaid", async () => { + const dialog = document.querySelector(".dialog-mermaid")!; + + expect(dialog.outerHTML).toMatchSnapshot(); + }); + + it("should close the popup and set the tool to selection when close button clicked", () => { + const dialog = document.querySelector(".dialog-mermaid")!; + const closeBtn = dialog.querySelector(".Dialog__close")!; + fireEvent.click(closeBtn); + expect(document.querySelector(".dialog-mermaid")).toBe(null); + expect(window.h.state.activeTool).toStrictEqual({ + customType: null, + lastActiveTool: null, + locked: false, + type: "selection", + }); + }); + + it("should show error in preview when mermaid library throws error", async () => { + const dialog = document.querySelector(".dialog-mermaid")!; + const selector = ".mermaid-to-excalidraw-wrapper-text textarea"; + const editor = getTextEditor(selector); + + expect(dialog.querySelector('[data-testid="mermaid-error"]')).toBeNull(); + + expect(editor.textContent).toMatchInlineSnapshot(` + "flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[test]" + `); + + await act(async () => { + updateTextEditor(editor, "flowchart TD1"); + await new Promise((cb) => setTimeout(cb, 0)); + }); + + expect(getTextEditor(selector).textContent).toBe("flowchart TD1"); + expect(dialog.querySelector('[data-testid="mermaid-error"]')) + .toMatchInlineSnapshot(` +
+ Error! +

+ ERROR +

+
+ `); + }); +}); diff --git a/src/tests/__snapshots__/MermaidToExcalidraw.test.tsx.snap b/src/tests/__snapshots__/MermaidToExcalidraw.test.tsx.snap new file mode 100644 index 000000000..631aa2342 --- /dev/null +++ b/src/tests/__snapshots__/MermaidToExcalidraw.test.tsx.snap @@ -0,0 +1,10 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Test > should open mermaid popup when active tool is mermaid 1`] = ` +"

Mermaid to Excalidraw

Currently only flowcharts are supported. The other types will be rendered as image in Excalidraw.

" +`; diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts index fed6b4897..30b8a1ecf 100644 --- a/src/tests/test-utils.ts +++ b/src/tests/test-utils.ts @@ -265,3 +265,15 @@ expect.extend({ }; }, }); + +export const updateTextEditor = ( + editor: HTMLTextAreaElement, + value: string, +) => { + fireEvent.change(editor, { target: { value } }); + editor.dispatchEvent(new Event("input")); +}; + +export const getTextEditor = (selector: string) => { + return document.querySelector(selector) as HTMLTextAreaElement; +};