// @vitest-environment jsdom // // The Download Center's confirm dialogs name their ACTION on the confirm button (E11.5): the shared // `confirmThen` once labelled it with the dialog's question — "Re-download this file?" on the button, // and the cancel dialog's own "Cancel" beside "Cancel". i18n is key-passthrough, so labels read as keys. import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type { DownloadJob } from "../lib/api"; const mocks = vi.hoisted(() => ({ jobs: [] as unknown[], confirm: vi.fn(async (_opts: { title: string; confirmLabel?: string }) => false), })); vi.mock("react-i18next", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useTranslation: () => ({ t: (key: string) => key }) }; }); vi.mock("./ConfirmProvider", () => ({ useConfirm: () => mocks.confirm })); vi.mock("../lib/useLiveQuery", () => ({ useLiveQuery: (key: readonly unknown[]) => ({ data: key[0] === "downloads" ? mocks.jobs : undefined, isError: false, isLoading: false, refetch: vi.fn(), }), })); vi.mock("../lib/useMe", () => ({ useMe: () => ({ role: "user" }) })); // The page toolbar (title + tabs) renders into the app shell's slot, which a bare render has none of. vi.mock("./ui/PageShell", async (importOriginal) => ({ ...(await importOriginal()), PageToolbar: ({ children }: { children: React.ReactNode }) => <>{children}, })); import DownloadCenter from "./DownloadCenter"; const job = (id: number, status: DownloadJob["status"]): DownloadJob => ({ id, status, progress: 0, speed_bps: null, eta_s: null, phase: null, error: null, error_code: null, error_detail: null, queue_pos: 0, created_at: null, source_kind: "youtube", source_ref: `v${id}`, source_url: null, poster_url: null, profile_id: null, spec: { mode: "av", max_height: null, container: null, audio_format: null, vcodec: null, embed_subs: false, embed_chapters: false, embed_thumbnail: false, sponsorblock: false, }, display_name: `Clip ${id}`, display_uploader: null, display_uploader_url: null, extra_links: [], can_download: status === "done", expired: false, asset: null, }); afterEach(() => { cleanup(); mocks.confirm.mockClear(); }); describe("DownloadCenter confirm dialogs", () => { it("label the confirm button with the action, not the dialog's question", async () => { mocks.jobs = [job(1, "running"), job(2, "done")]; render( , ); fireEvent.click(screen.getByTitle("downloads.actions.cancel")); await waitFor(() => expect(mocks.confirm).toHaveBeenCalledTimes(1)); // The tab's label shares its button with a count badge. fireEvent.click(screen.getByRole("button", { name: /downloads\.tabs\.done/ })); fireEvent.click(await screen.findByTitle("downloads.actions.redownload")); fireEvent.click(screen.getByTitle("downloads.actions.delete")); await waitFor(() => expect(mocks.confirm).toHaveBeenCalledTimes(3)); expect(mocks.confirm.mock.calls.map(([o]) => [o.title, o.confirmLabel])).toEqual([ ["downloads.confirm.cancelTitle", "downloads.confirm.cancelAction"], ["downloads.confirm.redownloadTitle", "downloads.confirm.redownloadAction"], ["downloads.confirm.deleteTitle", "downloads.actions.delete"], ]); }); });