diff --git a/.changeset/tidy-forms-report.md b/.changeset/tidy-forms-report.md new file mode 100644 index 000000000..b4d1a58d7 --- /dev/null +++ b/.changeset/tidy-forms-report.md @@ -0,0 +1,5 @@ +--- +"@effect-app/vue-components": patch +--- + +Handle string form validation errors in OmegaForm.Errors without crashing, and safely ignore unsupported error values. diff --git a/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts b/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts new file mode 100644 index 000000000..33a03f7ea --- /dev/null +++ b/packages/vue-components/__tests__/OmegaForm/GeneralErrors.test.ts @@ -0,0 +1,59 @@ +import { mount } from "@vue/test-utils" +import * as S from "effect-app/Schema" +import { describe, expect, it } from "vitest" +import { defineComponent, h, nextTick } from "vue" +import { useOmegaForm } from "../../src/components/OmegaForm" +import OmegaErrorsInternal from "../../src/components/OmegaForm/OmegaErrorsInternal.vue" + +describe("OmegaForm general errors", () => { + it("renders a string returned by an onSubmit validator and clears it after correction", async () => { + const message = "Passwörter stimmen nicht überein" + const Inner = defineComponent({ + setup() { + const form = useOmegaForm(S.Struct({ password: S.String, confirmation: S.String }), { + defaultValues: { password: "secret", confirmation: "different" }, + validators: { + onSubmit: ({ value }) => value.password === value.confirmation ? undefined : message + } + }) + return { form } + }, + template: `` + }) + const wrapper = mount(Inner) + + await wrapper.vm.form.handleSubmit() + await nextTick() + expect(wrapper.get("[role=\"alert\"]").text()).toContain(message) + + wrapper.vm.form.setFieldValue("confirmation", "secret") + await wrapper.vm.form.handleSubmit() + await nextTick() + expect(wrapper.find("[role=\"alert\"]").exists()).toBe(false) + wrapper.unmount() + }) + + it("preserves schema messages alongside strings and ignores unsupported values", () => { + const wrapper = mount(OmegaErrorsInternal, { + props: { + errors: [], + generalErrors: [ + "Form error", + { rows: [{ message: "Schema error" }, null, 42, { message: 123 }, { message: "" }] }, + null, + undefined, + false, + 42, + "", + { rows: "unsupported", other: null } + ] + }, + slots: { + default: ({ showedGeneralErrors }) => h("div", JSON.stringify(showedGeneralErrors)) + } + }) + + expect(wrapper.text()).toBe(JSON.stringify(["Form error", "Schema error"])) + wrapper.unmount() + }) +}) diff --git a/packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue b/packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue index ca07d7238..1e5a848e0 100644 --- a/packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue +++ b/packages/vue-components/src/components/OmegaForm/OmegaErrorsInternal.vue @@ -99,7 +99,6 @@