You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { ZodError, z } from 'zod'
  2. describe('Zod Features', () => {
  3. it('should support string', () => {
  4. const stringSchema = z.string()
  5. const numberLikeStringSchema = z.coerce.string() // 12 would be converted to '12'
  6. const stringSchemaWithError = z.string({
  7. required_error: 'Name is required',
  8. invalid_type_error: 'Invalid name type, expected string',
  9. })
  10. const urlSchema = z.string().url()
  11. const uuidSchema = z.string().uuid()
  12. expect(stringSchema.parse('hello')).toBe('hello')
  13. expect(() => stringSchema.parse(12)).toThrow()
  14. expect(numberLikeStringSchema.parse('12')).toBe('12')
  15. expect(numberLikeStringSchema.parse(12)).toBe('12')
  16. expect(() => stringSchemaWithError.parse(undefined)).toThrow('Name is required')
  17. expect(() => stringSchemaWithError.parse(12)).toThrow('Invalid name type, expected string')
  18. expect(urlSchema.parse('https://dify.ai')).toBe('https://dify.ai')
  19. expect(uuidSchema.parse('123e4567-e89b-12d3-a456-426614174000')).toBe('123e4567-e89b-12d3-a456-426614174000')
  20. })
  21. it('should support enum', () => {
  22. enum JobStatus {
  23. waiting = 'waiting',
  24. processing = 'processing',
  25. completed = 'completed',
  26. }
  27. expect(z.nativeEnum(JobStatus).parse(JobStatus.waiting)).toBe(JobStatus.waiting)
  28. expect(z.nativeEnum(JobStatus).parse('completed')).toBe('completed')
  29. expect(() => z.nativeEnum(JobStatus).parse('invalid')).toThrow()
  30. })
  31. it('should support number', () => {
  32. const numberSchema = z.number()
  33. const numberWithMin = z.number().gt(0) // alias min
  34. const numberWithMinEqual = z.number().gte(0)
  35. const numberWithMax = z.number().lt(100) // alias max
  36. expect(numberSchema.parse(123)).toBe(123)
  37. expect(numberWithMin.parse(50)).toBe(50)
  38. expect(numberWithMinEqual.parse(0)).toBe(0)
  39. expect(() => numberWithMin.parse(-1)).toThrow()
  40. expect(numberWithMax.parse(50)).toBe(50)
  41. expect(() => numberWithMax.parse(101)).toThrow()
  42. })
  43. it('should support boolean', () => {
  44. const booleanSchema = z.boolean()
  45. expect(booleanSchema.parse(true)).toBe(true)
  46. expect(booleanSchema.parse(false)).toBe(false)
  47. expect(() => booleanSchema.parse('true')).toThrow()
  48. })
  49. it('should support date', () => {
  50. const dateSchema = z.date()
  51. expect(dateSchema.parse(new Date('2023-01-01'))).toEqual(new Date('2023-01-01'))
  52. })
  53. it('should support object', () => {
  54. const userSchema = z.object({
  55. id: z.union([z.string(), z.number()]),
  56. name: z.string(),
  57. email: z.string().email(),
  58. age: z.number().min(0).max(120).optional(),
  59. })
  60. type User = z.infer<typeof userSchema>
  61. const validUser: User = {
  62. id: 1,
  63. name: 'John',
  64. email: 'john@example.com',
  65. age: 30,
  66. }
  67. expect(userSchema.parse(validUser)).toEqual(validUser)
  68. })
  69. it('should support object optional field', () => {
  70. const userSchema = z.object({
  71. name: z.string(),
  72. optionalField: z.optional(z.string()),
  73. })
  74. type User = z.infer<typeof userSchema>
  75. const user: User = {
  76. name: 'John',
  77. }
  78. const userWithOptionalField: User = {
  79. name: 'John',
  80. optionalField: 'optional',
  81. }
  82. expect(userSchema.safeParse(user).success).toEqual(true)
  83. expect(userSchema.safeParse(userWithOptionalField).success).toEqual(true)
  84. })
  85. it('should support object intersection', () => {
  86. const Person = z.object({
  87. name: z.string(),
  88. })
  89. const Employee = z.object({
  90. role: z.string(),
  91. })
  92. const EmployedPerson = z.intersection(Person, Employee)
  93. const validEmployedPerson = {
  94. name: 'John',
  95. role: 'Developer',
  96. }
  97. expect(EmployedPerson.parse(validEmployedPerson)).toEqual(validEmployedPerson)
  98. })
  99. it('should support record', () => {
  100. const recordSchema = z.record(z.string(), z.number())
  101. const validRecord = {
  102. a: 1,
  103. b: 2,
  104. }
  105. expect(recordSchema.parse(validRecord)).toEqual(validRecord)
  106. })
  107. it('should support array', () => {
  108. const numbersSchema = z.array(z.number())
  109. const stringArraySchema = z.string().array()
  110. expect(numbersSchema.parse([1, 2, 3])).toEqual([1, 2, 3])
  111. expect(stringArraySchema.parse(['a', 'b', 'c'])).toEqual(['a', 'b', 'c'])
  112. })
  113. it('should support promise', () => {
  114. const promiseSchema = z.promise(z.string())
  115. const validPromise = Promise.resolve('success')
  116. expect(promiseSchema.parse(validPromise)).resolves.toBe('success')
  117. })
  118. it('should support unions', () => {
  119. const unionSchema = z.union([z.string(), z.number()])
  120. expect(unionSchema.parse('success')).toBe('success')
  121. expect(unionSchema.parse(404)).toBe(404)
  122. })
  123. it('should support functions', () => {
  124. const functionSchema = z.function().args(z.string(), z.number(), z.optional(z.string())).returns(z.number())
  125. const validFunction = (name: string, age: number, _optional?: string): number => {
  126. return age
  127. }
  128. expect(functionSchema.safeParse(validFunction).success).toEqual(true)
  129. })
  130. it('should support undefined, null, any, and void', () => {
  131. const undefinedSchema = z.undefined()
  132. const nullSchema = z.null()
  133. const anySchema = z.any()
  134. expect(undefinedSchema.parse(undefined)).toBeUndefined()
  135. expect(nullSchema.parse(null)).toBeNull()
  136. expect(anySchema.parse('anything')).toBe('anything')
  137. expect(anySchema.parse(3)).toBe(3)
  138. })
  139. it('should safeParse would not throw', () => {
  140. expect(z.string().safeParse('abc').success).toBe(true)
  141. expect(z.string().safeParse(123).success).toBe(false)
  142. expect(z.string().safeParse(123).error).toBeInstanceOf(ZodError)
  143. })
  144. })