Files
openclaw/src/infra/clipboard.test.ts
2026-03-13 19:50:27 +00:00

53 lines
1.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn());
vi.mock("../process/exec.js", () => ({
runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeoutMock(...args),
}));
const { copyToClipboard } = await import("./clipboard.js");
describe("copyToClipboard", () => {
beforeEach(() => {
runCommandWithTimeoutMock.mockReset();
});
it("returns true on the first successful clipboard command", async () => {
runCommandWithTimeoutMock.mockResolvedValueOnce({ code: 0, killed: false });
await expect(copyToClipboard("hello")).resolves.toBe(true);
expect(runCommandWithTimeoutMock).toHaveBeenCalledWith(["pbcopy"], {
timeoutMs: 3000,
input: "hello",
});
expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(1);
});
it("falls through failed attempts until a later command succeeds", async () => {
runCommandWithTimeoutMock
.mockRejectedValueOnce(new Error("missing pbcopy"))
.mockResolvedValueOnce({ code: 1, killed: false })
.mockResolvedValueOnce({ code: 0, killed: false });
await expect(copyToClipboard("hello")).resolves.toBe(true);
expect(runCommandWithTimeoutMock.mock.calls.map((call) => call[0])).toEqual([
["pbcopy"],
["xclip", "-selection", "clipboard"],
["wl-copy"],
]);
});
it("returns false when every clipboard backend fails or is killed", async () => {
runCommandWithTimeoutMock
.mockResolvedValueOnce({ code: 0, killed: true })
.mockRejectedValueOnce(new Error("missing xclip"))
.mockResolvedValueOnce({ code: 1, killed: false })
.mockRejectedValueOnce(new Error("missing clip.exe"))
.mockResolvedValueOnce({ code: 2, killed: false });
await expect(copyToClipboard("hello")).resolves.toBe(false);
expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(5);
});
});