UNPKG

jquery.fancytree

Version:

jQuery tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkboxes, drag'n'drop, and lazy loading

1,339 lines (1,117 loc) 53.4 kB
jQuery(document).ready(function(){ /*globals TEST_TOOLS, QUnit */ /* jshint -W081 */ // Ignore 'W081: Too many var statements' var TEST_DATA, TESTDATA_NODES, TESTDATA_TOPNODES, TESTDATA_VISIBLENODES, $ = jQuery, // Use tools from test-tools.js tools = TEST_TOOLS; /******************************************************************************* * test data */ TEST_DATA = [ {title: "simple node (no explicit id, so a default key is generated)" }, {key: "2", title: "item1 with key and tooltip", tooltip: "Look, a tool tip!" }, {key: "3", title: "<span>item2 with <b>html</b> inside a span tag</span>" }, {key: "4", title: "this nodes uses 'nolink', so no &lt;a> tag is generated", nolink: true}, {key: "5", title: "using href", href: "http://www.wwWendt.de/" }, {key: "6", title: "node with some extra classes (will be added to the generated markup)", extraClasses: "my-extra-class" }, {key: "10", title: "Folder 1", folder: true, children: [ {key: "10_1", title: "Sub-item 1.1", children: [ {key: "10_1_1", title: "Sub-item 1.1.1"}, {key: "10_1_2", title: "Sub-item 1.1.2"} ]}, {key: "10_2", title: "Sub-item 1.2", children: [ {key: "10_2_1", title: "Sub-item 1.2.1"}, {key: "10_2_2", title: "Sub-item 1.2.2"} ]} ]}, {key: "20", title: "Simple node with active children (expand)", expanded: true, children: [ {key: "20_1", title: "Sub-item 2.1", children: [ {key: "20_1_1", title: "Sub-item 2.1.1"}, {key: "20_1_2", title: "Sub-item 2.1.2"} ]}, {key: "20_2", title: "Sub-item 2.2", children: [ {key: "20_2_1", title: "Sub-item 2.2.1"}, {key: "20_2_2", title: "Sub-item 2.2.2"} ]} ]}, {key: "30", title: "Lazy folder", folder: true, lazy: true }, {key: "31", title: "Lazy folder (preload)", folder: true, lazy: true, preload: true }, {key: "32", title: "Lazy folder (expand on load)", folder: true, lazy: true, expanded: true } ]; TESTDATA_NODES = 23, TESTDATA_TOPNODES = 11, TESTDATA_VISIBLENODES = 13; /******************************************************************************* * Initialize QUnit */ tools.initQUnit(); // Create an Info section (will be expanded when tests are completed) tools.createInfoSection(); // Silence, please $.ui.fancytree.debugLevel = 1; /******************************************************************************* * Module Init */ QUnit.module("Initialization"); QUnit.test("Static members", function(assert) { tools.setup(assert); assert.expect(5); assert.ok($.isFunction($.ui.fancytree.debug), "ui.fancytree.debug function is defined"); assert.equal($(":ui-fancytree").length, 0, "no tree instance exists"); // equal($.ui.fancytree._nextId, 1, "next tree instance counter is 1"); assert.equal($.ui.fancytree.getTree(), null, "getTree() no tree instance exists"); assert.equal($.ui.fancytree.getTree(0), null, "getTree(0) no tree instance exists"); assert.equal($.ui.fancytree.getTree(1), null, "getTree(0) no tree instance exists"); }); QUnit.test("Create Fancytree", function(assert) { tools.setup(assert); assert.expect(13); var tree, widget, insideConstructor = true; $("#tree").fancytree({ source: TEST_DATA, generateIds: true, // for testing create: function(event, data){ assert.ok(insideConstructor, "running synchronously"); widget = $("div#tree").data("ui-fancytree") || $("div#tree").data("fancytree"); tree = widget.tree; } }); insideConstructor = false; assert.equal($(":ui-fancytree").length, 1, ":ui-fancytree widget selector works"); assert.ok(!!widget, "widget is attached to div#tree"); assert.ok(!!widget.tree, "widget.tree is defined"); // equal(widget.tree._id, 1, "tree id is 1"); assert.ok($("#tree").fancytree("getTree") === tree, "$().fancytree('getTree')"); assert.ok($("#tree").fancytree("getActiveNode") === null, "$().fancytree('getActiveNode')"); assert.equal($.ui.fancytree.getTree(), tree, "getTree() exists"); assert.ok($.ui.fancytree.getTree(0) === tree, "getTree(0) exists"); assert.ok($.ui.fancytree.getTree("#tree") === tree, "getTree('#tree') exists"); assert.ok($.ui.fancytree.getTree(1) === null, "getTree(1) does not exist"); assert.ok($.ui.fancytree.getTree("#foobar") === null, "getTree(#foobar) does not exist"); assert.equal($("div#tree ul").length, 2, "collapsed children are NOT rendered"); assert.equal($("div#tree li").length, TESTDATA_VISIBLENODES, "collapsed nodes are NOT rendered"); }); QUnit.test("Create Fancytree - init", function(assert) { tools.setup(assert); assert.expect(19); var tree, widget, done = assert.async(), insideConstructor = true; $("#tree").fancytree({ source: TEST_DATA, generateIds: true, // for testing create: function(event, data){ assert.equal(event.type, "fancytreecreate", "receive `create` callback"); assert.ok(insideConstructor, "running synchronously"); assert.ok(!!data, "event data is empty"); assert.equal(this.nodeName, "DIV", "`this` is div#tree"); assert.ok($(">ul:first", this).hasClass("fancytree-container"), "div#tree contains ul.fancytree-container"); widget = $(this).data("ui-fancytree") || $(this).data("fancytree"); assert.ok(!!widget, "widget is attached to div#tree"); tree = widget.tree; assert.equal(tree.rootNode.children, null, "`tree.rootNode` is empty"); assert.equal($("div#tree").hasClass("ui-widget"), false, "div#tree has no widget style yet"); }, init: function(event, data){ assert.equal(event.type, "fancytreeinit", "receive `init` callback"); assert.equal(data.status, true, "`init` status is true"); assert.ok(insideConstructor, "running synchronously"); assert.ok(!!data.tree.rootNode, "`data.tree` is the tree object"); assert.equal(data.options.source.length, TESTDATA_TOPNODES, "data.options.contains widget options"); // equal($("div#tree").hasClass("ui-widget"), true, "div#tree has ui-widget class"); assert.equal($(this).attr("id"), "tree", "`this` is div#tree"); assert.equal(data.tree.rootNode.children.length, TESTDATA_TOPNODES, "tree.rootNode has all child nodes"); // var tree = data.tree; assert.equal($("li#ft_2 span.fancytree-title").attr("title"), "Look, a tool tip!", "tooltip set"); assert.equal($("li#ft_3 span.fancytree-title").html().toLowerCase(), "<span>item2 with <b>html</b> inside a span tag</span>", "raw html allowed"); // assert.equal($("li#ft_4 a.fancytree-title").html(), null, "`nolink` suppresses <a> tag"); // assert.equal($("li#ft_4 span.fancytree-title").length, 1, "`nolink` uses <span> tag"); assert.equal($("li#ft_4 span.fancytree-title").length, 1, "using <span> tag"); // assert.equal($("li#ft_5 a.fancytree-title").attr("href"), "http://www.wwWendt.de/", "href set"); assert.ok($("li#ft_6 span.fancytree-node").hasClass("my-extra-class"), "custom class added"); done(); } }).on("fancytreecreate", function(event, data){ // TODO: event is triggered, but only if we called done() before // but then, the equal() call is added to the following test // equal(event.type, "fancytreecreate", "receive `dynatreecreate` bound event"); }).on("fancytreeinit", function(event, data){ // equal(event.type, "fancytreeinit", "receive `init` bound event"); // done(); }); insideConstructor = false; }); QUnit.test("Init node status from source", function(assert) { tools.setup(assert); assert.expect(3); var done = assert.async(); // Add some status info to TEST_DATA (make a deep copy first!) var children = $.extend(true, [], TEST_DATA); // activate node #10_1_2 children[6].children[0].children[1].active = true; // select node #10_1_1 children[6].children[0].children[0].selected = true; $("#tree").fancytree({ source: children, init: function(event, data){ var tree = data.tree, node = tree.getNodeByKey("10_1_2"); assert.ok(tree.activeNode === node, "node was activated"); assert.ok($("#tree").fancytree("getActiveNode") === node, "$().fancytree('getActiveNode')"); node = tree.getNodeByKey("10_1_1"); assert.equal(node.selected, true, "node was selected"); done(); } }); }); QUnit.test("Init node with custom data", function(assert) { tools.setup(assert); assert.expect(2); var done = assert.async(); // Add some status info to TEST_DATA (make a deep copy first!) var children = $.extend(true, [], TEST_DATA); // node #10_1_1 children[6].children[0].children[0].foo = "phew"; // node #10_1_2 children[6].children[0].children[1].bar = false; $("#tree").fancytree({ source: children, init: function(event, data){ assert.equal(tools.getNode("10_1_1").data.foo, "phew", "add custom string data"); assert.equal(tools.getNode("10_1_2").data.bar, false, "add custom bool data"); done(); } }); }); QUnit.test("Custom icons (node.icon)", function(assert) { tools.setup(assert); assert.expect(13); var done = assert.async(); var children = [ {key: "1", title: "node 1" }, {key: "2", title: "node 2", icon: true}, {key: "3", title: "node 3", icon: false}, {key: "4", title: "node 4", icon: "custom-class"}, {key: "5", title: "node 5", icon: "custom_icon.png"}, {key: "6", title: "node 6", iconClass: "custom-class"} ]; $("#tree").fancytree({ source: children, generateIds: true, init: function(event, data){ assert.equal( tools.getNode("4").data.icon, undefined, "node.data.icon is not set"); assert.equal( tools.getNode("4").icon, "custom-class", "node.icon is set"); assert.equal( tools.getNode("5").icon, "custom_icon.png", "node.icon is set"); assert.ok( $("#ft_1 span.fancytree-icon").length === 1, "icon span exists by default"); assert.ok( $("#ft_2 span.fancytree-icon").length === 1, "icon: true shows icon"); assert.ok( $("#ft_3 span.fancytree-icon").length === 0, "icon: false hides icon"); // custom class assert.ok( $("#ft_4 span.fancytree-custom-icon").length === 1, "custom icons have span.fancytree-custom-icon"); assert.ok( $("#ft_4 span.fancytree-custom-icon").hasClass("custom-class"), "custom icons have custom class added"); assert.ok( $("#ft_4 .fancytree-icon").length === 0, "custom icons don't have .fancytree-icon"); // custom image // Note: IE <= 7 prepends the path to the src attribute, so we must test with indexOf: // assert.ok( $("#ft_5 img.fancytree-icon").attr("src") === "custom_icon.png", "image icon <img> exists"); assert.ok( $("#ft_5 img.fancytree-icon").attr("src").indexOf("custom_icon.png") >= 0, "image icon <img> exists"); assert.ok( $("#ft_5 span.fancytree-icon").length === 0, "image icon <span> not exists"); assert.ok( $("#ft_5 .fancytree-custom-icon").length === 0, "image icons don't have .fancytree-custom-icon"); // auto-migration for iconClass assert.ok( $("#ft_6 span.fancytree-custom-icon").hasClass("custom-class"), "migration for deprecated iconClass"); done(); } }); }); QUnit.test("Custom icons (options.icon)", function(assert) { tools.setup(assert); assert.expect(16); var done = assert.async(); var children = [ {key: "1", title: "node 1" }, {key: "2", title: "node 2" }, {key: "3", title: "node 3" }, {key: "4", title: "node 4" }, {key: "5", title: "node 5" }, {key: "6", title: "node 6", icon: false }, {key: "7", title: "node 7", icon: false }, {key: "8", title: "node 8", icon: true }, {key: "9", title: "node 9", icon: true }, {key: "10", title: "node 10", icon: "custom-class-2" }, {key: "11", title: "node 11", icon: "custom-class-2" } ]; $("#tree").fancytree({ source: children, generateIds: true, icon: function(event, data){ switch( data.node.key ){ case "2": return true; case "3": return false; case "4": return "custom-class"; case "5": return "custom_icon.png"; case "7": return true; case "9": return false; case "11": return "custom-class"; } }, init: function(event, data){ assert.equal( tools.getNode("8").data.icon, undefined, "node.data.icon is not set"); assert.ok( $("#ft_1 span.fancytree-icon").length === 1, "icon span exists by default ('undefined')"); assert.ok( $("#ft_2 span.fancytree-icon").length === 1, "icon: true shows icon"); assert.ok( $("#ft_3 span.fancytree-icon").length === 0, "icon: false hides icon"); // custom class assert.ok( $("#ft_4 span.fancytree-custom-icon").length === 1, "custom icons have span.fancytree-custom-icon"); assert.ok( $("#ft_4 span.fancytree-custom-icon").hasClass("custom-class"), "custom icons have custom class added"); assert.ok( $("#ft_4 .fancytree-icon").length === 0, "custom icons don't have .fancytree-icon"); // custom image // Note: IE <= 7 prepends the path to the src attribute, so we must test with indexOf: // assert.ok( $("#ft_5 img.fancytree-icon").attr("src") === "custom_icon.png", "image icon <img> exists"); assert.ok( $("#ft_5 img.fancytree-icon").attr("src").indexOf("custom_icon.png") >= 0, "image icon <img> exists"); assert.ok( $("#ft_5 span.fancytree-icon").length === 0, "image icon <span> not exists"); assert.ok( $("#ft_5 .fancytree-custom-icon").length === 0, "image icons don't have .fancytree-custom-icon"); // callback overrides node.icon assert.ok( $("#ft_6 span.fancytree-icon").length === 0, "icon hidden (node.icon=false, options.icon=undefined)"); assert.ok( $("#ft_7 span.fancytree-icon").length === 1, "icon visible (node.icon=false, options.icon=true)"); assert.ok( $("#ft_8 span.fancytree-icon").length === 1, "icon visible (node.icon=true, options.icon=undefined)"); assert.ok( $("#ft_9 span.fancytree-icon").length === 0, "icon hidden (node.icon=true, options.icon=false)"); assert.ok( $("#ft_10 span.fancytree-custom-icon").hasClass("custom-class-2"), "using custom-class-2 (node.icon='custom-class-2', options.icon=undefined)"); assert.ok( $("#ft_11 span.fancytree-custom-icon").hasClass("custom-class"), "using custom-class (node.icon='custom-class-2', options.icon='custom-class')"); done(); } }); }); /******************************************************************************* * */ QUnit.module("API"); QUnit.test("FancytreeNode class methods", function(assert) { tools.setup(assert); assert.expect(39); $("#tree").fancytree({ source: TEST_DATA }); var res, ROOT_NODE_KEY, nodeAdded, tree = $("#tree").fancytree("getTree"), root = tree.rootNode, node = tools.getNode("10_1_2"); // Properties assert.equal(node.tree, tree, "node.tree"); assert.equal(node.parent, tools.getNode("10_1"), "node.parent"); assert.equal(node.key, "10_1_2", "node.key"); assert.equal(node.children, null, "node.children"); assert.equal(node.isStatusNode(), false, "node.isStatusNode()"); // this.ul = null; // this.li = null; // <li id='key' ftnode=this> tag // this.data = {}; // Methods // addChildren: function(children){ // addNode assert.throws(function(){ root.addNode({"title": "my title"}, "undefined mode"); }, "Fancytree assertion failed: Invalid mode: undefined mode"); nodeAdded = root.addNode({"title": "my title 1", "key": "add-node-1"}); assert.equal(root.children.slice(-1)[0].key, "add-node-1", "Node added at the last position"); nodeAdded.addNode({"title": "my title 2", "key": "add-node-2"}); assert.equal(nodeAdded.countChildren(), 1, "Children added"); assert.equal(nodeAdded.children[0].key, "add-node-2", "Children at first position"); nodeAdded.addNode({"title": "my title 3", "key": "add-node-3"}, "child"); assert.equal(nodeAdded.countChildren(), 2, "Children added"); assert.equal(nodeAdded.children.slice(-1)[0].key, "add-node-3", "Children added at last position"); nodeAdded.addNode({"title": "my title 4", "key": "add-node-4"}, "firstChild"); assert.equal(nodeAdded.countChildren(), 3, "Children added"); assert.equal(nodeAdded.children[0].key, "add-node-4", "Children add at the first position"); nodeAdded.children[0].addNode({"title": "my title 5", "key": "add-node-5"}, "before"); assert.equal(nodeAdded.children[0].key, "add-node-5", "Children added before element"); nodeAdded.children[0].addNode({"title": "my title 6", "key": "add-node-6"}, "after"); assert.equal(nodeAdded.children[1].key, "add-node-6", "Children added after element"); nodeAdded.removeChildren(); nodeAdded.addNode({"title": "my title 7", "key": "add-node-7"}, "firstChild"); assert.equal(nodeAdded.countChildren(), 1, "Children added at first even if no child"); nodeAdded.remove(); // applyPatch: function(patch) { // collapseSiblings: function() { // copyTp: function(targetNode, mode, map) { assert.equal(root.countChildren(), TESTDATA_NODES, "countChildren() - root"); assert.equal(root.countChildren(true), TESTDATA_NODES, "countChildren(true) - root"); assert.equal(root.countChildren(false), TESTDATA_TOPNODES, "countChildren(false) - root"); // debug: function(msg){ // discard: function(){ // findAll() assert.deepEqual(root.findAll("nomatchexpected$$"), [], "findAll() - no match"); assert.deepEqual(tools.getNodeKeyArray(root.findAll("with key")), ["2"], "findAll() - match title"); assert.deepEqual(tools.getNodeKeyArray(root.findAll("with KEY")), ["2"], "findAll() - match title (ignore case)"); res = root.findAll(function(n){ return n.isFolder(); }); assert.deepEqual(tools.getNodeKeyArray(res), ["10", "30", "31", "32"], "findAll() - custom match"); // findFirst() assert.equal(root.findFirst("nomatchexpected$$"), null, "findFirst() - no match"); assert.equal(root.findFirst("with key").key, "2", "findFirst() - match title"); assert.equal(root.findFirst("with KEY").key, "2", "findFirst() - match title (ignore case)"); res = root.findFirst(function(n){ return n.isFolder(); }); assert.equal(res.key, "10", "findFirst() - custom match"); // getChildren: function() { // getFirstChild: function() { assert.equal(node.getIndex(), 1, "getIndex()"); assert.equal(node.getIndexHier(), "7.1.2", "getIndexHier()"); assert.equal(node.getIndexHier("/"), "7/1/2", "getIndexHier('/')"); assert.equal(node.getKeyPath(), "/10/10_1/10_1_2", "getKeyPath()"); assert.equal(node.getKeyPath(false), "/10/10_1/10_1_2", "getKeyPath(false)"); assert.equal(node.getKeyPath(true), "/10/10_1", "getKeyPath(true)"); // getLastChild: function() { // getLevel: function() { // getNextSibling: function() { assert.equal(node.getParent().key, "10_1", "getParent()"); // getParentList: function(includeRoot, includeSelf) { ROOT_NODE_KEY = tree.rootNode.key; assert.deepEqual(tools.getNodeKeyArray(node.getParentList()), ["10", "10_1"], "getParentList()"); assert.deepEqual(tools.getNodeKeyArray(node.getParentList(false, false)), ["10", "10_1"], "getParentList(false, false)"); assert.deepEqual(tools.getNodeKeyArray(node.getParentList(true, true)), [ROOT_NODE_KEY, "10", "10_1", "10_1_2"], "getParentList(true, true)"); assert.deepEqual(tools.getNodeKeyArray(node.getParentList(false, true)), ["10", "10_1", "10_1_2"], "getParentList(false, true)"); assert.deepEqual(tools.getNodeKeyArray(node.getParentList(true, false)), [ROOT_NODE_KEY, "10", "10_1"], "getParentList(true, false)"); // getPrevSibling: function() { // hasChildren: function() { // hasFocus: function() { // isActive: function() { // isChildOf: function(otherNode) { // isDescendantOf: function(otherNode) { // isExpanded: function() { // isFolder: function() { // isFirstSibling: function() { // isLastSibling: function() { // isLazy: function() { // isLoaded() // isLoading: function() { // isStatusNode() // isUndefined() // load() ////isStatusNode: function() { // isRoot: function() { // isSelected: function() { // isVisible: function() { // makeVisible // moveTo: function(targetNode, mode, map) { // reloadChildren: function() { // render: function(force, deep) { // renderTitle: function() { // renderStatus: function() { // remove: function() { // removeChild: function(childNode) { // removeChildren: function() { // resetLazy() // scheduleAction: function(mode, ms) { // scrollIntoView // setActive: function(flag){ // setExpanded: function(flag){ // setFocus: function(){ // setSelected: function(flag){ // setTitle: function(title){ // sortChildren: function(title){ // toDict // toggleExpanded: function(){ // toggleSelected: function(){ // toString: function() { // visit: function(fn, includeSelf) { // visitParents: function(fn, includeSelf) { }); QUnit.test("Fancytree class methods", function(assert) { tools.setup(assert); assert.expect(14); $("#tree").fancytree({ source: TEST_DATA }); var c, node, tree = $("#tree").fancytree("getTree"); // Properties // tree.widget = widget; // tree.$div = widget.element; // tree.options = widget.options; // tree._id = $.ui.fancytree._nextId++; // tree.activeNode = null; // tree.focusNode = null; // tree.statusClassPropName = "span"; // tree.lastSelectedNode = null; // tree.rootNode = new FancytreeNode(fakeParent, { // title: "root", // key: "root_" + tree._id, // children: null // }); // tree.rootNode.parent = null; // // // Create root markup // $ul = $("<ul>", { // "class": "fancytree-container" // }).appendTo(this.$div); // tree.rootNode.ul = $ul[0]; // tree.nodeContainerAttrName = "li"; // Methods // TODO: activateByKey() assert.equal(tree.count(), TESTDATA_NODES, "count()"); // TODO: getFirstChild() assert.equal(tree.getNodeByKey("10_2").key, "10_2", "getNodeByKey()"); assert.equal(tree.getNodeByKey("foobar"), null, "getNodeByKey() not found"); node = tools.getNode("10_2"); assert.equal(tree.getNodeByKey("10_2_1", node).key, "10_2_1", "getNodeByKey(.., root)"); assert.equal(tree.getNodeByKey("10_1_1", node), null, "getNodeByKey(.., root) not found"); // tree.getSelectedNodes() assert.deepEqual(tools.getNodeKeyArray(tree.getSelectedNodes()), [], "getSelectedNodes() - empty"); assert.deepEqual(tools.getNodeKeyArray(tree.getSelectedNodes(true)), [], "getSelectedNodes(true) - empty"); tools.getNode("10_2").setSelected(); tools.getNode("10_2_1").setSelected(); tools.getNode("10_2_2").setSelected(); assert.deepEqual(tools.getNodeKeyArray(tree.getSelectedNodes()), ["10_2", "10_2_1", "10_2_2"], "getSelectedNodes()"); assert.deepEqual(tools.getNodeKeyArray(tree.getSelectedNodes(true)), ["10_2"], "getSelectedNodes(true)"); // reactivate: function(source) { // reload: function(source) { // render: function(force, deep) { // tree.toString() assert.equal(tree.toString(), "<Fancytree(#" + tree._id + ")>", "toString()"); assert.equal("" + tree, tree.toString(), "toString() implicit"); // tree.visit() c = 0; tree.visit(function(n){ c += 1; }); assert.equal(c, TESTDATA_NODES, "visit() - all"); c = 0; tree.visit(function(n){ c += 1; if(n.key === "10_1"){ return false; } }); assert.equal(c, 8, "visit() - interrupt"); c = 0; tree.visit(function(n){ c += 1; if(n.key === "10_1"){ return "skip"; } }); assert.equal(c, 21, "visit() - skip branch"); }); /******************************************************************************* * */ QUnit.module("Asynchronous API"); QUnit.test("trigger async expand", function(assert) { tools.setup(assert); assert.expect(4); var done = assert.async(); $("#tree").fancytree({ source: TEST_DATA }); // var node = $("#tree").fancytree("getActiveNode"); var tree = $("#tree").fancytree("getTree"), node = tree.getNodeByKey("10"); node.setExpanded().done(function(){ assert.ok(true, "called done()"); assert.equal(this.key, "10", "`this` is a FancytreeNode"); assert.equal(this.expanded, true, "node was expanded"); assert.ok($(this.span).hasClass("fancytree-expanded"), "node was rendered as expanded"); done(); }); }); QUnit.test("makeVisible not rendered deep node", function(assert) { tools.setup(assert); assert.expect(5); var done = assert.async(); $("#tree").fancytree({ source: TEST_DATA }); var node = tools.getNode("10_2_2"); assert.ok(node); assert.ok(!node.parent.isExpanded()); assert.ok(!node.li); // not rendered yet node.makeVisible().done(function () { assert.ok(node.parent.isExpanded()); assert.ok(node.li); // rendered done(); }); }); /******************************************************************************* * Simulated click events */ QUnit.module("events"); QUnit.test(".mousedown() to expand a folder", function(assert) { tools.setup(assert); assert.expect(8); var done = assert.async(); $("#tree").fancytree({ source: TEST_DATA, generateIds: true, beforeExpand: function(event, data){ assert.equal(event.type, "fancytreebeforeexpand", "receive `beforeExpand` callback"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok(!$(data.node.span).hasClass("fancytree-expanded"), "data.node.span has NOT class fancytree-expanded"); }, expand: function(event, data){ assert.equal(event.type, "fancytreeexpand", "receive `expand` callback"); assert.equal($(this).attr("id"), "tree", "`this` is div#tree"); assert.ok(!!data.tree.rootNode, "`data.tree` is the tree object"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok($(data.node.span).hasClass("fancytree-expanded"), "data.node.span has class fancytree-expanded"); done(); } }); $("#tree #ft_10 span.fancytree-expander").mousedown(); }); QUnit.test(".mousedown() to activate a node", function(assert) { tools.setup(assert); assert.expect(8); var done = assert.async(); $("#tree").fancytree({ source: TEST_DATA, generateIds: true, // for testing beforeActivate: function(event, data){ assert.equal(event.type, "fancytreebeforeactivate", "receive `beforeActivate` callback"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok(!$(data.node.span).hasClass("fancytree-active"), "data.node.span has NOT class fancytree-active"); }, activate: function(event, data){ assert.equal(event.type, "fancytreeactivate", "receive `activate` callback"); assert.ok(!!data.tree.rootNode, "`data.tree` is the tree object"); assert.equal($(this).attr("id"), "tree", "`this` is div#tree"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok($(data.node.span).hasClass("fancytree-active"), "data.node.span has class fancytree-active"); done(); } }); $("#tree #ft_2 span.fancytree-title").mousedown(); }); QUnit.test(".mousedown() to activate a folder (clickFolderMode 3 triggers expand)", function(assert) { tools.setup(assert); assert.expect(4); var done = assert.async(), sequence = 1; $("#tree").fancytree({ source: TEST_DATA, clickFolderMode: 3, generateIds: true, // for testing beforeActivate: function(event, data){ assert.equal(sequence++, 1, "receive `beforeActivate` callback"); }, activate: function(event, data){ assert.equal(sequence++, 2, "receive `activate` callback"); }, beforeExpand: function(event, data){ assert.equal(sequence++, 3, "receive `beforeExpand` callback"); }, expand: function(event, data){ assert.equal(sequence++, 4, "receive `expand` callback"); done(); } }); $("#tree #ft_10 span.fancytree-title").mousedown(); }); QUnit.test(".mousedown() to select a node", function(assert) { tools.setup(assert); assert.expect(8); var done = assert.async(); $("#tree").fancytree({ source: TEST_DATA, checkbox: true, generateIds: true, // for testing beforeSelect: function(event, data){ assert.equal(event.type, "fancytreebeforeselect", "receive `beforeSelect` callback"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok(!$(data.node.span).hasClass("fancytree-selected"), "data.node.span has NOT class fancytree-selected"); }, select: function(event, data){ assert.equal(event.type, "fancytreeselect", "receive `select` callback"); assert.ok(!!data.tree.rootNode, "`data.tree` is the tree object"); assert.equal($(this).attr("id"), "tree", "`this` is div#tree"); assert.ok($(data.node.span).hasClass("fancytree-node"), "data.node.span has class fancytree-node"); assert.ok($(data.node.span).hasClass("fancytree-selected"), "data.node.span has class fancytree-selected"); done(); } }); $("#tree #ft_2 span.fancytree-checkbox").mousedown(); }); QUnit.test("'modifyChild' event", function(assert) { tools.setup(assert); assert.expect(3); $("#tree").fancytree({ source: TEST_DATA, modifyChild: function(event, data) { var msg = (data.node.isRoot() ? "root" : data.node.key) + "." + event.type + "(" + data.operation + ", " + (data.childNode ? data.childNode.key : null) + ")"; if( data.operation === "custom1" ) { assert.equal(data.foo, "bar", "pass custom args"); } tools.appendEvent(assert, msg); } }); var tree = tools.getTree(); tree.getNodeByKey("2").setTitle("New title"); tree.getNodeByKey("2").addChildren({title: "New child", key: "2.1"}); tree.getNodeByKey("3").remove(); // move beneath same parent: modify('move') tree.getNodeByKey("4").moveTo(tree.getNodeByKey("2"), "before"); // move to another parent: remove+add tree.getNodeByKey("4").moveTo(tree.getNodeByKey("2.1"), "after"); // sortChildren: sort tree.getNodeByKey("2").sortChildren(); // custom trigger tree.getNodeByKey("10").triggerModifyChild("data", tree.getNodeByKey("10_1")); assert.throws(function(){ tree.getNodeByKey("10").triggerModifyChild("data", tree.getNodeByKey("2")); }, "raise error if childNode is invalid"); tree.getNodeByKey("5").triggerModify("custom1", {foo: "bar"}); assert.deepEqual(assert.EVENT_SEQUENCE, ["root.modifyChild(rename, 2)", "2.modifyChild(add, 2.1)", "root.modifyChild(remove, 3)", "root.modifyChild(move, 4)", "root.modifyChild(remove, 4)", "2.modifyChild(add, 4)", "2.modifyChild(sort, null)", "10.modifyChild(data, 10_1)", "root.modifyChild(custom1, 5)" ], "event sequence"); }); /******************************************************************************* * generateFormElements */ QUnit.module("generateFormElements()"); QUnit.test("multi select", function(assert) { tools.setup(assert); assert.expect(22); var $result, tree; $("#tree").fancytree({ source: TEST_DATA, generateIds: true }); tree = $.ui.fancytree.getTree(); $result = $("#fancytree_result_" + tree._id); assert.equal($result.length, 0, "result <div> not yet created"); tree.generateFormElements(); $result = $("#fancytree_result_" + tree._id); assert.equal($result.length, 1, "result <div> created"); assert.equal($result.is(":visible"), false, "result is hidden"); assert.equal($result.find("input").length, 0, "initial result is empty"); tools.getNode("10_1").setActive(); tools.getNode("10").setSelected(); tools.getNode("10_1").setSelected(); tools.getNode("10_1_1").setSelected(); tools.getNode("10_1_2").setSelected(); tree.generateFormElements(); assert.equal($result.find("input[type=radio]").length, 1, "one radio input element created for active node"); assert.equal($result.find("input[type=radio]").attr("name"), "ft_" + tree._id + "_active", "radio input name is 'ft_TREEID_active'"); assert.equal($result.find("input[type=radio]").attr("value"), "10_1", "radio input value is set to node key"); assert.equal($result.find("input[type=radio]").attr("checked"), "checked", "radio input is checked"); assert.equal($result.find("input[type=checkbox]").length, 4, "multiple checkbox input elements created for selected nodes"); assert.equal($result.find("input[name=ft_" + tree._id + "\\[\\]]").length, 4, "checkboxes name is 'ft_TREEID[]'"); assert.equal($result.find("input[type=checkbox][value=10_1]").length, 1, "checkboxes value is set to node keys"); assert.equal($result.find("input[type=checkbox]:checked").length, 4, "checkboxes are checked"); tree.generateFormElements(false, true); assert.equal($result.find("input[type=radio]").length, 1, "only active node created"); assert.equal($result.find("input[type=checkbox]").length, 0, "disable generation of selcted nodes"); tree.generateFormElements(true, false); assert.equal($result.find("input[type=radio]").length, 0, "disable generation of active node"); assert.equal($result.find("input[type=checkbox]").length, 4, "only selected nodes created"); tree.generateFormElements("cust_sel", "cust_act"); assert.equal($result.find("input[name=cust_act]").length, 1, "custom name for active node"); assert.equal($result.find("input[name=cust_sel]").length, 4, "custom name for selected nodes"); tree.generateFormElements(true, true, {stopOnParents: true}); assert.equal($result.find("input[type=checkbox]").length, 4, "stopOnParents ignored for selectMode 2"); tree.generateFormElements(true, true, { filter: function(node) { return true; } }); assert.equal($result.find("input[type=checkbox]").length, TESTDATA_NODES, "filter => true: generate all nodes"); tree.generateFormElements(true, true, { filter: function(node) { return node.isSelected(); } }); assert.equal($result.find("input[type=checkbox]").length, 4, "filter => isSelected(): generate selected nodes"); tree.generateFormElements(true, true, { filter: function(node) { return node.isActive(); } }); assert.equal($result.find("input[type=checkbox]").length, 1, "filter => isActive(): generate selected nodes"); }); QUnit.test("selectMode: 3", function(assert) { tools.setup(assert); assert.expect(4); var $result, tree; $("#tree").fancytree({ source: TEST_DATA, selectMode: 3, generateIds: true }); tree = $.ui.fancytree.getTree(); tools.getNode("10_1").setActive(); tools.getNode("10").setSelected(); tools.getNode("10_1").setSelected(); tools.getNode("10_1_1").setSelected(); tools.getNode("10_1_2").setSelected(); tree.generateFormElements(); $result = $("#fancytree_result_" + tree._id); assert.equal($result.find("input[type=radio]").length, 1, "generation of active node"); assert.equal($result.find("input[type=checkbox]").length, 1, "stopOnParents: only top node created"); tree.generateFormElements(true, true, {stopOnParents: false}); assert.equal($result.find("input[type=radio]").length, 1, "generation of active node"); assert.equal($result.find("input[type=checkbox]").length, 7, "stopOnParents: false: all nodes created"); }); /******************************************************************************* * Lazy loading */ QUnit.module("lazy loading"); QUnit.test("Using ajax options for `source`; .mousedown() expands a lazy folder", function(assert) { tools.setup(assert); assert.expect(19); var done = assert.async(), sequence = 1, isClicked = false; $("#tree").fancytree({ source: {url: "ajax-tree.json"}, generateIds: true, init: function(event, data){ assert.equal(sequence++, 3, "receive `init` callback"); assert.equal(data.tree.count(), TESTDATA_NODES, "lazy tree has 23 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES, "lazy tree has rendered 13 node elements"); // now expand a lazy folder isClicked = true; $("#tree #ft_30 span.fancytree-expander").mousedown(); }, beforeExpand: function(event, data){ assert.equal(sequence++, 4, "receive `beforeExpand` callback"); }, lazyLoad: function(event, data){ assert.equal(sequence++, 5, "receive `lazyLoad` callback"); assert.equal(data.node.isLoading(), false, "node.isLoading()"); data.result = {url: "ajax-sub2.json"}; }, postProcess: function(event, data){ if( !isClicked ) { assert.equal(sequence++, 1, "receive `postProcess` callback for root"); assert.equal(data.node.isLoading(), true, "node.isLoading()"); assert.equal(data.node.children.length, 1, "Dummy status node exists"); assert.equal(data.node.children[0].statusNodeType, "loading", "node.statusNodeType === 'loading'"); } else { assert.equal(sequence++, 6, "receive `postProcess` callback for node"); assert.equal(data.node.isLoading(), true, "node.isLoading()"); } }, loadChildren: function(event, data){ if( !isClicked ) { assert.equal(sequence++, 2, "receive `loadChildren` callback on init tree"); } else { assert.equal(sequence++, 7, "receive `loadChildren` callback on load node"); assert.equal(data.tree.count(), TESTDATA_NODES + 2, "lazy tree has 25 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES, "lazy tree has not yet rendered new node elements"); } }, expand: function(event, data){ assert.equal(sequence++, 8, "receive `expand` callback"); assert.equal(data.tree.count(), TESTDATA_NODES + 2, "lazy tree has 25 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES + 2, "lazy tree has rendered 15 node elements"); done(); } }); }); QUnit.test("Using $.ajax promise for `source`; .mousedown() expands a lazy folder", function(assert) { tools.setup(assert); assert.expect(12); var done = assert.async(), sequence = 1, isClicked = false; $("#tree").fancytree({ source: $.ajax({ url: "ajax-tree.json", dataType: "json" }), generateIds: true, init: function(event, data){ assert.equal(sequence++, 2, "receive `init` callback"); assert.equal(data.tree.count(), TESTDATA_NODES, "lazy tree has 23 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES, "lazy tree has rendered 13 node elements"); // now expand a lazy folder isClicked = true; $("#tree #ft_30 span.fancytree-expander").mousedown(); }, beforeExpand: function(event, data){ assert.equal(sequence++, 3, "receive `beforeExpand` callback"); }, lazyLoad: function(event, data){ assert.equal(sequence++, 4, "receive `lazyLoad` callback"); data.result = $.getJSON("ajax-sub2.json"); }, loadChildren: function(event, data){ if( !isClicked ) { assert.equal(sequence++, 1, "receive `loadChildren` callback on init tree"); } else { assert.equal(sequence++, 5, "receive `loadChildren` callback on load node"); assert.equal(data.tree.count(), TESTDATA_NODES + 2, "lazy tree has 25 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES, "lazy tree has not yet rendered new node elements"); } }, expand: function(event, data){ assert.equal(sequence++, 6, "receive `expand` callback"); assert.equal(data.tree.count(), TESTDATA_NODES + 2, "lazy tree has 25 nodes"); assert.equal($("#tree li").length, TESTDATA_VISIBLENODES + 2, "lazy tree has rendered 15 node elements"); done(); } }); }); /******************************************************************************/ QUnit.module("Selection mode 3"); QUnit.test("load behavior", function(assert) { tools.setup(assert); assert.expect(30); var tree; $("#tree").fancytree({ selectMode: 3, checkbox: true, source: [ {title: "n1", children: [ {title: "n1.1", selected: true}, {title: "n1.2", selected: false}, {title: "n1.3", selected: null} ]}, {title: "n2 (all selected)", children: [ {title: "n2.1", selected: true, unselectable: true, unselectableStatus: true}, {title: "n2.2", selected: true, unselectable: true, unselectableStatus: false}, {title: "n2.3", selected: true, unselectable: true, unselectableStatus: null} ]}, {title: "n3", children: [ {title: "n3.1", children: [ {title: "n3.1.1 (unselectable)", unselectable: true}, {title: "n3.1.2 (unselectable)", unselectable: true}, {title: "n3.1.3"} ]}, {title: "n3.2", children: [ {title: "n3.2.1 (unselectableStatus: true)", unselectable: true, unselectableStatus: true}, {title: "n3.2.2 (unselectableStatus: false)", unselectable: true, unselectableStatus: false}, {title: "n3.2.3"} ]}, {title: "n3.3", children: [ {title: "n3.3.1 (unselectableStatus: true, unselectableIgnore)", unselectable: true, unselectableStatus: true, unselectableIgnore: true}, {title: "n3.3.2 (unselectableStatus: false, unselectableIgnore)", unselectable: true, unselectableStatus: false, unselectableIgnore: true}, {title: "n3.3.3"} ]} ]}, {title: "n4 (radiogroup)", radiogroup: true, unselectable: true, children: [ {title: "n4.1 (selected)", selected: true}, {title: "n4.2"}, {title: "n4.3"} ]} ], init: function(event, data) { // Set key from first part of title data.tree.visit(function(n) { n.key = n.title.split(" ")[0]; }); }, generateIds: true }); tree = $.ui.fancytree.getTree(); tools.getNode("n1").setSelected(); assert.equal(tools.getNode("n1.1").selected, true, "propagate down `select` (simple child) 1/3"); assert.equal(tools.getNode("n1.2").selected, true, "propagate down `select` (simple child) 2/3"); assert.equal(tools.getNode("n1.3").selected, true, "propagate down `select` (simple child) 3/3"); tools.getNode("n1").setSelected(false); assert.equal(tools.getNode("n1.1").selected, false, "propagate down `deselect` (simple child)"); tools.getNode("n2").setSelected(); assert.equal(tools.getNode("n2.1").selected, true, "propagate down `select` (unselectable status: true)"); assert.equal(tools.getNode("n2.2").selected, false, "propagate down `select` (unselectable status: false)"); assert.equal(tools.getNode("n2.3").selected, true, "propagate down `select` (unselectable status: undefined)"); tools.getNode("n2").setSelected(false); assert.equal(tools.getNode("n2.1").selected, true, "propagate down `deselect` (unselectable status: true)"); assert.equal(tools.getNode("n2.2").selected, false, "propagate down `deselect` (unselectable status: false)"); assert.equal(tools.getNode("n2.3").selected, false, "propagate down `deselect` (unselectable status: undefined)"); // Check upward propagation tools.getNode("n3").setSelected(true); assert.equal(tools.getNode("n3.1").isSelected(), true, "propagate down `select` (unselectable): parent selected"); assert.equal(tools.getNode("n3.1.1").isSelected(), true, "propagate down `select` (unselectable): selected by api"); assert.equal(tools.getNode("n3.2").isPartsel(), true, "propagate down `select` (unselectable status: true&false): parent partsel"); assert.equal(tools.getNode("n3.3").isSelected(), true, "propagate down `select` (unselectable status: true&false, ignore): parent selected"); assert.equal(tools.getNode("n3.3.2").isSelected(), false, "propagate down `select` (unselectable status: false): not selected"); tools.getNode("n3").setSelected(false); assert.equal(tools.getNode("n3.1").isPartsel(), false, "propagate down `deselect` (unselectable): parent not partsel"); assert.equal(tools.getNode("n3.1.1").isSelected(), false, "propagate down `deselect` (unselectable): deselected by api"); assert.equal(tools.getNode("n3.2").isPartsel(), true, "propagate down `deselect` (unselectable status: true&false): parent partsel"); assert.equal(tools.getNode("n3.2.1").isSelected(), true, "propagate down `deselect` (unselectable status: true): not deselected"); assert.equal(tools.getNode("n3.3").isPartsel(), false, "propagate down `deselect` (unselectable status: true&false, ignore): parent not partsel"); assert.equal(tools.getNode("n3.3").isSelected(), false, "propagate down `deselect` (unselectable status: true&false, ignore): parent not selected"); assert.equal(tools.getNode("n3.3.1").isSelected(), true, "propagate down `deselect` (unselectable status: true): not deselected"); tools.getNode("n3.2.3").setSelected(true); assert.equal(tools.getNode("n3.2").isPartsel(), true, "propagate up `select`: parent partsel, because of deselected sibling"); tools.getNode("n3.2.3").setSelected(false); assert.equal(tools.getNode("n3.2").isPartsel(), true, "propagate up `deselect`: parent partsel, because of selected sibling"); tools.getNode("n3.3.3").setSelected(true); assert.equal(tools.getNode("n3.3").isSelected(), true, "propagate up `select`: parent selected, because of ignored siblings"); tools.getNode("n3.3.3").setSelected(false); assert.equal(tools.getNode("n3.3").isSelected(), false, "propagate up `deselect`: parent deselected, because of ignored siblings"); // radiogroup tools.getNode("n4.1").setSelected(); assert.equal(tools.getNode("n4.1").isSelected(), true, "radiogroup `select`: select first"); assert.ok(!tools.getNode("n4.2").isSelected() && !tools.getNode("n4.3").isSelected(), "radiogroup `select`: deselect siblings"); tools.getNode("n4.3").setSelected(); assert.equal(tools.getNode("n4.3").isSelected(), true, "radiogroup `select`: select last"); assert.ok(!tools.getNode("n4.1").isSelected() && !tools.getNode("n4.2").isSelected(), "radiogroup `select`: deselect siblings"); tree.visit(function(n){ n.setExpanded(); }); }); /******************************************************************************* * */ QUnit.module("add children & patches"); QUnit.test("add children", function(assert) { tools.setup(assert); assert.expect(15); var done = assert.async(), childList = [ {title: "New 1", key: "test1", tooltip: "new tip", data: {foo: "works"}}, {title: "New 2", key: "test2", folder: true, children: [ {title: "New 2.1", key: "test2_1"}, {title: "New 2.2", key: "test2_2"} ]}, {title: "New 3", key: "test3", expanded: true, children: [ {title: "New 3.1", key: "test3_1", selected: true}, {title: "New 3.2", key: "test3_2", extraClasses: "customClass"} ]} ]; $("#tree").fancytree({ source: TEST_DATA, lazyLoad: function(event, data){ data.result = {url: "ajax-sub2.json"}; }, init: function(event, data){ data.tree.rootNode.addChildren(childList); assert.equal(tools.getNodeTitle("test1"), "New 1", "simple node"); var $span = $(tools.getNode("test1").span); // assert.equal($span.find("a.fancytree-title").attr("title"), "new tip", "set tooltip"); assert.equal($span.find("span.fancytree-title").attr("title"), "new tip", "set tooltip"); assert.equal(tools.getNode("test1").data.foo, "works", "set custom data"); assert.equal(tools.getNode("test2").isFolder(), true, "is folder"); assert.equal(tools.getNode("test2").isExpanded(), false, "folder was collapsed"); assert.equal($(tools.getNode("test2").span).hasClass("fancytree-expanded"), false, "folder was rendered as collapsed"); assert.equal(tools.getNode("test2_1").title, "New 2.1", "subnode created"); assert.equal(tools.getNodeTitle("test2_1"), null, "subnode NOT rendered"); assert.equal(tools.getNode("test3").expanded, true, "node was expanded"); assert.equal($(tools.getNode("test3").span).hasClass("fancytree-expanded"), true, "folder was rendered as expanded"); assert.equal(tools.getNode("test3_1").title, "New 3.1", "subnode created"); assert.equal(tools.getNodeTitle("test3_1"), "New 3.1", "subnode rendered expanded"); assert.equal(tools.getNode("test3_1").isSelected(), true, "select"); assert.equal($(tools.getNode("test3_1").span).hasClass("fancytree-selected"), true, "node was rendered as selected"); assert.equal($(tools.getNode("test3_2").span).hasClass("customClass"), true, "set custom class"); // deepEqual(tools.EVENT_SEQUENCE, [], "event sequence"); done(); } }); }); QUnit.test("apply patch", function(assert) { tools.setup(assert); assert.expect(19); var done = assert.async(), patchList = [ ["2", {title: "node 2: new", tooltip: "new tip", foo: "works"}], ["3", {selected: true}], ["4", {extraClasses: "customClass"}], ["10_1_1", {title: "Renamed 10_1_1"}], ["10_1_2", null ], ["5", {children: [{key: "5_1", title: "new 5_1"}]}], ["6", {children: [{key: "6_1", title: "new 6_1", expanded: true, children: [{key: "6_1_1", title: "new 6_1_1"}] }]}], ["10", {expanded: true} ], ["20", {expanded: false} ], ["30", {expanded: true} ] // [null, {appendChildren: [{key: "40", title: "new top-level 40"}]}] ]; $("#tree").fancytree({ source: TEST_DATA, lazyLoad: function(event, data){ data.result = {url: "ajax-sub2.json"}; }, init: function(event, data){ data.tree.applyPatch(patchList).done(function(){ // tools.appendEvent(assert, "done"); assert.ok(true, "called done()"); var $span = $(tools.getNode("2").span); assert.equal(tools.getNodeTitle("2"), "node 2: new", "rename nodes"); // assert.equal($span.find("a.fancytree-title").attr("title"), "new tip", "set tooltip"); assert.equal($span.find("span.fancytree-title").attr("title"), "new tip", "set tooltip"); assert.equal(tools.getNode("2").data.foo, "works", "set custom data"); assert.ok(tools.getNode("3").isSelected(), "select"); assert.ok($(tools.getNode("3").span).hasClass("fancytree-selected"), "node was rendered as selected"); assert.ok($(tools.getNode("4").span).hasClass("customClass"), "set custom class"); assert.equal(tools.getNode("10_1_1").title, "Renamed 10_1_1", "rename hidden"); assert.equal(tools.getNodeTitle("10_1_1"), null, "rename hidden (not rendered)"); assert.equal(tools.getNode("10_1_2"), null, "remove nodes"); assert.equal(tools.getNode("5_1").title, "new 5_1", "add child nodes (created)"); assert.equal(tools.getNodeTitle("5_1"), null, "add child nodes (NOT rendered)"); assert.equal(tools.getNode("5_1").parent, tools.getNode("5"), "add child nodes (linked)"); assert.equal(tools.getNode("10").expanded, true, "folder was expanded"); assert.ok($(tools.getNode("10").span).hasClass("fancytree-expanded"), "folder was rendered as expanded"); assert.equal(tools.getNode("20").expanded, false, "folder was collapsed"); assert.ok(!$(tools.getNode("20").span).hasClass("fancytree-expanded"), "folder was rendered as collapsed"); assert.equal(tools.getNode("30").e