UNPKG

ng2-tree-hackaday

Version:

angular2 component for visualizing data that can be naturally represented as a tree

765 lines (627 loc) 22.7 kB
import { Tree } from '../src/tree'; import { TreeModel, TreeModelSettings, FoldingType } from '../src/tree.types'; describe('Tree', () => { it('should detect empty string', () => { expect(Tree.isValueEmpty(' ')).toBe(true); expect(Tree.isValueEmpty(' \n \r ')).toBe(true); expect(Tree.isValueEmpty('\r')).toBe(true); expect(Tree.isValueEmpty(' \t ')).toBe(true); expect(Tree.isValueEmpty(' ')).toBe(true); expect(Tree.isValueEmpty('42')).toBe(false); expect(Tree.isValueEmpty(' 42 \n')).toBe(false); expect(Tree.isValueEmpty(' 42')).toBe(false); expect(Tree.isValueEmpty('42 ')).toBe(false); }); it('should be able to apply value to Renamable node', () => { const renamableNode = { name: '42', age: 'millions years', setName(value: string): void { this.name = value; }, toString(): string { return this.name; } }; const tree = new Tree({ value: renamableNode }); tree.value = '12'; expect(tree.value.toString()).toEqual('12'); expect((tree.value as any).age).toEqual(renamableNode.age); }); it('should be able to apply value to Renamable node: value might be another renamable node coerced to string', () => { const renamableNode = { name: '42', age: 'millions years', setName: function (value) { this.name = value; }, toString: function () { return this.name; } }; const tree = new Tree({ value: renamableNode }); tree.value = { setName: () => { }, toString: () => 'Hi!' }; expect(tree.value.toString()).toEqual('Hi!'); }); it('should be able to apply value to regular node', () => { const tree = new Tree({ value: 'bla' }); tree.value = '12'; expect(tree.value).toEqual('12'); }); it('assigns only RenamableNodes and strings as values', () => { const tree = new Tree({ value: 'bla' }); tree.value = ['boo!']; expect(tree.value).toEqual('bla'); }); it('should not apply value if it is empty', () => { const tree = new Tree({ value: 'bla' }); tree.value = ''; expect(tree.value).toEqual('bla'); tree.value = ' '; expect(tree.value).toEqual('bla'); tree.value = ' \n\r\t'; expect(tree.value).toEqual('bla'); }); it('should know how to detect Renamable node', () => { const renamableNode = { setName: () => { }, toString: () => { } }; const renamableNodeImposter = { setName: 42, toString: 'bla' }; const regularNode = { value: 42 }; expect(Tree.isRenamable(renamableNode)).toBe(true); expect(Tree.isRenamable(renamableNodeImposter)).toBe(false); expect(Tree.isRenamable(regularNode)).toBe(false); }); it('should build a Tree from TreeModel', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, { value: 'Slab serif', children: [ { value: 'Candida' }, { value: 'Swift' }, { value: 'Guardian Egyptian' } ] } ] }; const tree = new Tree(fonts); expect(tree).not.toBeFalsy('Constructed tree should exist'); expect(tree instanceof Tree).toBe(true, 'tree should be instance of Tree'); expect(tree.value).toEqual(fonts.value); expect(tree.children.length).toEqual(6); expect(tree.children[0].value).toEqual('Antiqua'); expect(tree.children[0].positionInParent).toEqual(0); const slabSerifFontsTree = tree.children[5]; expect(slabSerifFontsTree.value).toEqual('Slab serif'); expect(slabSerifFontsTree.children.length).toEqual(3); expect(slabSerifFontsTree.children[1].value).toEqual('Swift'); expect(slabSerifFontsTree.children[1].positionInParent).toEqual(1); }); it('builds completely new structure from TreeModel and changes to TreeModel should not affect built Tree', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, ] }; const tree = new Tree(fonts); fonts.children[0].value = 'bla'; expect(fonts.children[0].value).toEqual('bla'); expect(tree.children[0].value).toEqual('Antiqua'); }); it('merges TreeModelSettings during Tree construction from TreeModel', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, ] }; spyOn(TreeModelSettings, 'merge'); new Tree(fonts); expect(TreeModelSettings.merge).toHaveBeenCalledTimes(6); expect(TreeModelSettings.merge).toHaveBeenCalledWith(fonts, undefined); }); it('adds child', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, ] }; const tree = new Tree(fonts); const child = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' }, ] }); const addedChild = tree.addChild(child); expect(addedChild === child).toBe(false); expect(addedChild.positionInParent).toEqual(5); expect(addedChild.parent).toBe(tree); }); it('adds child and shallowly clones its TreeModel', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, ] }; const tree = new Tree(fonts); const child = new Tree({ value: 'Master' }); const addedChild = tree.addChild(child); addedChild.value = 'Boo!'; expect(addedChild.value).toEqual('Boo!'); expect(child.value).toEqual('Master'); }); it('adds child to a particular position in a tree', () => { const fonts: TreeModel = { value: 'Serif - All my children and I are STATIC ¯\\_(ツ)_/¯', children: [ { value: 'Antiqua' }, { value: 'DejaVu Serif' }, { value: 'Garamond' }, { value: 'Georgia' }, { value: 'Times New Roman' }, ] }; const tree = new Tree(fonts); const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const addedChild = tree.addChild(servantTree, 0); expect(tree.children.length).toEqual(6); expect(addedChild.positionInParent).toEqual(0); expect(tree.children[0].value).toEqual('Master'); }); it('adds child to tree with no children at creation moment', () => { const tree = new Tree({ value: 'Recipient', }); const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const addedChild = tree.addChild(servantTree); expect(tree.children.length).toEqual(1); expect(tree.children[0].value).toEqual('Master'); expect(addedChild.positionInParent).toEqual(0); expect(addedChild.children.length).toEqual(2); expect(addedChild.children[1].value).toEqual('Servant#2'); }); it('adds child to tree with a not array children property', () => { const tree = new Tree({ value: 'Recipient', children: null }); const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const addedChild = tree.addChild(servantTree); expect(tree.children.length).toEqual(1); expect(tree.children[0].value).toEqual('Master'); expect(addedChild.positionInParent).toEqual(0); expect(addedChild.children.length).toEqual(2); expect(addedChild.children[1].value).toEqual('Servant#2'); }); it('cannot add sibling if there is no parent: root node', () => { const tree = new Tree({ value: 'Recipient', children: null }); const addedSibling = tree.addSibling(new Tree({value: 'bla'})); expect(addedSibling).toBeNull(); expect(tree.parent).toBeNull(); }); it('creates child node (leaf)', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const child = servantTree.createNode(false); expect(servantTree.hasChild(child)).toEqual(true); expect(child.value).toEqual(''); expect(child.children).toEqual(null); expect(child.isLeaf()).toEqual(true); expect(child.isNew()).toEqual(true); expect(child.positionInParent).toEqual(2); }); it('creates sibling node (leaf)', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = servantTree.children[0]; expect(servantNumber1Tree.value).toEqual('Servant#1'); const sibling = servantNumber1Tree.createNode(false); expect(servantTree.hasChild(sibling)).toEqual(true); expect(sibling.value).toEqual(''); expect(sibling.children).toEqual(null); expect(sibling.isLeaf()).toEqual(true); expect(sibling.isNew()).toEqual(true); expect(sibling.positionInParent).toEqual(2); }); it('creates child node (branch)', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const child = servantTree.createNode(true); expect(servantTree.hasChild(child)).toEqual(true); expect(child.value).toEqual(''); expect(child.children).toEqual([]); expect(child.isBranch()).toEqual(true); expect(child.isNew()).toEqual(true); expect(child.positionInParent).toEqual(2); }); it('creates static tree', () => { const servantTree = new Tree({ value: 'Master', settings: { 'static': true }, children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(servantTree.isStatic()).toEqual(true); }); it('creates non-static tree by default', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(servantTree.isStatic()).toEqual(false); expect(servantTree.children[0].isStatic()).toEqual(false); expect(servantTree.children[1].isStatic()).toEqual(false); }); it('creates static tree and makes all children static as well', () => { const servantTree = new Tree({ value: 'Master', settings: { 'static': true }, children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(servantTree.isStatic()).toEqual(true); expect(servantTree.children[0].isStatic()).toEqual(true); expect(servantTree.children[1].isStatic()).toEqual(true); }); it('creates static tree and makes all children static as well: children can override static option', () => { const servantTree = new Tree({ value: 'Master', settings: { 'static': true }, children: [ { value: 'Servant#1', settings: { 'static': false } }, { value: 'Servant#2' } ] }); expect(servantTree.isStatic()).toEqual(true); expect(servantTree.children[0].isStatic()).toEqual(false); expect(servantTree.children[1].isStatic()).toEqual(true); }); it('knows that it is branch', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(servantTree.isLeaf()).toEqual(false); expect(servantTree.isBranch()).toEqual(true); }); it('knows that it is leaf', () => { const servantTree = new Tree({ value: 'Master' }); expect(servantTree.isLeaf()).toEqual(true); expect(servantTree.isBranch()).toEqual(false); }); it('knows that it is root', () => { const servantTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(servantTree.isRoot()).toEqual(true); expect(servantTree.children[0].isRoot()).toEqual(false); expect(servantTree.children[1].isRoot()).toEqual(false); }); it('knows its siblings', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; expect(masterTree.hasSibling(servantNumber1Tree)).toEqual(false); expect(servantNumber1Tree.hasSibling(servantNumber1Tree)).toEqual(true); expect(servantNumber1Tree.hasSibling(servantNumber2Tree)).toEqual(true); expect(servantNumber2Tree.hasSibling(servantNumber1Tree)).toEqual(true); }); it('knows its children', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; const imposter = new Tree({ value: 'HA-HA-HA!!!' }); expect(masterTree.hasChild(servantNumber1Tree)).toEqual(true); expect(masterTree.hasChild(servantNumber2Tree)).toEqual(true); expect(masterTree.hasChild(imposter)).toEqual(false); expect(servantNumber2Tree.hasChild(masterTree)).toEqual(false); expect(servantNumber1Tree.hasChild(servantNumber2Tree)).toEqual(false); }); it('can remove children', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; masterTree.removeChild(servantNumber2Tree); expect(masterTree.hasChild(servantNumber2Tree)).toEqual(false); expect(masterTree.children.length).toEqual(1); expect(masterTree.children[0]).toBe(servantNumber1Tree); }); it('cannot remove node that is not a child', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const imposter = new Tree({ value: 'HA-HA-HA!!!' }); masterTree.removeChild(imposter); expect(masterTree.children.length).toEqual(2); expect(masterTree.children[0].value).toEqual('Servant#1'); expect(masterTree.children[1].value).toEqual('Servant#2'); }); it('can remove itself from parent', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; servantNumber2Tree.removeItselfFromParent(); expect(masterTree.hasChild(servantNumber2Tree)).toEqual(false); expect(masterTree.children.length).toEqual(1); expect(masterTree.children[0]).toBe(servantNumber1Tree); }); it('should do nothing when some tries to remove a tree without a parent from parent simply cause it hasn\'t parent', () => { const masterTree = new Tree({ value: 'Master' }); masterTree.removeItselfFromParent(); }); it('can swap its position in parent with sibling', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; expect(servantNumber1Tree.positionInParent).toEqual(0); expect(servantNumber2Tree.positionInParent).toEqual(1); servantNumber1Tree.swapWithSibling(servantNumber2Tree); expect(servantNumber1Tree.positionInParent).toEqual(1); expect(servantNumber2Tree.positionInParent).toEqual(0); }); it('cannot swap its position in parent with node that is not its sibling', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); const imposter = new Tree({ value: 'HA-HA-HA!!!' }); const servantNumber1Tree = masterTree.children[0]; const servantNumber2Tree = masterTree.children[1]; expect(servantNumber1Tree.positionInParent).toEqual(0); expect(servantNumber2Tree.positionInParent).toEqual(1); servantNumber1Tree.swapWithSibling(imposter); expect(servantNumber1Tree.positionInParent).toEqual(0); expect(servantNumber2Tree.positionInParent).toEqual(1); }); it('has "Leaf" folding type if it is leaf (by default for leaves)', () => { const masterTree = new Tree({ value: 'Master' }); expect(masterTree.isLeaf()).toEqual(true); expect(masterTree.isNodeExpanded()).toEqual(false); expect(masterTree.foldingType).toEqual(FoldingType.Leaf); }); it('cannot switch "Leaf" folding type', () => { const masterTree = new Tree({ value: 'Master' }); expect(masterTree.foldingType).toEqual(FoldingType.Leaf); masterTree.switchFoldingType(); expect(masterTree.foldingType).toEqual(FoldingType.Leaf); }); it('has "Expanded" folding type if it is branch and expanded (by default for branches)', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(masterTree.isBranch()).toEqual(true); expect(masterTree.isNodeExpanded()).toEqual(true); expect(masterTree.foldingType).toEqual(FoldingType.Expanded); }); it('can switch "Branch" folding type', () => { const masterTree = new Tree({ value: 'Master', children: [ { value: 'Servant#1' }, { value: 'Servant#2' } ] }); expect(masterTree.foldingType).toEqual(FoldingType.Expanded); expect(masterTree.isNodeExpanded()).toEqual(true); masterTree.switchFoldingType(); expect(masterTree.foldingType).toEqual(FoldingType.Collapsed); expect(masterTree.isNodeExpanded()).toEqual(false); masterTree.switchFoldingType(); expect(masterTree.foldingType).toEqual(FoldingType.Expanded); expect(masterTree.isNodeExpanded()).toEqual(true); }); it('has undefined status by default', () => { const masterTree = new Tree({ value: 'Master' }); expect(masterTree.isNew()).toEqual(false); expect(masterTree.isBeingRenamed()).toEqual(false); expect(masterTree.isModified()).toEqual(false); }); it('can be marked as new', () => { const masterTree = new Tree({ value: 'Master' }); masterTree.markAsNew(); expect(masterTree.isNew()).toEqual(true); }); it('can be marked as modified', () => { const masterTree = new Tree({ value: 'Master' }); masterTree.markAsModified(); expect(masterTree.isModified()).toEqual(true); }); it('can be marked as being renamed', () => { const masterTree = new Tree({ value: 'Master' }); masterTree.markAsBeingRenamed(); expect(masterTree.isBeingRenamed()).toEqual(true); }); it('can load its children asynchronously', (done: Function) => { const tree = new Tree({ value: 'AsyncParent', loadChildren: (callback: Function) => { setTimeout(() => { callback([ { value: 'Child#1' }, { value: 'Child#2' } ]); }, 10); } }); tree.switchFoldingType(); tree.childrenAsync.subscribe((children: Tree[]) => { expect(tree.children.length).toEqual(2); expect(tree.children[0].value).toEqual(children[0].value); expect(tree.children[1].value).toEqual(children[1].value); done(); }); }); it('can load its children asynchronously: loads children only once', (done: Function) => { let loadCount = 0; const tree = new Tree({ value: 'AsyncParent', loadChildren: (callback: Function) => { loadCount++; setTimeout(() => { callback([ { value: 'Child#1' }, { value: 'Child#2' } ]); }, 10); } }); tree.switchFoldingType(); tree.childrenAsync.subscribe(() => { tree.childrenAsync.subscribe((children: Tree[]) => { expect(loadCount).toEqual(1, 'children should be loaded only once'); expect(tree.children.length).toEqual(2); expect(tree.children[0].value).toEqual(children[0].value); expect(tree.children[1].value).toEqual(children[1].value); done(); }); }); }); it('can load its children asynchronously: node with async children should be collapsed by defualt', () => { const tree = new Tree({ value: 'AsyncParent', loadChildren: (callback: Function) => { setTimeout(() => { callback([ { value: 'Child#1' }, { value: 'Child#2' } ]); }, 10); } }); expect(tree.foldingType).toEqual(FoldingType.Collapsed); }); });