From 102f24798c35998a4f2aa0aed52e6ad696b29cef Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 21 Aug 2026 08:27:23 +0000 Subject: [PATCH] fix(snapshot): wire code snapshot option switches Closes #6870 --- .../codeSnapshot/CodeSnapshotDialog.vue | 4 +- .../__tests__/CodeSnapshotDialog.spec.ts | 157 ++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/src/components/codeSnapshot/__tests__/CodeSnapshotDialog.spec.ts diff --git a/apps/desktop/src/components/codeSnapshot/CodeSnapshotDialog.vue b/apps/desktop/src/components/codeSnapshot/CodeSnapshotDialog.vue index a953ecc048..508e063349 100644 --- a/apps/desktop/src/components/codeSnapshot/CodeSnapshotDialog.vue +++ b/apps/desktop/src/components/codeSnapshot/CodeSnapshotDialog.vue @@ -195,11 +195,11 @@ const previewVisible = computed(() => open.value && !!props.source);
- +
- +
diff --git a/apps/desktop/src/components/codeSnapshot/__tests__/CodeSnapshotDialog.spec.ts b/apps/desktop/src/components/codeSnapshot/__tests__/CodeSnapshotDialog.spec.ts new file mode 100644 index 0000000000..d6abdf9149 --- /dev/null +++ b/apps/desktop/src/components/codeSnapshot/__tests__/CodeSnapshotDialog.spec.ts @@ -0,0 +1,157 @@ +// @vitest-environment happy-dom + +import { createApp, nextTick, type App } from "vue"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const { renderCodeSnapshotHtmlMock } = vi.hoisted(() => ({ + renderCodeSnapshotHtmlMock: vi.fn(), +})); + +vi.mock("vue-i18n", () => ({ + useI18n: () => ({ t: (key: string) => key }), +})); + +vi.mock("@/composables/useTheme", async () => { + const { ref } = await import("vue"); + return { useTheme: () => ({ isDark: ref(false) }) }; +}); + +vi.mock("@/composables/useToast", () => ({ + useToast: () => ({ toast: vi.fn() }), +})); + +vi.mock("@/lib/codeSnapshot/codeSnapshot", () => ({ + renderCodeSnapshotHtml: renderCodeSnapshotHtmlMock, + snapshotElementToPng: vi.fn(), + copyPngDataUrlToClipboard: vi.fn(), + savePngDataUrlToFile: vi.fn(), +})); + +vi.mock("@/components/ui/dialog", async () => { + const { defineComponent, h } = await import("vue"); + const passthrough = defineComponent({ + setup(_props, { slots }) { + return () => h("div", slots.default?.()); + }, + }); + return { + Dialog: passthrough, + DialogContent: passthrough, + DialogFooter: passthrough, + DialogHeader: passthrough, + DialogTitle: passthrough, + }; +}); + +vi.mock("@/components/ui/button", async () => { + const { defineComponent, h } = await import("vue"); + return { + Button: defineComponent({ + setup(_props, { attrs, slots }) { + return () => h("button", attrs, slots.default?.()); + }, + }), + }; +}); + +vi.mock("@/components/ui/input", async () => { + const { defineComponent, h } = await import("vue"); + return { + Input: defineComponent({ + setup(_props, { attrs }) { + return () => h("input", attrs); + }, + }), + }; +}); + +vi.mock("@/components/ui/label", async () => { + const { defineComponent, h } = await import("vue"); + return { + Label: defineComponent({ + setup(_props, { slots }) { + return () => h("label", slots.default?.()); + }, + }), + }; +}); + +vi.mock("@/components/ui/switch", async () => { + const { defineComponent, h } = await import("vue"); + return { + Switch: defineComponent({ + props: { modelValue: Boolean }, + emits: ["update:modelValue"], + setup(props, { emit }) { + return () => + h("button", { + class: "switch-stub", + "data-model-value": String(props.modelValue), + onClick: () => emit("update:modelValue", false), + }); + }, + }), + }; +}); + +vi.mock("@lucide/vue", async () => { + const { defineComponent } = await import("vue"); + const Icon = defineComponent({ template: "" }); + return { Camera: Icon, Check: Icon, ClipboardCopy: Icon, Download: Icon, Moon: Icon, Sun: Icon }; +}); + +import CodeSnapshotDialog from "@/components/codeSnapshot/CodeSnapshotDialog.vue"; + +const mountedApps: App[] = []; + +async function mountDialog() { + const container = document.createElement("div"); + document.body.append(container); + const app = createApp(CodeSnapshotDialog, { + open: true, + source: { code: "SELECT 1", lang: "sql" }, + }); + mountedApps.push(app); + app.mount(container); + await vi.waitFor(() => expect(renderCodeSnapshotHtmlMock).toHaveBeenCalled()); + return container; +} + +beforeEach(() => { + localStorage.clear(); + renderCodeSnapshotHtmlMock.mockReset().mockResolvedValue('
SELECT 1
'); +}); + +afterEach(() => { + for (const app of mountedApps.splice(0)) app.unmount(); + document.body.innerHTML = ""; + localStorage.clear(); +}); + +describe("CodeSnapshotDialog option switches", () => { + it("rerenders and persists when window controls are disabled", async () => { + const container = await mountDialog(); + const switches = container.querySelectorAll(".switch-stub"); + + switches.item(0).click(); + await nextTick(); + + await vi.waitFor(() => { + expect(renderCodeSnapshotHtmlMock).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({ showTrafficLights: false, showLineNumbers: true })); + }); + expect(JSON.parse(localStorage.getItem("dbx:code-snapshot-settings") ?? "null")).toMatchObject({ showTrafficLights: false, showLineNumbers: true }); + }); + + it("rerenders and persists when line numbers are disabled", async () => { + const container = await mountDialog(); + const switches = container.querySelectorAll(".switch-stub"); + + switches.item(1).click(); + await nextTick(); + + await vi.waitFor(() => { + expect(renderCodeSnapshotHtmlMock).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({ showTrafficLights: true, showLineNumbers: false })); + }); + expect(JSON.parse(localStorage.getItem("dbx:code-snapshot-settings") ?? "null")).toMatchObject({ showTrafficLights: true, showLineNumbers: false }); + }); +});