src/Model/itemsModel.test.js
import {Model} from "./index";
import {default as deepEqual} from "deep-equal";
import {
stringsCollection,
stringsMinMaxCollection,
objectCollection, objectCollectionDefaults
} from "../../fixtures/ItemsModel.schemas";
import {BaseModel} from "./base-model";
import {basicCollection} from "../../fixtures/PropertiesModel.schemas";
describe("ItemsModel Class", function () {
describe("Simple ItemsModel Tests", () => {
describe("LifeCycle: Instantiation", () => {
let _owner;
beforeEach(() => {
_owner = new Model({schemas: [stringsCollection]});
});
it("should initialize a schema and a schema object", () => {
expect(_owner.model.$model).toBeDefined();
expect(_owner.model.$model instanceof BaseModel).toBe(true);
});
it("should not initialize a invalid schema and schema object", () => {
let badSchema = Object.assign({}, stringsCollection, {
items: [{type: "INVALID"}],
});
expect(() => new Model({schemas: [badSchema]})).toThrow();
});
});
describe("ItemsModel LifeCycle: Creation", () => {
let _d, _owner;
beforeEach(() => {
_owner = new Model({schemas: [stringsCollection]});
});
it("should populate with valid data and make that data accessible", (done) => {
_d = ["abc", "def", "ghi"];
let _cnt = 0;
_owner.subscribe({
next: (m) => {
_cnt++;
expect(deepEqual(_owner.model, _d)).toBe(true);
},
error: done,
});
setTimeout(() => {
expect(_cnt).toEqual(1);
done();
}, 100);
_owner.model = _d;
});
it("should reject invalid data and leave model pristine", () => {
_d = [99, 100, 101];
_owner.model = _d;
expect(_owner.model.length).toEqual(0);
});
});
});
describe("Nested Elements Tests", () => {
describe("LifeCycle: Instantiation", () => {
let _owner;
beforeEach(() => {
_owner = new Model({schemas: [objectCollection]});
});
it("should initialize a valid schema and a schema object", () => {
expect(_owner.model.$model).toBeDefined();
expect(_owner.model.$model instanceof BaseModel).toBe(true);
expect(_owner.model.$model).toBeDefined();
expect(_owner.model.$model instanceof BaseModel).toBe(true);
});
});
describe("ItemsModel LifeCycle: Nested Create", () => {
let _d, _owner;
beforeEach(() => {
_owner = new Model({schemas: [objectCollection]});
});
it("should populate with valid data and make that data accessible", (done) => {
_d = [{
name: "Item A",
value: 1,
}, {
name: "Item B",
}, {
name: "Item C",
value: 2,
}];
let _cnt = 0;
_owner.subscribe({
next: (m) => {
_cnt++;
},
error: done,
});
setTimeout(() => {
expect(_cnt).toEqual(1);
done();
}, 100);
_owner.model = _d;
// expect(deepEqual(_owner.model, _d)).toBe(true);
});
it("should reject invalid data and leave model pristine", () => {
_d = [{
name: 123,
value: 1,
}, {
value: "Item B",
}, {
value: 2,
}];
_owner.model = _d;
expect(typeof _owner.errors).toBe("object");
expect(deepEqual(_owner.model, [])).toBe(true);
});
});
describe("LifeCycle: Update", () => {
let _d, _owner;
beforeEach(() => {
_owner = new Model({schemas: [objectCollection]});
});
it("should update nested models with valid data and pass validation", () => {
_d = [{
name: "Item A",
value: 1,
}, {
name: "Item B",
}, {
name: "Item C",
value: 2,
}];
_owner.model = _d;
_owner.model[1] = {
name: "Item B",
value: 3
};
expect(_owner.errors).toBe(null);
expect(_owner.model[1]).toEqual({name: "Item B", value: 3});
});
it("should updated properties in nested objects with valid data and pass validation", () => {
_d = [{
name: "Item A",
value: 1,
}, {
name: "Item B",
}, {
name: "Item C",
value: 2,
}];
_owner.model = _d;
_owner.model[1].value = 3;
expect(_owner.errors).toBe(null);
expect(_owner.model[1]).toEqual({name: "Item B", value: 3});
});
});
describe("LifeCycle: Delete", () => {
let _owner;
beforeEach(() => {
_owner = new Model({schemas: [stringsCollection]});
});
let _d = ["Item A", "Item B", "Item C"];
it("should allow deletion of nested properties that are not required", () => {
_owner.model = _d;
delete _owner.model[1];
expect(_owner.errors).toBe(null);
expect(_owner.model.length).toBe(2);
});
it("should prevent deletion of nested properties that are required", () => {
_owner.model = _d;
delete _owner.model[0];
delete _owner.model[1];
delete _owner.model[2];
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(1);
});
});
describe("LifeCycle: Reset", () => {
let _owner;
beforeEach(() => {
_owner = new Model({schemas: [objectCollection]});
});
it("should notify subsequent validations", (done) => {
const _d = [{
name: "Item A",
value: 1,
}, {
name: "Item B",
}, {
name: "Item C",
value: 2,
}];
_owner.model = _d;
setTimeout(() => {
_owner.subscribe({
next: (m) => {
expect(m.model.length).toEqual(3);
done()
},
error: done,
});
_owner.model = [{
name: "Item D",
value: 1,
}, {
name: "Item E",
value: 2,
}, {
name: "Item F",
value: 3,
}];
}, 100);
});
});
});
describe("Array Prototype method tests", () => {
let _owner;
beforeEach(() => {
_owner = new Model({schemas: [stringsMinMaxCollection]});
_owner.model = ["Item A", "Item B", "Item C"];
});
it("should fill with validation", () => {
_owner.model.fill(["Item A", "Item B", "Item C", "Item D"]);
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(3);
});
it("should pop with validation", () => {
_owner.model.pop();
_owner.model.pop();
_owner.model.pop();
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(1);
});
it("should push with validation", () => {
_owner.model.push("Item D");
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(3);
expect(_owner.model[2]).toBe("Item C");
});
it("should shift with validation", () => {
_owner.model.shift();
_owner.model.shift();
_owner.model.shift();
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(1);
});
it("should splice with validation", () => {
// remove all..
_owner.model.splice(0, -1);
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(3);
// append element...
_owner.model.splice(0, 0, "Item D");
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(3);
});
it("should unshift with validation", () => {
_owner.model.unshift("Item Z");
expect(typeof _owner.errors).toBe("object");
expect(_owner.model.length).toBe(3);
});
});
describe("Default Values", () => {
it("should apply defaults to items", () => {
const _owner = new Model({schemas: [objectCollectionDefaults]}, {ajvOptions: {useDefaults: true}});
_owner.model = [{}];
expect(_owner.model[0]).toEqual({name: "abc"});
});
});
describe("Model Class methods ", () => {
let _owner;
it("should not reset if it would invalidate model", () => {
_owner = new Model({schemas: [stringsMinMaxCollection]});
_owner.model = ["Item A", "Item B", "Item C"];
expect(_owner.model.length).toBe(3);
_owner.model.$model.reset();
expect(_owner.model.length).toBe(3);
});
it("should reset its collection if allowed", () => {
_owner = new Model({schemas: [stringsCollection]});
_owner.model = ["Item A", "Item B", "Item C"];
expect(_owner.model.length).toBe(3);
_owner.model.$model.reset();
expect(_owner.model.length).toBe(0);
});
it("should quietly validate data with the validate method", () => {
_owner = new Model({schemas: [stringsCollection]});
expect(_owner.model.$model.validate([1, 2, 3])).toBe("data/0 must be string");
expect(_owner.model.$model.validate(["1", "2", "3"])).toBe(true);
});
it("should freeze its model", () => {
_owner = new Model({schemas: [stringsCollection]});
_owner.model = ["Item A", "Item B", "Item C"];
_owner.model.$model.freeze();
expect(_owner.model.$model.isFrozen).toBe(true);
_owner.model = ["1", "2", "3"];
expect(deepEqual(_owner.model, ["Item A", "Item B", "Item C"])).toBe(true);
});
it("should freeze its model hierarchy", () => {
const _orig = [{
name: "My Name",
active: true,
}];
_owner = new Model({schemas: [basicCollection]});
_owner.model = _orig;
_owner.model.$model.freeze();
expect(() => _owner.model[0].name = "Other Name")
.toThrow("model path \"root#/items\" is non-configurable and non-writable");
});
});
});