sdlc-hub/web/src/issues/IssuesPage.test.tsx
2026-10-14 23:18:02 +04:00

89 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
});
});