Unverified Commit 66440460 authored by Lemon-miaow's avatar Lemon-miaow
Browse files

fix(panel): 搜索框的停顿计时只在搜索词变了才起,列表刚加载就翻的页不再被拉回第一页

parent 3c0322a5
Loading
Loading
Loading
Loading
+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";
@@ -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>
+6 −2
Changes for panel/src/pages/MySubmissionsPage.tsx: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -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(
    () =>
+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";
@@ -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));
+6 −2
Changes for panel/src/pages/admin/ImageBuildPage.tsx: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -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 }),
+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";
@@ -38,6 +38,7 @@ beforeEach(() => {
  calls.listSubmissions.mockResolvedValue(PAGE);
});
afterEach(() => {
  vi.useRealTimers();
  vi.restoreAllMocks();
  return i18next.changeLanguage("en-US");
});
@@ -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