| /** |
| * @filedescription Object Schema Tests |
| */ |
| /* global it, describe, beforeEach */ |
| |
| "use strict"; |
| |
| //----------------------------------------------------------------------------- |
| // 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" |
| } |
| }); |
| |
| }); |
| |
| }); |
| |
| }); |