json-schema#JSONSchema7Type TypeScript Examples

The following examples show how to use json-schema#JSONSchema7Type. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: promise.test.ts    From zod-to-json-schema with ISC License 6 votes vote down vote up
describe("promise", () => {
  it("should be possible to use promise", () => {
    const parsedSchema = parsePromiseDef(
      z.promise(z.string())._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "string",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #2
Source File: i18n.ts    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
/**
 * Adds component i18n messages to the frontend client i18n message packs.
 * Also adds a menu item caption to the i18n file for locale `en`. The caption is constructed from the class name.
 * If any message already exists in the file - it will NOT be overwritten with a new value.
 *
 * @deprecated moved to building-blocks
 *
 * @param fs - yeoman fs
 * @param className - component class name
 * @param dirShift - directory depth from project root
 * @param projectLocales - locales enabled for this project.
 * If provided, i18n messages will be added only for these locales.
 * Otherwise, i18n messages for all locales supported by Frontend UI will be added
 * (this situation is possible if the project model was created using an older Studio version and does not
 * contain locales info).
 * @param componentMessagesPerLocale - an object where keys are locale codes (such as 'en' or 'ru) and values
 * are objects containing i18n key/value pairs for that locale.
 */
export function writeComponentI18nMessages(
  fs: Editor,
  className: string,
  dirShift: string = './',
  projectLocales?: Locale[],
  componentMessagesPerLocale: Record<string, Record<string, string>> = {en: {}, ru: {}}
) {
  Object.entries(componentMessagesPerLocale).forEach(([localeCode, componentMessages]) => {
    if (projectLocales == null || projectLocales.some(projectLocale => projectLocale.code === localeCode)) {
      const existingMessagesPath = path.join(dirShift, `i18n/${localeCode}.json`);

      const existingMessages: JSONSchema7Type | undefined = fs.readJSON(existingMessagesPath);
      const mergedMessages = mergeI18nMessages(existingMessages, componentMessages, className, localeCode);

      if (mergedMessages != null) {
        fs.writeJSON(existingMessagesPath, mergedMessages);
      }
    }
  });
}
Example #3
Source File: i18n.ts    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
/**
 *
 * @param existingMessages - messages that already exist in the i18n file
 * @param componentMessages - messages required by the component
 * @param className - component class name, menu caption will be generated based on it
 * @param localeCode - e.g. 'en' or 'ru'
 *
 * @return messages to be written to the i18n file or `null` if no messages are to be added.
 * Messages to be added are determined as messages in `componentMessages` that are not
 * present in `existingMessages` plus (for `en` locale only) the menu caption
 * if not already present in `existingMessages`.
 */
function mergeI18nMessages(
  existingMessages: JSONSchema7Type | undefined,
  componentMessages: Record<string, string>,
  className: string,
  localeCode: string
): JSONSchema7Type | undefined {

  const screenCaption = splitByCapitalLetter(capitalizeFirst(className));

  if (localeCode === 'en') {
    componentMessages = {
      ...componentMessages,
      [`screen.${className}`]: screenCaption
    };
  }

  return hasNewEntries(componentMessages, existingMessages)
    ? {
      ...componentMessages,
      ...(
        typeof existingMessages === 'object'
          ? existingMessages
          : {}
      )
    }
    : undefined;
}
Example #4
Source File: map.test.ts    From zod-to-json-schema with ISC License 6 votes vote down vote up
describe("map", () => {
  it("should be possible to use Map", () => {
    const mapSchema = z.map(z.string(), z.number());

    const parsedSchema = parseMapDef(mapSchema._def, new References());

    const jsonSchema: JSONSchema7Type = {
      type: "array",
      maxItems: 125,
      items: {
        type: "array",
        items: [
          {
            type: "string",
          },
          {
            type: "number",
          },
        ],
        minItems: 2,
        maxItems: 2,
      },
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);

    const myMap: z.infer<typeof mapSchema> = new Map<string, number>();
    myMap.set("hello", 123);

    ajv.validate(jsonSchema, [...myMap]);
    const ajvResult = !ajv.errors;

    const zodResult = mapSchema.safeParse(myMap).success;

    expect(zodResult).toBe(true);
    expect(ajvResult).toBe(true);
  });
});
Example #5
Source File: bigint.test.ts    From zod-to-json-schema with ISC License 6 votes vote down vote up
describe('bigint', () => {
  it('should be possible to use bigint', () => {
    const parsedSchema = parseBigintDef();
    const jsonSchema: JSONSchema7Type = {
      type: 'integer',
      format: "int64"
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #6
Source File: effects.test.ts    From zod-to-json-schema with ISC License 6 votes vote down vote up
describe("effects", () => {
  it("should be possible to use refine", () => {
    const parsedSchema = parseEffectsDef(
      z.number().refine((x) => x + 1)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should default to the input type", () => {
    const schema = z.string().transform((arg) => parseInt(arg));

    const jsonSchema = parseEffectsDef(schema._def, new References());

    expect(jsonSchema).toStrictEqual({
      type: "string",
    });
  });

  it("should default to any if given that effectStrategy", () => {
    const schema = z.string().transform((arg) => parseInt(arg));

    const jsonSchema = parseEffectsDef(
      schema._def,
      new References(undefined, undefined, undefined, "any")
    );

    expect(jsonSchema).toStrictEqual({});
  });
});
Example #7
Source File: i18n.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
function hasNewEntries(newVals: Record<string, string>, oldVals: JSONSchema7Type | undefined): boolean {
  const newKeys = Object.keys(newVals);

  if (newKeys.length === 0) { return false; }
  if (!oldVals) { return true; }

  return !newKeys.every((newK) => Object.keys(oldVals).some((oldK) => oldK === newK));
}
Example #8
Source File: number.test.ts    From zod-to-json-schema with ISC License 5 votes vote down vote up
describe("Number validations", () => {
  it("should be possible to describe minimum number", () => {
    const parsedSchema = parseNumberDef(
      z.number().min(5)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
      minimum: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe maximum number", () => {
    const parsedSchema = parseNumberDef(
      z.number().max(5)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
      maximum: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe both minimum and maximum number", () => {
    const parsedSchema = parseNumberDef(
      z.number().min(5).max(5)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
      minimum: 5,
      maximum: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe an integer", () => {
    const parsedSchema = parseNumberDef(
      z.number().int()._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "integer",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe multiples of n", () => {
    const parsedSchema = parseNumberDef(
      z.number().multipleOf(2)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
      multipleOf: 2,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe positive, negative, nonpositive and nonnegative numbers", () => {
    const parsedSchema = parseNumberDef(
      z.number().positive().negative().nonpositive().nonnegative()._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "number",
      minimum: 0,
      maximum: 0,
      exclusiveMaximum: 0,
      exclusiveMinimum: 0,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #9
Source File: nativeEnum.test.ts    From zod-to-json-schema with ISC License 5 votes vote down vote up
describe('Native enums', () => {
  it('should be possible to convert a basic native number enum', () => {
    enum MyEnum {
      val1,
      val2,
      val3,
    }

    const parsedSchema = parseNativeEnumDef(z.nativeEnum(MyEnum)._def);
    const jsonSchema: JSONSchema7Type = {
      type: 'number',
      enum: [0, 1, 2],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it('should be possible to convert a native string enum', () => {
    enum MyEnum {
      val1 = 'a',
      val2 = 'b',
      val3 = 'c',
    }

    const parsedSchema = parseNativeEnumDef(z.nativeEnum(MyEnum)._def);
    const jsonSchema: JSONSchema7Type = {
      type: 'string',
      enum: ['a', 'b', 'c'],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it('should be possible to convert a mixed value native enum', () => {
    enum MyEnum {
      val1 = 'a',
      val2 = 1,
      val3 = 'c',
    }

    const parsedSchema = parseNativeEnumDef(z.nativeEnum(MyEnum)._def);
    const jsonSchema: JSONSchema7Type = {
      type: ['string', 'number'],
      enum: ['a', 1, 'c'],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #10
Source File: default.test.ts    From zod-to-json-schema with ISC License 5 votes vote down vote up
describe("promise", () => {
  it("should be possible to use default on objects", () => {
    const parsedSchema = parseDefaultDef(
      z.object({ foo: z.boolean() }).default({ foo: true })._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "object",
      additionalProperties: false,
      required: ["foo"],
      properties: {
        foo: {
          type: "boolean",
        },
      },
      default: {
        foo: true,
      },
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should be possible to use default on primitives", () => {
    const parsedSchema = parseDefaultDef(
      z.string().default("default")._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "string",
      default: "default",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  test("default with transform", () => {
    const stringWithDefault = z
      .string()
      .transform((val) => val.toUpperCase())
      .default("default");

    const parsedSchema = parseDefaultDef(
      stringWithDefault._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "string",
      default: "default",
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #11
Source File: array.test.ts    From zod-to-json-schema with ISC License 5 votes vote down vote up
describe("Arrays and array validations", () => {
  it("should be possible to describe a simple array", () => {
    const parsedSchema = parseArrayDef(z.array(z.string())._def, new References());
    const jsonSchema: JSONSchema7Type = {
      type: "array",
      items: {
        type: "string"
      },
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe a simple array with any item", () => {
    const parsedSchema = parseArrayDef(z.array(z.any())._def, new References());
    const jsonSchema: JSONSchema7Type = {
      type: "array"
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe a string array with a minimum and maximum length", () => {
    const parsedSchema = parseArrayDef(
      z.array(z.string()).min(2).max(4)._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "array",
      items: {
        type: "string",
      },
      minItems: 2,
      maxItems: 4,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to describe a string array with a minimum length of 1 by using nonempty", () => {
    const parsedSchema = parseArrayDef(
      z.array(z.any()).nonempty()._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: "array",
      minItems: 1,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should be possible do properly reference array items", () => {
    const willHaveBeenSeen = z.object({ hello: z.string() });
    const unionSchema = z.union([willHaveBeenSeen, willHaveBeenSeen]);
    const arraySchema = z.array(unionSchema);
    const jsonSchema = parseArrayDef(arraySchema._def, new References());
    //TODO: Remove 'any'-cast when json schema type package supports it. 'anyOf' in 'items' should be completely according to spec though.
    expect((jsonSchema.items as any).anyOf[1].$ref).toEqual("#/items/anyOf/0");

    const resolvedSchema = deref(jsonSchema)
    expect(resolvedSchema.items.anyOf[1]).toBe(resolvedSchema.items.anyOf[0]);
  });
});
Example #12
Source File: i18n.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
function hasNewEntries(newVals: Record<string, string>, oldVals: JSONSchema7Type | undefined): boolean {
  const newKeys = Object.keys(newVals);

  if (newKeys.length === 0) { return false; }
  if (!oldVals) { return true; }

  return !newKeys.every((newK) => Object.keys(oldVals).some((oldK) => oldK === newK));
}
Example #13
Source File: i18n.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
/**
 *
 * @param existingMessages - messages that already exist in the i18n file
 * @param componentMessages - messages required by the component
 * @param className - component class name, menu caption will be generated based on it
 * @param localeCode - e.g. 'en' or 'ru'
 *
 * @return messages to be written to the i18n file or `null` if no messages are to be added.
 * Messages to be added are determined as messages in `componentMessages` that are not
 * present in `existingMessages` plus (for `en` locale only) the menu caption
 * if not already present in `existingMessages`.
 */
function mergeI18nMessages(
  existingMessages: JSONSchema7Type | undefined,
  componentMessages: Record<string, string>,
  className: string,
  localeCode: string,
  componentType: ComponentType
): JSONSchema7Type | undefined {

  const screenCaption = splitByCapitalLetter(capitalizeFirst(className));

  if (localeCode === 'en') {
    componentMessages = {
      ...componentMessages,
      [`${componentType}.${className}`]: screenCaption
    };
  }

  return hasNewEntries(componentMessages, existingMessages)
    ? {
      ...componentMessages,
      ...(
        typeof existingMessages === 'object'
          ? existingMessages
          : {}
      )
    }
    : undefined;
}
Example #14
Source File: parseDef.test.ts    From zod-to-json-schema with ISC License 4 votes vote down vote up
describe("Basic parsing", () => {
  it("should return a proper json schema with some common types without validation", () => {
    const zodSchema = z.object({
      requiredString: z.string(),
      optionalString: z.string().optional(),
      literalString: z.literal("literalStringValue"),
      stringArray: z.array(z.string()),
      stringEnum: z.enum(["stringEnumOptionA", "stringEnumOptionB"]),
      tuple: z.tuple([z.string(), z.number(), z.boolean()]),
      record: z.record(z.boolean()),
      requiredNumber: z.number(),
      optionalNumber: z.number().optional(),
      numberOrNull: z.number().nullable(),
      numberUnion: z.union([z.literal(1), z.literal(2), z.literal(3)]),
      mixedUnion: z.union([
        z.literal("abc"),
        z.literal(123),
        z.object({ nowItGetsAnnoying: z.literal(true) }),
      ]),
      objectOrNull: z.object({ myString: z.string() }).nullable(),
      passthrough: z.object({ myString: z.string() }).passthrough(),
    });
    const expectedJsonSchema: JSONSchema7Type = {
      type: "object",
      properties: {
        requiredString: {
          type: "string",
        },
        optionalString: {
          type: "string",
        },
        literalString: {
          type: "string",
          const: "literalStringValue",
        },
        stringArray: {
          type: "array",
          items: {
            type: "string",
          },
        },
        stringEnum: {
          type: "string",
          enum: ["stringEnumOptionA", "stringEnumOptionB"],
        },
        tuple: {
          type: "array",
          minItems: 3,
          items: [
            {
              type: "string",
            },
            {
              type: "number",
            },
            {
              type: "boolean",
            },
          ],
          maxItems: 3,
        },
        record: {
          type: "object",
          additionalProperties: {
            type: "boolean",
          },
        },
        requiredNumber: {
          type: "number",
        },
        optionalNumber: {
          type: "number",
        },
        numberOrNull: {
          type: ["number", "null"],
        },
        numberUnion: {
          type: "number",
          enum: [1, 2, 3],
        },
        mixedUnion: {
          anyOf: [
            {
              type: "string",
              const: "abc",
            },
            {
              type: "number",
              const: 123,
            },
            {
              type: "object",
              properties: {
                nowItGetsAnnoying: {
                  type: "boolean",
                  const: true,
                },
              },
              required: ["nowItGetsAnnoying"],
              additionalProperties: false,
            },
          ],
        },
        objectOrNull: {
          anyOf: [
            {
              type: "object",
              properties: {
                myString: {
                  type: "string",
                },
              },
              required: ["myString"],
              additionalProperties: false,
            },
            {
              type: "null",
            },
          ],
        },
        passthrough: {
          type: "object",
          properties: {
            myString: {
              type: "string",
            },
          },
          required: ["myString"],
          additionalProperties: true,
        },
      },
      required: [
        "requiredString",
        "literalString",
        "stringArray",
        "stringEnum",
        "tuple",
        "record",
        "requiredNumber",
        "numberOrNull",
        "numberUnion",
        "mixedUnion",
        "objectOrNull",
        "passthrough",
      ],
      additionalProperties: false,
    };
    const parsedSchema = parseDef(zodSchema._def, new References());
    expect(parsedSchema).toStrictEqual(expectedJsonSchema);
    expect(ajv.validateSchema(parsedSchema!)).toEqual(true);
  });
});
Example #15
Source File: optional.test.ts    From zod-to-json-schema with ISC License 4 votes vote down vote up
describe.only("Standalone optionals", () => {
  it("should work as unions with undefined", () => {
    const parsedSchema = parseDef(z.string().optional()._def, new References());

    const jsonSchema: JSONSchema7Type = {
      anyOf: [
        {
          not: {},
        },
        {
          type: "string",
        },
      ],
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should not affect object properties", () => {
    const parsedSchema = parseDef(
      z.object({ myProperty: z.string().optional() })._def,
      new References()
    );

    const jsonSchema: JSONSchema7Type = {
      type: "object",
      properties: {
        myProperty: {
          type: "string",
        },
      },
      additionalProperties: false,
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should work with nested properties", () => {
    const parsedSchema = parseDef(
      z.object({ myProperty: z.string().optional().array() })._def,
      new References()
    );

    const jsonSchema: JSONSchema7Type = {
      type: "object",
      properties: {
        myProperty: {
          type: "array",
          items: {
            anyOf: [{ not: {} }, { type: "string" }],
          },
        },
      },
      required: ["myProperty"],
      additionalProperties: false,
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should work with nested properties as object properties", () => {
    const parsedSchema = parseDef(
      z.object({
        myProperty: z.object({ myInnerProperty: z.string().optional() }),
      })._def,
      new References()
    );

    const jsonSchema: JSONSchema7Type = {
      type: "object",
      properties: {
        myProperty: {
          type: "object",
          properties: {
            myInnerProperty: {
              type: "string",
            },
          },
          additionalProperties: false,
        },
      },
      required: ["myProperty"],
      additionalProperties: false,
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should work with nested properties with nested object property parents", () => {
    const parsedSchema = parseDef(
      z.object({
        myProperty: z.object({
          myInnerProperty: z.string().optional().array(),
        }),
      })._def,
      new References()
    );

    const jsonSchema: JSONSchema7Type = {
      type: "object",
      properties: {
        myProperty: {
          type: "object",
          properties: {
            myInnerProperty: {
              type: "array",
              items: {
                anyOf: [
                  { not: {} },
                  {
                    type: "string",
                  },
                ],
              },
            },
          },
          required: ["myInnerProperty"],
          additionalProperties: false,
        },
      },
      required: ["myProperty"],
      additionalProperties: false,
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should work with ref pathing", () => {
    const recurring = z.string();

    const schema = z.tuple([recurring.optional(), recurring]);

    const parsedSchema = parseDef(schema._def, new References());

    const jsonSchema: JSONSchema7Type = {
      type: "array",
      minItems: 2,
      maxItems: 2,
      items: [
        { anyOf: [{ not: {} }, { type: "string" }] },
        { $ref: "#/items/0/anyOf/1" },
      ],
    };

    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #16
Source File: string.test.ts    From zod-to-json-schema with ISC License 4 votes vote down vote up
describe("String validations", () => {
  it("should be possible to describe minimum length of a string", () => {
    const parsedSchema = parseStringDef(z.string().min(5)._def);
    const jsonSchema: JSONSchema7Type = {
      type: "string",
      minLength: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);

    ajv.validate(parsedSchema, "1234");
    expect(ajv.errors).toStrictEqual([
      {
        keyword: "minLength",
        instancePath: "",
        schemaPath: "#/minLength",
        params: { limit: 5 },
        message: "must NOT have fewer than 5 characters",
      },
    ]);
  });
  it("should be possible to describe maximum length of a string", () => {
    const parsedSchema = parseStringDef(z.string().max(5)._def);
    const jsonSchema: JSONSchema7Type = {
      type: "string",
      maxLength: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "123456");
    expect(ajv.errors).toStrictEqual([
      {
        keyword: "maxLength",
        instancePath: "",
        schemaPath: "#/maxLength",
        params: { limit: 5 },
        message: "must NOT have more than 5 characters",
      },
    ]);
  });
  it("should be possible to describe both minimum and maximum length of a string", () => {
    const parsedSchema = parseStringDef(z.string().min(5).max(5)._def);
    const jsonSchema: JSONSchema7Type = {
      type: "string",
      minLength: 5,
      maxLength: 5,
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
  it("should be possible to use email constraint", () => {
    const parsedSchema = parseStringDef(z.string().email()._def);
    const jsonSchema: JsonSchema7Type = {
      type: "string",
      format: "email",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "herpderp");
    expect(ajv.errors).toStrictEqual([
      {
        instancePath: "",
        schemaPath: "#/format",
        keyword: "format",
        params: { format: "email" },
        message: 'must match format "email"',
      },
    ]);
    expect(ajv.validate(parsedSchema, "[email protected]")).toEqual(true);
  });
  it("should be possible to use uuid constraint", () => {
    const parsedSchema = parseStringDef(z.string().uuid()._def);
    const jsonSchema: JsonSchema7Type = {
      type: "string",
      format: "uuid",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "herpderp");
    expect(ajv.errors).toStrictEqual([
      {
        instancePath: "",
        schemaPath: "#/format",
        keyword: "format",
        params: { format: "uuid" },
        message: 'must match format "uuid"',
      },
    ]);
    expect(
      ajv.validate(parsedSchema, "2ad7b2ce-e571-44b8-bee3-84fb3ac80d6b")
    ).toEqual(true);
  });
  it("should be possible to use url constraint", () => {
    const parsedSchema = parseStringDef(z.string().url()._def);
    const jsonSchema: JsonSchema7Type = {
      type: "string",
      format: "uri",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "herpderp");
    expect(ajv.errors).toStrictEqual([
      {
        instancePath: "",
        schemaPath: "#/format",
        keyword: "format",
        params: { format: "uri" },
        message: 'must match format "uri"',
      },
    ]);
    expect(ajv.validate(parsedSchema, "http://hello.com")).toEqual(true);
  });

  it("should be possible to use regex constraint", () => {
    const parsedSchema = parseStringDef(z.string().regex(/[A-C]/)._def);
    const jsonSchema: JsonSchema7Type = {
      type: "string",
      pattern: "[A-C]",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "herpderp");
    expect(ajv.errors).toStrictEqual([
      {
        instancePath: "",
        schemaPath: "#/pattern",
        keyword: "pattern",
        params: { pattern: "[A-C]" },
        message: 'must match pattern "[A-C]"',
      },
    ]);
    expect(ajv.validate(parsedSchema, "B")).toEqual(true);
  });

  it("should be possible to use CUID constraint", () => {
    const parsedSchema = parseStringDef(z.string().cuid()._def);
    const jsonSchema: JsonSchema7Type = {
      type: "string",
      pattern: "^c[^\\s-]{8,}$",
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
    ajv.validate(parsedSchema, "herpderp");
    expect(ajv.errors).toStrictEqual([
      {
        instancePath: "",
        schemaPath: "#/pattern",
        keyword: "pattern",
        params: { pattern: "^c[^\\s-]{8,}$" },
        message: 'must match pattern "^c[^\\s-]{8,}$"',
      },
    ]);
    expect(ajv.validate(parsedSchema, "ckopqwooh000001la8mbi2im9")).toEqual(
      true
    );
  });

  it('should gracefully ignore the .trim() "check"', () => {
    const parsedSchema = parseStringDef(z.string().trim()._def);
    const jsonSchema = { type: "string" };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });
});
Example #17
Source File: union.test.ts    From zod-to-json-schema with ISC License 4 votes vote down vote up
describe("Unions", () => {
  it("Should be possible to get a simple type array from a union of only unvalidated primitives", () => {
    const parsedSchema = parseUnionDef(
      z.union([z.string(), z.number(), z.boolean(), z.null()])._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: ["string", "number", "boolean", "null"],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("Should be possible to get a simple type array with enum values from a union of literals", () => {
    const parsedSchema = parseUnionDef(
      z.union([
        z.literal("string"),
        z.literal(123),
        z.literal(true),
        z.literal(null),
      ])._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      type: ["string", "number", "boolean", "null"],
      enum: ["string", 123, true, null],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("Should be possible to create a union with objects, arrays and validated primitives as an anyOf", () => {
    const parsedSchema = parseUnionDef(
      z.union([
        z.object({ herp: z.string(), derp: z.boolean() }),
        z.array(z.number()),
        z.string().min(3),
        z.number(),
      ])._def,
      new References()
    );
    const jsonSchema: JSONSchema7Type = {
      anyOf: [
        {
          type: "object",
          properties: {
            herp: {
              type: "string",
            },
            derp: {
              type: "boolean",
            },
          },
          required: ["herp", "derp"],
          additionalProperties: false,
        },
        {
          type: "array",
          items: {
            type: "number",
          },
        },
        {
          type: "string",
          minLength: 3,
        },
        {
          type: "number",
        },
      ],
    };
    expect(parsedSchema).toStrictEqual(jsonSchema);
  });

  it("should be possible to deref union schemas", () => {
    const recurring = z.object({ foo: z.boolean() });

    const union = z.union([recurring, recurring, recurring]);

    const jsonSchema = parseUnionDef(union._def, new References());

    expect(jsonSchema).toStrictEqual({
      anyOf: [
        {
          type: "object",
          properties: {
            foo: {
              type: "boolean",
            },
          },
          required: ["foo"],
          additionalProperties: false,
        },
        {
          $ref: "#/anyOf/0",
        },
        {
          $ref: "#/anyOf/0",
        },
      ],
    });

    const resolvedSchema = deref(jsonSchema);
    expect(resolvedSchema.anyOf[0]).toBe(resolvedSchema.anyOf[1]);
    expect(resolvedSchema.anyOf[1]).toBe(resolvedSchema.anyOf[2]);
  });

  it("nullable primitives should come out fine", () => {
    const union = z.union([z.string(), z.null()]);

    const jsonSchema = parseUnionDef(union._def, new References());

    expect(jsonSchema).toStrictEqual({
      type: ["string", "null"],
    });
  });

  it("should join a union of Zod enums into a single enum", () => {
    const union = z.union([z.enum(["a", "b", "c"]), z.enum(["c", "d", "e"])]);

    const jsonSchema = parseUnionDef(union._def, new References());

    expect(jsonSchema).toStrictEqual({
      type: "string",
      enum: ["a", "b", "c", "d", "e"],
    });
  });

  it("should work with discriminated union type", () => {
    const discUnion = z.discriminatedUnion("kek", [
      z.object({ kek: z.literal("A"), lel: z.boolean() }),
      z.object({ kek: z.literal("B"), lel: z.number() }),
    ]);

    const jsonSchema = parseUnionDef(discUnion._def, new References());

    expect(jsonSchema).toStrictEqual({
      anyOf: [
        {
          type: "object",
          properties: {
            kek: {
              type: "string",
              const: "A",
            },
            lel: {
              type: "boolean",
            },
          },
          required: ["kek", "lel"],
          additionalProperties: false
        },
        {
          type: "object",
          properties: {
            kek: {
              type: "string",
              const: "B",
            },
            lel: {
              type: "number",
            },
          },
          required: ["kek", "lel"],
          additionalProperties: false
        },
      ],
    });
  });
});