Buckets:
| // @ts-ignore TS6133 | |
| import { expect, test } from "vitest"; | |
| import * as z from "zod/v3"; | |
| import { ZodIssueCode } from "../ZodError.js"; | |
| import { util } from "../helpers/util.js"; | |
| test("refinement", () => { | |
| const obj1 = z.object({ | |
| first: z.string(), | |
| second: z.string(), | |
| }); | |
| const obj2 = obj1.partial().strict(); | |
| const obj3 = obj2.refine((data) => data.first || data.second, "Either first or second should be filled in."); | |
| expect(obj1 === (obj2 as any)).toEqual(false); | |
| expect(obj2 === (obj3 as any)).toEqual(false); | |
| expect(() => obj1.parse({})).toThrow(); | |
| expect(() => obj2.parse({ third: "adsf" })).toThrow(); | |
| expect(() => obj3.parse({})).toThrow(); | |
| obj3.parse({ first: "a" }); | |
| obj3.parse({ second: "a" }); | |
| obj3.parse({ first: "a", second: "a" }); | |
| }); | |
| test("refinement 2", () => { | |
| const validationSchema = z | |
| .object({ | |
| email: z.string().email(), | |
| password: z.string(), | |
| confirmPassword: z.string(), | |
| }) | |
| .refine((data) => data.password === data.confirmPassword, "Both password and confirmation must match"); | |
| expect(() => | |
| validationSchema.parse({ | |
| email: "aaaa@gmail.com", | |
| password: "aaaaaaaa", | |
| confirmPassword: "bbbbbbbb", | |
| }) | |
| ).toThrow(); | |
| }); | |
| test("refinement type guard", () => { | |
| const validationSchema = z.object({ | |
| a: z.string().refine((s): s is "a" => s === "a"), | |
| }); | |
| type Input = z.input<typeof validationSchema>; | |
| type Schema = z.infer<typeof validationSchema>; | |
| util.assertEqual<"a", Input["a"]>(false); | |
| util.assertEqual<string, Input["a"]>(true); | |
| util.assertEqual<"a", Schema["a"]>(true); | |
| util.assertEqual<string, Schema["a"]>(false); | |
| }); | |
| test("refinement Promise", async () => { | |
| const validationSchema = z | |
| .object({ | |
| email: z.string().email(), | |
| password: z.string(), | |
| confirmPassword: z.string(), | |
| }) | |
| .refine( | |
| (data) => Promise.resolve().then(() => data.password === data.confirmPassword), | |
| "Both password and confirmation must match" | |
| ); | |
| await validationSchema.parseAsync({ | |
| email: "aaaa@gmail.com", | |
| password: "password", | |
| confirmPassword: "password", | |
| }); | |
| }); | |
| test("custom path", async () => { | |
| const result = await z | |
| .object({ | |
| password: z.string(), | |
| confirm: z.string(), | |
| }) | |
| .refine((data) => data.confirm === data.password, { path: ["confirm"] }) | |
| .spa({ password: "asdf", confirm: "qewr" }); | |
| expect(result.success).toEqual(false); | |
| if (!result.success) { | |
| expect(result.error.issues[0].path).toEqual(["confirm"]); | |
| } | |
| }); | |
| test("use path in refinement context", async () => { | |
| const noNested = z.string()._refinement((_val, ctx) => { | |
| if (ctx.path.length > 0) { | |
| ctx.addIssue({ | |
| code: ZodIssueCode.custom, | |
| message: `schema cannot be nested. path: ${ctx.path.join(".")}`, | |
| }); | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| }); | |
| const data = z.object({ | |
| foo: noNested, | |
| }); | |
| const t1 = await noNested.spa("asdf"); | |
| const t2 = await data.spa({ foo: "asdf" }); | |
| expect(t1.success).toBe(true); | |
| expect(t2.success).toBe(false); | |
| if (t2.success === false) { | |
| expect(t2.error.issues[0].message).toEqual("schema cannot be nested. path: foo"); | |
| } | |
| }); | |
| test("superRefine", () => { | |
| const Strings = z.array(z.string()).superRefine((val, ctx) => { | |
| if (val.length > 3) { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.too_big, | |
| maximum: 3, | |
| type: "array", | |
| inclusive: true, | |
| exact: true, | |
| message: "Too many items 😡", | |
| }); | |
| } | |
| if (val.length !== new Set(val).size) { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: `No duplicates allowed.`, | |
| }); | |
| } | |
| }); | |
| const result = Strings.safeParse(["asfd", "asfd", "asfd", "asfd"]); | |
| expect(result.success).toEqual(false); | |
| if (!result.success) expect(result.error.issues.length).toEqual(2); | |
| Strings.parse(["asfd", "qwer"]); | |
| }); | |
| test("superRefine async", async () => { | |
| const Strings = z.array(z.string()).superRefine(async (val, ctx) => { | |
| if (val.length > 3) { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.too_big, | |
| maximum: 3, | |
| type: "array", | |
| inclusive: true, | |
| exact: true, | |
| message: "Too many items 😡", | |
| }); | |
| } | |
| if (val.length !== new Set(val).size) { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: `No duplicates allowed.`, | |
| }); | |
| } | |
| }); | |
| const result = await Strings.safeParseAsync(["asfd", "asfd", "asfd", "asfd"]); | |
| expect(result.success).toEqual(false); | |
| if (!result.success) expect(result.error.issues.length).toEqual(2); | |
| Strings.parseAsync(["asfd", "qwer"]); | |
| }); | |
| test("superRefine - type narrowing", () => { | |
| type NarrowType = { type: string; age: number }; | |
| const schema = z | |
| .object({ | |
| type: z.string(), | |
| age: z.number(), | |
| }) | |
| .nullable() | |
| .superRefine((arg, ctx): arg is NarrowType => { | |
| if (!arg) { | |
| // still need to make a call to ctx.addIssue | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: "cannot be null", | |
| fatal: true, | |
| }); | |
| return false; | |
| } | |
| return true; | |
| }); | |
| util.assertEqual<z.infer<typeof schema>, NarrowType>(true); | |
| expect(schema.safeParse({ type: "test", age: 0 }).success).toEqual(true); | |
| expect(schema.safeParse(null).success).toEqual(false); | |
| }); | |
| test("chained mixed refining types", () => { | |
| type firstRefinement = { first: string; second: number; third: true }; | |
| type secondRefinement = { first: "bob"; second: number; third: true }; | |
| type thirdRefinement = { first: "bob"; second: 33; third: true }; | |
| const schema = z | |
| .object({ | |
| first: z.string(), | |
| second: z.number(), | |
| third: z.boolean(), | |
| }) | |
| .nullable() | |
| .refine((arg): arg is firstRefinement => !!arg?.third) | |
| .superRefine((arg, ctx): arg is secondRefinement => { | |
| util.assertEqual<typeof arg, firstRefinement>(true); | |
| if (arg.first !== "bob") { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: "`first` property must be `bob`", | |
| }); | |
| return false; | |
| } | |
| return true; | |
| }) | |
| .refine((arg): arg is thirdRefinement => { | |
| util.assertEqual<typeof arg, secondRefinement>(true); | |
| return arg.second === 33; | |
| }); | |
| util.assertEqual<z.infer<typeof schema>, thirdRefinement>(true); | |
| }); | |
| test("get inner type", () => { | |
| z.string() | |
| .refine(() => true) | |
| .innerType() | |
| .parse("asdf"); | |
| }); | |
| test("chained refinements", () => { | |
| const objectSchema = z | |
| .object({ | |
| length: z.number(), | |
| size: z.number(), | |
| }) | |
| .refine(({ length }) => length > 5, { | |
| path: ["length"], | |
| message: "length greater than 5", | |
| }) | |
| .refine(({ size }) => size > 7, { | |
| path: ["size"], | |
| message: "size greater than 7", | |
| }); | |
| const r1 = objectSchema.safeParse({ | |
| length: 4, | |
| size: 9, | |
| }); | |
| expect(r1.success).toEqual(false); | |
| if (!r1.success) expect(r1.error.issues.length).toEqual(1); | |
| const r2 = objectSchema.safeParse({ | |
| length: 4, | |
| size: 3, | |
| }); | |
| expect(r2.success).toEqual(false); | |
| if (!r2.success) expect(r2.error.issues.length).toEqual(2); | |
| }); | |
| test("fatal superRefine", () => { | |
| const Strings = z | |
| .string() | |
| .superRefine((val, ctx) => { | |
| if (val === "") { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: "foo", | |
| fatal: true, | |
| }); | |
| } | |
| }) | |
| .superRefine((val, ctx) => { | |
| if (val !== " ") { | |
| ctx.addIssue({ | |
| code: z.ZodIssueCode.custom, | |
| message: "bar", | |
| }); | |
| } | |
| }); | |
| const result = Strings.safeParse(""); | |
| expect(result.success).toEqual(false); | |
| if (!result.success) expect(result.error.issues.length).toEqual(1); | |
| }); | |
| test("superRefine after skipped transform", () => { | |
| const schema = z | |
| .string() | |
| .regex(/^\d+$/) | |
| .transform((val) => Number(val)) | |
| .superRefine((val) => { | |
| if (typeof val !== "number") { | |
| throw new Error("Called without transform"); | |
| } | |
| }); | |
| const result = schema.safeParse(""); | |
| expect(result.success).toEqual(false); | |
| }); | |
Xet Storage Details
- Size:
- 8.17 kB
- Xet hash:
- 02bc94441a90364b2f8bf1e0b0815446d27f665450aebce66b5a1aab6393f67c
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.