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