feat: support custom required validation messages
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { test, expect } from "bun:test";
|
||||
import { v, invalid, parseBody, checkField, parseEnv } from "../src/index.ts";
|
||||
import { Window } from "happy-dom";
|
||||
import { v, invalid, parseBody, checkField, parseEnv, VALIDATE_RUNTIME } from "../src/index.ts";
|
||||
|
||||
const login = v.object({
|
||||
email: v.string().email(),
|
||||
@@ -33,6 +34,51 @@ test("required (non-optional) empty fields fail", () => {
|
||||
expect(r.errors.password).toBe("Required");
|
||||
});
|
||||
|
||||
test("required(message) customizes empty-field errors and overrides optional", () => {
|
||||
const schema = v.object({
|
||||
email: v.string().required("Enter your email or username"),
|
||||
password: v.string().optional().required("Enter your password"),
|
||||
accepted: v.boolean().required("Accept the terms to continue"),
|
||||
});
|
||||
const result = schema.parse({ accepted: false });
|
||||
|
||||
expect(result.errors).toEqual({
|
||||
email: "Enter your email or username",
|
||||
password: "Enter your password",
|
||||
accepted: "Accept the terms to continue",
|
||||
});
|
||||
expect(schema.describe().fields.email.requiredMessage).toBe("Enter your email or username");
|
||||
});
|
||||
|
||||
test("browser validation displays the serialized custom required message", () => {
|
||||
const schema = v.object({
|
||||
email: v.string().required("Enter your email or username"),
|
||||
});
|
||||
const win = new Window() as unknown as Window & Record<string, unknown>;
|
||||
win.document.body.innerHTML = `
|
||||
<form data-schema="login">
|
||||
<input name="email">
|
||||
<span data-error="email"></span>
|
||||
</form>`;
|
||||
win.__wireSchemas = { login: schema.describe() };
|
||||
(globalThis as Record<string, unknown>).window = win;
|
||||
(globalThis as Record<string, unknown>).document = win.document;
|
||||
|
||||
try {
|
||||
(0, eval)(VALIDATE_RUNTIME);
|
||||
const runtime = win.__wireValidate as { init(root: Document): void };
|
||||
runtime.init(win.document as unknown as Document);
|
||||
win.document.querySelector("input")!.dispatchEvent(new win.Event("blur", { bubbles: true }));
|
||||
|
||||
expect(win.document.querySelector("[data-error=email]")!.textContent).toBe(
|
||||
"Enter your email or username",
|
||||
);
|
||||
} finally {
|
||||
delete (globalThis as Record<string, unknown>).window;
|
||||
delete (globalThis as Record<string, unknown>).document;
|
||||
}
|
||||
});
|
||||
|
||||
test("describe emits a JSON descriptor", () => {
|
||||
const d = login.describe();
|
||||
expect(d.type).toBe("object");
|
||||
@@ -46,6 +92,9 @@ test("checkField coerces and applies rules", () => {
|
||||
);
|
||||
expect(checkField({ type: "number", rules: [{ kind: "min", n: 18 }] }, "20").value).toBe(20);
|
||||
expect(checkField({ type: "string", optional: true, rules: [] }, "").error).toBeNull();
|
||||
expect(
|
||||
checkField({ type: "string", requiredMessage: "Email is required", rules: [] }, "").error,
|
||||
).toBe("Email is required");
|
||||
});
|
||||
|
||||
test("invalid() returns a 400 with errors", async () => {
|
||||
|
||||
Reference in New Issue
Block a user