import { afterEach, describe, expect, it, vi } from "vitest";
import { copyMenuFor, copyText, selectableText } from "./selectableText";
function mount(html: string) {
document.body.innerHTML = html;
return document.body;
}
function select(node: Node) {
const range = document.createRange();
range.selectNodeContents(node);
const sel = window.getSelection()!;
sel.removeAllRanges();
sel.addRange(range);
}
afterEach(() => {
window.getSelection()?.removeAllRanges();
document.body.innerHTML = "";
vi.restoreAllMocks();
});
describe("selectableText", () => {
it("is a real class name", () => {
expect(selectableText).toBeTruthy();
});
});
describe("copyMenuFor", () => {
it("offers Copy of the selection in a selectable pane (issue #36: Markdown preview)", () => {
mount(`
`);
select(document.getElementById("p")!);
const items = copyMenuFor(document.getElementById("b"));
expect(items).toHaveLength(1);
expect(items![0]).toMatchObject({ label: "Copy", disabled: false });
});
it("offers a disabled Copy in a selectable pane with nothing selected", () => {
mount(``);
expect(copyMenuFor(document.getElementById("p"))![0]).toMatchObject({ label: "Copy", disabled: true });
});
it("resolves text-node targets", () => {
mount(``);
expect(copyMenuFor(document.getElementById("p")!.firstChild)).not.toBeNull();
});
it("offers Copy on a selection in any pane that opted in with its own CSS", () => {
mount(`line 1 `);
select(document.getElementById("console")!);
expect(copyMenuFor(document.getElementById("console"))![0]).toMatchObject({ disabled: false });
});
it("stays out of chrome without a selection", () => {
mount(`Run tree row
`);
expect(copyMenuFor(document.getElementById("b"))).toBeNull();
expect(copyMenuFor(document.getElementById("row"))).toBeNull();
});
it("ignores a selection elsewhere when right-clicking chrome", () => {
mount(`tree row
`);
select(document.getElementById("p")!);
expect(copyMenuFor(document.getElementById("row"))).toBeNull();
});
it.each([" ", "", "x
"])(
"leaves editable targets alone: %s",
(html) => {
mount(`${html}
`);
expect(copyMenuFor(document.getElementById("t"))).toBeNull();
},
);
it("copies the text captured when the menu opened", async () => {
const writeText = vi.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", { value: { writeText }, configurable: true });
mount(``);
select(document.getElementById("p")!);
const items = copyMenuFor(document.getElementById("p"))!;
window.getSelection()!.removeAllRanges();
(items[0] as { run: () => void }).run();
await Promise.resolve();
expect(writeText).toHaveBeenCalledWith("Hello");
});
it("falls back to execCommand when the clipboard API is missing", async () => {
Object.defineProperty(navigator, "clipboard", { value: undefined, configurable: true });
const exec = vi.fn().mockReturnValue(true);
document.execCommand = exec;
await copyText("x");
expect(exec).toHaveBeenCalledWith("copy");
});
});