Loading panel/src/pages/MySubmissionsPage.test.tsx +21 −1 Changes for panel/src/pages/MySubmissionsPage.test.tsx: 21 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { act, render, screen } from "@testing-library/react"; import { act, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -78,6 +78,26 @@ describe("MySubmissionsPage", () => { await vi.waitFor(() => expect(screen.queryByText(/^Page \d+ of/)).toBeNull()); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the list loads", async () => { vi.useFakeTimers(); render( <MemoryRouter> <MySubmissionsPage /> </MemoryRouter>, ); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText("Page 1 of 2")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: "Next" })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listMySubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listMySubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); expect(screen.getByText("Page 2 of 2")).toBeTruthy(); }); it("refuses a file over the server's per-upload cap before sending it", async () => { render( <MemoryRouter> Loading panel/src/pages/MySubmissionsPage.tsx +6 −2 Changes for panel/src/pages/MySubmissionsPage.tsx: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -113,13 +113,17 @@ export function MySubmissionsPage() { const [statusFilter, setStatusFilter] = useState<"all" | SubmissionStatus>("all"); const [page, setPage] = useState(1); const [expandedId, setExpandedId] = useState<string | null>(null); // A search already asked (the first render's included) starts no timer, so // a page turned just after the list loads is not sent back to page one. useEffect(() => { const next = search.trim(); if (next === query) return; const timer = setTimeout(() => { setQuery(search.trim()); setQuery(next); setPage(1); }, SEARCH_DEBOUNCE_MS); return () => clearTimeout(timer); }, [search]); }, [search, query]); const listMine = useCallback( () => Loading panel/src/pages/admin/ImageBuildPage.test.tsx +16 −1 Changes for panel/src/pages/admin/ImageBuildPage.test.tsx: 16 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from "vitest"; import { render, screen, within } from "@testing-library/react"; import { act, fireEvent, render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -100,6 +100,21 @@ describe("ImageBuildPage list", () => { await vi.waitFor(() => expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 })); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the list loads", async () => { vi.useFakeTimers(); calls.listBuilds.mockResolvedValue(page([MINE], 25)); renderPage(); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText(MINE.image_ref)).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: t("common:pagination_next") })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 }); }); it("searches on the server, sending a status typed as the badge shows it by its code, from page one", async () => { await i18next.changeLanguage("zh-CN"); calls.listBuilds.mockResolvedValue(page([MINE], 25)); Loading panel/src/pages/admin/ImageBuildPage.tsx +6 −2 Changes for panel/src/pages/admin/ImageBuildPage.tsx: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -158,13 +158,17 @@ export function ImageBuildPage() { const [search, setSearch] = useState(""); const [query, setQuery] = useState(""); const [page, setPage] = useState(1); // A search already asked (the first render's included) starts no timer, so // a page turned just after the list loads is not sent back to page one. useEffect(() => { const next = serverQuery(search, t); if (next === query) return; const timer = setTimeout(() => { setQuery(serverQuery(search, t)); setQuery(next); setPage(1); }, SEARCH_DEBOUNCE_MS); return () => clearTimeout(timer); }, [search, t]); }, [search, t, query]); const listBuilds = useCallback( () => api.listBuilds({ query: query || undefined, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE }), Loading panel/src/pages/admin/SubmissionsPage.test.tsx +17 −1 Changes for panel/src/pages/admin/SubmissionsPage.test.tsx: 17 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen } from "@testing-library/react"; import { act, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -38,6 +38,7 @@ beforeEach(() => { calls.listSubmissions.mockResolvedValue(PAGE); }); afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); return i18next.changeLanguage("en-US"); }); Loading Loading @@ -84,6 +85,21 @@ describe("SubmissionsPage", () => { ); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the queue loads", async () => { vi.useFakeTimers(); renderPage(); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText("Page 1 of 3")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: "Next" })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listSubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listSubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); expect(screen.getByText("Page 2 of 3")).toBeTruthy(); }); it("steps back to the last page that still has rows when its own page comes back empty", async () => { // Reviews elsewhere shrank the queue to 20 rows: page 3 no longer exists. calls.listSubmissions.mockImplementation(async ({ offset }: { offset: number }) => Loading Loading
panel/src/pages/MySubmissionsPage.test.tsx +21 −1 Changes for panel/src/pages/MySubmissionsPage.test.tsx: 21 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { act, render, screen } from "@testing-library/react"; import { act, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -78,6 +78,26 @@ describe("MySubmissionsPage", () => { await vi.waitFor(() => expect(screen.queryByText(/^Page \d+ of/)).toBeNull()); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the list loads", async () => { vi.useFakeTimers(); render( <MemoryRouter> <MySubmissionsPage /> </MemoryRouter>, ); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText("Page 1 of 2")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: "Next" })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listMySubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listMySubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); expect(screen.getByText("Page 2 of 2")).toBeTruthy(); }); it("refuses a file over the server's per-upload cap before sending it", async () => { render( <MemoryRouter> Loading
panel/src/pages/MySubmissionsPage.tsx +6 −2 Changes for panel/src/pages/MySubmissionsPage.tsx: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -113,13 +113,17 @@ export function MySubmissionsPage() { const [statusFilter, setStatusFilter] = useState<"all" | SubmissionStatus>("all"); const [page, setPage] = useState(1); const [expandedId, setExpandedId] = useState<string | null>(null); // A search already asked (the first render's included) starts no timer, so // a page turned just after the list loads is not sent back to page one. useEffect(() => { const next = search.trim(); if (next === query) return; const timer = setTimeout(() => { setQuery(search.trim()); setQuery(next); setPage(1); }, SEARCH_DEBOUNCE_MS); return () => clearTimeout(timer); }, [search]); }, [search, query]); const listMine = useCallback( () => Loading
panel/src/pages/admin/ImageBuildPage.test.tsx +16 −1 Changes for panel/src/pages/admin/ImageBuildPage.test.tsx: 16 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from "vitest"; import { render, screen, within } from "@testing-library/react"; import { act, fireEvent, render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -100,6 +100,21 @@ describe("ImageBuildPage list", () => { await vi.waitFor(() => expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 })); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the list loads", async () => { vi.useFakeTimers(); calls.listBuilds.mockResolvedValue(page([MINE], 25)); renderPage(); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText(MINE.image_ref)).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: t("common:pagination_next") })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listBuilds).toHaveBeenLastCalledWith({ query: undefined, limit: 10, offset: 10 }); }); it("searches on the server, sending a status typed as the badge shows it by its code, from page one", async () => { await i18next.changeLanguage("zh-CN"); calls.listBuilds.mockResolvedValue(page([MINE], 25)); Loading
panel/src/pages/admin/ImageBuildPage.tsx +6 −2 Changes for panel/src/pages/admin/ImageBuildPage.tsx: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -158,13 +158,17 @@ export function ImageBuildPage() { const [search, setSearch] = useState(""); const [query, setQuery] = useState(""); const [page, setPage] = useState(1); // A search already asked (the first render's included) starts no timer, so // a page turned just after the list loads is not sent back to page one. useEffect(() => { const next = serverQuery(search, t); if (next === query) return; const timer = setTimeout(() => { setQuery(serverQuery(search, t)); setQuery(next); setPage(1); }, SEARCH_DEBOUNCE_MS); return () => clearTimeout(timer); }, [search, t]); }, [search, t, query]); const listBuilds = useCallback( () => api.listBuilds({ query: query || undefined, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE }), Loading
panel/src/pages/admin/SubmissionsPage.test.tsx +17 −1 Changes for panel/src/pages/admin/SubmissionsPage.test.tsx: 17 added lines, 1 removed line. Original line number Diff line number Diff line // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen } from "@testing-library/react"; import { act, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import i18next from "i18next"; Loading Loading @@ -38,6 +38,7 @@ beforeEach(() => { calls.listSubmissions.mockResolvedValue(PAGE); }); afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); return i18next.changeLanguage("en-US"); }); Loading Loading @@ -84,6 +85,21 @@ describe("SubmissionsPage", () => { ); }); // The clock moves only when the test moves it, so the page is turned before // the search box's pause has run out however fast the machine is. it("keeps a page turned to right after the queue loads", async () => { vi.useFakeTimers(); renderPage(); await act(() => vi.advanceTimersByTimeAsync(0)); expect(screen.getByText("Page 1 of 3")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: "Next" })); await act(() => vi.advanceTimersByTimeAsync(0)); expect(calls.listSubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); await act(() => vi.advanceTimersByTimeAsync(1_000)); expect(calls.listSubmissions).toHaveBeenLastCalledWith({ limit: 10, offset: 10 }); expect(screen.getByText("Page 2 of 3")).toBeTruthy(); }); it("steps back to the last page that still has rows when its own page comes back empty", async () => { // Reviews elsewhere shrank the queue to 20 rows: page 3 no longer exists. calls.listSubmissions.mockImplementation(async ({ offset }: { offset: number }) => Loading