sdlc-hub/web/src/projects/NewProjectDialog.test.tsx
2026-10-24 15:30:12 +04:00

126 lines
6.1 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 { 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(
<QueryClientProvider client={new QueryClient()}>
<NewProjectDialog onClose={() => {}} onCreated={onCreated} />
</QueryClientProvider>,
);
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(); }
});
});