diff --git a/packages/mini-markdown-editor/src/components/providers/__test__/config-provider.test.tsx b/packages/mini-markdown-editor/src/components/providers/__test__/config-provider.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..39a4242cc66a3aaae9d82a150503474b413a49b6 --- /dev/null +++ b/packages/mini-markdown-editor/src/components/providers/__test__/config-provider.test.tsx @@ -0,0 +1,87 @@ +import { render, screen } from "@testing-library/react"; +import { describe, test, expect } from "vitest"; +import { ConfigContext, ConfigProvider } from "../config-provider"; +import { defaultGlobalConfig } from "@/config/global"; +import type { GlobalConfig } from "@/types/global-config"; +import { useContext } from "react"; + +// 测试用消费者组件 +const ConfigConsumer = () => { + const config = useContext(ConfigContext); + return ( +
+ {config.theme} + {config.locale} +
+ ); +}; + +describe("ConfigProvider Provider测试", () => { + // 测试默认配置 + test("应提供默认配置", () => { + render( + + + , + ); + + expect(screen.getByTestId("theme")).toHaveTextContent(defaultGlobalConfig.theme!); + expect(screen.getByTestId("locale")).toHaveTextContent(defaultGlobalConfig.locale!); + }); + + // 测试自定义配置合并 + test("应合并自定义配置", () => { + const customConfig: GlobalConfig = { + theme: "dark", + }; + + render( + + + , + ); + + expect(screen.getByTestId("theme")).toHaveTextContent("dark"); + expect(screen.getByTestId("locale")).toHaveTextContent(defaultGlobalConfig.locale!); + }); + + // 测试空配置处理 + test("应处理空配置", () => { + render( + + + , + ); + + expect(screen.getByTestId("theme")).toHaveTextContent(defaultGlobalConfig.theme!); + expect(screen.getByTestId("locale")).toHaveTextContent(defaultGlobalConfig.locale!); + }); + + // 测试深层属性覆盖 + test("应深度合并配置", () => { + const customConfig = { + toolbars: { + addTools: [], + excludeTools: [], + }, + } as GlobalConfig; + + const TestComponent = () => { + const config = useContext(ConfigContext); + return {JSON.stringify(config.toolbars)}; + }; + + render( + + + , + ); + + const expected = { + ...defaultGlobalConfig.toolbars, + ...customConfig.toolbars, + }; + + expect(screen.getByTestId("editor")).toHaveTextContent(JSON.stringify(expected)); + }); +}); diff --git a/packages/mini-markdown-editor/src/components/providers/__test__/toolbar-provider.test.tsx b/packages/mini-markdown-editor/src/components/providers/__test__/toolbar-provider.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ba80eb23df75181960a3ebfa714e6fbebeffa2db --- /dev/null +++ b/packages/mini-markdown-editor/src/components/providers/__test__/toolbar-provider.test.tsx @@ -0,0 +1,142 @@ +import { render, screen } from "@testing-library/react"; +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { ToolbarProvider, ToolbarContext } from "../toolbar-provider"; +import { toolbarConfig as toolbarManager } from "@/config/toolbar"; +import { useContext } from "react"; + +// 模拟工具栏配置模块 +vi.mock("@/config/toolbar", () => ({ + toolbarConfig: { + getDefaultToolbar: vi.fn(() => [ + { type: "bold", title: "加粗" }, + { type: "italic", title: "斜体" }, + ]), + updateToolbars: vi.fn(), + }, +})); + +// 测试消费者组件 +const TestConsumer = () => { + const context = useContext(ToolbarContext); + return ( +
+ {context?.toolbars.map((item) => ( + + {item.title} + + ))} +
+ ); +}; + +describe("ToolbarProvider Provider测试", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + // 基础功能测试 + test("应渲染默认工具栏", () => { + render( + + + , + ); + + expect(screen.getByTestId("bold")).toBeInTheDocument(); + expect(screen.getByTestId("italic")).toBeInTheDocument(); + expect(toolbarManager.updateToolbars).toHaveBeenCalledWith([ + expect.objectContaining({ type: "bold" }), + expect.objectContaining({ type: "italic" }), + ]); + }); + + // 添加工具测试 + test("应合并新增工具", () => { + const customConfig = { + addTools: [{ type: "underline", title: "下划线" }], + }; + + render( + + + , + ); + + expect(screen.getByTestId("bold")).toBeInTheDocument(); + expect(screen.getByTestId("italic")).toBeInTheDocument(); + expect(screen.getByTestId("underline")).toBeInTheDocument(); + }); + + // 排除工具测试 + test("应过滤排除的工具", () => { + const customConfig = { + excludeTools: ["italic"], + }; + + render( + + + , + ); + + expect(screen.getByTestId("bold")).toBeInTheDocument(); + expect(screen.queryByTestId("italic")).toBeNull(); + }); + + // 排序工具测试 + test("应按指定顺序排序工具", () => { + const customConfig = { + orderTools: [{ type: "italic", order: 1 }], + }; + + const { container } = render( + + + , + ); + + const items = container.querySelectorAll("[data-testid]"); + expect(items[0].getAttribute("data-testid")).toBe("italic"); + expect(items[1].getAttribute("data-testid")).toBe("bold"); + }); + + // 组合功能测试 + test("应同时处理添加、排除和排序", () => { + const customConfig = { + addTools: [{ type: "underline", title: "下划线" }], + excludeTools: ["bold"], + orderTools: [{ type: "underline", order: 0 }], + }; + + render( + + + , + ); + + const items = screen.getAllByTestId(/.*/); + expect(items).toHaveLength(2); + expect(items[0]).toHaveAttribute("data-testid", "underline"); + expect(items[1]).toHaveAttribute("data-testid", "italic"); + }); + + // 配置更新测试 + test("配置变化时应更新工具栏", async () => { + const { rerender } = render( + + + , + ); + + expect(screen.queryByTestId("bold")).toBeNull(); + + rerender( + + + , + ); + + expect(screen.getByTestId("bold")).toBeInTheDocument(); + expect(screen.queryByTestId("italic")).toBeNull(); + }); +}); diff --git a/packages/mini-markdown-editor/src/store/__test__/editor.test.ts b/packages/mini-markdown-editor/src/store/__test__/editor.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9132f25ccac41d42bf2e4d0d27095801e4028163 --- /dev/null +++ b/packages/mini-markdown-editor/src/store/__test__/editor.test.ts @@ -0,0 +1,76 @@ +import { describe, test, expect, vi } from "vitest"; +import { useEditorContentStore } from "../editor"; +import type { EditorView } from "@codemirror/view"; + +// 模拟 EditorView 和 HTMLElement +const mockEditorView = { destroy: vi.fn() } as unknown as EditorView; +const mockPreviewElement = document.createElement("div"); + +describe("useEditorContentStore Store测试", () => { + // 创建独立的 store 实例用于测试 + const useTestStore = useEditorContentStore; + + test("初始状态应为空值", () => { + const state = useTestStore.getState(); + + expect(state.content).toBe(""); + expect(state.scrollWrapper).toBe(""); + expect(state.editorView).toBeNull(); + expect(state.previewView).toBeNull(); + }); + + test("应正确更新内容", () => { + // 测试普通字符串 + useTestStore.getState().setContent("Hello World"); + expect(useTestStore.getState().content).toBe("Hello World"); + + // 测试特殊字符 + useTestStore.getState().setContent("
HTML
"); + expect(useTestStore.getState().content).toBe("
HTML
"); + }); + + test("应正确设置滚动容器标识", () => { + useTestStore.getState().setScrollWrapper("#editor"); + expect(useTestStore.getState().scrollWrapper).toBe("#editor"); + + useTestStore.getState().setScrollWrapper(""); + expect(useTestStore.getState().scrollWrapper).toBe(""); + }); + + test("应正确处理编辑器视图", () => { + // 设置编辑器视图 + useTestStore.getState().setEditorView(mockEditorView); + expect(useTestStore.getState().editorView).toBe(mockEditorView); + + // 清除编辑器视图 + useTestStore.getState().setEditorView(null); + expect(useTestStore.getState().editorView).toBeNull(); + }); + + test("应正确处理预览视图", () => { + // 设置预览元素 + useTestStore.getState().setPreviewView(mockPreviewElement); + expect(useTestStore.getState().previewView).toBe(mockPreviewElement); + + // 清除预览元素 + useTestStore.getState().setPreviewView(null); + expect(useTestStore.getState().previewView).toBeNull(); + }); + + test("应支持多次连续更新", () => { + // 连续更新内容 + useTestStore.getState().setContent("First"); + useTestStore.getState().setContent("Second"); + expect(useTestStore.getState().content).toBe("Second"); + + // 混合更新不同类型状态 + useTestStore.getState().setScrollWrapper("wrapper"); + useTestStore.getState().setPreviewView(mockPreviewElement); + useTestStore.getState().setEditorView(mockEditorView); + + const finalState = useTestStore.getState(); + expect(finalState.scrollWrapper).toBe("wrapper"); + expect(finalState.previewView).toBe(mockPreviewElement); + expect(finalState.editorView).toBe(mockEditorView); + }); +}); diff --git a/packages/mini-markdown-editor/src/store/__test__/toolbar.test.tsx b/packages/mini-markdown-editor/src/store/__test__/toolbar.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b5dbfe79d0c0cee35cd55a000e57b3cafd63b325 --- /dev/null +++ b/packages/mini-markdown-editor/src/store/__test__/toolbar.test.tsx @@ -0,0 +1,120 @@ +import { describe, test, expect } from "vitest"; +import { useToolbarStore } from "../toolbar"; + +// 创建独立的 store 实例用于测试 +const useTestStore = useToolbarStore; + +// 模拟 React 组件 +const MockComponent =
Sidebar
; + +describe("useToolbarStore Store测试", () => { + // 每次测试前重置状态 + beforeEach(() => { + useTestStore.setState({ + isFullScreen: false, + isOnlyWrite: false, + isOnlyPreview: false, + isSidebar: false, + sidebarComponent: null, + componentMark: null, + }); + }); + + test("初始状态应为默认值", () => { + const state = useTestStore.getState(); + + expect(state.isFullScreen).toBe(false); + expect(state.isOnlyWrite).toBe(false); + expect(state.isOnlyPreview).toBe(false); + expect(state.isSidebar).toBe(false); + expect(state.sidebarComponent).toBeNull(); + expect(state.componentMark).toBeNull(); + }); + + describe("全屏状态", () => { + test("应正确切换全屏状态", () => { + useTestStore.getState().setIsFullScreen(true); + expect(useTestStore.getState().isFullScreen).toBe(true); + + useTestStore.getState().setIsFullScreen(false); + expect(useTestStore.getState().isFullScreen).toBe(false); + }); + }); + + describe("编辑/预览模式", () => { + test("setIsOnlyWrite 应切换仅编辑状态并关闭预览", () => { + // 初始状态 + useTestStore.setState({ isOnlyPreview: true }); + + // 第一次切换 + useTestStore.getState().setIsOnlyWrite(); + expect(useTestStore.getState().isOnlyWrite).toBe(true); + expect(useTestStore.getState().isOnlyPreview).toBe(false); + + // 再次切换 + useTestStore.getState().setIsOnlyWrite(); + expect(useTestStore.getState().isOnlyWrite).toBe(false); + }); + + test("setIsOnlyPreview 应切换仅预览状态并关闭编辑", () => { + // 初始状态 + useTestStore.setState({ isOnlyWrite: true }); + + // 第一次切换 + useTestStore.getState().setIsOnlyPreview(); + expect(useTestStore.getState().isOnlyPreview).toBe(true); + expect(useTestStore.getState().isOnlyWrite).toBe(false); + + // 再次切换 + useTestStore.getState().setIsOnlyPreview(); + expect(useTestStore.getState().isOnlyPreview).toBe(false); + }); + }); + + describe("侧边栏控制", () => { + test("设置相同标记应切换显示状态", () => { + const mark = "settings"; + + // 第一次设置 + useTestStore.getState().setSidebar(MockComponent, mark); + expect(useTestStore.getState().isSidebar).toBe(true); + expect(useTestStore.getState().componentMark).toBe(mark); + + // 相同标记再次设置 + useTestStore.getState().setSidebar(MockComponent, mark); + expect(useTestStore.getState().isSidebar).toBe(false); + }); + + test("设置不同标记应更新组件", () => { + // 初始设置 + useTestStore.getState().setSidebar(MockComponent, "settings"); + + // 设置不同标记 + useTestStore.getState().setSidebar(
New
, "help"); + expect(useTestStore.getState().isSidebar).toBe(true); + expect(useTestStore.getState().componentMark).toBe("help"); + }); + }); + + describe("状态互斥测试", () => { + test("全屏模式不应影响其他状态", () => { + useTestStore.getState().setIsFullScreen(true); + useTestStore.getState().setIsOnlyWrite(); + useTestStore.getState().setSidebar(MockComponent, "test"); + + const state = useTestStore.getState(); + expect(state.isFullScreen).toBe(true); + expect(state.isOnlyWrite).toBe(true); + expect(state.isSidebar).toBe(true); + }); + + test("仅写和仅预览应互斥", () => { + useTestStore.getState().setIsOnlyWrite(); + expect(useTestStore.getState().isOnlyPreview).toBe(false); + + useTestStore.getState().setIsOnlyPreview(); + expect(useTestStore.getState().isOnlyWrite).toBe(false); + expect(useTestStore.getState().isOnlyPreview).toBe(true); + }); + }); +});