fix(panel): 服务器列表无匹配时给出真的清除筛选按钮,清掉搜索和状态筛选并回到第一页
This commit is contained in:
2 files changed
+73
-4
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
// @vitest-environment jsdom
|
// @vitest-environment jsdom
|
||||||
import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest";
|
import { describe, it, expect, vi, beforeEach, beforeAll, afterEach } from "vitest";
|
||||||
import { render, screen, within } from "@testing-library/react";
|
import { act, render, screen, within } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { MemoryRouter } from "react-router-dom";
|
import { MemoryRouter } from "react-router-dom";
|
||||||
import type { FleetServer, MyServerView } from "@/lib/types";
|
import type { FleetServer, MyServerView } from "@/lib/types";
|
||||||
@@ -341,3 +341,60 @@ describe("ServersPage order and paging", () => {
|
|||||||
expect(await tableOrder()).toEqual(names.slice(0, 20));
|
expect(await tableOrder()).toEqual(names.slice(0, 20));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("ServersPage with nothing matching", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears the search and the phase filter from the empty state", async () => {
|
||||||
|
tier.isAdmin = false;
|
||||||
|
const mine = (name: string): MyServerView => ({
|
||||||
|
name,
|
||||||
|
subdomain: name,
|
||||||
|
owned: true,
|
||||||
|
claimable: false,
|
||||||
|
phase: "Running",
|
||||||
|
playersOnline: 0,
|
||||||
|
playersMax: 20,
|
||||||
|
});
|
||||||
|
calls.myServers.mockResolvedValue([mine("survival"), mine("creative")]);
|
||||||
|
const user = renderPage();
|
||||||
|
await tableOrder();
|
||||||
|
|
||||||
|
const search = screen.getByPlaceholderText(/Search/) as HTMLInputElement;
|
||||||
|
await user.type(search, "zzz");
|
||||||
|
await user.click(screen.getByRole("combobox", { name: "Filter by phase" }));
|
||||||
|
await user.click(await screen.findByRole("option", { name: "Stopping" }));
|
||||||
|
expect(screen.getByText("No matching servers found.")).toBeTruthy();
|
||||||
|
expect(screen.getByText("No server matches the current search or filter.")).toBeTruthy();
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "Clear filters" }));
|
||||||
|
expect(await tableOrder()).toEqual(["creative", "survival"]);
|
||||||
|
expect(search.value).toBe("");
|
||||||
|
expect(screen.getByRole("combobox", { name: "Filter by phase" }).textContent).toBe("All phases");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("starts the cleared list from its first page", async () => {
|
||||||
|
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||||
|
const names = Array.from({ length: 21 }, (_, i) => `srv-${String(i + 1).padStart(2, "0")}`);
|
||||||
|
calls.fleet.mockResolvedValue(names.map((n) => row(n, { phase: "Running", ready: true })));
|
||||||
|
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<ServersPage />
|
||||||
|
</MemoryRouter>,
|
||||||
|
);
|
||||||
|
await tableOrder();
|
||||||
|
await user.click(screen.getByRole("combobox", { name: "Filter by phase" }));
|
||||||
|
await user.click(await screen.findByRole("option", { name: "Running" }));
|
||||||
|
await user.click(screen.getByRole("button", { name: /Next/ }));
|
||||||
|
expect(await tableOrder()).toEqual(["srv-21"]);
|
||||||
|
|
||||||
|
// The next reread finds every server stopped, so the filtered page empties.
|
||||||
|
calls.fleet.mockResolvedValue(names.map((n) => row(n, {})));
|
||||||
|
await act(() => vi.advanceTimersByTimeAsync(10_000));
|
||||||
|
await user.click(await screen.findByRole("button", { name: "Clear filters" }));
|
||||||
|
expect(await tableOrder()).toEqual(names.slice(0, 20));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -402,8 +402,20 @@ export function ServersPage() {
|
|||||||
) : visible.length === 0 ? (
|
) : visible.length === 0 ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title={isAdmin ? t("fleet_no_match_title") : t("servers:search_no_match")}
|
title={isAdmin ? t("fleet_no_match_title") : t("servers:search_no_match")}
|
||||||
hint={isAdmin ? t("fleet_no_match_hint") : t("servers:search_clear_btn")}
|
hint={t("fleet_no_match_hint")}
|
||||||
/>
|
>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
setQuery("");
|
||||||
|
setPhaseFilter("all");
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("servers:search_clear_btn")}
|
||||||
|
</Button>
|
||||||
|
</EmptyState>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Cards until the table fits (two per row from md). The admin
|
{/* Cards until the table fits (two per row from md). The admin
|
||||||
|
|||||||
Reference in new issue
Block a user