import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { fireEvent, render, screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { setToken } from "../auth/token.ts"; import { jiraModule } from "../modules/jira/index.tsx"; import type { HubModule } from "../modules/types.ts"; import { json, mockFetch } from "../test/fetchMock.ts"; import { MODULES } from "../modules/index.ts"; import { NewProjectDialog } from "./NewProjectDialog.tsx"; // второй модуль с текстовым полем: окно собирает поля из всех модулей, а не только из jira vi.mock("../modules/index.ts", async () => { const { jiraModule: jira } = await import("../modules/jira/index.tsx"); const { wikiModule: wiki } = await import("../modules/wiki/index.tsx"); const extra: HubModule = { name: "extra", title: "Extra", pages: [], nav: [], projectFields: [{ name: "space_key", label: "Пространство", hint: "ключ", kind: "text" }], }; return { MODULES: [jira, wiki, extra] }; }); const CREATED = { project: { label: "P", name: "Пэ", created_at: "" }, batch_id: 3, batches: [{ module: "jira", id: 3 }, { module: "wiki", id: 4 }] }; function setup() { const posts: unknown[] = []; mockFetch((method, _url, body) => { if (method === "POST") { posts.push(body); return json(201, CREATED); } return json(404, { error: "not_found" }); }); const onCreated = vi.fn(); render( {}} onCreated={onCreated} /> , ); return { posts, onCreated }; } const fill = async () => { await userEvent.type(screen.getByLabelText("Метка"), "P"); await userEvent.type(screen.getByLabelText("Название"), "Пэ"); }; describe("NewProjectDialog: поля модулей", () => { beforeEach(() => setToken("tok")); it("jira описывает поле доски", () => { expect(jiraModule.projectFields).toEqual([ { name: "board_id", label: "Номер доски Jira", hint: "scrum или kanban определится сам", kind: "int" }, ]); }); it("пустые поля в тело не попадают", async () => { const { posts } = setup(); await fill(); await userEvent.click(screen.getByRole("button", { name: "Создать" })); await waitFor(() => expect(posts).toHaveLength(1)); expect(posts[0]).toEqual({ label: "P", name: "Пэ", author: "viewer" }); }); it("доска «5120» уходит числом, текстовое поле другого модуля — строкой", async () => { const { posts } = setup(); await fill(); await userEvent.type(screen.getByLabelText("Номер доски Jira"), "5120"); await userEvent.type(screen.getByLabelText("Пространство"), " KEY "); await userEvent.click(screen.getByRole("button", { name: "Создать" })); await waitFor(() => expect(posts).toHaveLength(1)); expect(posts[0]).toEqual({ label: "P", name: "Пэ", board_id: 5120, space_key: "KEY", author: "viewer" }); }); it("поля сгруппированы по модулям: fieldset с legend «Jira» и «Вики»", () => { setup(); for (const [legend, field] of [["Jira", "Номер доски Jira"], ["Вики", "Корень вики"]]) { const g = screen.getByRole("group", { name: legend }); expect(within(g).getByLabelText(field)).toBeInTheDocument(); } }); it("подсказки — текст под полем, placeholder пустой", () => { setup(); const board = screen.getByLabelText("Номер доски Jira"); expect(board).not.toHaveAttribute("placeholder"); expect(board).toHaveAccessibleDescription("scrum или kanban определится сам"); expect(screen.getByLabelText("Метка")).toHaveAccessibleDescription("метка задач в Jira и имя проекта в хабе"); }); it("51.5 в номере доски — «нужно целое число», запроса нет (T4.1)", async () => { const { posts } = setup(); await fill(); await userEvent.type(screen.getByLabelText("Номер доски Jira"), "51.5"); await userEvent.click(screen.getByRole("button", { name: "Создать" })); expect(await screen.findByText("нужно целое число")).toBeInTheDocument(); expect(screen.getByLabelText("Номер доски Jira")).toHaveAttribute("aria-invalid", "true"); expect(posts).toHaveLength(0); }); it("onCreated получает пакеты с модулями (M-14)", async () => { const { onCreated } = setup(); await fill(); await userEvent.click(screen.getByRole("button", { name: "Создать" })); await waitFor(() => expect(onCreated).toHaveBeenCalled()); expect(onCreated.mock.calls[0][1]).toEqual([{ module: "jira", id: 3 }, { module: "wiki", id: 4 }]); }); it("два быстрых ввода в разные поля не теряют первое (T4.2)", async () => { const { posts } = setup(); await fill(); const board = screen.getByLabelText("Номер доски Jira"), space = screen.getByLabelText("Пространство"); fireEvent.change(board, { target: { value: "7" } }); fireEvent.change(space, { target: { value: "K" } }); await userEvent.click(screen.getByRole("button", { name: "Создать" })); await waitFor(() => expect(posts).toHaveLength(1)); expect(posts[0]).toMatchObject({ board_id: 7, space_key: "K" }); }); it("поля собираются при отрисовке, а не при импорте (T4.5)", () => { const late: HubModule = { name: "late", title: "Late", pages: [], nav: [], projectFields: [{ name: "x", label: "Позднее поле", hint: "", kind: "text" }] }; (MODULES as HubModule[]).push(late); try { setup(); expect(screen.getByLabelText("Позднее поле")).toBeInTheDocument(); } finally { (MODULES as HubModule[]).pop(); } }); });