From 388bf4aeb64dd87103b7e5c3a00e5690499f82d5 Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Fri, 3 Jul 2026 18:10:10 +0700 Subject: [PATCH] =?UTF-8?q?fix(web):=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B5?= =?UTF-8?q?=D0=B9=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D0=B0=20=E2=80=94?= =?UTF-8?q?=20=D0=BF=D1=83=D1=81=D1=82=D1=8B=D0=B5=20values=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D1=83=D1=85=D0=BE=D0=B4=D1=8F=D1=82=20=D0=B2=20API,=20?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2=D0=B8=D0=B4=D0=B8?= =?UTF-8?q?=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/TemplatesPage.test.tsx | 38 ++++++++++++++++++++++++++++ web/src/pages/TemplatesPage.tsx | 29 +++++++++++++++++---- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/web/src/pages/TemplatesPage.test.tsx b/web/src/pages/TemplatesPage.test.tsx index 9434339..cb7e86a 100644 --- a/web/src/pages/TemplatesPage.test.tsx +++ b/web/src/pages/TemplatesPage.test.tsx @@ -121,6 +121,44 @@ test("ошибка создания шаблона отображается по expect(await screen.findByRole("alert")).toHaveTextContent("Не удалось создать шаблон") }) +test("запись с нетронутым (пустым) values не уходит в api.createTemplate, показывается ошибка", async () => { + const createSpy = vi.spyOn(api, "createTemplate").mockClear() + const user = userEvent.setup() + renderPage() + + await screen.findByText("Standard") + + await user.type(screen.getByLabelText(/имя шаблона/i), "New") + await user.click(screen.getByRole("button", { name: /добавить запись/i })) + + fireEvent.change(screen.getByLabelText(/имя записи 1/i), { target: { value: "www" } }) + // значения записи намеренно не заполняются — остаётся дефолтная пустая строка + + await user.click(screen.getByRole("button", { name: /сохранить шаблон/i })) + + expect(await screen.findByRole("alert")).toHaveTextContent(/заполните имя и значения/i) + expect(createSpy).not.toHaveBeenCalled() +}) + +test("сабмит с пустым именем записи показывает ошибку и не вызывает api.createTemplate", async () => { + const createSpy = vi.spyOn(api, "createTemplate").mockClear() + const user = userEvent.setup() + renderPage() + + await screen.findByText("Standard") + + await user.type(screen.getByLabelText(/имя шаблона/i), "New") + await user.click(screen.getByRole("button", { name: /добавить запись/i })) + + fireEvent.change(screen.getByLabelText(/значения записи 1/i), { target: { value: "1.1.1.1" } }) + // имя записи намеренно оставлено пустым + + await user.click(screen.getByRole("button", { name: /сохранить шаблон/i })) + + expect(await screen.findByRole("alert")).toHaveTextContent(/заполните имя и значения/i) + expect(createSpy).not.toHaveBeenCalled() +}) + test("пустое состояние при отсутствии шаблонов", async () => { vi.spyOn(api, "listTemplates").mockResolvedValue([]) renderPage() diff --git a/web/src/pages/TemplatesPage.tsx b/web/src/pages/TemplatesPage.tsx index 170fe5d..6c99aad 100644 --- a/web/src/pages/TemplatesPage.tsx +++ b/web/src/pages/TemplatesPage.tsx @@ -29,7 +29,9 @@ const recordSchema = z.object({ type: z.string().min(1), name: z.string().min(1, "Укажите имя записи"), ttl: z.number().int("TTL — целое число").nonnegative("TTL не может быть отрицательным"), - values: z.array(z.string()), + values: z + .array(z.string().trim().min(1, "Значение не может быть пустым")) + .min(1, "Добавьте хотя бы одно значение"), }) const templateFormSchema = z.object({ @@ -41,6 +43,15 @@ type TemplateForm = z.infer const EMPTY_FORM: TemplateForm = { name: "", records: [] } +function sanitizeRecords(records: TemplateForm["records"]) { + return records + .map((record) => ({ + ...record, + values: record.values.map((v) => v.trim()).filter(Boolean), + })) + .filter((record) => record.values.length > 0) +} + export function TemplatesPage() { const templates = useTemplates() const createTemplate = useCreateTemplate() @@ -73,9 +84,10 @@ export function TemplatesPage() { } function onSubmit(values: TemplateForm) { + const input = { ...values, records: sanitizeRecords(values.records) } if (editingId) { updateTemplate.mutate( - { id: editingId, input: values }, + { id: editingId, input }, { onSuccess: () => { setEditingId(null) @@ -84,7 +96,7 @@ export function TemplatesPage() { }, ) } else { - createTemplate.mutate(values, { onSuccess: () => reset(EMPTY_FORM) }) + createTemplate.mutate(input, { onSuccess: () => reset(EMPTY_FORM) }) } } @@ -96,6 +108,7 @@ export function TemplatesPage() { } const saveMutation = editingId ? updateTemplate : createTemplate + const hasFormErrors = !!errors.name || !!errors.records return (
@@ -147,10 +160,16 @@ export function TemplatesPage() {
- {saveMutation.isError && ( + {hasFormErrors ? ( - {saveMutation.error.message} + Заполните имя и значения всех записей + ) : ( + saveMutation.isError && ( + + {saveMutation.error.message} + + ) )}
{editingId && (