admin-bro
Version:
Admin panel for apps written in node.js
269 lines (212 loc) • 8.79 kB
text/typescript
import chai, { expect } from 'chai'
import chaiChange from 'chai-change'
import sinon from 'sinon'
import sinonChai from 'sinon-chai'
import chaiAsPromised from 'chai-as-promised'
import { ParamsType } from './params.type'
import BaseRecord from './base-record'
import BaseResource from '../resource/base-resource'
import BaseProperty from '../property/base-property'
import ValidationError, { PropertyErrors } from '../../utils/errors/validation-error'
import { ActionDecorator, ResourceDecorator } from '../../decorators'
chai.use(chaiAsPromised)
chai.use(chaiChange)
chai.use(sinonChai)
describe('Record', function () {
let record: BaseRecord
let params: BaseRecord['params'] = { param1: 'john' }
afterEach(function () {
sinon.restore()
})
describe('#get', function () {
context('record with nested parameters', function () {
const nested3level = 'value'
beforeEach(function () {
params = {
nested1level: { nested2level: { nested3level } },
}
record = new BaseRecord(params, {} as BaseResource)
})
it('returns deepest field when all up-level keys are given', function () {
expect(record.get('nested1level.nested2level.nested3level')).to.equal(nested3level)
})
it('returns object when all up-level keys are given except one', function () {
expect(record.get('nested1level.nested2level')).to.deep.equal({ nested3level })
})
it('returns object when only first level key is given', function () {
expect(record.get('nested1level')).to.deep.equal({
nested2level: { nested3level },
})
})
it('returns undefined when passing unknown param', function () {
expect(record.get('nested1level.nested2')).to.be.undefined
})
})
})
describe('#constructor', function () {
it('returns empty object if params are not passed to the constructor', function () {
record = new BaseRecord({}, {} as BaseResource)
expect((record as any).params).to.deep.equal({})
})
it('stores flatten object params', function () {
record = new BaseRecord({ auth: { login: 'login' } }, {} as BaseResource)
expect((record as any).params).to.deep.equal({ 'auth.login': 'login' })
})
})
describe('#save', function () {
const newParams = { param2: 'doe' }
const properties = [new BaseProperty({ path: '_id', isId: true })]
let resource: BaseResource
beforeEach(function () {
resource = sinon.createStubInstance(BaseResource, {
properties: sinon.stub<[], BaseProperty[]>().returns(properties),
create: sinon.stub<[Record<string, any>], Promise<ParamsType>>()
.resolves(newParams),
update: sinon.stub<[string, Record<string, any>], Promise<ParamsType>>()
.resolves(newParams),
})
})
it('uses BaseResource#create method when there is no id property', async function () {
record = new BaseRecord(newParams, resource)
record.save()
expect(resource.create).to.have.been.calledWith(newParams)
})
it('uses BaseResource#update method when there is a id property', function () {
const _id = '1231231313'
record = new BaseRecord({ ...newParams, _id }, resource)
record.save()
expect(resource.update).to.have.been.calledWith(_id, { ...newParams, _id })
})
it('stores validation error when they happen', async function () {
const propertyErrors: PropertyErrors = {
param2: {
type: 'required',
message: 'Field is required',
},
}
resource.create = sinon.stub().rejects(new ValidationError(propertyErrors))
record = new BaseRecord(newParams, resource)
await record.save()
expect(record.error('param2')).to.deep.equal(propertyErrors.param2)
})
})
describe('#update', function () {
const newParams = { param2: 'doe' }
const properties = [new BaseProperty({ path: '_id', isId: true })]
params = { param1: 'john', _id: '1381723981273' }
let resource: BaseResource
context('resource stores the value', function () {
beforeEach(async function () {
resource = sinon.createStubInstance(BaseResource, {
properties: sinon.stub<[], BaseProperty[]>().returns(properties),
update: sinon.stub<[string, Record<string, any>], Promise<ParamsType>>()
.resolves(newParams),
})
record = new BaseRecord(params, resource)
await record.update(newParams)
})
it('stores what was returned by BaseResource#update to this.params', function () {
expect(record.get('param2')).to.equal(newParams.param2)
})
it('resets the errors when there are no', function () {
expect((record as any).errors).to.deep.equal({})
})
it('calls the BaseResource#update function with the id and new params', function () {
expect(resource.update).to.have.been.calledWith(params._id, newParams)
})
})
context('resource throws validation error', function () {
const propertyErrors: PropertyErrors = {
param2: {
type: 'required',
message: 'Field is required',
},
}
beforeEach(async function () {
resource = sinon.createStubInstance(BaseResource, {
properties: sinon.stub<[], BaseProperty[]>().returns(properties),
update: sinon.stub<[string, Record<string, any>], Promise<ParamsType>>()
.rejects(new ValidationError(propertyErrors)),
})
record = new BaseRecord(params, resource)
this.returnedValue = await record.update(newParams)
})
it('stores validation errors', function () {
expect(record.error('param2')).to.deep.equal(propertyErrors.param2)
})
it('returns itself', function () {
expect(this.returnedValue).to.equal(record)
})
})
})
describe('#isValid', function () {
it('returns true when there are no errors', function () {
(record as any).errors = {}
expect(record.isValid()).to.equal(true)
})
it('returns false when there is at least on error', function () {
(record as any).errors = {
pathWithError: { type: 'required', message: 'I am error' },
}
expect(record.isValid()).to.equal(false)
})
})
describe('#title', function () {
const properties = [new BaseProperty({ path: 'name' })]
params = { name: 'john', _id: '1381723981273' }
it('returns value in title property', function () {
const resource = sinon.createStubInstance(BaseResource, {
properties: sinon.stub<[], BaseProperty[]>().returns(properties),
})
record = new BaseRecord(params, resource)
expect(record.title()).to.equal(params.name)
})
})
describe('#populate', function () {
it('sets populated field', function () {
const populated = { value: new BaseRecord({}, {} as BaseResource) }
record = new BaseRecord(params, {} as BaseResource)
record.populate('value', populated.value)
expect((record as any).populated.value).to.equal(populated.value)
})
it('clears populated field when record is null or undefined', () => {
record = new BaseRecord(params, {} as BaseResource)
record.populate('value', 'something' as any)
expect(() => {
record.populate('value', null)
}).to.alter(() => record.populated.value, { from: 'something', to: undefined })
})
})
describe('#toJSON', () => {
const param = 'populatedProperty'
let resource: BaseResource
beforeEach(() => {
resource = sinon.createStubInstance(BaseResource, {
properties: sinon.stub<[], BaseProperty[]>().returns([]),
decorate: sinon.stub<[], ResourceDecorator>().returns(
sinon.createStubInstance(ResourceDecorator, {
recordActions: sinon.stub<[BaseRecord], ActionDecorator[]>().returns([]),
bulkActions: sinon.stub<[BaseRecord], ActionDecorator[]>().returns([]),
}) as unknown as ResourceDecorator,
),
})
record = new BaseRecord(params, resource)
})
it('changes populated records to JSON', () => {
const refRecord = sinon.createStubInstance(BaseRecord, {
toJSON: sinon.stub(),
})
record.populate(param, refRecord)
sinon.stub(record, 'id').returns('1')
record.toJSON()
expect(refRecord.toJSON).to.have.been.calledOnce
})
it('does not changes to JSON when in populated there is something else than BaseRecord', () => {
record.populate(param, 'something else' as unknown as BaseRecord)
sinon.stub(record, 'id').returns('1')
expect(() => {
record.toJSON()
}).not.to.throw()
})
})
})