diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index 897d270..fc68089 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -1,4 +1,5 @@
 define('pgadmin.browser', [
+  'sources/tree/tree',
   'sources/gettext', 'sources/url_for', 'require', 'jquery', 'underscore', 'underscore.string',
   'bootstrap', 'sources/pgadmin', 'pgadmin.alertifyjs', 'bundled_codemirror',
   'sources/check_node_visibility', 'sources/modify_animation', 'pgadmin.browser.utils', 'wcdocker',
@@ -10,6 +11,7 @@ define('pgadmin.browser', [
   'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
   'pgadmin.browser.keyboard',
 ], function(
+  tree,
   gettext, url_for, require, $, _, S, Bootstrap, pgAdmin, Alertify,
   codemirror, checkNodeVisibility, modifyAnimation
 ) {
@@ -86,6 +88,7 @@ define('pgadmin.browser', [
       });
 
       b.tree = $('#tree').aciTree('api');
+      b.treeMenu.register($('#tree'));
     };
 
   // Extend the browser class attributes
@@ -100,6 +103,7 @@ define('pgadmin.browser', [
     editor:null,
     // Left hand browser tree
     tree:null,
+    treeMenu: new tree.Tree(),
     // list of script to be loaded, when a certain type of node is loaded
     // It will be used to register extensions, tools, child node scripts,
     // etc.
diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js
new file mode 100644
index 0000000..8048771
--- /dev/null
+++ b/web/pgadmin/static/js/tree/tree.js
@@ -0,0 +1,275 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+
+class BaseTreeNode {
+  constructor(id, data, domNode, parent) {
+    this.id = id;
+    this.data = data;
+    this.setParent(parent);
+    this.children = [];
+    this.domNode = domNode;
+  }
+
+  hasParent() {
+    return isNotNullOrUndefined(this.parentNode);
+  }
+
+  parent() {
+    return this.parentNode;
+  }
+
+  setParent(parent) {
+    this.parentNode = parent;
+    if (isNotNullOrUndefined(parent)) {
+      this.path = this.id;
+      if (isNotNullOrUndefined(parent) && isNotNullOrUndefined(parent.path)) {
+        this.path = parent.path + '.' + this.id;
+      }
+    } else {
+      this.path = null;
+    }
+  }
+
+  getData() {
+    if (this.data === undefined) {
+      return undefined;
+    } else if (this.data === null) {
+      return null;
+    }
+    return Object.assign({}, this.data);
+  }
+
+  getHtmlIdentifier() {
+    return this.domNode;
+  }
+
+  reload(tree) {
+    // Implement it in the actual implementation
+    // This is more of a shell object.
+    throw 'Not Implemented', tree;
+  }
+
+  unload(tree) {
+    this.children.forEach(function(child) {
+      child.unload(tree);
+      child.setParent(null);
+    });
+    this.children = [];
+  }
+
+  anyParent(condition) {
+    let node = this;
+
+    while (node.hasParent()) {
+      node = node.parent();
+      if (condition(node)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  anyFamilyMember(condition) {
+    if(condition(this)) {
+      return true;
+    }
+
+    return this.anyParent(condition);
+  }
+}
+
+
+class BaseTree {
+  constructor() {
+    this.rootNode = this._createNewTreeNode(undefined, {});
+  }
+
+  addNewNode(id, data, domNode, parentPath) {
+    const parent = this.findNode(parentPath);
+    return this.createOrUpdateNode(id, data, parent, domNode);
+  }
+
+  findNode(path) {
+    if (path === null || path === undefined || path.length === 0) {
+      return this.rootNode;
+    }
+    return findInTree(this.rootNode, path.join('.'));
+  }
+
+  findNodeByDomElement(/* domElement */) {
+    // TODO::
+    // Think through the new implementation - How is it going to work with real
+    // domElement?
+    //
+    // NOTE: We already have implementation for the aciTree.
+    throw 'Not Implemented';
+  }
+
+  selected() {
+    throw 'Not Implemented';
+  }
+
+  selectNode(/* node */) {
+    throw 'Not Implemented';
+  }
+
+  register(/* $treeJQuery */) {
+    throw 'Not Implemented';
+  }
+
+  _createNewTreeNode(/* id, data, domNode, parent */) {
+    throw 'Not Implemented';
+  }
+
+  createOrUpdateNode(id, data, parent, domNode) {
+    let oldNodePath = [id];
+    if(parent !== null && parent !== undefined) {
+      oldNodePath = [parent.path, id];
+    }
+    const oldNode = this.findNode(oldNodePath);
+    if (oldNode !== null) {
+      oldNode.data = Object.assign({}, data);
+      return oldNode;
+    }
+
+    const node = this._createNewTreeNode(id, data, domNode, parent);
+    if (parent === this.rootNode) {
+      node.parentNode = null;
+    }
+    if (isNotNullOrUndefined(parent)) {
+      parent.children.push(node);
+    }
+    return node;
+  }
+}
+
+
+function findInTree(rootNode, path) {
+  if (path === null) {
+    return rootNode;
+  }
+  return (function findInNode(currentNode) {
+    for (let i = 0, length = currentNode.children.length; i < length; i++) {
+      const calculatedNode = findInNode(currentNode.children[i]);
+      if (calculatedNode !== null) {
+        return calculatedNode;
+      }
+    }
+
+    if (currentNode.path === path) {
+      return currentNode;
+    } else {
+      return null;
+    }
+  })(rootNode);
+}
+
+
+class ACITreeNode extends BaseTreeNode {
+  constructor(id, data, domNode, parent) {
+    super(id, data, domNode, parent);
+  }
+
+  reload(tree) {
+    this.unload(tree);
+    tree.aciTreeApi.setInode(this.domNode);
+    tree.aciTreeApi.deselect(this.domNode);
+
+    setTimeout(() => {
+      tree.selectNode(this.domNode);
+    }, 0);
+  }
+
+  unload(tree) {
+    // Do not pass on tree object to 'super' object to avoid unloading the
+    // children recursively for performance reason.
+    //
+    // Any way - unloading using 'aciTreeApi.unload(...)' will any way unload
+    // all children.
+    super.unload(null);
+
+    if (isNotNullOrUndefined(tree) && isNotNullOrUndefined(tree.aciTreeApi)) {
+      tree.aciTreeApi.unload(this.domNode);
+    } else {
+      this.domNode = null;
+    }
+  }
+}
+
+
+function isNotNullOrUndefined(val) {
+  return (val !== undefined && val !== null);
+}
+
+
+
+class ACITree extends BaseTree {
+  constructor() {
+    super(arguments);
+    this.aciTreeApi = null;
+  }
+
+  _createNewTreeNode(id, data, domNode, parent) {
+    let node = new ACITreeNode(id, data, domNode, parent);
+    return node;
+  }
+
+  findNodeByDomElement(domElement) {
+    const path = this.translateTreeNodeIdFromACITree(domElement);
+    if(!path || !path[0]) {
+      return undefined;
+    }
+
+    return this.findNode(path);
+  }
+
+  selected() {
+    return this.aciTreeApi.selected();
+  }
+
+  selectNode(aciTreeIdentifier) {
+    this.aciTreeApi.select(aciTreeIdentifier);
+  }
+
+  register($treeJQuery) {
+    $treeJQuery.on('acitree', function (event, api, item, eventName) {
+      if (api.isItem(item)) {
+        if (eventName === 'added') {
+          const id = api.getId(item);
+          const data = api.itemData(item);
+          const parentId = this.translateTreeNodeIdFromACITree(api.parent(item));
+          this.addNewNode(id, data, item, parentId);
+        }
+      }
+    }.bind(this));
+    this.aciTreeApi = $treeJQuery.aciTree('api');
+  }
+
+  translateTreeNodeIdFromACITree(aciTreeNode) {
+    let currentTreeNode = aciTreeNode;
+    let path = [];
+    while (currentTreeNode !== null && currentTreeNode !== undefined && currentTreeNode.length > 0) {
+      path.unshift(this.aciTreeApi.getId(currentTreeNode));
+      if (this.aciTreeApi.hasParent(currentTreeNode)) {
+        currentTreeNode = this.aciTreeApi.parent(currentTreeNode);
+      } else {
+        break;
+      }
+    }
+    return path;
+  }
+
+  static test() {
+    return 'ACITree';
+  }
+}
+
+export { ACITree as Tree, ACITreeNode as TreeNode };
diff --git a/web/regression/javascript/tree/tree_fake.js b/web/regression/javascript/tree/tree_fake.js
new file mode 100644
index 0000000..b285a45
--- /dev/null
+++ b/web/regression/javascript/tree/tree_fake.js
@@ -0,0 +1,69 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {Tree} from '../../../pgadmin/static/js/tree/tree';
+
+export class TreeFake extends Tree {
+  constructor() {
+    super();
+    this.aciTreeToOurTreeTranslator = {};
+    this.aciTreeApi = jasmine.createSpyObj(
+      ['ACITreeApi'], ['setInode', 'unload', 'deselect', 'select']);
+  }
+
+  addNewNode(id, data, domNode, path) {
+    this.aciTreeToOurTreeTranslator[id] = [id];
+    if (path !== null && path !== undefined) {
+      this.aciTreeToOurTreeTranslator[id] = path.concat(id);
+    }
+    return super.addNewNode(id, data, domNode, path);
+  }
+
+  addChild(parent, child) {
+    child.setParent(parent);
+    this.aciTreeToOurTreeTranslator[child.id] = this.aciTreeToOurTreeTranslator[parent.id].concat(child.id);
+    parent.children.push(child);
+  }
+
+  hasParent(aciTreeNode) {
+    return this.translateTreeNodeIdFromACITree(aciTreeNode).length > 1;
+  }
+
+  parent(aciTreeNode) {
+    if (this.hasParent(aciTreeNode)) {
+      let path = this.translateTreeNodeIdFromACITree(aciTreeNode);
+      return [{id: this.findNode(path).parent().id}];
+    }
+
+    return null;
+  }
+
+  translateTreeNodeIdFromACITree(aciTreeNode) {
+    if(aciTreeNode === undefined || aciTreeNode[0] === undefined) {
+      return null;
+    }
+    return this.aciTreeToOurTreeTranslator[aciTreeNode[0].id];
+  }
+
+  itemData(aciTreeNode) {
+    let node = this.findNodeByDomElement(aciTreeNode);
+    if (node === undefined || node === null) {
+      return undefined;
+    }
+    return node.getData();
+  }
+
+  selected() {
+    return this.selectedNode;
+  }
+
+  selectNode(selectedNode) {
+    this.selectedNode = selectedNode;
+  }
+}
diff --git a/web/regression/javascript/tree/tree_spec.js b/web/regression/javascript/tree/tree_spec.js
new file mode 100644
index 0000000..164ceb5
--- /dev/null
+++ b/web/regression/javascript/tree/tree_spec.js
@@ -0,0 +1,421 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import {Tree, TreeNode} from '../../../pgadmin/static/js/tree/tree';
+import {TreeFake} from './tree_fake';
+
+const context = describe;
+
+const treeTests = (treeClass, setDefaultCallBack) => {
+  let tree;
+  beforeEach(() => {
+    tree = new treeClass();
+  });
+
+  describe('#addNewNode', () => {
+    describe('when add a new root element', () => {
+      context('using [] as the parent', () => {
+        beforeEach(() => {
+          tree.addNewNode('some new node', {data: 'interesting'}, undefined, []);
+        });
+
+        it('can be retrieved', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.data).toEqual({data: 'interesting'});
+        });
+
+        it('return false for #hasParent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.hasParent()).toBe(false);
+        });
+
+        it('return null for #parent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.parent()).toBeNull();
+        });
+      });
+
+      context('using null as the parent', () => {
+        beforeEach(() => {
+          tree.addNewNode('some new node', {data: 'interesting'}, undefined, null);
+        });
+
+        it('can be retrieved', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.data).toEqual({data: 'interesting'});
+        });
+
+        it('return false for #hasParent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.hasParent()).toBe(false);
+        });
+
+        it('return null for #parent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.parent()).toBeNull();
+        });
+      });
+
+      context('using undefined as the parent', () => {
+        beforeEach(() => {
+          tree.addNewNode('some new node', {data: 'interesting'});
+        });
+
+        it('can be retrieved', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.data).toEqual({data: 'interesting'});
+        });
+
+        it('return false for #hasParent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.hasParent()).toBe(false);
+        });
+
+        it('return null for #parent()', () => {
+          const node = tree.findNode(['some new node']);
+          expect(node.parent()).toBeNull();
+        });
+      });
+    });
+
+    describe('when add a new element as a child', () => {
+      let parentNode;
+      beforeEach(() => {
+        parentNode = tree.addNewNode('parent node', {data: 'parent data'}, undefined, []);
+        tree.addNewNode('some new node', {data: 'interesting'}, undefined, ['parent' +
+        ' node']);
+      });
+
+      it('can be retrieved', () => {
+        const node = tree.findNode(['parent node', 'some new node']);
+        expect(node.data).toEqual({data: 'interesting'});
+      });
+
+      it('return true for #hasParent()', () => {
+        const node = tree.findNode(['parent node', 'some new node']);
+        expect(node.hasParent()).toBe(true);
+      });
+
+      it('return "parent node" object for #parent()', () => {
+        const node = tree.findNode(['parent node', 'some new node']);
+        expect(node.parent()).toEqual(parentNode);
+      });
+    });
+
+    describe('when add an element that already exists under a parent', () => {
+      beforeEach(() => {
+        tree.addNewNode('parent node', {data: 'parent data'}, undefined, []);
+        tree.addNewNode('some new node', {data: 'interesting'}, undefined, ['parent' +
+        ' node']);
+      });
+
+      it('does not add a new child', () => {
+        tree.addNewNode('some new node', {data: 'interesting 1'}, undefined, ['parent' +
+        ' node']);
+        const parentNode = tree.findNode(['parent node']);
+        expect(parentNode.children.length).toBe(1);
+      });
+
+      it('updates the existing node data', () => {
+        tree.addNewNode('some new node', {data: 'interesting 1'}, undefined, ['parent' +
+        ' node']);
+        const node = tree.findNode(['parent node', 'some new node']);
+        expect(node.data).toEqual({data: 'interesting 1'});
+      });
+    });
+  });
+
+  describe('#translateTreeNodeIdFromACITree', () => {
+    let aciTreeApi;
+    beforeEach(() => {
+      aciTreeApi = jasmine.createSpyObj('ACITreeApi', [
+        'hasParent',
+        'parent',
+        'getId',
+      ]);
+
+      aciTreeApi.getId.and.callFake((node) => {
+        return node[0].id;
+      });
+      tree.aciTreeApi = aciTreeApi;
+    });
+
+    describe('When tree as a single level', () => {
+      beforeEach(() => {
+        aciTreeApi.hasParent.and.returnValue(false);
+      });
+
+      it('returns an array with the ID of the first level', () => {
+        let node = [{
+          id: 'some id',
+        }];
+        tree.addNewNode('some id', {}, undefined, []);
+
+        expect(tree.translateTreeNodeIdFromACITree(node)).toEqual(['some id']);
+      });
+    });
+
+    describe('When tree as a 2 levels', () => {
+      describe('When we try to retrieve the node in the second level', () => {
+        it('returns an array with the ID of the first level and second level', () => {
+          aciTreeApi.hasParent.and.returnValues(true, false);
+          aciTreeApi.parent.and.returnValue([{
+            id: 'parent id',
+          }]);
+          let node = [{
+            id: 'some id',
+          }];
+
+          tree.addNewNode('parent id', {}, undefined, []);
+          tree.addNewNode('some id', {}, undefined, ['parent id']);
+
+          expect(tree.translateTreeNodeIdFromACITree(node))
+            .toEqual(['parent id', 'some id']);
+        });
+      });
+    });
+  });
+
+  describe('#selected', () => {
+    context('a node in the tree is selected', () => {
+      it('returns that node object', () => {
+        let selectedNode = new TreeNode('bamm', {}, []);
+        setDefaultCallBack(tree, selectedNode);
+        expect(tree.selected()).toEqual(selectedNode);
+      });
+    });
+  });
+
+  describe('#findNodeByTreeElement', () => {
+    context('retrieve data from node not found', () => {
+      it('return undefined', () => {
+        let aciTreeApi = jasmine.createSpyObj('ACITreeApi', [
+          'hasParent',
+          'parent',
+          'getId',
+        ]);
+
+        aciTreeApi.getId.and.callFake((node) => {
+          return node[0].id;
+        });
+        tree.aciTreeApi = aciTreeApi;
+        expect(tree.findNodeByDomElement(['<li>something</li>'])).toBeUndefined();
+      });
+    });
+  });
+};
+
+describe('tree tests', () => {
+  describe('TreeNode', () => {
+    describe('#hasParent', () => {
+      context('parent is null', () => {
+        it('returns false', () => {
+          let treeNode = new TreeNode('123', {}, [], null);
+          expect(treeNode.hasParent()).toBe(false);
+        });
+      });
+      context('parent is undefined', () => {
+        it('returns false', () => {
+          let treeNode = new TreeNode('123', {}, [], undefined);
+          expect(treeNode.hasParent()).toBe(false);
+        });
+      });
+      context('parent exists', () => {
+        it('returns true', () => {
+          let parentNode = new TreeNode('456', {}, []);
+          let treeNode = new TreeNode('123', {}, [], parentNode);
+          expect(treeNode.hasParent()).toBe(true);
+        });
+      });
+    });
+
+    describe('#reload', () => {
+      let tree;
+      let level2;
+      beforeEach(() => {
+        tree = new TreeFake();
+        tree.addNewNode('level1', {data: 'interesting'}, [{id: 'level1'}], []);
+        level2 = tree.addNewNode('level2', {data: 'data'}, [{id: 'level2'}], ['level1']);
+        tree.addNewNode('level3', {data: 'more data'}, [{id: 'level3'}], ['level1', 'level2']);
+
+        tree.aciTreeApi = jasmine.createSpyObj(
+          'ACITreeApi', ['setInode', 'unload', 'deselect', 'select']);
+      });
+
+      it('reloads the node and its children', () => {
+        level2.reload(tree);
+        expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2);
+      });
+
+      it('does not reload the children of node', () => {
+        level2.reload(tree);
+        expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull();
+      });
+
+      it('select the node', (done) => {
+        level2.reload(tree);
+        setTimeout(() => {
+          expect(tree.selected()).toEqual([{id: 'level2'}]);
+          done();
+        }, 20);
+      });
+
+      describe('ACITree specific', () => {
+        it('sets the current node as a Inode, changing the Icon back to +', () => {
+          level2.reload(tree);
+          expect(tree.aciTreeApi.setInode).toHaveBeenCalledWith([{id: 'level2'}]);
+        });
+
+        it('deselect the node and selects it again to trigger ACI tree' +
+          ' events', (done) => {
+          level2.reload(tree);
+          setTimeout(() => {
+            expect(tree.aciTreeApi.deselect).toHaveBeenCalledWith([{id: 'level2'}]);
+            done();
+          }, 20);
+        });
+      });
+    });
+
+    describe('#unload', () => {
+      let tree;
+      let level2;
+      beforeEach(() => {
+        tree = new TreeFake();
+        tree.addNewNode('level1', {data: 'interesting'}, ['<li>level1</li>'], []);
+        level2 = tree.addNewNode('level2', {data: 'data'}, ['<li>level2</li>'], ['level1']);
+        tree.addNewNode('level3', {data: 'more data'}, ['<li>level3</li>'], ['level1', 'level2']);
+        tree.aciTreeApi = jasmine.createSpyObj('ACITreeApi', ['unload']);
+      });
+
+      it('unloads the children of the current node', () => {
+        level2.unload(tree);
+        expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2);
+        expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull();
+      });
+
+      it('calls unload on the ACI Tree', () => {
+        level2.unload(tree);
+        expect(tree.aciTreeApi.unload).toHaveBeenCalledWith(['<li>level2</li>']);
+      });
+    });
+  });
+
+  describe('Tree', () => {
+    function realTreeSelectNode(tree, selectedNode) {
+      let aciTreeApi = jasmine.createSpyObj('ACITreeApi', [
+        'selected',
+      ]);
+      tree.aciTreeApi = aciTreeApi;
+      aciTreeApi.selected.and.returnValue(selectedNode);
+    }
+
+    treeTests(Tree, realTreeSelectNode);
+  });
+
+  describe('TreeFake', () => {
+    function fakeTreeSelectNode(tree, selectedNode) {
+      tree.selectNode(selectedNode);
+    }
+
+    treeTests(TreeFake, fakeTreeSelectNode);
+
+    describe('#hasParent', () => {
+      context('tree contains multiple levels', () => {
+        let tree;
+        beforeEach(() => {
+          tree = new TreeFake();
+          tree.addNewNode('level1', {data: 'interesting'}, undefined, []);
+          tree.addNewNode('level2', {data: 'interesting'}, undefined, ['level1']);
+        });
+
+        context('node is at the first level', () => {
+          it('returns false', () => {
+            expect(tree.hasParent([{id: 'level1'}])).toBe(false);
+          });
+        });
+
+        context('node is at the second level', () => {
+          it('returns true', () => {
+            expect(tree.hasParent([{id: 'level2'}])).toBe(true);
+          });
+        });
+      });
+    });
+
+    describe('#parent', () => {
+      let tree;
+      beforeEach(() => {
+        tree = new TreeFake();
+        tree.addNewNode('level1', {data: 'interesting'}, undefined, []);
+        tree.addNewNode('level2', {data: 'interesting'}, undefined, ['level1']);
+      });
+
+      context('node is the root', () => {
+        it('returns null', () => {
+          expect(tree.parent([{id: 'level1'}])).toBeNull();
+        });
+      });
+
+      context('node is not root', () => {
+        it('returns root element', () => {
+          expect(tree.parent([{id: 'level2'}])).toEqual([{id: 'level1'}]);
+        });
+      });
+    });
+
+    describe('#itemData', () => {
+      let tree;
+      beforeEach(() => {
+        tree = new TreeFake();
+        tree.addNewNode('level1', {data: 'interesting'}, undefined, []);
+        tree.addNewNode('level2', {data: 'expected data'}, undefined, ['level1']);
+      });
+
+      context('retrieve data from the node', () => {
+        it('return the node data', () => {
+          expect(tree.itemData([{id: 'level2'}])).toEqual({
+            data: 'expected' +
+            ' data',
+          });
+        });
+      });
+
+      context('retrieve data from node not found', () => {
+        it('return undefined', () => {
+          expect(tree.itemData([{id: 'bamm'}])).toBeUndefined();
+        });
+      });
+    });
+
+    describe('#addChild', () => {
+      let root, child;
+      beforeEach(() => {
+        let tree = new TreeFake();
+        root = tree.addNewNode('root', {}, [{id: 'root'}]);
+        child = new TreeNode('node.1', {}, [{id: 'node.1'}]);
+        tree.addChild(root, child);
+      });
+
+      it('adds a new child to a node', () => {
+        expect(root.children).toEqual([child]);
+      });
+
+      it('changes the parent of the child node', () => {
+        expect(root.children[0].parentNode).toEqual(root);
+        expect(child.parentNode).toEqual(root);
+      });
+
+      it('changes the path of the child', () => {
+        expect(child.path).toEqual('root.node.1');
+      });
+    });
+  });
+});
+
