// @vitest-environment jsdom // // The channel page's video grid follows a running full-history backfill (bug hunt 2026-09, item 6). // The rule itself — when a poll reading should refresh the grid — is pinned on the hook in // lib/channelDetailPoll.backfill.dom.test.tsx. This pins that the PAGE runs it: deleting the hook's // one call site left every lane green, knip included (the hook test's import kept the export "used"). // // The page's own poll is what feeds the hook; a newer reading is handed to it here through the same // query key the poll writes, rather than waiting out the 4 s interval. The Feed itself is stubbed — // what matters is whether its queries are told they are stale. import { describe, it, expect, vi, beforeAll, afterEach } from "vitest"; import { render, waitFor, cleanup, act } 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("./navigationContext", () => ({ useNavigationActions: () => ({ closeChannel: vi.fn(), openChannel: vi.fn() }), })); vi.mock("./confirmContext", () => ({ useConfirm: () => async () => true })); vi.mock("./feedFiltersContext", () => ({ useFeedFilters: () => ({ includeNormal: true, includeShorts: false, includeLive: false }), })); vi.mock("./Feed", () => ({ default: () => null })); vi.mock("./ChannelAbout", () => ({ default: () => null })); import ChannelPage from "./ChannelPage"; import { api, type ChannelDetail, type FeedFilters, type Me } from "../lib/api"; import { qk } from "../lib/queryKeys"; beforeAll(() => { class Stub { observe() {} unobserve() {} disconnect() {} takeRecords() { return []; } } globalThis.ResizeObserver ??= Stub as unknown as typeof ResizeObserver; globalThis.IntersectionObserver ??= Stub as unknown as typeof IntersectionObserver; }); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); const CH = "UCsXVk37bltHxD1rDPwtNM8Q"; const reading = (stored_videos: number): ChannelDetail => ({ id: CH, title: "LEMMiNO", subscribed: true, deep_requested: true, backfill_done: false, stored_videos, }) as unknown as ChannelDetail; const feedKey = qk.feed({ q: "", channel: CH } as unknown as FeedFilters); describe("ChannelPage — the grid follows a running backfill", () => { it("marks the channel's video grid stale when the poll sees more stored videos", async () => { vi.spyOn(api, "channelDetail").mockResolvedValue(reading(19)); vi.spyOn(api, "tags").mockResolvedValue([] as never); const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }); qc.setQueryData(feedKey, { pages: [{ items: [], next_cursor: null }], pageParams: [null] }); render( {}} onShowInFeed={() => {}} /> , ); await waitFor(() => expect(api.channelDetail).toHaveBeenCalled()); await waitFor(() => expect(qc.getQueryData(qk.channel(CH))).toBeTruthy()); expect(qc.getQueryState(feedKey)?.isInvalidated).toBe(false); // The next poll tick: the background job has stored 60 more. act(() => qc.setQueryData(qk.channel(CH), reading(79))); await waitFor(() => expect(qc.getQueryState(feedKey)?.isInvalidated).toBe(true)); }); });