chore: update the clipboard fix commit

This commit is contained in:
Abhishek Kumar 2026-07-25 15:19:55 +05:30
parent 8645f18608
commit bb30d95b78
2 changed files with 35 additions and 18 deletions

View file

@ -13,7 +13,6 @@ describe("copyTextToClipboard", () => {
configurable: true,
value: originalExecCommand,
});
document.querySelectorAll("textarea").forEach((element) => element.remove());
});
it("uses the Clipboard API when it is available", async () => {
@ -32,7 +31,15 @@ describe("copyTextToClipboard", () => {
});
it("uses the DOM fallback when the Clipboard API is unavailable", async () => {
const execCommand = vi.fn().mockReturnValue(true);
const setData = vi.fn();
const execCommand = vi.fn(() => {
const event = new Event("copy", { cancelable: true });
Object.defineProperty(event, "clipboardData", {
value: { setData },
});
document.dispatchEvent(event);
return true;
});
vi.stubGlobal("navigator", {});
Object.defineProperty(document, "execCommand", {
configurable: true,
@ -42,12 +49,20 @@ describe("copyTextToClipboard", () => {
await copyTextToClipboard("secret");
expect(execCommand).toHaveBeenCalledWith("copy");
expect(document.querySelector("textarea")).toBeNull();
expect(setData).toHaveBeenCalledWith("text/plain", "secret");
});
it("uses the DOM fallback when Clipboard API access is denied", async () => {
const writeText = vi.fn().mockRejectedValue(new Error("denied"));
const execCommand = vi.fn().mockReturnValue(true);
const setData = vi.fn();
const execCommand = vi.fn(() => {
const event = new Event("copy", { cancelable: true });
Object.defineProperty(event, "clipboardData", {
value: { setData },
});
document.dispatchEvent(event);
return true;
});
vi.stubGlobal("navigator", { clipboard: { writeText } });
Object.defineProperty(document, "execCommand", {
configurable: true,
@ -58,9 +73,10 @@ describe("copyTextToClipboard", () => {
expect(writeText).toHaveBeenCalledWith("secret");
expect(execCommand).toHaveBeenCalledWith("copy");
expect(setData).toHaveBeenCalledWith("text/plain", "secret");
});
it("rejects and removes the temporary element when copying fails", async () => {
it("rejects when copying fails", async () => {
const execCommand = vi.fn().mockReturnValue(false);
vi.stubGlobal("navigator", {});
Object.defineProperty(document, "execCommand", {
@ -71,6 +87,5 @@ describe("copyTextToClipboard", () => {
await expect(copyTextToClipboard("secret")).rejects.toThrow(
"Copy command was unsuccessful",
);
expect(document.querySelector("textarea")).toBeNull();
});
});

View file

@ -12,22 +12,24 @@ export async function copyTextToClipboard(text: string): Promise<void> {
throw new Error("Clipboard access is unavailable");
}
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.setAttribute("readonly", "");
textArea.style.position = "fixed";
textArea.style.opacity = "0";
textArea.style.pointerEvents = "none";
document.body.appendChild(textArea);
let copiedText = false;
const handleCopy = (event: ClipboardEvent) => {
if (!event.clipboardData) {
return;
}
event.clipboardData.setData("text/plain", text);
event.preventDefault();
copiedText = true;
};
document.addEventListener("copy", handleCopy);
try {
textArea.select();
textArea.setSelectionRange(0, text.length);
if (!document.execCommand("copy")) {
if (!document.execCommand("copy") || !copiedText) {
throw new Error("Copy command was unsuccessful");
}
} finally {
textArea.remove();
document.removeEventListener("copy", handleCopy);
}
}