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 });
+ });
+});