UNPKG

backbone-tree-model

Version:

Tree data structure using Backbone Model and Collection

410 lines (357 loc) 15.9 kB
/*global mocha, describe, beforeEach, it*/ function runTests(BackboneTreeModel, expect) { var _ = BackboneTreeModel._, Backbone = BackboneTreeModel.Backbone, tree; describe('Backbone Tree', ()=> { beforeEach(()=> { tree = new Backbone.TreeModel({ id: 'root', tagname: 'body', nodes: [ { id: 'wrapper', tagname: 'div', nodes: [ { id: 'sidebar', tagname: 'div', width: 300, nodes: [ { tagname: 'p' }, { tagname: 'ul', nodes: [ { tagname: 'li' }, { tagname: 'li' }, { tagname: 'li' } ] }, { tagname: 'span' } ] }, { id: 'content', tagname: 'div', width: 600, nodes: [ { id: 'title', tagname: 'h2' }, { tagname: 'p', nodes: [ { tagname: 'anchor', nodes: [ { tagname: 'span' } ] } ] } ] } ] } ] }); }); describe('#find', ()=> { it('Should return matched node', ()=> { var sidebar = tree.find('sidebar'); expect(sidebar).to.be.ok(); expect(sidebar.get('width')).to.be(300); expect(sidebar.get('tagname')).to.be('div'); expect(tree.find('title').get('tagname')).to.be('h2'); }); it('should return root node if id matches root', ()=> { expect(tree.find('root')).to.be(tree); }); }); describe('#where', ()=> { it('Should return all matched descendants', ()=> { expect(tree.where({tagname: 'body'}).length).to.be(1); expect(tree.where({tagname: 'div'}).length).to.be(3); }); it('Should support #find/#where context chaining', ()=> { expect(tree.find('sidebar').where({tagname: 'div'}).length).to.be(1); }); }); describe('#isRoot', ()=> { it('Should return true when current node is root node', ()=> { expect(tree.isRoot()).to.be.ok(); expect(tree.find('title').root().isRoot()).to.be.ok(); expect(tree.find('title').isRoot()).to.not.be.ok(); expect(tree.find('sidebar').isRoot()).to.not.be.ok(); }); }); describe('#isRootBranch', ()=> { it('should check if child of root node', ()=> { expect(tree.find('wrapper').isRootBranch()).to.be.ok(); expect(tree.find('sidebar').isRootBranch()).to.not.be.ok(); }); }); describe('#root', ()=> { it('Should return the root node', ()=> { var sidebar = tree.find('sidebar'); var content = tree.find('content'); expect(sidebar.root().get('tagname')).to.be('body'); expect(content.root().get('tagname')).to.be('body'); }); }); describe('#contains', ()=> { it('Should return true if node is a descendant of current node', ()=> { expect(tree.contains(tree.find('sidebar'))).to.be.ok(); }); it('Should return false if node is not a descendant of current node', ()=> { expect(tree.find('sidebar').contains(tree)).to.not.be.ok(); }); }); describe('#parent', ()=> { it('Should return the parent node', ()=> { var sidebar = tree.find('sidebar'); var content = tree.find('content'); expect(sidebar.parent().get('id')).to.be('wrapper'); expect(content.parent().get('id')).to.be('wrapper'); }); }); describe('#nodes', ()=> { it('Should return backbone collection consisting of children nodes if children exist', ()=> { var titleNodes = tree.find('title').nodes(); expect(titleNodes).to.not.be.ok(); var sidebarNodes = tree.find('sidebar').nodes(); expect(sidebarNodes).to.be.ok(); expect(sidebarNodes.length).to.be(3); expect(sidebarNodes instanceof Backbone.Collection).to.be.ok(); }); }); describe('#add', ()=> { it('Should support adding single node', ()=> { var sidebar = tree.find('sidebar').add({id: 'title_1'}); expect(sidebar.nodes().length).to.be(4); expect(sidebar.find('title_1')).to.be.ok(); }); it('Should support adding array of nodes', ()=> { var sidebar = tree.find('sidebar').add([ { id: 'title_2', tagname: 'h1', nodes: [ { id: 'anchor', tagname: 'a', nodes: [ { tagname: 'span' } ] } ] }, { id: 'title_3', tagname: 'h2' } ]); expect(sidebar.nodes().length).to.be(5); expect(sidebar.find('title_2')).to.be.ok(); expect(sidebar.find('title_3')).to.be.ok(); expect(sidebar.find('anchor')).to.be.ok(); expect(sidebar.find('anchor').nodes().length).to.be(1); }); it('Should support adding TreeModel nodes', ()=> { // place sidebar before title node tree.find('content').add(tree.find('sidebar')); expect(tree.find('content').nodes().length).to.be(3); expect(tree.find('wrapper').nodes().length).to.be(1); expect(tree.find('sidebar').parent()).to.be(tree.find('content')); }); }); describe('#insertBefore', ()=> { it('Should support adding objects to the left of a node', ()=> { var wrapper = tree.find('wrapper').insertBefore({id: 'left_wrapper'}); expect(tree.nodes().length).to.be(2); expect(tree.nodes().first().id).to.be('left_wrapper'); expect(wrapper.prev().id).to.be('left_wrapper'); }); it('Should support inserting TreeModel nodes', ()=> { // place sidebar before title node tree.find('title').insertBefore(tree.find('sidebar')); expect(tree.find('content').nodes().length).to.be(3); expect(tree.find('wrapper').nodes().length).to.be(1); expect(tree.find('sidebar').parent()).to.be(tree.find('content')); }); }); describe('#insertAfter', ()=> { it('Should support adding objects to the right of a node', ()=> { var wrapper = tree.find('wrapper').insertAfter({id: 'right_wrapper'}); expect(tree.nodes().length).to.be(2); expect(tree.nodes().last().id).to.be('right_wrapper'); expect(wrapper.next().id).to.be('right_wrapper'); }); it('Should support inserting TreeModel nodes', ()=> { // place sidebar before title node tree.find('title').insertAfter(tree.find('sidebar')); expect(tree.find('content').nodes().length).to.be(3); expect(tree.find('wrapper').nodes().length).to.be(1); expect(tree.find('sidebar').parent()).to.be(tree.find('content')); }); }); describe('#next', ()=> { it('Should return the node on the right', ()=> { expect(tree.find('sidebar').next().id).to.be('content'); }); }); describe('#prev', ()=> { it('Should return the node on the left', ()=> { expect(tree.find('content').prev().id).to.be('sidebar'); }); }); describe('#remove', ()=> { it('Should remove current node', ()=> { tree.find('sidebar').remove(); expect(tree.find('wrapper').nodes().length).to.be(1); expect(tree.find('wrapper').nodes().first().id).to.be('content'); }); it('Should remove first matched descedant', ()=> { tree.remove({tagname: 'p'}, true); expect(tree.where({tagname: 'p'}).length).to.be(1); }); it('Should remove all matched descedants', ()=> { tree.remove({tagname: 'p'}); expect(tree.where({tagname: 'p'}).length).to.be(0); }); }); describe('#empty', ()=> { it('Should remove all children in current node', ()=> { tree.empty(); expect(tree.find('wrapper')).to.not.be.ok(); expect(tree.nodes()).to.not.be.ok(); }); }); describe('#where for Collection/Array', ()=> { it('Should be supported for node children collection', ()=> { expect(tree.nodes().where({tagname: 'p'}, {deep: true}).length).to.be(2); expect(tree.find('sidebar').nodes().where({tagname: 'p'}, {deep: true}).length).to.be(1); }); it('Should be supported from a chained #where call', ()=> { expect(tree.where({tagname: 'div'}).where({tagname: 'span'}).length).to.be(2); expect(tree.where({tagname: 'anchor'}).where({tagname: 'span'}).length).to.be(1); }); }); describe('#toJSON', ()=> { it('Should return JSON starting from node, account for node changes', ()=> { tree.find('sidebar').remove(); // perform a change var treeJSON = tree.toJSON(); expect(treeJSON.nodes[0].nodes.length).to.be(1); expect(treeJSON.nodes[0].nodes[0].id).to.be('content'); var wrapperJSON = tree.find('wrapper').toJSON(); expect(wrapperJSON.nodes.length).to.be(1); expect(wrapperJSON.nodes[0].id).to.be('content'); }); }); describe('#flatten', ()=> { it('should return a flattend array', ()=> { var arr = tree.flatten(); expect(arr.length).to.be(14); }); it('should support toArray alias', ()=> { var arr = tree.toArray(); expect(arr.length).to.be(14); }); }); describe('should be able to specify model constructor with Model', ()=> { var MyCollection = Backbone.TreeCollection.extend({}), MyModel = Backbone.TreeModel.extend({ collectionConstructor : MyCollection }), tree = new MyModel({ id: 'root', tagname: 'body', nodes: [ { id: 'wrapper', tagname: 'div', nodes: [ { tagname: 'p' } ] } ] }); it('Children should be instance of MyModel', ()=> { expect(tree instanceof MyModel).to.be.ok(); tree.nodes().forEach((child)=> { expect(child instanceof MyModel).to.be.ok(); }); }); it('Child collection should be instance of MyCollection', ()=> { expect(tree.nodes() instanceof MyCollection).to.be.ok(); tree.nodes().forEach((child)=> { expect(child.nodes() instanceof MyCollection).to.be.ok(); }); }); }); describe('toJSON should consider tree changes', ()=> { it('toJSON should return tree without Sydney', ()=> { tree = new Backbone.TreeCollection([ { id:1, title:'Australia', nodes: [ { id: 2, title : 'Sydney' } ] }, ]); //tree without changes: expect(tree.toJSON()).eql([ { id:1, title:'Australia', nodes: [ { id: 2, title : 'Sydney' } ] } ]); var Sydney = tree.at(0).nodes().at(0); Sydney.collection.remove(Sydney); //tree after changes: expect(tree.toJSON()).eql([ { id:1, title:'Australia' } ]); }); }); describe('#setPropDescendants', ()=> { it('should set descendant properties', ()=> { tree.setPropDescendants({newProperty: 'foo'}); expect(tree.get('newProperty')).to.be('foo'); expect(tree.find('content').get('newProperty')).to.be('foo'); }); }); describe('#getClosestAncestorProperty', ()=> { it('should get closest property', ()=> { var title = tree.find('title'); var closestPropWidth = title.getClosestAncestorProperty('width'); expect(closestPropWidth).to.be(600); var closestPropTagname = title.getClosestAncestorProperty('tagname'); expect(closestPropTagname).to.be('h2'); }); }); }); } if(typeof define === 'function' && define.amd) { define(['treemodel'], (TreeModel)=> { mocha.setup('bdd'); runTests(TreeModel, window.expect); mocha.run(); }); } else if(typeof exports !== 'undefined') { runTests(require('../src/backbone.treemodel'), require('expect.js')); } else { runTests(window.BackboneTreeModel, window.expect); }