89 lines
4.9 KiB
TypeScript
89 lines
4.9 KiB
TypeScript
import { screen, waitFor } from "@testing-library/react";
|
||
import userEvent from "@testing-library/user-event";
|
||
import { readFileSync } from "node:fs";
|
||
import { join } from "node:path";
|
||
import { beforeEach, describe, expect, it } from "vitest";
|
||
import { setToken } from "../auth/token.ts";
|
||
import settings from "../board/__fixtures__/settings.json";
|
||
import { json, mockFetch } from "../test/fetchMock.ts";
|
||
import { renderWithProviders } from "../test/render.tsx";
|
||
import { IssuesPage } from "./IssuesPage.tsx";
|
||
|
||
const SNAP = JSON.parse(readFileSync(join(import.meta.dirname, "..", "board", "__fixtures__", "hub-basic.board.json"), "utf8"));
|
||
const row = (n: number) => ({ ...SNAP.issues[1], key: `AURORA-${9000 + n}`, summary: `Задача ${n}`, fetched_at: "" });
|
||
|
||
function hub(pages: (params: URLSearchParams) => unknown[]) {
|
||
return mockFetch((_m, url) => {
|
||
if (url.pathname === "/settings") return json(200, settings);
|
||
if (url.pathname === "/board") return json(200, SNAP);
|
||
if (url.pathname === "/issues") return json(200, { issues: pages(url.searchParams) });
|
||
return json(404, {});
|
||
});
|
||
}
|
||
const issuesCalls = (fetch: ReturnType<typeof hub>) =>
|
||
fetch.mock.calls.map(([u]) => new URL(String(u), "http://hub.test")).filter((u) => u.pathname === "/issues");
|
||
|
||
describe("список задач", () => {
|
||
beforeEach(() => setToken("tok"));
|
||
|
||
it("фильтры из адреса уходят в GET /issues; ключ ведёт на карточку", async () => {
|
||
const fetch = hub(() => [row(1)]);
|
||
renderWithProviders(<IssuesPage />, { route: "/issues?sprint=AWPCA_Спринт 2&neighbor=1", path: "/issues" });
|
||
expect(await screen.findByRole("link", { name: "AURORA-9001" })).toHaveAttribute("href", "/issues/AURORA-9001");
|
||
const q = issuesCalls(fetch)[0].searchParams;
|
||
expect(q.get("sprint")).toBe("AWPCA_Спринт 2");
|
||
expect(q.get("neighbor")).toBe("1");
|
||
expect(q.get("limit")).toBe("50");
|
||
expect(q.get("offset")).toBe("0");
|
||
});
|
||
|
||
it("выбор исполнителя пишет его в адрес и в запрос", async () => {
|
||
const fetch = hub(() => [row(1)]);
|
||
const { router } = renderWithProviders(<IssuesPage />, { route: "/issues", path: "/issues" });
|
||
await screen.findByRole("link", { name: "AURORA-9001" });
|
||
await userEvent.selectOptions(screen.getByLabelText("Исполнитель"), "Петров Пётр Петрович");
|
||
await waitFor(() => expect(router.state.location.search).toContain("assignee="));
|
||
await waitFor(() => expect(issuesCalls(fetch).at(-1)!.searchParams.get("assignee")).toBe("Петров Пётр Петрович"));
|
||
});
|
||
|
||
it("«Показать ещё» до неполной порции", async () => {
|
||
const fetch = hub((p) => (p.get("offset") === "0" ? Array.from({ length: 50 }, (_, i) => row(i + 1)) : Array.from({ length: 17 }, (_, i) => row(i + 51))));
|
||
renderWithProviders(<IssuesPage />, { route: "/issues", path: "/issues" });
|
||
await userEvent.click(await screen.findByRole("button", { name: "Показать ещё" }));
|
||
await waitFor(() => expect(screen.getAllByRole("row")).toHaveLength(1 + 67));
|
||
expect(screen.queryByRole("button", { name: "Показать ещё" })).toBeNull();
|
||
expect(issuesCalls(fetch).at(-1)!.searchParams.get("offset")).toBe("50");
|
||
});
|
||
|
||
it("поиск — один запрос после паузы ввода", async () => {
|
||
const fetch = hub(() => [row(1)]);
|
||
renderWithProviders(<IssuesPage />, { route: "/issues", path: "/issues" });
|
||
await screen.findByRole("link", { name: "AURORA-9001" });
|
||
const before = issuesCalls(fetch).length;
|
||
await userEvent.type(screen.getByLabelText("Поиск по ключу или теме"), "свод");
|
||
await waitFor(() => expect(issuesCalls(fetch).at(-1)!.searchParams.get("q")).toBe("свод"));
|
||
expect(issuesCalls(fetch).length).toBe(before + 1);
|
||
});
|
||
|
||
it("хаб не отвечает — сообщение и «Повторить»", async () => {
|
||
let down = true;
|
||
mockFetch((_m, url) => {
|
||
if (url.pathname === "/issues" && down) throw new TypeError("Failed to fetch");
|
||
if (url.pathname === "/settings") return json(200, settings);
|
||
if (url.pathname === "/board") return json(200, SNAP);
|
||
return json(200, { issues: [row(1)] });
|
||
});
|
||
renderWithProviders(<IssuesPage />, { route: "/issues", path: "/issues" });
|
||
expect(await screen.findByRole("alert")).toHaveTextContent("хаб не отвечает");
|
||
down = false;
|
||
await userEvent.click(screen.getByRole("button", { name: "Повторить" }));
|
||
expect(await screen.findByRole("link", { name: "AURORA-9001" })).toBeInTheDocument();
|
||
});
|
||
|
||
it("эпик показывается названием из снимка", async () => {
|
||
hub(() => [row(1)]);
|
||
renderWithProviders(<IssuesPage />, { route: "/issues", path: "/issues" });
|
||
expect(await screen.findByRole("cell", { name: "Тестовый эпик" })).toBeInTheDocument();
|
||
});
|
||
});
|