UNPKG

json-schema-library

Version:

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

267 lines (235 loc) 8.91 kB
import { compileSchema } from "./compileSchema"; import { strict as assert } from "assert"; import { draftEditor } from "./draftEditor"; import { SchemaNode } from "./SchemaNode"; import { draft04 } from "./draft04"; import { draft07 } from "./draft07"; import { draft2020 } from "./draft2020"; describe("compileSchema vocabulary", () => { let root: SchemaNode; beforeEach( () => (root = compileSchema({ type: "object", additionalProperties: false, properties: { image: { type: "object", properties: { title: { name: "title", type: "string" } } } } })) ); it("should return rootNode from rootNode", () => { const node = root.getNodeRoot(); assert(node === root); }); it("should return rootNode from childNode", () => { const { node } = root.getNode("/image/title"); assert(node); assert(node.getNodeRoot() === root); }); }); describe("compileSchema draft-version", () => { it("should select last draft", () => { const node = compileSchema({}, { drafts: [draft04, draft07, draft2020] }); assert(node.getDraftVersion(), "draft-2020"); }); it("should select draft specified by $schema", () => { const node = compileSchema( { $schema: "http://json-schema.org/draft-07/schema#" }, { drafts: [draft04, draft07, draft2020] } ); assert(node.getDraftVersion(), "draft-07"); }); it("should select fallback draft specified by `draft`", () => { const node = compileSchema( {}, { draft: "http://json-schema.org/draft-07/schema#", drafts: [draft04, draft07, draft2020] } ); assert(node.getDraftVersion(), "draft-07"); }); it("should prefer `$schema` over `draft` options", () => { const node = compileSchema( { $schema: "http://json-schema.org/draft-04/schema#" }, { draft: "http://json-schema.org/draft-07/schema#", drafts: [draft04, draft07, draft2020] } ); assert(node.getDraftVersion(), "draft-04"); }); it("should always fallback to last draft if nothing matches", () => { const node = compileSchema( { $schema: "http://json-schema.org/draft-A/schema#" }, { draft: "http://json-schema.org/draft-B/schema#", drafts: [draft04, draft07, draft2020] } ); assert(node.getDraftVersion(), "draft-2020"); }); }); describe("compileSchema remotes", () => { it("should add list of remotes to returned SchemaNode node", () => { const node = compileSchema( { type: "object", required: ["string", "number"], properties: { string: { $ref: "https://remote-a.com/schema" }, number: { $ref: "https://remote-b.com/schema" } } }, { drafts: [draft2020], remotes: [ { $id: "https://remote-a.com/schema", type: "string", default: "a" }, { $id: "https://remote-b.com/schema", type: "number", default: 9 } ] } ); const data = node.getData(); assert.deepEqual(data, { string: "a", number: 9 }); }); }); describe("compileSchema vocabulary", () => { it("should add remote schema on compile", () => { const remote = compileSchema({ $id: "https://remote/schema", minLength: 10 }); const node = compileSchema({ $ref: "https://remote/schema" }, { remote, formatAssertion: "meta-schema" }); const { errors } = node.validate("123"); assert.deepEqual(errors.length, 1); assert.deepEqual(errors[0].code, "min-length-error"); }); it("should NOT validate formats when $vocabulary.format-assertion = false", () => { const remote = compileSchema({ $id: "https://remote/schema", $vocabulary: { "https://json-schema.org/draft/2020-12/vocab/format-assertion": false }, allOf: [{ $ref: "https://json-schema.org/draft/2020-12/vocab/format-assertion" }] }); const node = compileSchema( { $schema: "https://remote/schema", format: "date-time" }, { remote, formatAssertion: "meta-schema" } ); const { errors } = node.validate("123"); assert.deepEqual(errors.length, 0); }); it("should validate formats when $vocabulary.format-assertion = true", () => { const remote = compileSchema({ $id: "https://remote/schema", $vocabulary: { "https://json-schema.org/draft/2020-12/vocab/format-assertion": true }, allOf: [{ $ref: "https://json-schema.org/draft/2020-12/vocab/format-assertion" }] }); const node = compileSchema( { $schema: "https://remote/schema", format: "date-time" }, { remote, formatAssertion: "meta-schema" } ); const { errors } = node.validate("123"); assert.deepEqual(errors.length, 1); }); }); describe("compileSchema getDataDefaultOptions", () => { it("should apply `getDataDefaultOptions.addOptionalProps` to getData", () => { const schema = { properties: { type: { const: "node" }, nodes: { items: { $ref: "#" } } } }; let data = compileSchema(schema).getData(); assert.deepEqual(data, {}); data = compileSchema(schema, { getDataDefaultOptions: { addOptionalProps: true } }).getData(); assert.deepEqual(data, { type: "node", nodes: [] }); }); it("should apply `getDataDefaultOptions.recursiveLimit` to getData", () => { const schema = { required: ["type", "nodes"], properties: { type: { const: "node" }, nodes: { items: { $ref: "#" }, minItems: 1 } } }; let data = compileSchema(schema).getData(); assert.deepEqual(data, { type: "node", nodes: [ { type: "node", nodes: [] } ] }); data = compileSchema(schema, { getDataDefaultOptions: { recursionLimit: 2 } }).getData(); assert.deepEqual(data, { type: "node", nodes: [ { type: "node", nodes: [ { type: "node", nodes: [] } ] } ] }); }); }); describe("compileSchema `schemaLocation`", () => { it("should store path from rootSchema as schemaLocation", () => { const node = compileSchema({ if: { type: "string" }, then: { type: "string" }, properties: { title: { type: "string" } }, $defs: { asset: { type: "string" } } }); assert(node.if && node.then && node.properties && node.$defs); assert.deepEqual(node.schemaLocation, "#"); assert.deepEqual(node.if.schemaLocation, "#/if"); assert.deepEqual(node.then.schemaLocation, "#/then"); assert.deepEqual(node.properties.title.schemaLocation, "#/properties/title"); assert.deepEqual(node.$defs.asset.schemaLocation, "#/$defs/asset"); }); it("should maintain schemaLocation when resolved by ref", () => { const { node } = compileSchema({ properties: { title: { $ref: "#/$defs/asset" } }, $defs: { asset: { type: "string" } } }).getNodeChild("title"); assert.deepEqual(node?.schemaLocation, "#/$defs/asset"); }); it("should maintain schemaLocation when resolved by root-ref", () => { const { node } = compileSchema({ properties: { title: { $ref: "#" } } }).getNodeChild("title"); assert.deepEqual(node?.schemaLocation, "#"); }); }); describe("compileSchema `errors`", () => { it("draftEditor come with custom minLengthOneError", () => { const { errors } = compileSchema( { type: "string", minLength: 1 }, { drafts: [draftEditor] } ).validate(""); assert.equal(errors.length, 1); assert.deepEqual(errors[0].code, "min-length-one-error"); }); });