import { act, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it } from "vitest"; import { qk, useBoard } from "../api/queries.ts"; import { json, mockFetch } from "../test/fetchMock.ts"; import { renderWithProviders } from "../test/render.tsx"; import { AuthGate } from "./AuthGate.tsx"; import { clearToken, getAuth, setToken } from "./token.ts"; const app =

страница приложения

; describe("вход", () => { beforeEach(() => clearToken()); it("без токена — экран входа, страница не видна", () => { renderWithProviders(app); expect(screen.getByRole("heading", { name: "Вход в хаб" })).toBeInTheDocument(); expect(screen.queryByText("страница приложения")).not.toBeInTheDocument(); expect(screen.getByText(/uv run jira-hub tokens/)).toBeInTheDocument(); }); it("верный токен сохраняется и открывает приложение", async () => { const fetch = mockFetch(() => json(200, {})); renderWithProviders(app); await userEvent.type(screen.getByLabelText("Клиентский токен хаба"), "good-token"); await userEvent.click(screen.getByRole("button", { name: "Войти" })); expect(await screen.findByText("страница приложения")).toBeInTheDocument(); expect(localStorage.getItem("jira-hub-token")).toBe("good-token"); expect(fetch.mock.calls[0][0]).toBe("/settings"); }); it("неверный токен не сохраняется, поле не стирается", async () => { mockFetch(() => json(401, { error: "unauthorized" })); renderWithProviders(app); const input = screen.getByLabelText("Клиентский токен хаба"); await userEvent.type(input, "wrong"); await userEvent.click(screen.getByRole("button", { name: "Войти" })); expect(await screen.findByText(/Токен не подошёл/)).toBeInTheDocument(); expect(input).toHaveValue("wrong"); expect(localStorage.getItem("jira-hub-token")).toBeNull(); }); it("кириллица или пробел — «недопустимые символы», запроса нет", async () => { const fetch = mockFetch(() => json(200, {})); renderWithProviders(app); await userEvent.type(screen.getByLabelText("Клиентский токен хаба"), "ток ен"); await userEvent.click(screen.getByRole("button", { name: "Войти" })); expect(screen.getByText(/недопустимые символы/)).toBeInTheDocument(); expect(fetch).not.toHaveBeenCalled(); }); it("хаб не отвечает — своё сообщение, не «токен не подошёл»", async () => { mockFetch(() => { throw new TypeError("Failed to fetch"); }); renderWithProviders(app); await userEvent.type(screen.getByLabelText("Клиентский токен хаба"), "tok"); await userEvent.click(screen.getByRole("button", { name: "Войти" })); expect(await screen.findByText(/Хаб не отвечает/)).toBeInTheDocument(); }); it("401 в запросе страницы — экран входа «токен не подошёл», страница скрыта (T3.6)", async () => { setToken("stale"); mockFetch(() => json(401, { error: "unauthorized" })); function Page() { useBoard(); return

страница приложения

; } renderWithProviders(); expect(await screen.findByText(/Токен не подошёл/)).toBeInTheDocument(); expect(screen.queryByText("страница приложения")).not.toBeInTheDocument(); expect(getAuth()).toEqual({ token: "", reason: "bad" }); }); it("запоздавший 401 от прежнего токена после входа с новым не выбрасывает на вход (T3.5)", async () => { setToken("old-token"); let release!: (r: Response) => void; const slow = new Promise((r) => { release = r; }); mockFetch((_m, url) => (url.pathname === "/board" ? slow : json(200, {}))); function Page() { useBoard(); return

страница приложения

; } const { client } = renderWithProviders(); act(() => setToken("new-token")); release(json(401, { error: "unauthorized" })); await waitFor(() => expect(client.getQueryState(qk.board)?.status).toBe("error")); // 401 точно обработан expect(screen.getByText("страница приложения")).toBeInTheDocument(); expect(getAuth()).toEqual({ token: "new-token", reason: "none" }); }); it("хаб отверг проверку токена (4xx) — текст хаба, не «хаб не отвечает» (T3.2)", async () => { mockFetch(() => json(400, { error: "bad_request", detail: "заголовок Authorization без Bearer" })); renderWithProviders(app); await userEvent.type(screen.getByLabelText("Клиентский токен хаба"), "tok"); await userEvent.click(screen.getByRole("button", { name: "Войти" })); expect(await screen.findByText(/заголовок Authorization без Bearer/)).toBeInTheDocument(); expect(screen.queryByText(/Хаб не отвечает/)).not.toBeInTheDocument(); }); });