Spaces:
Sleeping
Sleeping
| /** | |
| * @filedescription Object Schema Tests | |
| */ | |
| /* global it, describe, beforeEach */ | |
| ; | |
| //----------------------------------------------------------------------------- | |
| // Requirements | |
| //----------------------------------------------------------------------------- | |
| const assert = require("chai").assert; | |
| const { ObjectSchema } = require("../src/"); | |
| //----------------------------------------------------------------------------- | |
| // Class | |
| //----------------------------------------------------------------------------- | |
| describe("ObjectSchema", () => { | |
| let schema; | |
| describe("new ObjectSchema()", () => { | |
| it("should add a new key when a strategy is passed", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() {}, | |
| validate() {} | |
| } | |
| }); | |
| assert.isTrue(schema.hasKey("foo")); | |
| }); | |
| it("should throw an error when a strategy is missing a merge() method", () => { | |
| assert.throws(() => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| validate() { } | |
| } | |
| }); | |
| }, /Definition for key "foo" must have a merge property/); | |
| }); | |
| it("should throw an error when a strategy is missing a merge() method", () => { | |
| assert.throws(() => { | |
| schema = new ObjectSchema(); | |
| }, /Schema definitions missing/); | |
| }); | |
| it("should throw an error when a strategy is missing a validate() method", () => { | |
| assert.throws(() => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { }, | |
| } | |
| }); | |
| }, /Definition for key "foo" must have a validate\(\) method/); | |
| }); | |
| it("should throw an error when merge is an invalid string", () => { | |
| assert.throws(() => { | |
| new ObjectSchema({ | |
| foo: { | |
| merge: "bar", | |
| validate() { } | |
| } | |
| }); | |
| }, /key "foo" missing valid merge strategy/); | |
| }); | |
| it("should throw an error when validate is an invalid string", () => { | |
| assert.throws(() => { | |
| new ObjectSchema({ | |
| foo: { | |
| merge: "assign", | |
| validate: "s" | |
| } | |
| }); | |
| }, /key "foo" missing valid validation strategy/); | |
| }); | |
| }); | |
| describe("merge()", () => { | |
| it("should throw an error when an unexpected key is found", () => { | |
| let schema = new ObjectSchema({}); | |
| assert.throws(() => { | |
| schema.merge({ foo: true }, { foo: true }); | |
| }, /Unexpected key "foo"/); | |
| }); | |
| it("should throw an error when merge() throws an error", () => { | |
| let schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| throw new Error("Boom!"); | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.merge({ foo: true }, { foo: true }); | |
| }, /Key "foo": Boom!/); | |
| }); | |
| it("should call the merge() strategy for one key when called", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| const result = schema.merge({ foo: true }, { foo: false }); | |
| assert.propertyVal(result, "foo", "bar"); | |
| }); | |
| it("should not call the merge() strategy when both objects don't contain the key", () => { | |
| let called = false; | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| called = true; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| schema.merge({}, {}); | |
| assert.isFalse(called, "The merge() strategy should not have been called."); | |
| }); | |
| it("should omit returning the key when the merge() strategy returns undefined", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return undefined; | |
| }, | |
| validate() { } | |
| } | |
| }); | |
| const result = schema.merge({ foo: true }, { foo: false }); | |
| assert.notProperty(result, "foo"); | |
| }); | |
| it("should call the merge() strategy for two keys when called", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() { } | |
| }, | |
| bar: { | |
| merge() { | |
| return "baz"; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| const result = schema.merge({ foo: true, bar: 1 }, { foo: true, bar: 2 }); | |
| assert.propertyVal(result, "foo", "bar"); | |
| assert.propertyVal(result, "bar", "baz"); | |
| }); | |
| it("should call the merge() strategy for two keys when called on three objects", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() { } | |
| }, | |
| bar: { | |
| merge() { | |
| return "baz"; | |
| }, | |
| validate() { } | |
| } | |
| }); | |
| const result = schema.merge( | |
| { foo: true, bar: 1 }, | |
| { foo: true, bar: 3 }, | |
| { foo: false, bar: 2 } | |
| ); | |
| assert.propertyVal(result, "foo", "bar"); | |
| assert.propertyVal(result, "bar", "baz"); | |
| }); | |
| it("should call the merge() strategy when defined as 'overwrite'", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge: "overwrite", | |
| validate() { } | |
| } | |
| }); | |
| const result = schema.merge( | |
| { foo: true }, | |
| { foo: false } | |
| ); | |
| assert.propertyVal(result, "foo", false); | |
| }); | |
| it("should call the merge() strategy when defined as 'assign'", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge: "assign", | |
| validate() { } | |
| } | |
| }); | |
| const result = schema.merge( | |
| { foo: { bar: true } }, | |
| { foo: { baz: false } } | |
| ); | |
| assert.strictEqual(result.foo.bar, true); | |
| assert.strictEqual(result.foo.baz, false); | |
| }); | |
| it("should call the merge strategy when there's a subschema", () => { | |
| schema = new ObjectSchema({ | |
| name: { | |
| schema: { | |
| first: { | |
| merge: "replace", | |
| validate: "string" | |
| }, | |
| last: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| }); | |
| const result = schema.merge({ | |
| name: { | |
| first: "n", | |
| last: "z" | |
| } | |
| }, { | |
| name: { | |
| first: "g" | |
| } | |
| }); | |
| assert.strictEqual(result.name.first, "g"); | |
| assert.strictEqual(result.name.last, "z"); | |
| }); | |
| it("should return separate objects when using subschema", () => { | |
| schema = new ObjectSchema({ | |
| age: { | |
| merge: "replace", | |
| validate: "number" | |
| }, | |
| address: { | |
| schema: { | |
| street: { | |
| schema: { | |
| number: { | |
| merge: "replace", | |
| validate: "number" | |
| }, | |
| streetName: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| }, | |
| state: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| }); | |
| const baseObject = { | |
| address: { | |
| street: { | |
| number: 100, | |
| streetName: "Foo St" | |
| }, | |
| state: "HA" | |
| } | |
| }; | |
| const result = schema.merge(baseObject, { | |
| age: 29 | |
| }); | |
| assert.notStrictEqual(result.address.street, baseObject.address.street); | |
| assert.deepStrictEqual(result.address, baseObject.address); | |
| }); | |
| it("should not error when calling the merge strategy when there's a subschema and no matching key in second object", () => { | |
| schema = new ObjectSchema({ | |
| name: { | |
| schema: { | |
| first: { | |
| merge: "replace", | |
| validate: "string" | |
| }, | |
| last: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| }); | |
| const result = schema.merge({ | |
| name: { | |
| first: "n", | |
| last: "z" | |
| } | |
| }, { | |
| }); | |
| assert.strictEqual(result.name.first, "n"); | |
| assert.strictEqual(result.name.last, "z"); | |
| }); | |
| it("should not error when calling the merge strategy when there's multiple subschemas and no matching key in second object", () => { | |
| schema = new ObjectSchema({ | |
| user: { | |
| schema: { | |
| name: { | |
| schema: { | |
| first: { | |
| merge: "replace", | |
| validate: "string" | |
| }, | |
| last: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| const result = schema.merge({ | |
| user: { | |
| name: { | |
| first: "n", | |
| last: "z" | |
| } | |
| } | |
| }, { | |
| }); | |
| assert.strictEqual(result.user.name.first, "n"); | |
| assert.strictEqual(result.user.name.last, "z"); | |
| }); | |
| }); | |
| describe("validate()", () => { | |
| it("should throw an error when an unexpected key is found", () => { | |
| let schema = new ObjectSchema({}); | |
| assert.throws(() => { | |
| schema.validate({ foo: true }); | |
| }, /Unexpected key "foo"/); | |
| }); | |
| it("should not throw an error when an expected key is found", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| schema.validate({ foo: true }); | |
| }); | |
| it("should pass the property value into validate() when key is found", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate(value) { | |
| assert.isTrue(value); | |
| } | |
| } | |
| }); | |
| schema.validate({ foo: true }); | |
| }); | |
| it("should not throw an error when expected keys are found", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() {} | |
| }, | |
| bar: { | |
| merge() { | |
| return "baz"; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| schema.validate({ foo: true, bar: true }); | |
| }); | |
| it("should not throw an error when expected keys are found with required keys", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() { } | |
| }, | |
| bar: { | |
| requires: ["foo"], | |
| merge() { | |
| return "baz"; | |
| }, | |
| validate() { } | |
| } | |
| }); | |
| schema.validate({ foo: true, bar: true }); | |
| }); | |
| it("should throw an error when expected keys are found without required keys", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() { } | |
| }, | |
| baz: { | |
| merge() { | |
| return "baz"; | |
| }, | |
| validate() { } | |
| }, | |
| bar: { | |
| name: "bar", | |
| requires: ["foo", "baz"], | |
| merge() { }, | |
| validate() { } | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({ bar: true }); | |
| }, /Key "bar" requires keys "foo", "baz"./); | |
| }); | |
| it("should throw an error when an expected key is found but is invalid", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() { | |
| throw new Error("Invalid key."); | |
| } | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({ foo: true }); | |
| }, /Key "foo": Invalid key/); | |
| }); | |
| it("should throw an error when an expected key is found but is invalid with a string validator", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate: "string" | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({ foo: true }); | |
| }, /Key "foo": Expected a string/); | |
| }); | |
| it("should throw an error when an expected key is found but is invalid with a number validator", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate: "number" | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({ foo: true }); | |
| }, /Key "foo": Expected a number/); | |
| }); | |
| it("should throw an error when a required key is missing", () => { | |
| schema = new ObjectSchema({ | |
| foo: { | |
| required: true, | |
| merge() { | |
| return "bar"; | |
| }, | |
| validate() {} | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({}); | |
| }, /Missing required key "foo"/); | |
| }); | |
| it("should throw an error when a subschema is provided and the value doesn't validate", () => { | |
| schema = new ObjectSchema({ | |
| name: { | |
| schema: { | |
| first: { | |
| merge: "replace", | |
| validate: "string" | |
| }, | |
| last: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| }); | |
| assert.throws(() => { | |
| schema.validate({ | |
| name: { | |
| first: 123, | |
| last: "z" | |
| } | |
| }); | |
| }, /Key "name": Key "first": Expected a string/); | |
| }); | |
| it("should not throw an error when a subschema is provided and the value validates", () => { | |
| schema = new ObjectSchema({ | |
| name: { | |
| schema: { | |
| first: { | |
| merge: "replace", | |
| validate: "string" | |
| }, | |
| last: { | |
| merge: "replace", | |
| validate: "string" | |
| } | |
| } | |
| } | |
| }); | |
| schema.validate({ | |
| name: { | |
| first: "n", | |
| last: "z" | |
| } | |
| }); | |
| }); | |
| }); | |
| }); | |