UNPKG

d2

Version:

Javascript library for DHIS2

764 lines (630 loc) 24 kB
import ModelValidation from '../ModelValidation' import ModelBase, { DIRTY_PROPERTY_LIST } from '../ModelBase' jest.mock('../ModelValidation') describe('ModelBase', () => { // TODO: For some reason we have to setup the mock before the beforeEach and reset the spy, should figure out a way to perhaps do this differently. let validateAgainstSchemaSpy beforeEach(() => { validateAgainstSchemaSpy = ModelValidation.getModelValidation() .validateAgainstSchema validateAgainstSchemaSpy.mockReset() }) it('should have a save method', () => { const modelBase = new ModelBase() expect(typeof modelBase.save).toBe('function') }) it('should have a validate method', () => { const modelBase = new ModelBase() expect(typeof modelBase.validate).toBe('function') }) it('should have a clone method', () => { const modelBase = new ModelBase() expect(typeof modelBase.clone).toBe('function') }) describe('saving', () => { let modelDefinition let model let validateFunction beforeEach(() => { validateFunction = jest.fn() modelDefinition = { apiEndpoint: '/dataElements', save: jest.fn().mockReturnValue(Promise.resolve()), saveNew: jest.fn().mockReturnValue(Promise.resolve()), } class Model extends ModelBase { constructor(modelDef) { super() this.modelDefinition = modelDef this.validate = validateFunction this.dirty = true this[DIRTY_PROPERTY_LIST] = new Set(['name']) this.dataValues = {} } } model = new Model(modelDefinition) Object.defineProperty(model, 'id', { get() { return this.dataValues.id }, }) }) describe('create()', () => { it('should call validate before calling save', () => { validateFunction.mockReturnValue( Promise.resolve({ status: true }) ) model.create() // TODO: Fix assertion when the .toBeCalledBefore(model.save) is available https://github.com/facebook/jest/issues/4402 expect(model.validate).toBeCalled() }) it('should call saveNew on the model', () => { validateFunction.mockReturnValue( Promise.resolve({ status: true }) ) return model.create().then(() => { expect(modelDefinition.saveNew).toBeCalledWith(model) }) }) it('should not call saveNew when validate fails', () => { validateFunction.mockReturnValue( Promise.resolve({ status: false }) ) return model .create() .catch(e => e) .then(() => { expect(modelDefinition.save).not.toBeCalled() }) }) }) describe('save()', () => { beforeEach(() => { model.validate.mockReturnValue( Promise.resolve({ status: true }) ) }) it('should call the save on the model modelDefinition with itself as a parameter', () => model.save().then(() => { expect(modelDefinition.save).toBeCalledWith(model) })) it('should call validate before calling save', () => { model.save() expect(model.validate).toBeCalled() }) it('should not call save when validate fails', () => { model.validate.mockReturnValue( Promise.resolve({ status: false }) ) return model .save() .catch(e => e) .then(() => { expect(modelDefinition.save).not.toBeCalled() }) }) it('should not call save when the model is not dirty', () => { model.dirty = false model.save() expect(modelDefinition.save).not.toBeCalled() }) it('should reset dirty to false after save', () => { expect.assertions(1) return model.save().then(() => { expect(model.dirty).toBe(false) }) }) it('should reset the DIRTY_PROPERTY_LIST to an empty set after save', () => { expect.assertions(1) return model.save().then(() => { expect(model[DIRTY_PROPERTY_LIST].size).toBe(0) }) }) it('should return a promise that resolves to an empty object when the model is not dirty', () => { model.dirty = false expect.assertions(1) return model.save().then(result => { expect(result).toEqual({}) }) }) it('should return rejected promise when the model is not valid', () => { model.validate.mockReturnValue( Promise.resolve({ status: false }) ) expect.assertions(1) return model.save().catch(message => { expect(message).toEqual({ status: false }) }) }) it('should set the newly created id onto the model', () => { modelDefinition.save.mockReturnValue( Promise.resolve({ httpStatus: 'Created', response: { uid: 'DXyJmlo9rge', }, }) ) return model.save().then(() => { expect(model.id).toBe('DXyJmlo9rge') }) }) it('should set the correct href property onto the object', () => { modelDefinition.save.mockReturnValue( Promise.resolve({ httpStatus: 'Created', response: { uid: 'DXyJmlo9rge', }, }) ) return model.save().then(() => { expect(model.dataValues.href).toBe( '/dataElements/DXyJmlo9rge' ) }) }) it("should set the dirty children's dirty flag back to false", () => { model.modelDefinition.modelValidations = { organisationUnits: { owner: true, }, } model.organisationUnits = { size: 1, dirty: true, } return model.save().then(() => { expect(model.organisationUnits.dirty).toBe(false) }) }) }) }) describe('validate', () => { let modelValidations let model beforeEach(() => { modelValidations = { age: { persisted: true, type: 'NUMBER', required: true, min: 0, max: 2342, owner: true, unique: false, }, } class Model extends ModelBase { constructor(validations) { super() this.modelDefinition = {} this.modelDefinition.modelValidations = validations this.dataValues = { age: 4, } } } model = new Model(modelValidations) validateAgainstSchemaSpy.mockReturnValue(Promise.resolve([])) }) it('should fail when the async validate fails', () => { const message = 'Validation against schema endpoint failed.' validateAgainstSchemaSpy.mockReturnValue(Promise.reject(message)) expect.assertions(1) return model.validate().catch(errMessage => { expect(errMessage).toBe(message) }) }) it('should call the validateAgainstSchema method on the modelValidator', () => { expect.assertions(1) return model.validate().then(() => { expect(validateAgainstSchemaSpy).toBeCalled() }) }) it('should call validateAgainstSchema with the model', () => { expect.assertions(1) return model.validate().then(() => { expect(validateAgainstSchemaSpy).toBeCalledWith(model) }) }) it('should return false when there are the asyncValidation against the schema failed', () => { validateAgainstSchemaSpy.mockReturnValue( Promise.resolve([ { message: 'Required property missing.', property: 'name' }, ]) ) expect.assertions(1) return model.validate().then(validationState => { expect(validationState.status).toBe(false) }) }) it('should return false when there are the asyncValidation against the schema failed', () => { validateAgainstSchemaSpy.mockReturnValue(Promise.resolve([])) expect.assertions(1) return model.validate().then(validationState => { expect(validationState.status).toBe(true) }) }) }) describe('clone', () => { let modelDefinition let model class Model extends ModelBase { constructor(modelDef) { super() this.modelDefinition = modelDef this.validate = jest .fn() .mockReturnValue(Promise.resolve({ status: true })) this.dirty = false this[DIRTY_PROPERTY_LIST] = new Set([]) this.dataValues = { id: 'DXyJmlo9rge', name: 'My metadata object', } } static create(modelDef) { model = new Model(modelDef) Object.defineProperty(model, 'id', { get() { return this.dataValues.id }, enumerable: true, }) Object.defineProperty(model, 'name', { get() { return this.dataValues.name }, set(newValue) { this.dataValues.name = newValue }, enumerable: true, }) Object.defineProperty(model, 'userGroups', { get() { return this.dataValues.userGroups }, enumerable: true, }) return model } } beforeEach(() => { modelDefinition = { apiEndpoint: '/dataElements', save: jest.fn().mockReturnValue(Promise.resolve()), saveNew: jest.fn().mockReturnValue(Promise.resolve()), create: jest .fn() .mockReturnValue(Model.create(modelDefinition)), modelValidations: { id: {}, name: {}, userGroups: { type: 'COLLECTION', }, }, } model = Model.create(modelDefinition) }) it('should call create on the modelDefinition', () => { model.clone() expect(modelDefinition.create).toBeCalled() }) it('should pass all the dataValues to the create function', () => { model.clone() expect(modelDefinition.create).toBeCalledWith({ id: 'DXyJmlo9rge', name: 'My metadata object', }) }) it('should pass collections arrays of ids', () => { // Would generally be a ModelCollection, but it extends Map so for simplicity we use Map directly. model.dataValues.userGroups = new Map([ ['P3jJH5Tu5VC', { id: 'P3jJH5Tu5VC' }], ['FQ2o8UBlcrS', { id: 'FQ2o8UBlcrS' }], ]) model.clone() expect(modelDefinition.create).toBeCalledWith({ id: 'DXyJmlo9rge', name: 'My metadata object', userGroups: [{ id: 'P3jJH5Tu5VC' }, { id: 'FQ2o8UBlcrS' }], }) }) it('should retain all of the values in the child collections', () => { model.dataValues.userGroups = new Map([ [ 'P3jJH5Tu5VC', { id: 'P3jJH5Tu5VC', name: 'Administrators', clone() { return { id: 'P3jJH5Tu5VC', name: 'Administrators' } }, }, ], [ 'FQ2o8UBlcrS', { id: 'FQ2o8UBlcrS', name: 'Super users', clone() { return { id: 'FQ2o8UBlcrS', name: 'Super users' } }, }, ], ]) model.clone() expect(modelDefinition.create).toBeCalledWith({ id: 'DXyJmlo9rge', name: 'My metadata object', userGroups: [ { id: 'P3jJH5Tu5VC', name: 'Administrators' }, { id: 'FQ2o8UBlcrS', name: 'Super users' }, ], }) }) it('should return an independent clone', () => { const modelClone = model.clone() expect(model).not.toBe(modelClone) modelClone.name = 'NewName' expect(modelClone.dataValues.name).toBe('NewName') expect(model.dataValues.name).toBe('My metadata object') }) it('should preserve the dirty state of the original model', () => { model.dirty = true model[DIRTY_PROPERTY_LIST] = new Set(['name']) const modelClone = model.clone() expect(modelClone.dirty).toBe(true) }) }) describe('delete', () => { let modelDefinition let model beforeEach(() => { modelDefinition = { delete: jest.fn().mockReturnValue( new Promise(resolve => { resolve() }) ), } class Model extends ModelBase { constructor(modelDef) { super() this.modelDefinition = modelDef this.validate = jest .fn() .mockReturnValue(Promise.resolve({ status: true })) this.dirty = true this[DIRTY_PROPERTY_LIST] = new Set(['name']) } } model = new Model(modelDefinition) }) it('should have a delete method', () => { expect(model.delete).toBeInstanceOf(Function) }) it('should call delete on the modeldefinition when called', () => { model.delete() expect(model.modelDefinition.delete).toBeCalled() }) it('should call the modelDefinition.delete method with the model', () => { model.delete() expect(model.modelDefinition.delete).toBeCalledWith(model) }) it('should return a promise', () => { expect(model.delete()).toBeInstanceOf(Promise) }) }) describe('getCollectionChildren', () => { let model beforeEach(() => { model = new ModelBase() model.modelDefinition = { modelValidations: { dataElements: { owner: true, }, userGroups: {}, }, } model.dataElements = { name: 'dataElements', dirty: true, size: 2, } model.userGroups = { name: 'userGroups', } }) it('should return the collection children', () => { expect(model.getCollectionChildren()).toContain(model.dataElements) }) it('should not return the children that are not collections', () => { expect(model.getCollectionChildren()).not.toContain( model.userGroups ) }) }) describe('getCollectionChildrenPropertyNames', () => { let model beforeEach(() => { model = new ModelBase() model.modelDefinition = { modelValidations: { dataElements: { type: 'COLLECTION', }, dataEntryForm: { type: 'COMPLEX', }, }, } model.dataElements = [] }) it('should return the correct property collections', () => { expect(model.getCollectionChildrenPropertyNames()).toContain( 'dataElements' ) }) it('should not return the collection for the property if there is no modelValidation for the property', () => { model.indicators = [] expect(model.getCollectionChildrenPropertyNames()).not.toContain( 'indicators' ) }) it('should not return the collection for the property if there is no modelValidation for the property', () => { model.modelDefinition.modelValidations.indicators = { type: 'COLLECTION', } expect(model.getCollectionChildrenPropertyNames()).not.toContain( 'indicators' ) }) }) describe('getReferenceProperties', () => { let model beforeEach(() => { model = new ModelBase() model.modelDefinition = { modelValidations: { dataElements: { type: 'COLLECTION', embeddedObject: false, }, dataEntryForm: { type: 'COMPLEX', }, user: { type: 'REFERENCE', embeddedObject: false, }, accesses: { type: 'REFERENCE', embeddedObject: true, }, }, } model.dataElements = [] model.user = { id: 'xE7jOejl9FI', firstName: 'John', } model.accesses = { read: true, write: true, } }) it('should return the correct reference properties', () => { expect(model.getReferenceProperties()).toContain('user') }) it('should not return the reference property if there is no modelValidation for the property', () => { model.randomObject = {} expect(model.getReferenceProperties()).not.toContain('randomObject') }) it('should not return the reference property if there is no value for the property', () => { model.modelDefinition.modelValidations.randomObject = { type: 'REFERENCE', } expect(model.getReferenceProperties()).not.toContain('randomObject') }) it('should not return the property if the reference property is embedded', () => { expect(model.getReferenceProperties()).not.toContain('accesses') }) }) describe('getEmbeddedObjectCollectionPropertyNames', () => { let model beforeEach(() => { model = new ModelBase() model.modelDefinition = { modelValidations: { dataElements: { type: 'COLLECTION', embeddedObject: false, }, dataEntryForm: { type: 'COMPLEX', }, legends: { type: 'COLLECTION', embeddedObject: true, }, }, } model.dataElements = [] model.legends = [] }) it('should include the embedded collection', () => { expect(model.getEmbeddedObjectCollectionPropertyNames()).toContain( 'legends' ) }) it('should not include non embedded object collections', () => { expect( model.getEmbeddedObjectCollectionPropertyNames() ).not.toContain('dataElements') }) }) describe('getDirtyChildren', () => { let model beforeEach(() => { model = new ModelBase() model.modelDefinition = { modelValidations: { dataElements: { owner: true, }, }, } model.dataElements = { name: 'dataElements', dirty: true, size: 2, } }) it('should return the dirty children properties', () => { expect(model.getDirtyChildren()).toEqual([model.dataElements]) }) }) describe('toJSON', () => { let model beforeEach(() => { model = new ModelBase() }) it('should be a function', () => { expect(typeof model.toJSON).toBe('function') }) it('should not throw an exception on `toJSON` for base models', () => { expect(model.toJSON()).toEqual({}) }) it('should return a json representation of the model', () => { model.modelDefinition = { modelValidations: { name: { owner: true, }, dataElements: { owner: true, type: 'COLLECTION', }, }, } model.dataValues = { name: 'ANC', dataElements: new Map([ ['P3jJH5Tu5VC', { id: 'P3jJH5Tu5VC', name: 'anc1' }], ['FQ2o8UBlcrS', { id: 'FQ2o8UBlcrS', name: 'anc2' }], ]), } model.name = model.dataValues.name model.dataElements = model.dataValues.dataElements const expected = { name: 'ANC', dataElements: [{ id: 'P3jJH5Tu5VC' }, { id: 'FQ2o8UBlcrS' }], } expect(model.toJSON()).toEqual(expected) }) }) })