adminjs
Version:
Admin panel for apps written in node.js
245 lines (244 loc) • 9.35 kB
JavaScript
import sinon from 'sinon';
import { expect } from 'chai';
import ResourceDecorator from './resource-decorator.js';
import PropertyDecorator from '../property/property-decorator.js';
import AdminJS, { defaultOptions } from '../../../adminjs.js';
import resourceStub, { expectedResult } from '../../../../spec/backend/helpers/resource-stub.js';
const someID = 'someID';
const currentAdmin = {
email: 'some@email.com',
name: 'someName',
otherValue: 'someOther-value'
};
const stubAdminJS = () => {
const stubbedAdmin = sinon.createStubInstance(AdminJS);
return Object.assign(stubbedAdmin, {
translateProperty: sinon.stub().returns('translated property'),
translateAction: sinon.stub().returns('translated action'),
translateMessage: sinon.stub().returns('translate message'),
options: {
...defaultOptions,
rootPath: '/admin'
}
});
};
describe('ResourceDecorator', function () {
let stubbedAdmin;
let stubbedRecord;
let stubbedResource;
let args;
beforeEach(function () {
stubbedRecord = sinon.stub();
stubbedResource = resourceStub();
stubbedResource._decorated = {
id: () => 'resourceId'
};
stubbedAdmin = stubAdminJS();
args = {
resource: stubbedResource,
admin: stubbedAdmin
};
});
afterEach(function () {
sinon.restore();
});
describe('#getResourceName', function () {
it('returns resource when name is not specified in options', function () {
expect(new ResourceDecorator({
...args,
options: {}
}).getResourceName()).to.equal(someID);
});
});
describe('#getNavigation', function () {
it('returns custom name with icon when options were specified', function () {
const options = {
navigation: {
name: 'someName',
icon: 'someIcon',
show: true
}
};
expect(new ResourceDecorator({
...args,
options
}).getNavigation()).to.deep.equal(options.navigation);
});
});
describe('#getProperties', function () {
context('all properties are visible', function () {
beforeEach(function () {
sinon.stub(PropertyDecorator.prototype, 'isVisible').returns(true);
});
it('returns first n items when limit is given', function () {
const max = 3;
const decorator = new ResourceDecorator(args);
expect(decorator.getProperties({
where: 'list',
max
})).to.have.lengthOf(max);
});
it('returns all properties when limit is not given', function () {
const decorator = new ResourceDecorator(args);
expect(decorator.getProperties({
where: 'list'
})).to.have.lengthOf(expectedResult.properties.length);
});
it('returns only showProperties from options if they were given', function () {
const path = expectedResult.properties[0].path();
const decorator = new ResourceDecorator({
...args,
options: {
showProperties: [path]
}
});
expect(decorator.getProperties({
where: 'show'
})).to.have.lengthOf(1);
});
});
});
describe('#resourceActions', function () {
context('no action were specified in custom settings', function () {
let decorator;
beforeEach(function () {
const options = {};
decorator = new ResourceDecorator({
...args,
options
});
});
it('returns 2 default resource actions', function () {
const actions = decorator.resourceActions(currentAdmin);
const [action] = actions;
expect(actions).to.have.lengthOf(2);
expect(action).to.have.property('name', 'new');
});
});
});
describe('#getPropertyByKey', function () {
let decorator;
beforeEach(function () {
decorator = new ResourceDecorator(args);
});
it('returns property by giving its key', function () {
const propertyPath = expectedResult.properties[0].path();
expect(decorator.getPropertyByKey(propertyPath)).to.be.an.instanceof(PropertyDecorator);
});
it('returns null when there is no property by given key', function () {
expect(decorator.getPropertyByKey('some-unknown-name')).to.eq(null);
});
it('returns mixed property', function () {
const propertyPath = expectedResult.properties.find(p => p.type() === 'mixed')?.path();
expect(decorator.getPropertyByKey(propertyPath)).to.be.an.instanceof(PropertyDecorator);
});
it('returns nested property under mixed', function () {
const property = expectedResult.properties.find(p => p.type() === 'mixed');
const nested1Property = property?.subProperties().find(p => p.type() !== 'mixed');
const path = [property.path(), nested1Property.path()].join('.');
const decoratedProperty = decorator.getPropertyByKey(path);
expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator);
expect(decoratedProperty.propertyPath).to.eq(path);
});
it('returns nested property under 2 level nested mixed', function () {
const property = expectedResult.properties.find(p => p.type() === 'mixed');
const nested1Property = property?.subProperties().find(p => p.type() === 'mixed');
const nested2Property = nested1Property?.subProperties()[0];
const path = [property.path(), nested1Property.path(), nested2Property.path()].join('.');
const decoratedProperty = decorator.getPropertyByKey(path);
expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator);
expect(decoratedProperty.propertyPath).to.eq(path);
});
it('returns property when it is an array', function () {
const arrayProperty = expectedResult.properties.find(p => p.isArray());
// checking of a property of first item in an array
const path = [arrayProperty.path(), '0'].join('.');
const decoratedProperty = decorator.getPropertyByKey(path);
expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator);
expect(decoratedProperty.propertyPath).to.eq(arrayProperty.path());
});
it('returns property when it is an nested array', function () {
const arrayProperty = expectedResult.properties.find(p => p.isArray() && p.type() === 'mixed');
const nested1Property = arrayProperty?.subProperties()[0];
// checking of a property of first item in an array
const path = [arrayProperty.path(), '0', nested1Property.path()].join('.');
const decoratedProperty = decorator.getPropertyByKey(path);
expect(decoratedProperty).to.be.an.instanceof(PropertyDecorator);
expect(decoratedProperty.propertyPath).to.eq([arrayProperty.path(), nested1Property.path()].join('.'));
});
});
describe('#recordAction', function () {
it('returns default actions', function () {
const actions = new ResourceDecorator({
...args,
options: {}
}).recordActions(stubbedRecord, currentAdmin);
expect(actions).to.have.lengthOf(3);
});
it('shows custom actions specified by the user', function () {
const options = {
actions: {
customAction: {
actionType: 'record'
}
}
};
const actions = new ResourceDecorator({
...args,
options
}).recordActions(stubbedRecord, currentAdmin);
expect(actions).to.have.lengthOf(4);
});
it('hides the given action if user set isVisible to false', function () {
const options = {
actions: {
show: {
isVisible: false
}
}
};
const actions = new ResourceDecorator({
...args,
options
}).recordActions(stubbedRecord, currentAdmin);
expect(actions).to.have.lengthOf(2);
});
it('passes properties to isVisible when it is a function', function () {
const someRecord = {
params: {
param: 'someRecord'
}
};
const options = {
actions: {
show: {
isVisible: data => {
// it passes current admin to the isVisible function
expect(data.currentAdmin).to.deep.equal(currentAdmin);
expect(data.resource.id).to.equal(stubbedResource.id);
expect(data.action.name).to.equal('show');
expect(data.record).to.equal(someRecord);
return false;
}
}
}
};
const actions = new ResourceDecorator({
...args,
options
}).recordActions(someRecord, currentAdmin);
expect(actions).to.have.lengthOf(2);
});
});
describe('#toJSON', function () {
it('returns JSON representation of a resource', function () {
const json = new ResourceDecorator(args).toJSON(currentAdmin);
expect(json).to.have.keys('id', 'name', 'navigation', 'href', 'actions', 'titleProperty', 'resourceActions', 'listProperties', 'editProperties', 'showProperties', 'filterProperties', 'properties');
});
it('passes current admin to the resourceActions', function () {
const resourceActionsSpy = sinon.spy(ResourceDecorator.prototype, 'resourceActions');
new ResourceDecorator(args).toJSON(currentAdmin);
expect(resourceActionsSpy).to.have.been.calledWith(currentAdmin);
});
});
});