import { render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { createMemoryRouter } from "react-router";
import { beforeEach, describe, expect, it } from "vitest";
import { App } from "../../../App.tsx";
import { setToken } from "../../../auth/token.ts";
import settings from "../board/__fixtures__/settings.json";
import { routes } from "../../../routes.tsx";
import { json, mockFetch } from "../../../test/fetchMock.ts";
import type { AgileResponse } from "../api/types.ts";
const S = { label: "AWPCA", name: "АРМ КА", board_id: 4216, board_type: "scrum", created_at: "", issues_count: 9, jira_synced_at: null };
const K = { label: "KAN", name: "Канбан", board_id: 5120, board_type: "kanban", created_at: "", issues_count: 4, jira_synced_at: null };
const data: AgileResponse = {
board: { id: 4216, type: "scrum", columns: [{ name: "Новая", statuses: ["Новая"] }], kanban_backlog: null, fetched_at: "2026-10-24T10:00:00+00:00" },
sprints: [], backlog: [], board_issues: [],
};
const open = (path: string) => {
const router = createMemoryRouter(routes, { initialEntries: [path], basename: "/app" });
render();
return router;
};
const base = (method: string, pathname: string, projects: unknown[]) => {
const k = `${method} ${pathname}`;
if (k === "GET /projects") return json(200, { projects });
if (k === "GET /settings") return json(200, settings);
if (k === "GET /issues") return json(200, { issues: [] });
if (k === "GET /batches") return json(200, { batches: [] });
if (k === "GET /agents") return json(200, { agents: [] });
return null;
};
describe("AgileGate (T9.4)", () => {
beforeEach(() => { setToken("tok"); localStorage.clear(); });
it("пока /agile не ответил — «Загрузка…»", async () => {
mockFetch((method, url) => {
if (`${method} ${url.pathname}` === "GET /agile") return new Promise(() => {});
return base(method, url.pathname, [S]) ?? json(404, { error: "not_found" });
});
open("/app/jira/sprint?project=AWPCA");
expect(await screen.findByText("Загрузка…")).toBeInTheDocument();
});
it("ошибка /agile — текст ошибки и «Повторить», щелчок повторяет запрос", async () => {
let calls = 0;
mockFetch((method, url) => {
if (`${method} ${url.pathname}` === "GET /agile") { calls += 1; return calls === 1 ? json(500, { error: "hub_down" }) : json(200, data); }
return base(method, url.pathname, [S]) ?? json(404, { error: "not_found" });
});
open("/app/jira/sprint?project=AWPCA");
const retry = await screen.findByRole("button", { name: "Повторить" });
expect(screen.queryByText("Загрузка…")).not.toBeInTheDocument();
await userEvent.click(retry);
await waitFor(() => expect(calls).toBe(2));
await waitFor(() => expect(screen.queryByRole("button", { name: "Повторить" })).not.toBeInTheDocument());
});
it("меню kanban-проекта: «Kanban» есть, «Бэклог» и «Спринт» нет", async () => {
mockFetch((method, url) => base(method, url.pathname, [K]) ?? json(200, { ...data, board: { ...data.board, type: "kanban" }, sprints: [], backlog: null }));
open("/app/jira/kanban?project=KAN");
const menu = await screen.findByRole("group", { name: "Jira" });
expect(await within(menu).findByRole("link", { name: "Kanban" })).toBeInTheDocument();
expect(within(menu).queryByRole("link", { name: "Бэклог" })).not.toBeInTheDocument();
expect(within(menu).queryByRole("link", { name: "Спринт" })).not.toBeInTheDocument();
});
});