mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-26 18:06:00 +00:00
f68bcb771c
* fix(frontend): guard message copy clipboard access * fix(frontend): reuse clipboard guard across copy actions
147 lines
3.7 KiB
TypeScript
147 lines
3.7 KiB
TypeScript
import { afterEach, expect, test, vi } from "vitest";
|
|
|
|
import { writeTextToClipboard } from "@/core/clipboard";
|
|
|
|
const originalNavigator = globalThis.navigator;
|
|
const hadOriginalNavigator = "navigator" in globalThis;
|
|
const originalDocument = globalThis.document;
|
|
const hadOriginalDocument = "document" in globalThis;
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
if (!hadOriginalNavigator) {
|
|
Reflect.deleteProperty(globalThis, "navigator");
|
|
} else {
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: originalNavigator,
|
|
});
|
|
}
|
|
|
|
if (!hadOriginalDocument) {
|
|
Reflect.deleteProperty(globalThis, "document");
|
|
} else {
|
|
Object.defineProperty(globalThis, "document", {
|
|
configurable: true,
|
|
value: originalDocument,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("writes text with the Clipboard API when available", async () => {
|
|
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: {
|
|
clipboard: {
|
|
writeText,
|
|
},
|
|
},
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(true);
|
|
expect(writeText).toHaveBeenCalledWith("hello");
|
|
});
|
|
|
|
test("returns false when Clipboard API is unavailable", async () => {
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: {},
|
|
});
|
|
Object.defineProperty(globalThis, "document", {
|
|
configurable: true,
|
|
value: undefined,
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(false);
|
|
});
|
|
|
|
test("falls back to execCommand when Clipboard API is unavailable", async () => {
|
|
const textarea = {
|
|
remove: vi.fn(),
|
|
select: vi.fn(),
|
|
setAttribute: vi.fn(),
|
|
style: {},
|
|
value: "",
|
|
};
|
|
const appendChild = vi.fn();
|
|
const execCommand = vi.fn().mockReturnValue(true);
|
|
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: {},
|
|
});
|
|
Object.defineProperty(globalThis, "document", {
|
|
configurable: true,
|
|
value: {
|
|
body: {
|
|
appendChild,
|
|
},
|
|
createElement: vi.fn().mockReturnValue(textarea),
|
|
execCommand,
|
|
},
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(true);
|
|
expect(textarea.value).toBe("hello");
|
|
expect(appendChild).toHaveBeenCalledWith(textarea);
|
|
expect(textarea.select).toHaveBeenCalled();
|
|
expect(execCommand).toHaveBeenCalledWith("copy");
|
|
expect(textarea.remove).toHaveBeenCalled();
|
|
});
|
|
|
|
test("returns false when execCommand fallback fails", async () => {
|
|
const textarea = {
|
|
remove: vi.fn(),
|
|
select: vi.fn(),
|
|
setAttribute: vi.fn(),
|
|
style: {},
|
|
value: "",
|
|
};
|
|
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: {},
|
|
});
|
|
Object.defineProperty(globalThis, "document", {
|
|
configurable: true,
|
|
value: {
|
|
body: {
|
|
appendChild: vi.fn(),
|
|
},
|
|
createElement: vi.fn().mockReturnValue(textarea),
|
|
execCommand: vi.fn().mockReturnValue(false),
|
|
},
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(false);
|
|
expect(textarea.remove).toHaveBeenCalled();
|
|
});
|
|
|
|
test("returns false when navigator is unavailable", async () => {
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: undefined,
|
|
});
|
|
Object.defineProperty(globalThis, "document", {
|
|
configurable: true,
|
|
value: undefined,
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(false);
|
|
});
|
|
|
|
test("returns false when Clipboard API rejects", async () => {
|
|
const writeText = vi.fn().mockRejectedValue(new Error("denied"));
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
configurable: true,
|
|
value: {
|
|
clipboard: {
|
|
writeText,
|
|
},
|
|
},
|
|
});
|
|
|
|
await expect(writeTextToClipboard("hello")).resolves.toBe(false);
|
|
});
|