jsmf-core
Version:
Javascript Modelling Framework
436 lines (380 loc) • 18.5 kB
JavaScript
'use strict'
const should = require('should')
, JSMF = require('../src/index')
, Class = JSMF.Class
describe('Class', function() {
describe('Class construction', function() {
it('should have the given name', function(done) {
var Instance = new Class('Instance')
Instance.__name.should.equal('Instance')
done()
})
it('should ConformsTo Class', function(done) {
var Instance = new Class('Instance')
Instance.conformsTo().should.equal(Class)
done()
})
it('is a JSMF element', function(done) {
var Instance = new Class('Instance')
JSMF.isJSMFElement(Instance).should.be.true()
done()
})
it('has an UUID', function(done) {
var s = new Class('S')
should(JSMF.jsmfId(s)).not.equal(undefined)
done()
})
it('can be built with newInstance', function(done) {
var Instance = Class.newInstance('Instance')
Instance.__name.should.equal('Instance')
done()
})
})
describe('Class inheritance', function() {
it('should work with assigniation of superClasses', function(done) {
var Instance = new Class('Instance')
var SuperInstance = new Class('SuperInstance')
var NonRelatedInstance = new Class('NonRelatedInstance')
Instance.superClasses = [SuperInstance]
Instance.superClasses.should.containEql(SuperInstance)
done()
}),
it('should work with push of new superClass', function(done) {
var Instance = new Class('Instance')
var SuperInstance = new Class('SuperInstance')
var NonRelatedInstance = new Class('NonRelatedInstance')
Instance.superClasses.push(SuperInstance)
Instance.superClasses.should.containEql(SuperInstance)
done()
}),
it('should work at class initialization', function(done) {
var SuperInstance = new Class('SuperInstance')
var Instance = new Class('Instance', SuperInstance)
var NonRelatedInstance = new Class('NonRelatedInstance')
Instance.superClasses = [SuperInstance]
Instance.superClasses.should.containEql(SuperInstance)
done()
}),
it('can be done in cascade', function(done) {
var Instance = new Class('Instance')
var SuperInstance = new Class('SuperInstance')
var SuperSuperInstance = new Class('SuperSuperInstance')
Instance.superClasses = [SuperInstance]
SuperInstance.superClasses = [SuperSuperInstance]
Instance.superClasses.should.containEql(SuperInstance)
Instance.superClasses.should.not.containEql(SuperSuperInstance)
SuperInstance.superClasses.should.containEql(SuperSuperInstance)
SuperInstance.superClasses.should.not.containEql(SuperInstance)
done()
}),
it('should support multi-inheritance', function(done) {
var SuperInstance = new Class('SuperInstance')
var OtherSuperInstance = new Class('OtherSuperInstance')
var Instance = new Class('Instance', [SuperInstance, OtherSuperInstance])
Instance.superClasses.should.have.length(2)
Instance.superClasses.should.containEql(SuperInstance)
Instance.superClasses.should.containEql(OtherSuperInstance)
done()
})
})
describe('Class attributes', function() {
it('allows jsmf primitive types', function(done) {
var State = new Class('State')
State.attributes.should.be.empty
State.setAttribute('name', JSMF.String)
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', false)
done()
})
it('allows normalize built-in primitive types', function(done) {
var State = new Class('State')
State.attributes.should.be.empty()
State.setAttribute('name', String)
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', false)
done()
})
it('allows mandatory attributes', function(done) {
var State = new Class('State')
State.attributes.should.be.empty()
State.setAttribute('name', String, true)
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', true)
done()
})
it('has addAttribute is a synonym to setAttribute', function(done) {
var State = new Class('State')
State.attributes.should.be.empty()
State.addAttribute('name', JSMF.String)
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', false)
done()
})
it('accept bulk setting', function(done) {
var State = new Class('State')
State.attributes.should.be.empty
State.addAttributes({name: {type: JSMF.String, mandatory: true}, age: Number})
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', true)
State.attributes.should.have.property('age')
State.attributes.age.should.have.property('type', JSMF.Number)
State.attributes.age.should.have.property('mandatory', false)
done()
})
it('can be modified', function(done) {
var State = new Class('State')
State.attributes.should.be.empty
State.addAttributes({name: JSMF.Number})
State.addAttributes({name: JSMF.String})
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', false)
done()
})
it('can be set at initialization', function(done) {
var State = new Class('State', [], {name: JSMF.String, age: Number})
State.attributes.should.have.property('name')
State.attributes.name.should.have.property('type', JSMF.String)
State.attributes.name.should.have.property('mandatory', false)
State.attributes.should.have.property('age')
State.attributes.age.should.have.property('type', JSMF.Number)
State.attributes.age.should.have.property('mandatory', false)
done()
})
it ('can be removed', function(done) {
var State = new Class('State', [], {name: JSMF.String, age: Number})
State.removeAttribute('name')
State.attributes.should.not.have.property('name')
State.attributes.should.have.property('age')
State.attributes.age.should.have.property('type', JSMF.Number)
State.attributes.age.should.have.property('mandatory', false)
done()
})
})
describe('Class references', function() {
it('can be assign', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, JSMF.Cardinality.one)
Hero.references.should.have.property('has')
Hero.references.has.should.have.property('type', SuperPower)
Hero.references.has.should.have.property('cardinality')
Hero.references.has.cardinality.should.have.property('min' , 1)
Hero.references.has.cardinality.should.have.property('max' , 1)
done()
})
it('has a default cardinality of {0,n}', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower)
Hero.references.should.have.property('has')
Hero.references.has.should.have.property('cardinality')
Hero.references.has.cardinality.should.have.property('min', 0)
Hero.references.has.cardinality.should.have.property('max', undefined)
done()
})
it('accepts only the max cardinality', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, 2)
Hero.references.should.have.property('has')
Hero.references.has.should.have.property('cardinality')
Hero.references.has.cardinality.should.have.property('max', 2)
done()
})
it('has unlimited max cardinality on negative number', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, -1)
Hero.references.should.have.property('has')
Hero.references.has.should.have.property('cardinality')
Hero.references.has.cardinality.should.have.property('max', undefined)
done()
})
it('push opposite reference in the target class', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, JSMF.Cardinality.any, 'owners')
SuperPower.references.should.have.property('owners')
Hero.references.has.should.have.property('opposite', 'owners')
done()
})
it('keep the cardinality of the opposite ref if it exists', function(done) {
const Hero = new Class('Hero')
const SuperPower = new Class('SuperPower')
SuperPower.setReference('owners', Hero, JSMF.Cardinality.one)
Hero.setReference('has', SuperPower, JSMF.Cardinality.any, 'owners')
SuperPower.references.should.have.property('owners')
SuperPower.references.owners.cardinality.should.have.properties({min: 1, max: 1})
SuperPower.references.owners.should.have.property('opposite', 'has')
done()
})
it('support bulk definition', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReferences({has: {target: SuperPower}, weakness: {target: SuperPower}})
Hero.references.should.have.property('has')
Hero.references.should.have.property('weakness')
done()
})
it('can be set at initialization', function(done) {
var SuperPower = new Class('SuperPower')
var Hero = new Class('Hero', [], {}, {has: {target: SuperPower}, weakness: {target: SuperPower}})
Hero.references.should.have.property('has')
Hero.references.should.have.property('weakness')
done()
})
it('has addReference as a synonym', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.addReference('has', SuperPower, JSMF.Cardinality.one)
Hero.references.should.have.property('has')
Hero.references.has.should.have.property('type', SuperPower)
Hero.references.has.should.have.property('cardinality')
Hero.references.has.cardinality.should.have.property('min' , 1)
Hero.references.has.cardinality.should.have.property('max' , 1)
done()
})
it ('can be removed', function(done) {
var SuperPower = new Class('SuperPower')
var Hero = new Class('Hero', [], {}, {has: {target: SuperPower}, weakness: {target: SuperPower}})
Hero.removeReference('has')
Hero.references.should.not.have.property('has')
Hero.references.should.have.property('weakness')
done()
})
it('remove a removed reference from its opposite', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, JSMF.Cardinality.any, 'owners')
Hero.removeReference('has')
SuperPower.references.owners.should.not.have.property('opposite')
done()
})
it('can removed a reference and its opposite simulteneaously', function(done) {
var Hero = new Class('Hero')
var SuperPower = new Class('SuperPower')
Hero.setReference('has', SuperPower, JSMF.Cardinality.any, 'owners')
Hero.removeReference('has', true)
SuperPower.references.should.not.have.property('owners')
done()
})
})
describe('Class getInheritanceChain', function() {
it('returns the class alone if no superClasses', function(done) {
var Hero = new Class('Hero')
Hero.getInheritanceChain().should.be.eql([Hero])
done()
})
it('returns the class and it\'s parent for simple inheritance', function(done) {
var Hero = new Class('Hero')
var SuperHero = new Class('SuperHero', [Hero])
SuperHero.getInheritanceChain().should.be.eql([Hero, SuperHero])
done()
})
it('returns the class and it\'s parent for multiple inheritance', function(done) {
var A = new Class('A')
var B = new Class('B', [A])
var C = new Class('C', [A])
var D = new Class('D', [B,C])
D.getInheritanceChain().should.be.eql([A, B, A, C, D])
done()
})
})
describe('Class getAllAttributes', function() {
it('returns the attributes of the class if no inheritance', function(done) {
var State = new Class('State')
State.attributes.should.be.empty
State.addAttributes({name: JSMF.String, age: Number})
const res = State.getAllAttributes()
res.should.have.property('name')
res.name.should.have.property('type', JSMF.String)
res.name.should.have.property('mandatory', false)
res.should.have.property('age')
res.age.should.have.property('type', JSMF.Number)
res.age.should.have.property('mandatory', false)
done()
})
it('get parents attributes, override them if necessary', function(done) {
const State = new Class('State')
const TargetState = new Class('TargetState', State)
State.attributes.should.be.empty()
State.addAttributes({name: JSMF.String, age: Number})
function positive(x) {return x > 0;}
TargetState.addAttributes({age: positive, value: JSMF.Any})
const res = TargetState.getAllAttributes()
res.should.have.property('name')
res.name.should.have.property('type', JSMF.String)
res.name.should.have.property('mandatory', false)
res.should.have.property('age')
res.age.should.have.property('type', positive)
res.age.should.have.property('mandatory', false)
res.should.have.property('value')
res.value.should.have.property('type', JSMF.Any)
res.value.should.have.property('mandatory', false)
done()
})
it ('kept the attributes of the last inherited class', function(done) {
var A = new Class('A', [], {foo: JSMF.Number})
var B = new Class('B', [], {foo: JSMF.String})
var C = new Class('C', [A, B])
const res = C.getAllAttributes()
res.should.have.property('foo')
res.foo.should.have.property('type', JSMF.String)
res.foo.should.have.property('mandatory', false)
done()
})
})
describe('Class getAllReferences', function() {
it('returns the references of the class if no inheritance', done => {
var State = new Class('State')
State.attributes.should.be.empty
State.addReference('next', State)
State.getAllReferences().should.have.property('next')
done()
})
it('get parents references, override them if necessary', done => {
var State = new Class('State')
var TargetState = new Class('TargetState', State)
State.attributes.should.be.empty()
State.addAttributes({name: JSMF.String, age: Number})
State.addReference('next', State)
TargetState.addReference('next', TargetState)
var references = TargetState.getAllReferences()
references.should.have.property('next')
references.next.type.should.eql(TargetState)
done()
})
it ('kept the references of the last inherited class', done => {
const A = new Class('A')
A.addReference('foo', A)
const B = new Class('B')
B.addReference('foo', B)
const C = new Class('C', [A, B])
const references = C.getAllReferences()
references.should.have.property('foo')
references.foo.type.should.eql(B)
done()
})
})
describe('Class semanticReference', function() {
it('create the semantic references (URI) building the Class', done => {
var Bus = new Class('Bus',[],[],[],false,'https://schema.org/BusOrCoach')
Bus.__semanticReference.should.equal('https://schema.org/BusOrCoach')
Bus.getSemanticReference().should.equal('https://schema.org/BusOrCoach')
done()
})
it('set the semantic reference (URI) by setSemanticReference function and retrives it', done => {
var Bus = new Class('Bus')
Bus.getSemanticReference.should.be.empty
Bus.setSemanticReference('https://schema.org/BusOrCoach')
Bus.getSemanticReference().should.equal('https://schema.org/BusOrCoach')
done()
})
})
})