// @vitest-environment jsdom // // E11 S4: the video card's half of keyboard navigation (`lib/listNav`). Every layout's ROOT carries the // list index — that is the element `j`/`k` focus and ring — and the title link is the card's `open` // element, so Enter on a selected card runs the very click a mouse would: the in-app player. import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanup, render, screen } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; vi.mock("react-i18next", async (importOriginal) => ({ ...(await importOriginal()), useTranslation: () => ({ t: (key: string) => key, i18n: { language: "en" } }), })); vi.mock("./NavigationProvider", () => ({ useNavigationActions: () => ({ openChannel: vi.fn() }), })); import VideoCard from "./VideoCard"; import type { Video } from "../lib/api"; import type { FeedView } from "../lib/feedView"; beforeAll(() => { class Stub { observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } } globalThis.ResizeObserver ??= Stub as unknown as typeof ResizeObserver; globalThis.IntersectionObserver ??= Stub as unknown as typeof IntersectionObserver; }); const video = { id: "abc123", title: "Cicada 3301", channel_id: "UCsXVk37bltHxD1rDPwtNM8Q", channel_title: "LEMMiNO", status: "new", saved: false, position_seconds: 0, duration_seconds: 1200, published_at: "2026-01-01T00:00:00Z", watch_url: "https://www.youtube.com/watch?v=abc123", thumbnail_url: "", } as unknown as Video; beforeEach(cleanup); function renderCard(view: FeedView, navIndex?: number, onOpen = vi.fn()) { render( {}} onToggleSave={() => {}} onOpen={onOpen} /> , ); return onOpen; } // One per layout FAMILY: the stacked card, the thumbnailed row, the one-line row — three roots. const VIEWS: FeedView[] = ["cards", "rows", "rowsCompact"]; describe("VideoCard — keyboard navigation hooks", () => { for (const view of VIEWS) { it(`puts the list index on the ${view} root and opens in-app from the title`, () => { const onOpen = renderCard(view, 7); const root = screen.getByTestId("video-card"); expect(root.getAttribute("data-nav-index")).toBe("7"); const open = root.querySelectorAll("[data-nav-open]"); expect(open).toHaveLength(1); expect(open[0]!.textContent).toBe("Cicada 3301"); (open[0] as HTMLElement).click(); expect(onOpen).toHaveBeenCalledWith(video); }); } it("carries no index outside a navigable list (Home's rails)", () => { renderCard("cards"); expect(screen.getByTestId("video-card").hasAttribute("data-nav-index")).toBe(false); }); });