// @vitest-environment jsdom // // Downloading is an action you start anywhere: a lone http(s) link pasted onto a page (not into a // field, not at a dialog) opens the app's one download dialog; the Files link box hands its link to // the same dialog; the palette's "Download from link…" focuses that box on the entry it navigated to. // The dialog itself is stubbed (it names the link it was opened on). import { afterEach, describe, expect, it, vi } from "vitest"; import { act, cleanup, fireEvent, render, screen } from "@testing-library/react"; vi.mock("react-i18next", async (importOriginal) => ({ ...(await importOriginal()), useTranslation: () => ({ i18n: { language: "en" }, t: (key: string) => key }), })); vi.mock("./DownloadDialog", () => ({ default: ({ source, onClose }: { source: string; onClose: () => void }) => (
{source}
), })); import DownloadIntake from "./DownloadIntake"; import { LinkBox } from "./LinkBox"; import { clearPendingDownload, requestLinkBox } from "../lib/downloadIntake"; import { ensureNavId } from "../lib/history"; import { pushLayer, removeLayer } from "../lib/layerStack"; afterEach(() => { cleanup(); act(clearPendingDownload); }); function paste(target: EventTarget, text: string) { const event = new Event("paste", { bubbles: true, cancelable: true }) as ClipboardEvent; Object.defineProperty(event, "clipboardData", { value: { getData: (type: string) => (type === "text/plain" ? text : "") }, }); act(() => { target.dispatchEvent(event); }); return event; } const dialog = () => screen.queryByRole("dialog", { name: "download" }); describe("DownloadIntake · a pasted link", () => { it("a link pasted onto the page opens the download dialog on it", async () => { render(); const event = paste(document.body, " https://www.tiktok.com/@a/video/1 "); expect(await screen.findByText("https://www.tiktok.com/@a/video/1")).toBeTruthy(); // Taken: the browser does nothing else with it. expect(event.defaultPrevented).toBe(true); }); it("text that is not one link, or a paste into a field, is left alone", async () => { render( <> , ); paste(document.body, "just some words"); paste(document.body, "https://a.example/1 https://a.example/2"); const field = screen.getByRole("textbox", { name: "search" }); expect(paste(field, "https://a.example/1").defaultPrevented).toBe(false); await act(async () => {}); expect(dialog()).toBeNull(); }); it("with a dialog on top, a paste is the dialog's, not a download", async () => { render( <>
, ); paste(document.body, "https://a.example/1"); await act(async () => {}); expect(dialog()).toBeNull(); }); it("with a player or a menu on top (a layer, no aria-modal), a paste is not a download", async () => { render(); const layer = pushLayer(); try { paste(document.body, "https://a.example/1"); await act(async () => {}); expect(dialog()).toBeNull(); } finally { removeLayer(layer); } }); it("closing the dialog closes it", async () => { render(); paste(document.body, "https://a.example/1"); fireEvent.click(await screen.findByText("close")); expect(dialog()).toBeNull(); }); }); describe("LinkBox", () => { it("Enter hands the typed link to the dialog and empties the box", async () => { render( <> , ); const field = screen.getByRole("textbox", { name: "files.link.label" }); fireEvent.change(field, { target: { value: " https://vimeo.com/123 " } }); fireEvent.keyDown(field, { key: "Enter" }); expect(await screen.findByText("https://vimeo.com/123")).toBeTruthy(); expect((field as HTMLInputElement).value).toBe(""); }); it("a clipboard link read on focus never replaces what was typed while it was read", async () => { let answer: (text: string) => void = () => {}; Object.defineProperty(navigator, "clipboard", { configurable: true, value: { readText: () => new Promise((resolve) => (answer = resolve)) }, }); render(); const box = screen.getByRole("textbox"); fireEvent.focus(box); fireEvent.change(box, { target: { value: "https://typed.example/1" } }); await act(async () => answer("https://clipboard.example/2")); expect((box as HTMLInputElement).value).toBe("https://typed.example/1"); }); it("an empty box downloads nothing", async () => { render( <> , ); fireEvent.click(screen.getByText("files.link.download")); await act(async () => {}); expect(dialog()).toBeNull(); }); it("the palette's request focuses the box on its entry, once", () => { ensureNavId(); act(requestLinkBox); const { unmount } = render(); expect(document.activeElement).toBe(screen.getByRole("textbox", { name: "files.link.label" })); unmount(); // Spent: the next mount on the same entry is not focused again. render(); expect(document.activeElement).toBe(document.body); }); it("a request made for another entry (the navigation never got here) focuses nothing", () => { ensureNavId(); act(requestLinkBox); // A later entry — say Back, then elsewhere — mounts the box. window.history.pushState({}, ""); ensureNavId(); render(); expect(document.activeElement).toBe(document.body); }); });