UNPKG

json-schema-library

Version:

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

1,217 lines (1,096 loc) 65.7 kB
import { compileSchema } from "../compileSchema"; import { strict as assert } from "assert"; describe("getData", () => { it("should not modify input schema", () => { const schema = { type: "object", properties: { title: { type: "string", default: "title" }, list: { type: "array", items: { allOf: [{ type: "object" }, { properties: { index: { type: "number", default: 4 } } }] } }, author: { anyOf: [ { type: "string", default: "jane" }, { type: "string", default: "john" } ] }, source: { type: "string", enum: ["dpa", "getty"] } } }; const originalSchema = JSON.stringify(schema); const node = compileSchema(schema); node.getData({}); assert.deepEqual(JSON.stringify(schema), originalSchema); }); describe("values", () => { it("should return default value missing input and type", () => { const data = compileSchema({ default: 123 }).getData(); assert.deepEqual(data, 123); }); it("should NOT override input value for missing type", () => { const data = compileSchema({ default: 123 }).getData("input"); assert.deepEqual(data, "input"); }); // @attention, changing input data it("should alwayys return const value", () => { const data = compileSchema({ const: "const", default: 123 }).getData(123); assert.deepEqual(data, "const"); }); it("should prefer const over default", () => { const data = compileSchema({ type: "string", const: "static", default: "should be overwritten" }).getData(); assert.deepEqual(data, "static"); }); describe("string", () => { it("should return empty string for missing default value", () => { const data = compileSchema({ type: "string" }).getData(); assert.deepEqual(data, ""); }); it("should return default value", () => { const data = compileSchema({ type: "string", default: "default" }).getData(); assert.deepEqual(data, "default"); }); it("should return string data", () => { const data = compileSchema({ type: "string", default: "default" }).getData("input"); assert.deepEqual(data, "input"); }); }); describe("number", () => { it("should return 0 for missing default value", () => { const data = compileSchema({ type: "number" }).getData(); assert.deepEqual(data, 0); }); it("should return default value", () => { const data = compileSchema({ type: "number", default: 99 }).getData(); assert.deepEqual(data, 99); }); it("should return number data", () => { const data = compileSchema({ type: "number", default: 99 }).getData(123); assert.deepEqual(data, 123); }); }); describe("integer", () => {}); describe("boolean", () => { it("should return `false` for missing default value", () => { const data = compileSchema({ type: "boolean", default: false }).getData(); assert.deepEqual(data, false); }); it("should return default value of boolean", () => { const data = compileSchema({ type: "boolean", default: false }).getData(); assert.deepEqual(data, false); }); it("should not override given boolean if it is `false`", () => { const data = compileSchema({ type: "boolean", default: true }).getData(false); assert.deepEqual(data, false); }); it("should not override given boolean if it is `true`", () => { const data = compileSchema({ type: "boolean", default: false }).getData(true); assert.deepEqual(data, true); }); }); describe("null", () => { it("should return `null` for missing default value", () => { const data = compileSchema({ type: "null" }).getData(); assert.deepEqual(data, null); }); it("should return `null` when first type in type-array", () => { const node = compileSchema({ type: ["null", "string"] }); const res = node.getData(); assert.deepEqual(res, null); }); it("should return default value of null", () => { const data = compileSchema({ type: "null", default: null }).getData(); assert.deepEqual(data, null); }); it("should return default value of null even for wrong typye", () => { const data = compileSchema({ type: "number", default: null }).getData(); assert.deepEqual(data, null); }); it("should support `null` type properties", () => { const data = compileSchema({ type: "object", required: ["nullType"], properties: { nullType: { type: "null" } } }).getData(); assert.deepEqual(data, { nullType: null }); }); it("should return `null` input for strings", () => { const data = compileSchema({ type: "string" }).getData(null); assert.deepEqual(data, null); }); it("should return `null` input for value-property", () => { const data = compileSchema({ type: "object", required: ["title"], properties: { title: { type: "number" } } }).getData({ title: null }); assert.deepEqual(data, { title: null }); }); }); describe("enum", () => { it("should set the first enum option for a missing default", () => { const data = compileSchema({ enum: ["first", "second"] }).getData(); assert.deepEqual(data, "first"); }); it("should use default value in any case", () => { const data = compileSchema({ enum: ["first", "second"], default: "" }).getData(); assert.deepEqual(data, ""); }); }); describe("file", () => { it("should not modify file-instance", () => { const file = new File([], "testfile.pdf"); const data = compileSchema({ type: ["string", "object"], format: "file" }).getData(file); assert.deepEqual(data, file); }); it("should not modify file-instance on object", () => { const file = new File([], "testfile.pdf"); const data = compileSchema({ type: "object", properties: { file: { type: ["string", "object"], format: "file" } } }).getData({ file }); assert.deepEqual(data, { file }); }); }); describe("oneOf", () => { it("should return first schema for mixed types", () => { const node = compileSchema({ oneOf: [{ type: "string", default: "jane" }, { type: "number" }] }); const res = node.getData(); assert.deepEqual(res, "jane"); }); }); }); describe("object", () => { describe("behaviour", () => { it("should return {} for a missing default value", () => { const data = compileSchema({ type: "object" }).getData(); assert.deepEqual(data, {}); }); it("should return default value of object", () => { const data = compileSchema({ type: "object", default: { init: true } }).getData(); assert.deepEqual(data, { init: true }); }); it("should return input data", () => { const data = compileSchema({ type: "object" }).getData({ init: false }); assert.deepEqual(data, { init: false }); }); it("should override default by input data", () => { const data = compileSchema({ type: "object", default: { init: true } }).getData({ init: false }); assert.deepEqual(data, { init: false }); }); }); describe("properties", () => { it("should return default object", () => { const data = compileSchema({ type: "object", properties: { first: { type: "string" }, second: { type: "number" } }, default: { first: "john", second: 4 } }).getData(); assert.deepEqual(data, { first: "john", second: 4 }); }); it("should return only required object properties", () => { const data = compileSchema({ type: "object", required: ["first"], properties: { first: { type: "string" }, second: { type: "number" } } }).getData(); assert.deepEqual(data, { first: "" }); }); it("should not fail on falsy input data", () => { const data = compileSchema({ type: "object", properties: { first: { type: "boolean", default: true }, second: { type: "boolean", default: false } } }).getData({ first: false, second: true }); assert.deepEqual(data, { first: false, second: true }); }); it("should return all object properties with `addOptionalProps=true`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" }, second: { type: "number" } } }).getData({}, { addOptionalProps: true }); assert.deepEqual(data, { first: "", second: 0 }); }); it("should NOT override given default values by other default values", () => { const data = compileSchema({ type: "object", properties: { first: { type: "string", default: "jane" }, second: { type: "number" } }, default: { first: "john", second: 4 } }).getData(); assert.deepEqual(data, { first: "john", second: 4 }); }); it("should extend given template data by property default values", () => { const data = compileSchema({ type: "object", properties: { first: { type: "string", default: "jane" }, second: { type: "number" } }, default: { first: "john", second: 4 } }).getData({ second: 8 }); assert.deepEqual(data, { first: "john", second: 8 }); }); }); describe("additionalProperties & option: removeInvalidData", () => { it("should NOT remove additional properties `additionalProperties=undefined`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } } }).getData({ first: "first", second: 42 }); assert.deepEqual(data, { first: "first", second: 42 }); }); it("should NOT remove additional properties `additionalProperties=true`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } }, additionalProperties: true }).getData({ first: "first", second: 42 }); assert.deepEqual(data, { first: "first", second: 42 }); }); it("should NOT remove non matching properties with `additionalProperties={schema}`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } }, additionalProperties: { type: "string" } }).getData({ first: "first", second: 42 }); assert.deepEqual(data, { first: "first", second: 42 }); }); it("should NOT remove additional properties with `additionalProperties=false`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } }, additionalProperties: false }).getData({ first: "first", second: 42 }); assert.deepEqual(data, { first: "first", second: 42 }); }); it("should remove unmatched properties with option `removeInvalidData=true`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } }, additionalProperties: false }).getData({ first: "first", second: 42, thrid: "third" }, { removeInvalidData: true }); assert.deepEqual(data, { first: "first" }); }); it("should remove invalid properties with option `removeInvalidData=true`", () => { const data = compileSchema({ type: "object", required: ["first", "second"], properties: { first: { type: "string" } }, additionalProperties: { type: "number" } }).getData({ first: "first", second: 42, third: "third", fourth: false }, { removeInvalidData: true }); assert.deepEqual(data, { first: "first", second: 42 }); }); }); // patternNames // patternProperties // dependentSchemas // dependentRequired // (unevaluatedPoperties) describe("allOf", () => { it("should create template for merged allOf schema", () => { const data = compileSchema({ type: "object", allOf: [ { required: ["name"], properties: { name: { type: "string", default: "jane" } } }, { required: ["stage"], properties: { stage: { type: "string", default: "test" } } } ] }).getData({ name: "john" }); assert.deepEqual(data, { name: "john", stage: "test" }); }); }); describe("anyOf", () => { it("should create template for first anyOf schema", () => { const node = compileSchema({ type: "object", anyOf: [ { required: ["name", "stage"], properties: { name: { type: "string", default: "jane" }, stage: { type: "string", default: "develop" } } }, { required: ["stage"], properties: { stage: { type: "number", default: 0 } } } ] }); const res = node.getData({ name: "john" }); assert.deepEqual(res, { name: "john", stage: "develop" }); }); }); describe("oneOf", () => { describe("oneOf", () => { it("should return template of first oneOf schema", () => { const node = compileSchema({ type: "object", oneOf: [ { type: "object", required: ["title"], properties: { title: { type: "string", default: "jane" } } }, { type: "object", required: ["value"], properties: { value: { type: "number" } } } ] }); const res = node.getData(); assert.deepEqual(res, { title: "jane" }); }); it("should extend empty object with first oneOf schema", () => { const node = compileSchema({ type: "object", oneOf: [ { type: "object", required: ["title"], properties: { title: { type: "string", default: "jane" } } }, { type: "object", required: ["value"], properties: { value: { type: "number" } } } ] }); const res = node.getData({}); assert.deepEqual(res, { title: "jane" }); }); it("should return template of matching oneOf schema", () => { const node = compileSchema({ type: "object", oneOf: [ { type: "object", required: ["value"], properties: { value: { type: "string", default: "jane" } } }, { type: "object", required: ["value", "test"], properties: { value: { type: "number" }, test: { type: "string", default: "test" } } } ] }); const res = node.getData({ value: 111 }); assert.deepEqual(res, { value: 111, test: "test" }); }); it("should return input value if no oneOf-schema matches ", () => { const node = compileSchema({ type: "object", oneOf: [ { type: "object", properties: { value: { type: "string", default: "jane" } } }, { type: "object", properties: { value: { type: "number" }, test: { type: "string", default: "test" } } } ] }); const res = node.getData({ value: ["keep-me"] }); assert.deepEqual(res, { value: ["keep-me"] }); }); it("should not require object type definition in oneOf schemas", () => { const node = compileSchema({ type: "object", oneOf: [ { required: ["type"], properties: { type: { const: "header" } } }, { required: ["type"], properties: { type: { const: "paragraph" } } } ] }); const res = node.getData({ type: "paragraph" }); assert.deepEqual(res, { type: "paragraph" }); }); it("should return valid default data", () => { const node = compileSchema({ type: "object", default: { value: 123 }, oneOf: [ { type: "object", required: ["title"], properties: { title: { type: "string", default: "jane" } } }, { type: "object", required: ["value"], properties: { value: { type: "number" } } } ] }); const res = node.getData(); assert.deepEqual(res, { value: 123 }); }); it("should return invalid default data", () => { const node = compileSchema({ type: "object", default: { value: "wrong type" }, oneOf: [ { type: "object", required: ["title"], properties: { title: { type: "string", default: "jane" } } }, { type: "object", required: ["value"], properties: { value: { type: "number" } } } ] }); const res = node.getData(); assert.deepEqual(res, { value: "wrong type" }); }); it("should add correct optional properties from schema matching default data", () => { const node = compileSchema({ type: "object", default: { value: 123 }, oneOf: [ { type: "object", required: ["title"], properties: { title: { type: "string", default: "jane" } } }, { type: "object", required: ["value"], properties: { value: { type: "number" }, optional: { type: "string" } } } ] }); const res = node.getData(undefined, { addOptionalProps: true }); assert.deepEqual(res, { value: 123, optional: "" }); }); }); }); }); describe("array", () => { describe("behaviour", () => { it("should return [] for a missing default value", () => { const data = compileSchema({ type: "array" }).getData(); assert.deepEqual(data, []); }); it("should return default value of object", () => { const data = compileSchema({ type: "array", default: [true] }).getData(); assert.deepEqual(data, [true]); }); it("should return input data", () => { const data = compileSchema({ type: "array" }).getData(["input"]); assert.deepEqual(data, ["input"]); }); it("should override default by input data", () => { const data = compileSchema({ type: "array", default: ["default"] }).getData(["input"]); assert.deepEqual(data, ["input"]); }); }); describe("items: {}", () => { it("should return empty array if minItems is undefined", () => { const data = compileSchema({ items: { type: "boolean" } }).getData(); assert.deepEqual(data, []); }); it("should return array with length of minItems", () => { const data = compileSchema({ minItems: 3, items: { type: "boolean" } }).getData(); assert(Array.isArray(data)); assert.deepEqual(data.length, 3); assert.deepEqual(data, [false, false, false]); }); it("should return default array even if minItems is not set", () => { const data = compileSchema({ default: ["a", "b"], items: { type: "string" } }).getData(); assert.deepEqual(data, ["a", "b"]); }); it("should return default array if part of object", () => { const data = compileSchema({ required: ["list"], properties: { list: { type: "array", default: ["a", "b"], items: { type: "string" } } } }).getData(); assert.deepEqual(data, { list: ["a", "b"] }); }); it("should not override given default values", () => { const data = compileSchema({ minItems: 2, default: ["abba", "doors"], items: { type: "string", default: "elvis" } }).getData(); assert.deepEqual(data, ["abba", "doors"]); }); it("should extend given template data by default values", () => { const data = compileSchema({ minItems: 2, default: ["abba", "doors"], items: { type: "string" } }).getData(["elvis"]); assert.deepEqual(data, ["elvis", "doors"]); }); it("should extend all input objects by missing properties", () => { const data = compileSchema({ default: ["abba", "doors"], items: { type: "object", required: ["first", "second"], properties: { first: { type: "string", default: "first" }, second: { type: "string", default: "second" } } } }).getData([{ first: "user input" }, {}]); assert.deepEqual(data, [ { first: "user input", second: "second" }, { first: "first", second: "second" } ]); }); }); describe("prefixItems: []", () => { // - Tuple validation is useful when the array is a collection of items where each has a different schema // and the ordinal index of each item is meaningful. // - It’s ok to not provide all of the items: // https://spacetelescope.github.io/understanding-json-schema/reference/array.html#tuple-validation it("should return array with minItems in given order", () => { const data = compileSchema({ type: "array", minItems: 2, prefixItems: [{ type: "string" }, { type: "boolean" }] }).getData(); assert.deepEqual(data, ["", false]); }); it("should not override input items when complementing minItems", () => { const data = compileSchema({ type: "array", minItems: 2, prefixItems: [{ type: "boolean", default: false }, { type: "string" }] }).getData([true]); assert.deepEqual(data, [true, ""]); }); it("should not override wrong input items", () => { const data = compileSchema({ type: "array", prefixItems: [ { type: "boolean", default: false }, { type: "string", default: "default" } ] }).getData([42, false]); assert.deepEqual(data, [42, "false"]); }); it("should return default array", () => { const data = compileSchema({ type: "array", minItems: 1, default: [true], items: { type: "boolean" } }).getData(); assert.deepEqual(data, [true]); }); it("should convert input data for strings", () => { const node = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "string" }] }); const res = node.getData([43]); assert.deepEqual(res, ["43"]); }); it("should convert input data for numbers", () => { const data = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "number" }] }).getData(["43"]); assert.deepEqual(data, [43]); }); it("should convert input data for strings", () => { const data = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "string" }] }).getData([43]); assert.deepEqual(data, ["43"]); }); it("should NOT convert invalid number if we would lose data", () => { const data = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "number" }] }).getData(["asd"]); assert.deepEqual(data, ["asd"]); }); it("should convert input data for booleans", () => { const data = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "boolean" }] }).getData(["false"]); assert.deepEqual(data, [false]); }); it("should NOT convert invalid boolean if we would lose data", () => { const data = compileSchema({ type: "array", minItems: 1, prefixItems: [{ type: "boolean" }] }).getData(["43"]); assert.deepEqual(data, ["43"]); }); it("should add defaults from `items`", () => { const data = compileSchema({ type: "array", minItems: 2, items: { type: "number", default: 2 } }).getData([43]); assert.deepEqual(data, [43, 2]); }); it("should add defaults from `items` for items not in prefixItems", () => { const data = compileSchema({ type: "array", minItems: 2, prefixItems: [{ type: "boolean" }], items: { type: "number", default: 2 } }).getData([43]); assert.deepEqual(data, [43, 2]); }); }); describe("oneOf", () => { it("should return template of first oneOf schema", () => { const node = compileSchema({ type: "array", minItems: 1, items: { oneOf: [ { type: "string", default: "target" }, { type: "number", default: 9 } ] } }); const res = node.getData(); assert(Array.isArray(res)); assert.deepEqual(res.length, 1); assert.deepEqual(res, ["target"]); }); it("should merge with input data", () => { const node = compileSchema({ type: "array", minItems: 1, items: { oneOf: [ { type: "object", required: ["notitle"], properties: { notitle: { type: "string", default: "nottitle" } } }, { type: "object", required: ["title", "subtitle"], properties: { title: { type: "string", default: "Standardtitel" }, subtitle: { type: "string", default: "do not replace with" } } }, { type: "number", default: 9 } ] } }); const res = node.getData([{ subtitle: "Subtitel" }]); assert(Array.isArray(res)); assert.deepEqual(res.length, 1); assert.deepEqual(res, [{ title: "Standardtitel", subtitle: "Subtitel" }]); }); it("should not remove invalid oneOf schema if 'removeInvalidData' is unset", () => { const node = compileSchema({ type: "object", properties: { filter: { $ref: "#/definitions/filter" } }, definitions: { filter: { type: "array", items: { oneOf: [ { type: "object", properties: { op: { const: "in" }, property: { type: "string", minLength: 1 } } } ] } } } }); const res = node.getData({ filter: [{ op: "möp" }] }); assert.deepEqual(res, { filter: [{ op: "möp" }] }); }); }); describe("allOf", () => { it("should create template for merged allOf schema", () => { const node = compileSchema({ type: "array", minItems: 2, items: { type: "object", allOf: [ { required: ["title"], properties: { title: { type: "string", default: "title" } } }, { required: ["caption"], properties: { caption: { type: "string", default: "caption" } } } ] } }); const res = node.getData([{ title: "given-title" }]); assert.deepEqual(res, [ { title: "given-title", caption: "caption" }, { title: "title", caption: "caption" } ]); }); }); describe("anyOf", () => { it("should create template for first anyOf schema", () => { const node = compileSchema({ type: "array", minItems: 2, items: { type: "object", anyOf: [ { required: ["title"], properties: { title: { type: "string", default: "title" } } }, { required: ["properties"], properties: { caption: { type: "string", default: "caption" } } } ] } }); const res = node.getData([{ title: "given-title" }]); assert.deepEqual(res, [{ title: "given-title" }, { title: "title" }]); }); }); // draft07 (backwards compatible) describe("dependencies", () => { describe("option: `additionalProps: false`", () => { const TEMPLATE_OPTIONS = { addOptionalProps: false }; describe("dependency required", () => { it("should not add dependency if it is not required", () => { const node = compileSchema({ type: "object", properties: { trigger: { type: "string" }, dependency: { type: "string", default: "default" } }, dependencies: { trigger: ["dependency"] } }); const res = node.getData({}, TEMPLATE_OPTIONS); assert.deepEqual(res, {}); }); it("should add dependency if triggered as required", () => { const node = compileSchema({ type: "object", properties: { trigger: { type: "string" }, dependency: { type: "string", default: "default" } }, dependencies: { trigger: ["dependency"] } }); const res = node.getData({ trigger: "yes" }, TEMPLATE_OPTIONS); assert.deepEqual(res, { trigger: "yes", dependency: "default" }); }); it("should add dependency if initially triggered as required", () => { const node = compileSchema({ type: "object", required: ["trigger"], properties: { trigger: { type: "string" }, dependency: { type: "string", default: "default" } }, dependencies: { trigger: ["dependency"] } }); const res = node.getData({}, TEMPLATE_OPTIONS); assert.deepEqual(res, { trigger: "", dependency: "default" }); }); }); describe("dependency schema", () => { it("should not add dependency from schema if it is not required", () => { const node = compileSchema({ type: "object", properties: { trigger: { type: "string" } }, dependencies: { trigger: { properties: { dependency: { type: "string", default: "default" } } } } }); const res = node.getData({}, TEMPLATE_OPTIONS); assert.deepEqual(res, {}); }); it("should add dependency from schema if triggered as required", () => { const node = compileSchema({ type: "object", properties: { trigger: { type: "string" } }, dependencies: { trigger: { required: ["dependency"], properties: { dependency: { type: "string", default: "default" } } } } }); const res = node.getData({ trigger: "yes" }, TEMPLATE_OPTIONS); assert.deepEqual(res, { trigger: "yes", dependency: "default" }); }); }); }); describe("option: `additionalProps: true`", () => { it("should create template for valid dependency", () => { const node = compileSchema({ type: "object", properties: { test: { type: "string", default: "tested value" } }, dependencies: { test: { properties: { additionalValue: { type: "string", default: "additional" } } } } }); const res = node.getData(undefined, { addOptionalProps: true }); assert.deepEqual(res, { test: "tested value", additionalValue: "additional" }); }); it("should not change passed value of dependency", () => { const node = compileSchema({ type: "object", properties: { test: { type: "string", default: "tested value" } }, dependencies: { test: { properties: { additionalValue: { type: "string", default: "additional" } } } } }); const res = node.getData( { additionalValue: "input value" }, { addOptionalProps: true } ); assert.deepEqual(res, { test: "tested value", additionalValue: "input value" }); }); it("should not create data for non matching dependency", () => { const node = compileSchema({ type: "object", properties: { test: { type: "string", default: "tested value" } }, dependencies: { unknown: { properties: { additionalValue: { type: "string", default: "additional" } } } } }); const res = node.getData(undefined, { addOptionalProps: true }); assert.deepEqual(res, { test: "tested value" }); }); }); }); }); describe("$ref", () => { it("should return default value of resolved ref", () => { const data = compileSchema({ $ref: "#/$defs/once", $defs: { once: { default: "once" } } }).getData(); assert.deepEqual(data, "once"); }); it("should follow all refs", () => { const data = compileSchema({ $ref: "#/$defs/once", $defs: { once: { $ref: "#/$defs/twice" }, twice: { default: "twice" } } }).getData(); assert.deepEqual(data, "twice"); }); it("should resolve $ref in object schema", () => { const data = compileSchema({ type: "object", required: ["first"], properties: { first: { $ref: "#/definitions/first" } }, definitions: { first: { type: "string", default: "john" } } }).getData(); assert.deepEqual(data, { first: "john" }); }); it("should create template for all followed refs (draft 2019-09)", () => { const data = compileSchema({ $ref: "#/$defs/once", $defs: { once: { required: ["once"], properties: { once: { type: "number" } }, $ref: "#/$defs/twice" }, twice: { required: ["twice"], properties: { twice: { type: "boolean", default: true } } } } }).getData(); assert.deepEqual(data, { once: 0, twice: true }); }); it("should resolve $ref in items-array", () => { const data = compileSchema({ type: "array", prefixItems: [{ $ref: "#/definitions/first" }], definitions: { first: { type: "object", required: ["first"], properties: { first: { type: "string", default: "john" } } } } }).getData([{}, {}]); assert.deepEqual(data, [{ first: "john" }, {}]); }); it("should follow $ref once", () => { const data = compileSchema({ type: "object", required: ["value", "nodes"], properties: { value: { type: "string", default: "node" }, nodes: { type: "array", minItems: 1, items: { $ref: "#" } } } }).getData({}, { recursionLimit: 1 }); assert.deepEqual(data, { value: "node", nodes: [{ value: "node", nodes: [] }] }); }); it("should resolve all reoccuring refs ", () => { const data = compileSchema({ minItems: 3, items: { $ref: "#/$defs/item" }, $defs: { item: { required: ["type"], properties: { type: {