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 && (