From 9386cb0e71f61ddde4145b23c8138a50051635ca Mon Sep 17 00:00:00 2001 From: link <3399652842@qq.com> Date: Sun, 23 Feb 2025 02:50:00 +0800 Subject: [PATCH] =?UTF-8?q?test(mini-markdown-editor):=20=E5=AE=8C?= =?UTF-8?q?=E6=88=90Toolbar=E7=9A=84unit=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toolbar/__test__/CopyCodeButton.test.tsx | 96 ++++++++++++ .../Toolbar/__test__/Emoji.test.tsx | 89 +++++++++++ .../Toolbar/__test__/FullScreen.test.tsx | 77 +++++++++ .../components/Toolbar/__test__/Save.test.tsx | 97 ++++++++++++ .../Toolbar/__test__/ShowLayout.test.tsx | 146 ++++++++++++++++++ .../Toolbar/__test__/ToolbarItem.test.tsx | 102 ++++++++++++ .../Toolbar/__test__/Upload.test.tsx | 102 ++++++++++++ .../Toolbar/__test__/index.test.tsx | 66 ++++++++ 8 files changed, 775 insertions(+) create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/CopyCodeButton.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/Emoji.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/FullScreen.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/Save.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/ShowLayout.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/ToolbarItem.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/Upload.test.tsx create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/__test__/index.test.tsx diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/CopyCodeButton.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/CopyCodeButton.test.tsx new file mode 100644 index 0000000..9d2e12e --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/CopyCodeButton.test.tsx @@ -0,0 +1,96 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { CopyCodeButton } from "../CopyCodeButton"; +import { message } from "antd"; +import { getCurrentLocale } from "@/locales"; + +// 模拟依赖项 +vi.mock("@/locales", () => ({ + getCurrentLocale: vi.fn(() => "en"), +})); + +vi.mock("antd", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + message: { + success: vi.fn(), + error: vi.fn(), + }, + }; +}); + +// 模拟剪贴板API +const mockClipboard = { + writeText: vi.fn().mockResolvedValue(undefined), +}; +(global as any).navigator.clipboard = mockClipboard; + +// 模拟环境变量 +vi.mock("import.meta.env", () => ({ + PROD: false, +})); +//监听console.error +const consoleError = vi.spyOn(console, "error"); + +describe("CopyCodeButton 组件测试", () => { + beforeEach(() => { + //默认开发环境及英文语言 + vi.mocked(import.meta.env).PROD = false; + vi.mocked(getCurrentLocale).mockReturnValue("en"); + vi.clearAllMocks(); + }); + + test("生产环境下不渲染", () => { + vi.mocked(import.meta.env).PROD = true; + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); + + test("开发环境下根据语言显示正确文本", () => { + // 测试英文环境 + render(); + screen.debug(); + expect(screen.getByText("Copy Test Code")).toBeInTheDocument(); + + // 测试中文环境 + vi.mocked(getCurrentLocale).mockReturnValue("cn"); + render(); + expect(screen.getByText("复制测试代码")).toBeInTheDocument(); + + // 测试繁体中文环境 + vi.mocked(getCurrentLocale).mockReturnValue("tw"); + render(); + expect(screen.getByText("複製測試代碼")).toBeInTheDocument(); + }); + + test("点击按钮触发复制操作", async () => { + render(); + await userEvent.click(screen.getByRole("button")); + + await waitFor(() => { + expect(navigator.clipboard.writeText).toHaveBeenCalled(); + expect(message.success).toHaveBeenCalledWith({ + content: "Copied!", + duration: 1.5, + }); + }); + }); + + test("复制失败时显示错误提示", async () => { + const error = new Error("Clipboard error"); + mockClipboard.writeText.mockRejectedValueOnce(error); + + render(); + await userEvent.click(screen.getByRole("button")); + + await waitFor(() => { + expect(consoleError).toHaveBeenCalledWith("Copy Error:", error); + expect(message.error).toHaveBeenCalledWith({ + content: "Copy failed", + duration: 2, + }); + }); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/Emoji.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Emoji.test.tsx new file mode 100644 index 0000000..1805db4 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Emoji.test.tsx @@ -0,0 +1,89 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import Emoji from "../Emoji"; +import { useGlobalConfig } from "@/hooks/use-global-config"; +import { InsertEmojiEvent } from "@/config/toolbar/event"; +import EmojiIcon from "@/assets/images/emoji.svg?raw"; +import userEvent from "@testing-library/user-event"; +// 模拟依赖项 +vi.mock("@/hooks/use-global-config", () => ({ + useGlobalConfig: vi.fn(() => ({ + theme: "light", + locale: "en", + })), +})); + +vi.mock("@/config/toolbar/event", () => ({ + InsertEmojiEvent: vi.fn(), +})); + +vi.mock("@emoji-mart/react", () => ({ + default: (props: any) => ( +
+ MockEmojiPicker +
+ ), +})); + +vi.mock("@/components/base/DropDownMenu", () => ({ + DropDownMenu: ({ children, dropdownRender }: any) => ( +
+
{children}
+
{dropdownRender()}
+
+ ), +})); + +describe("Emoji 组件测试", () => { + const mockUseGlobalConfig = vi.mocked(useGlobalConfig); + const mockInsertEmojiEvent = vi.mocked(InsertEmojiEvent); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + test("应正确渲染图标和下拉菜单", () => { + render(); + + expect(screen.getByTestId("trigger")).toContainHTML(`
${EmojiIcon}
`); + expect(screen.getByTestId("dropdown")).toContainElement(screen.getByTestId("emoji-picker")); + }); + + test("应根据主题配置渲染 Picker", () => { + // 默认 light 主题 + const { rerender } = render(); + expect(screen.getByTestId("emoji-picker")).toHaveAttribute("data-theme", "light"); + + // 切换 dark 主题 + mockUseGlobalConfig.mockReturnValue({ theme: "dark", locale: "en" }); + rerender(); + expect(screen.getByTestId("emoji-picker")).toHaveAttribute("data-theme", "dark"); + }); + + test("应根据语言配置设置 locale", () => { + // 英文环境 + const { rerender } = render(); + expect(screen.getByTestId("emoji-picker")).toHaveAttribute("data-locale", "en"); + + // 中文环境 + mockUseGlobalConfig.mockReturnValue({ theme: "light", locale: "cn" }); + rerender(); + expect(screen.getByTestId("emoji-picker")).toHaveAttribute("data-locale", "zh"); + }); + + test("选择表情应触发事件", async () => { + render(); + const user = userEvent.setup(); + // 模拟选择表情 + user.click(screen.getByTestId("emoji-picker")); + + await waitFor(() => { + expect(mockInsertEmojiEvent).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/FullScreen.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/FullScreen.test.tsx new file mode 100644 index 0000000..90e5e33 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/FullScreen.test.tsx @@ -0,0 +1,77 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import FullScreen from "../FullScreen"; +import { useToolbarStore } from "@/store/toolbar"; +import { t } from "@/locales"; + +// 模拟依赖项 +vi.mock("@/store/toolbar", () => ({ + useToolbarStore: vi.fn(), +})); + +vi.mock("@/locales", () => ({ + t: vi.fn((key: string) => key + "_translated"), +})); + +vi.mock("@/components/base/IconTooltip", () => ({ + default: ({ children, ...props }: any) => ( +
+ {children} +
+ ), +})); + +describe("FullScreen 组件测试", () => { + const mockSetIsFullScreen = vi.fn(); + const mockUseToolbarStore = vi.mocked(useToolbarStore); + + beforeEach(() => { + vi.clearAllMocks(); + mockUseToolbarStore.mockReturnValue({ + isFullScreen: false, + setIsFullScreen: mockSetIsFullScreen, + }); + }); + + test("应正确渲染默认状态", () => { + const { container } = render(); + expect(container.querySelector(".icon")).toBeInTheDocument(); + // 验证 Tooltip 属性 + const tooltip = screen.getByTestId("tooltip"); + expect(tooltip).toHaveAttribute("placement", "top"); + expect(tooltip).toHaveAttribute("content", "TOOLBAR.FULLSCREEN_translated"); + expect(tooltip).toHaveAttribute("description", "Ctrl + Alt + F"); + }); + + test("点击应切换全屏状态", async () => { + render(); + await userEvent.click(screen.getByTestId("tooltip")); + expect(mockSetIsFullScreen).toHaveBeenCalledWith(true); + }); + + test("全屏状态应显示退出图标", () => { + mockUseToolbarStore.mockReturnValue({ + isFullScreen: true, + setIsFullScreen: mockSetIsFullScreen, + }); + + render(); + expect(screen.getByTestId("tooltip")).toHaveAttribute("placement", "bottom"); + }); + + test("应正确使用国际化", () => { + render(); + expect(t).toHaveBeenCalledWith("TOOLBAR.FULLSCREEN"); + }); + + test("应处理图标加载异常", () => { + const originalConsoleError = console.error; + console.error = vi.fn(); + + const { container } = render(); + expect(container.querySelector(".icon")).not.toBeNull(); + + console.error = originalConsoleError; + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/Save.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Save.test.tsx new file mode 100644 index 0000000..448903d --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Save.test.tsx @@ -0,0 +1,97 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import Save from "../Save"; +import { useEditorContentStore } from "@/store/editor"; +import { useSaveContent } from "@/hooks/use-save-content"; +import { useGlobalConfig } from "@/hooks/use-global-config"; +import { t } from "@/locales"; + +// 模拟依赖项 +vi.mock("@/store/editor", () => ({ + useEditorContentStore: vi.fn(), +})); + +vi.mock("@/hooks/use-save-content", () => ({ + useSaveContent: vi.fn(), +})); + +vi.mock("@/hooks/use-global-config", () => ({ + useGlobalConfig: vi.fn(), +})); + +vi.mock("@/locales", () => ({ + t: vi.fn((key: string) => key + "_translated"), +})); + +vi.mock("@/components/base/IconTooltip", () => ({ + default: ({ children, ...props }: any) => ( +
+ {children} +
+ ), +})); + +describe("Save 组件测试", () => { + const mockSaveContent = vi.fn(); + const mockOnSave = vi.fn(); + const mockUseEditorContentStore = vi.mocked(useEditorContentStore); + const mockUseGlobalConfig = vi.mocked(useGlobalConfig); + + beforeEach(() => { + vi.clearAllMocks(); + mockUseEditorContentStore.mockReturnValue({ + content: "test content", + editorView: {} as any, + }); + mockUseGlobalConfig.mockReturnValue({ onSave: mockOnSave }); + (useSaveContent as any).mockReturnValue(mockSaveContent); + }); + + test("应正确渲染保存按钮", () => { + const { container } = render(); + + // 验证图标容器 + expect(container.querySelector(".icon")).toBeInTheDocument(); + // 验证提示内容 + expect(screen.getByTestId("tooltip")).toHaveAttribute("content", "TOOLBAR.SAVE_translated"); + // 验证快捷键描述 + expect(screen.getByTestId("tooltip")).toHaveAttribute("description", "Ctrl + S"); + }); + + test("点击应触发保存操作", async () => { + render(); + await userEvent.click(screen.getByTestId("tooltip")); + + expect(mockSaveContent).toHaveBeenCalledWith("test content"); + expect(mockOnSave).toHaveBeenCalledWith("test content", expect.any(Object)); + }); + + test("无内容时应跳过保存操作", async () => { + mockUseEditorContentStore.mockReturnValue({ + content: "", + editorView: null, + }); + + render(); + await userEvent.click(screen.getByTestId("tooltip")); + + expect(mockSaveContent).not.toHaveBeenCalled(); + expect(mockOnSave).not.toHaveBeenCalled(); + }); + + test("应正确处理空回调函数", async () => { + mockUseGlobalConfig.mockReturnValue({ onSave: undefined }); + + render(); + await userEvent.click(screen.getByTestId("tooltip")); + + expect(mockSaveContent).toHaveBeenCalled(); + expect(mockOnSave).not.toHaveBeenCalled(); + }); + + test("应正确使用国际化", () => { + render(); + expect(t).toHaveBeenCalledWith("TOOLBAR.SAVE"); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/ShowLayout.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/ShowLayout.test.tsx new file mode 100644 index 0000000..0270189 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/ShowLayout.test.tsx @@ -0,0 +1,146 @@ +import { render } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { Write, Read, Contents, Help, Output } from "../ShowLayout"; +import { useToolbarStore } from "@/store/toolbar"; +import { t } from "@/locales"; + +// 模拟依赖项 +vi.mock("@/store/toolbar", () => ({ + useToolbarStore: vi.fn(), +})); + +vi.mock("@/locales", () => ({ + t: vi.fn((key: string) => key + "_translated"), +})); + +// 模拟子组件 +vi.mock("@/components/Sidebar/Contents", () => ({ + default: () =>
Contents Sidebar
, +})); +vi.mock("@/components/Sidebar/Help", () => ({ + default: () =>
Help Sidebar
, +})); +vi.mock("@/components/Sidebar/Output", () => ({ + default: () =>
Output Sidebar
, +})); + +// 通用测试配置 +const mockUseToolbarStore = vi.mocked(useToolbarStore); +const mockSetSidebar = vi.fn(); + +beforeEach(() => { + vi.clearAllMocks(); + mockUseToolbarStore.mockReturnValue({ + isOnlyWrite: false, + setIsOnlyWrite: vi.fn(), + isOnlyPreview: false, + setIsOnlyPreview: vi.fn(), + isSidebar: false, + componentMark: null, + setSidebar: mockSetSidebar, + }); +}); + +describe("ShowLayout 组件测试", () => { + describe("Write 组件", () => { + test("应正确切换只写模式", async () => { + const mockSetIsOnlyWrite = vi.fn(); + mockUseToolbarStore.mockReturnValue({ + isOnlyWrite: false, + setIsOnlyWrite: mockSetIsOnlyWrite, + }); + + const { container } = render(); + await userEvent.click(container.querySelector("svg")!); + expect(mockSetIsOnlyWrite).toHaveBeenCalled(); + }); + + test("激活时应显示选中颜色", () => { + mockUseToolbarStore.mockReturnValue({ + isOnlyWrite: true, + setIsOnlyWrite: vi.fn(), + }); + + const { container } = render(); + expect(container.querySelector("div.sc-FEMpB")).toHaveStyle("color: #0366d6"); + }); + }); + + describe("Read 组件", () => { + test("应正确切换仅预览模式", async () => { + const mockSetIsOnlyPreview = vi.fn(); + mockUseToolbarStore.mockReturnValue({ + isOnlyPreview: false, + setIsOnlyPreview: mockSetIsOnlyPreview, + }); + + const { container } = render(); + await userEvent.click(container.querySelector("svg")!); + expect(mockSetIsOnlyPreview).toHaveBeenCalled(); + }); + }); + + describe("Contents 组件", () => { + test("点击应打开目录侧边栏", async () => { + const { container } = render(); + await userEvent.click(container.querySelector("svg")!); + + expect(mockSetSidebar).toHaveBeenCalledWith(expect.anything(), "Contents"); + }); + + test("激活时应高亮显示", () => { + mockUseToolbarStore.mockReturnValue({ + isSidebar: true, + componentMark: "Contents", + }); + + const { container } = render(); + expect(container.querySelector("div.sc-FEMpB")).toHaveStyle("color: #0366d6"); + }); + }); + + describe("Help 组件", () => { + test("点击应打开帮助侧边栏", async () => { + const { container } = render(); + await userEvent.click(container.querySelector("svg")!); + + expect(mockSetSidebar).toHaveBeenCalledWith(expect.anything(), "Help"); + }); + }); + + describe("Output 组件", () => { + test("点击应打开导出侧边栏", async () => { + const { container } = render(); + await userEvent.click(container.querySelector("svg")!); + + expect(mockSetSidebar).toHaveBeenCalledWith(expect.anything(), "Output"); + }); + + test("当其他侧边栏打开时应取消高亮", () => { + mockUseToolbarStore.mockReturnValue({ + isSidebar: true, + componentMark: "Help", + }); + + const { container } = render(); + expect(container.querySelector("div.sc-FEMpB")).not.toHaveStyle("color: #0366d6"); + }); + }); + + describe("公共功能", () => { + test("应正确应用国际化", () => { + render( + <> + + + + , + ); + + expect(t).toHaveBeenCalledWith("TOOLBAR.WRITE"); + expect(t).toHaveBeenCalledWith("TOOLBAR.PREVIEW"); + expect(t).toHaveBeenCalledWith("TOOLBAR.CONTENTS"); + }); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/ToolbarItem.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/ToolbarItem.test.tsx new file mode 100644 index 0000000..1f6b698 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/ToolbarItem.test.tsx @@ -0,0 +1,102 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { ToolbarItem } from "../ToolbarItem"; +import { CLASS_PREFIX } from "@/common"; + +// 模拟子组件 +vi.mock("@/components/base/DropDownMenu", () => ({ + DropDownMenu: ({ children, list }: any) => ( +
+ {children} +
{list?.length}
+
+ ), +})); + +vi.mock("@/components/base/IconTooltip", () => ({ + default: ({ children, onClick, ...props }: any) => ( +
+ {children} +
+ ), +})); + +describe("ToolbarItem 组件测试", () => { + const mockOnClick = vi.fn(); + const mockT = vi.fn((key: string) => key + "_translated"); + + const baseProps = { + type: "bold", + title: "toolbar.bold", + icon: "bold-icon", + t: mockT, + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + test("应渲染普通工具项", () => { + render(); + + // 验证基础结构 + expect(screen.getByTestId("icon-tooltip")).toBeInTheDocument(); + expect(screen.getByText("bold-icon")).toBeInTheDocument(); + expect(screen.getByTestId("icon-tooltip")).toHaveAttribute("data-testid", "icon-tooltip"); + }); + + test("应触发点击事件", async () => { + render(); + await userEvent.click(screen.getByTestId("icon-tooltip")); + expect(mockOnClick).toHaveBeenCalled(); + }); + + test("应渲染带下拉菜单的项", () => { + const list = [ + { type: "header1", label: "H1" }, + { type: "header2", label: "H2" }, + ]; + render(); + + expect(screen.getByTestId("dropdown-menu")).toBeInTheDocument(); + expect(screen.getByTestId("dropdown-items")).toHaveTextContent("2"); + expect(screen.getByText("bold-icon")).toBeInTheDocument(); + }); + + test("应渲染自定义组件", () => { + const CustomComponent = () =>
Custom
; + render(} />); + + expect(screen.getByTestId("custom-component")).toBeInTheDocument(); + expect(screen.queryByTestId("icon-tooltip")).toBeNull(); + }); + + test("应正确应用国际化", () => { + render(); + expect(mockT).toHaveBeenCalledWith("toolbar.bold"); + expect(screen.getByTestId("icon-tooltip")).toHaveAttribute( + "content", + "toolbar.bold_translated", + ); + }); + + test("应处理t函数返回值为空的情况", () => { + mockT.mockReturnValue(""); + render(); + expect(screen.getByTestId("icon-tooltip")).toHaveAttribute("content", "toolbar.bold"); + }); + + test("应正确应用类名", () => { + const { container } = render(); + screen.debug(); + expect(container.firstChild).toHaveClass( + `${CLASS_PREFIX}-toolbar-item ${CLASS_PREFIX}-toolbar-item-${baseProps.type}`, + ); + }); + + test("应处理空图标情况", () => { + render(); + expect(screen.queryByText("bold-icon")).toBeNull(); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/Upload.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Upload.test.tsx new file mode 100644 index 0000000..86424d2 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/Upload.test.tsx @@ -0,0 +1,102 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import UploadCom from "../Upload"; +import { useGlobalConfig } from "@/hooks/use-global-config"; +import { InsertImageEvent } from "@/config/toolbar/event"; +import { t } from "@/locales"; +import type { UploadProps } from "antd"; +import { RcFile } from "antd/es/upload"; +import { TOOLBAR_KEYS } from "@/locales/keys"; + +// 模拟依赖项 +vi.mock("@/hooks/use-global-config", () => ({ + useGlobalConfig: vi.fn(() => ({ + onUpload: vi.fn(), + })), +})); + +vi.mock("@/config/toolbar/event", () => ({ + InsertImageEvent: vi.fn(), +})); + +vi.mock("@/locales", () => ({ + t: vi.fn((key: string) => key + "_translated"), +})); + +// 模拟 Ant Design 的 Upload 组件 +vi.mock("antd", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + Upload: ({ children, beforeUpload }: UploadProps) => ( +
+ { + const file = e.target.files?.[0]; + if (file && beforeUpload) { + await beforeUpload(file as RcFile, [file] as [RcFile]); + } + }} + /> + {children} +
+ ), + }; +}); + +describe("Upload 组件测试", () => { + const mockOnUpload = vi.fn(); + const mockInsertImageEvent = vi.mocked(InsertImageEvent); + const mockUseGlobalConfig = vi.mocked(useGlobalConfig); + + beforeEach(() => { + vi.clearAllMocks(); + mockUseGlobalConfig.mockReturnValue({ onUpload: mockOnUpload }); + }); + + test("应正确渲染上传按钮", () => { + render(); + expect(screen.getByTestId("upload-mock")).toBeInTheDocument(); + expect( + screen.getByText(`${TOOLBAR_KEYS.TOOLBAR.IMAGE_ITEMS["image-upload"]}_translated`), + ).toBeInTheDocument(); + }); + + test("上传文件应触发回调链", async () => { + const mockFile = new File(["test"], "test.png", { type: "image/png" }); + + render(); + await userEvent.upload(screen.getByTestId("upload-input"), mockFile); + + // 验证 onUpload 调用 + await waitFor(() => { + expect(mockOnUpload).toHaveBeenCalledWith(mockFile, expect.any(Function)); + }); + + // 模拟 onUpload 回调 + const uploadCallback = mockOnUpload.mock.calls[0][1]; + uploadCallback({ url: "test-url", alt: "test-alt" }); + + // 验证图片插入事件 + expect(mockInsertImageEvent).toHaveBeenCalledWith("test-url", "test-alt"); + }); + + test("无 onUpload 配置时应跳过处理", async () => { + mockUseGlobalConfig.mockReturnValue({}); + const mockFile = new File(["test"], "test.png", { type: "image/png" }); + + render(); + await userEvent.upload(screen.getByTestId("upload-input"), mockFile); + + expect(mockOnUpload).not.toHaveBeenCalled(); + expect(mockInsertImageEvent).not.toHaveBeenCalled(); + }); + + test("应正确使用国际化", () => { + render(); + expect(t).toHaveBeenCalledWith(TOOLBAR_KEYS.TOOLBAR.IMAGE_ITEMS["image-upload"]); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/__test__/index.test.tsx b/packages/mini-markdown-editor/src/components/Toolbar/__test__/index.test.tsx new file mode 100644 index 0000000..5f71041 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/Toolbar/__test__/index.test.tsx @@ -0,0 +1,66 @@ +import { render, screen } from "@testing-library/react"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import Toolbar from "../index"; +import { toolbarConfig } from "@/config/toolbar"; +import { useSyncEditorView } from "@/hooks/use-sync-editorview"; + +// 模拟依赖项 +vi.mock("@/config/toolbar", () => ({ + toolbarConfig: { + getAllToolbars: vi.fn(() => [ + { type: "bold", label: "Bold" }, + { type: "line" }, // 分隔线 + { type: "italic", label: "Italic" }, + ]), + }, +})); + +vi.mock("@/hooks/use-sync-editorview", () => ({ + useSyncEditorView: vi.fn(), +})); + +vi.mock("../ToolbarItem", () => ({ + ToolbarItem: ({ label }: { label: string }) => , +})); + +vi.mock("../CopyCodeButton", () => ({ + CopyCodeButton: () => , +})); + +describe("Toolbar 组件测试", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test("应正确渲染组件结构", () => { + const { container } = render(); + expect(container.querySelector(".mini-editor-toolbar-content")).toBeInTheDocument(); + }); + + test("应正确渲染工具栏项", () => { + render(); + + // 验证工具栏项 + expect(screen.getByText("Bold_translated")).toBeInTheDocument(); + expect(screen.getByText("Italic_translated")).toBeInTheDocument(); + }); + + test("应渲染复制代码按钮", () => { + render(); + expect(screen.getByTestId("copy-button")).toBeInTheDocument(); + }); + + test("应调用同步编辑器视图钩子", () => { + render(); + expect(useSyncEditorView).toHaveBeenCalled(); + }); + + test("应正确处理空工具栏配置", () => { + vi.mocked(toolbarConfig.getAllToolbars).mockReturnValue([]); + render(); + + expect(screen.queryByTestId("divider")).toBeNull(); + const reg = /[^(Copy Code)]/; + expect(screen.queryByRole("button", { name: reg })).toBeNull(); + }); +}); -- Gitee