ng2-tree-hackaday
Version:
angular2 component for visualizing data that can be naturally represented as a tree
765 lines (627 loc) • 22.7 kB
text/typescript
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);
});
});