/*\ title: test-widget.js type: application/javascript tags: [[$:/tags/test-spec]] Tests the wikitext rendering pipeline end-to-end. We also need tests that individually test parsers, rendertreenodes etc., but this gets us started. \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; describe("Widget module", function() { var widget = require("$:/core/modules/widgets/widget.js"); function createWidgetNode(parseTreeNode,wiki) { return new widget.widget(parseTreeNode,{ wiki: wiki, document: $tw.fakeDocument }); } function parseText(text,wiki,options) { var parser = wiki.parseText("text/vnd.tiddlywiki",text,options); return parser ? {type: "widget", children: parser.tree} : undefined; } function renderWidgetNode(widgetNode) { $tw.fakeDocument.setSequenceNumber(0); var wrapper = $tw.fakeDocument.createElement("div"); widgetNode.render(wrapper,null); // console.log(require("util").inspect(wrapper,{depth: 8})); return wrapper; } function refreshWidgetNode(widgetNode,wrapper,changes) { var changedTiddlers = {}; if(changes) { $tw.utils.each(changes,function(title) { changedTiddlers[title] = true; }); } widgetNode.refresh(changedTiddlers,wrapper,null); // console.log(require("util").inspect(wrapper,{depth: 8})); } it("should deal with text nodes and HTML elements", function() { var wiki = new $tw.Wiki(); // Test parse tree var parseTreeNode = {type: "widget", children: [ {type: "text", text: "A text node"}, {type: "element", tag: "div", attributes: { "class": {type: "string", value: "myClass"}, "title": {type: "string", value: "myTitle"} }, children: [ {type: "text", text: " and the content of a DIV"}, {type: "element", tag: "div", children: [ {type: "text", text: " and an inner DIV"}, ]}, {type: "text", text: " and back in the outer DIV"} ]} ]}; // Construct the widget node var widgetNode = createWidgetNode(parseTreeNode,wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("A text node
and the content of a DIV
and an inner DIV
and back in the outer DIV
"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[1].sequenceNumber).toBe(2); expect(wrapper.children[1].children[0].sequenceNumber).toBe(3); expect(wrapper.children[1].children[1].sequenceNumber).toBe(4); expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5); expect(wrapper.children[1].children[2].sequenceNumber).toBe(6); }); it("should deal with transclude widgets and indirect attributes", function() { var wiki = new $tw.Wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "the quick brown fox"} ]); // Test parse tree var parseTreeNode = {type: "widget", children: [ {type: "text", text: "A text node"}, {type: "element", tag: "div", attributes: { "class": {type: "string", value: "myClass"}, "title": {type: "indirect", textReference: "TiddlerOne"} }, children: [ {type: "text", text: " and the content of a DIV"}, {type: "element", tag: "div", children: [ {type: "text", text: " and an inner DIV"}, ]}, {type: "text", text: " and back in the outer DIV"}, {type: "transclude", attributes: { "tiddler": {type: "string", value: "TiddlerOne"} }} ]}, {type: "transclude", attributes: { "tiddler": {type: "string", value: "TiddlerOne"} }} ]}; // Construct the widget node var widgetNode = createWidgetNode(parseTreeNode,wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("A text node
and the content of a DIV
and an inner DIV
and back in the outer DIVthe quick brown fox
the quick brown fox"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[1].sequenceNumber).toBe(2); expect(wrapper.children[1].children[0].sequenceNumber).toBe(3); expect(wrapper.children[1].children[1].sequenceNumber).toBe(4); expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5); expect(wrapper.children[1].children[2].sequenceNumber).toBe(6); expect(wrapper.children[1].children[3].sequenceNumber).toBe(7); expect(wrapper.children[2].sequenceNumber).toBe(8); // Change the transcluded tiddler wiki.addTiddler({title: "TiddlerOne", text: "jumps over the lazy dog"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerOne"]); // Test the refreshing expect(wrapper.innerHTML).toBe("A text node
and the content of a DIV
and an inner DIV
and back in the outer DIVjumps over the lazy dog
jumps over the lazy dog"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[1].sequenceNumber).toBe(2); expect(wrapper.children[1].children[0].sequenceNumber).toBe(3); expect(wrapper.children[1].children[1].sequenceNumber).toBe(4); expect(wrapper.children[1].children[1].children[0].sequenceNumber).toBe(5); expect(wrapper.children[1].children[2].sequenceNumber).toBe(6); expect(wrapper.children[1].children[3].sequenceNumber).toBe(9); expect(wrapper.children[2].sequenceNumber).toBe(10); }); it("should detect recursion of the transclude macro", function() { var wiki = new $tw.Wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>"}, {title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"} ]); // Test parse tree var parseTreeNode = {type: "widget", children: [ {type: "transclude", attributes: { "tiddler": {type: "string", value: "TiddlerOne"} }} ]}; // Construct the widget node var widgetNode = createWidgetNode(parseTreeNode,wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("1001 - 1 - Recursive transclusion error in transclude widget"); }); it("should deal with SVG elements", function() { var wiki = new $tw.Wiki(); // Construct the widget node var text = "\n"; var widgetNode = createWidgetNode(parseText(text,wiki,{parseAsInline:true}),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("\n"); expect(wrapper.firstChild.namespaceURI).toBe("http://www.w3.org/2000/svg"); }); it("should parse and render transclusions", function() { var wiki = new $tw.Wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "<$transclude tiddler={{TiddlerThree}}/>"}, {title: "TiddlerThree", text: "TiddlerOne"} ]); // Construct the widget node var text = "My <$transclude tiddler='TiddlerTwo'/> is Jolly" var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

My Jolly Old World is Jolly

"); }); it("should render the view widget", function() { var wiki = new $tw.Wiki(); // Add a tiddler wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"} ]); // Construct the widget node var text = "<$view tiddler='TiddlerOne'/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Jolly Old World

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(2); // Change the transcluded tiddler wiki.addTiddler({title: "TiddlerOne", text: "World-wide Jelly"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerOne"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

World-wide Jelly

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(3); }); it("should deal with the set widget", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "<$transclude tiddler={{TiddlerThree}}/>"}, {title: "TiddlerThree", text: "TiddlerOne"}, {title: "TiddlerFour", text: "TiddlerTwo"} ]); // Construct the widget node var text = "My <$set name='currentTiddler' value={{TiddlerFour}}><$transclude tiddler={{!!title}}/> is Jolly" var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

My Jolly Old World is Jolly

"); // Change the transcluded tiddler wiki.addTiddler({title: "TiddlerFour", text: "TiddlerOne"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerFour"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

My Jolly Old World is Jolly

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(2); expect(wrapper.children[0].children[1].sequenceNumber).toBe(5); expect(wrapper.children[0].children[2].sequenceNumber).toBe(4); }); it("should deal with the let widget", function() { var wiki = new $tw.Wiki(); wiki.addTiddlers([ {title: "TiddlerOne", text: "lookup"}, {title: "TiddlerTwo", lookup: "value", newlookup: "value", wrong: "wrong"}, {title: "TiddlerThree", text: "wrong", value: "Happy Result", wrong: "ALL WRONG!!"} ]); var text="\\define macro() TiddlerThree\n"+ "\\define currentTiddler() TiddlerOne\n"+ "<$let "+ "field={{!!text}} "+ "currentTiddler='TiddlerTwo' "+ "field={{{ [all[current]get] }}} "+ "currentTiddler=<>>"+ "<$transclude field=<>/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); var wrapper = renderWidgetNode(widgetNode); expect(wrapper.innerHTML).toBe("

Happy Result

"); // This is important. $Let needs to be aware enough not to let its // own variables interfere with its ability to recognise no change. // Doesn't matter that nothing has changed, we just need to make sure // it recognizes that that its outward facing variables are unchanged // EVEN IF some intermediate variables did change, there's no need to // refresh. wiki.addTiddler({title: "TiddlerOne", text: "newlookup"}); expect(widgetNode.refresh({})).toBe(false); // But if we make a change that might result in different outfacing // variables, then it should refresh wiki.addTiddler({title: "TiddlerOne", text: "badlookup"}); expect(widgetNode.refresh({})).toBe(true); }); it("should deal with attributes specified as macro invocations", function() { var wiki = new $tw.Wiki(); // Construct the widget node var text = "\\define myMacro(one:\"paramOne\",two,three:\"paramTwo\")\nMy something $one$, $two$ or other $three$\n\\end\n
>>Content
"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Content

"); }); it("should deal with built-in macros", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World", type: "text/vnd.tiddlywiki"} ]); // Construct the widget node var text = "\\define makelink(text,type)\n>>My linky link\n\\end\n\n<$macrocall $name=\"makelink\" text={{TiddlerOne}} type={{TiddlerOne!!type}}/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

My linky link

"); }); /* This test reproduces issue #4693. */ it("should render the entity widget", function() { var wiki = new $tw.Wiki(); // Construct the widget node var text = "\n\n<$entity entity=' ' />\n\n<$entity entity='✓' />\n"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe(" ✓"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].nodeType).toBe(wrapper.children[0].TEXT_NODE); expect(wrapper.children[1].sequenceNumber).toBe(2); expect(wrapper.children[1].nodeType).toBe(wrapper.children[1].TEXT_NODE); }); it("should deal with the list widget", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "Worldly Old Jelly"}, {title: "TiddlerThree", text: "Golly Gosh"}, {title: "TiddlerFour", text: "Lemon Squash"} ]); // Construct the widget node var text = "<$list><$view field='title'/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

TiddlerFourTiddlerOneTiddlerThreeTiddlerTwo

"); // Add another tiddler wiki.addTiddler({title: "TiddlerFive", text: "Jalapeno Peppers"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerFive"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(6); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(4); expect(wrapper.children[0].children[4].sequenceNumber).toBe(5); // Remove a tiddler wiki.deleteTiddler("TiddlerThree"); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerTwo

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(6); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(5); // Add it back a tiddler wiki.addTiddler({title: "TiddlerThree", text: "Something"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwo

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(6); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(7); expect(wrapper.children[0].children[4].sequenceNumber).toBe(5); }); it("should deal with the list widget using a counter variable", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "Worldly Old Jelly"}, {title: "TiddlerThree", text: "Golly Gosh"}, {title: "TiddlerFour", text: "Lemon Squash"} ]); // Construct the widget node var text = "<$list counter='index'><$view field='text'/><$text text=<>/><$text text=<>/><$text text=<>/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Lemon Squash1yesnoJolly Old World2nonoGolly Gosh3nonoWorldly Old Jelly4noyes

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(2); expect(wrapper.children[0].children[1].sequenceNumber).toBe(3); expect(wrapper.children[0].children[2].sequenceNumber).toBe(4); expect(wrapper.children[0].children[3].sequenceNumber).toBe(5); expect(wrapper.children[0].children[4].sequenceNumber).toBe(6); expect(wrapper.children[0].children[5].sequenceNumber).toBe(7); expect(wrapper.children[0].children[6].sequenceNumber).toBe(8); expect(wrapper.children[0].children[7].sequenceNumber).toBe(9); expect(wrapper.children[0].children[8].sequenceNumber).toBe(10); expect(wrapper.children[0].children[9].sequenceNumber).toBe(11); expect(wrapper.children[0].children[10].sequenceNumber).toBe(12); expect(wrapper.children[0].children[11].sequenceNumber).toBe(13); expect(wrapper.children[0].children[12].sequenceNumber).toBe(14); expect(wrapper.children[0].children[13].sequenceNumber).toBe(15); expect(wrapper.children[0].children[14].sequenceNumber).toBe(16); expect(wrapper.children[0].children[15].sequenceNumber).toBe(17); // Add another tiddler wiki.addTiddler({title: "TiddlerFive", text: "Jalapeno Peppers"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerFive"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

Jalapeno Peppers1yesnoLemon Squash2nonoJolly Old World3nonoGolly Gosh4nonoWorldly Old Jelly5noyes

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(18); expect(wrapper.children[0].children[1].sequenceNumber).toBe(19); expect(wrapper.children[0].children[2].sequenceNumber).toBe(20); expect(wrapper.children[0].children[3].sequenceNumber).toBe(21); expect(wrapper.children[0].children[4].sequenceNumber).toBe(22); expect(wrapper.children[0].children[5].sequenceNumber).toBe(23); expect(wrapper.children[0].children[6].sequenceNumber).toBe(24); expect(wrapper.children[0].children[7].sequenceNumber).toBe(25); expect(wrapper.children[0].children[8].sequenceNumber).toBe(26); expect(wrapper.children[0].children[9].sequenceNumber).toBe(27); expect(wrapper.children[0].children[10].sequenceNumber).toBe(28); expect(wrapper.children[0].children[11].sequenceNumber).toBe(29); expect(wrapper.children[0].children[12].sequenceNumber).toBe(30); expect(wrapper.children[0].children[13].sequenceNumber).toBe(31); expect(wrapper.children[0].children[14].sequenceNumber).toBe(32); expect(wrapper.children[0].children[15].sequenceNumber).toBe(33); expect(wrapper.children[0].children[16].sequenceNumber).toBe(34); expect(wrapper.children[0].children[17].sequenceNumber).toBe(35); expect(wrapper.children[0].children[18].sequenceNumber).toBe(36); expect(wrapper.children[0].children[19].sequenceNumber).toBe(37); // Remove a tiddler wiki.deleteTiddler("TiddlerThree"); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

Jalapeno Peppers1yesnoLemon Squash2nonoJolly Old World3nonoWorldly Old Jelly4noyes

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(18); expect(wrapper.children[0].children[1].sequenceNumber).toBe(19); expect(wrapper.children[0].children[2].sequenceNumber).toBe(20); expect(wrapper.children[0].children[3].sequenceNumber).toBe(21); expect(wrapper.children[0].children[4].sequenceNumber).toBe(22); expect(wrapper.children[0].children[5].sequenceNumber).toBe(23); expect(wrapper.children[0].children[6].sequenceNumber).toBe(24); expect(wrapper.children[0].children[7].sequenceNumber).toBe(25); expect(wrapper.children[0].children[8].sequenceNumber).toBe(26); expect(wrapper.children[0].children[9].sequenceNumber).toBe(27); expect(wrapper.children[0].children[10].sequenceNumber).toBe(28); expect(wrapper.children[0].children[11].sequenceNumber).toBe(29); expect(wrapper.children[0].children[12].sequenceNumber).toBe(38); expect(wrapper.children[0].children[13].sequenceNumber).toBe(39); expect(wrapper.children[0].children[14].sequenceNumber).toBe(40); expect(wrapper.children[0].children[15].sequenceNumber).toBe(41); // Add it back a tiddler wiki.addTiddler({title: "TiddlerThree", text: "Something"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

Jalapeno Peppers1yesnoLemon Squash2nonoJolly Old World3nonoSomething4nonoWorldly Old Jelly5noyes

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(18); expect(wrapper.children[0].children[1].sequenceNumber).toBe(19); expect(wrapper.children[0].children[2].sequenceNumber).toBe(20); expect(wrapper.children[0].children[3].sequenceNumber).toBe(21); expect(wrapper.children[0].children[4].sequenceNumber).toBe(22); expect(wrapper.children[0].children[5].sequenceNumber).toBe(23); expect(wrapper.children[0].children[6].sequenceNumber).toBe(24); expect(wrapper.children[0].children[7].sequenceNumber).toBe(25); expect(wrapper.children[0].children[8].sequenceNumber).toBe(26); expect(wrapper.children[0].children[9].sequenceNumber).toBe(27); expect(wrapper.children[0].children[10].sequenceNumber).toBe(28); expect(wrapper.children[0].children[11].sequenceNumber).toBe(29); expect(wrapper.children[0].children[12].sequenceNumber).toBe(42); expect(wrapper.children[0].children[13].sequenceNumber).toBe(43); expect(wrapper.children[0].children[14].sequenceNumber).toBe(44); expect(wrapper.children[0].children[15].sequenceNumber).toBe(45); //Remove last tiddler wiki.deleteTiddler("TiddlerTwo"); //Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerTwo"]); //Test the refreshing expect(wrapper.innerHTML).toBe("

Jalapeno Peppers1yesnoLemon Squash2nonoJolly Old World3nonoSomething4noyes

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(18); expect(wrapper.children[0].children[1].sequenceNumber).toBe(19); expect(wrapper.children[0].children[2].sequenceNumber).toBe(20); expect(wrapper.children[0].children[3].sequenceNumber).toBe(21); expect(wrapper.children[0].children[4].sequenceNumber).toBe(22); expect(wrapper.children[0].children[5].sequenceNumber).toBe(23); expect(wrapper.children[0].children[6].sequenceNumber).toBe(24); expect(wrapper.children[0].children[7].sequenceNumber).toBe(25); expect(wrapper.children[0].children[8].sequenceNumber).toBe(26); expect(wrapper.children[0].children[9].sequenceNumber).toBe(27); expect(wrapper.children[0].children[10].sequenceNumber).toBe(28); expect(wrapper.children[0].children[11].sequenceNumber).toBe(29); expect(wrapper.children[0].children[12].sequenceNumber).toBe(50); expect(wrapper.children[0].children[13].sequenceNumber).toBe(51); expect(wrapper.children[0].children[14].sequenceNumber).toBe(52); expect(wrapper.children[0].children[15].sequenceNumber).toBe(53); }); var testListJoin = function(oldList, newList) { return function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddler({title: "Numbers", text: "", list: oldList}); var text = "<$list filter='[list[Numbers]]' variable='item' join=', '><>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

" + oldList.split(' ').join(', ') + "

"); // Change the list and ensure new rendering is still right wiki.addTiddler({title: "Numbers", text: "", list: newList}); refreshWidgetNode(widgetNode,wrapper,["Numbers"]); expect(wrapper.innerHTML).toBe("

" + newList.split(' ').join(', ') + "

"); } } it("the list widget with join should update correctly when empty list gets one item", testListJoin("", "1")); it("the list widget with join should update correctly when empty list gets two items", testListJoin("", "1 2")); it("the list widget with join should update correctly when single-item list is appended to", testListJoin("1", "1 2")); it("the list widget with join should update correctly when single-item list is prepended to", testListJoin("1", "2 1")); it("the list widget with join should update correctly when list is appended", testListJoin("1 2 3 4", "1 2 3 4 5")); it("the list widget with join should update correctly when last item is removed", testListJoin("1 2 3 4", "1 2 3")); it("the list widget with join should update correctly when first item is inserted", testListJoin("1 2 3 4", "0 1 2 3 4")); it("the list widget with join should update correctly when first item is removed", testListJoin("1 2 3 4", "2 3 4")); it("the list widget with join should update correctly when first two items are swapped", testListJoin("1 2 3 4", "2 1 3 4")); it("the list widget with join should update correctly when last two items are swapped", testListJoin("1 2 3 4", "1 2 4 3")); it("the list widget with join should update correctly when last item is moved to the front", testListJoin("1 2 3 4", "4 1 2 3")); it("the list widget with join should update correctly when last item is moved to the middle", testListJoin("1 2 3 4", "1 4 2 3")); it("the list widget with join should update correctly when first item is moved to the back", testListJoin("1 2 3 4", "2 3 4 1")); it("the list widget with join should update correctly when middle item is moved to the back", testListJoin("1 2 3 4", "1 3 4 2")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 1", testListJoin("1 3 4", "1 2 3")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 2", testListJoin("1 3 4", "1 3 2")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 3", testListJoin("1 3 4", "2 1 3")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 4", testListJoin("1 3 4", "2 3 1")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 5", testListJoin("1 3 4", "3 1 2")); it("the list widget with join should update correctly when the last item disappears at the same time as other edits 6", testListJoin("1 3 4", "3 2 1")); var testCounterLast = function(oldList, newList) { return function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddler({title: "Numbers", text: "", list: oldList}); var text = "<$list filter='[list[Numbers]]' variable='item' counter='c'><><$text text={{{ [match[no]then[, ]] }}} />"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

" + oldList.split(' ').join(', ') + "

"); // Append a number wiki.addTiddler({title: "Numbers", text: "", list: newList}); refreshWidgetNode(widgetNode,wrapper,["Numbers"]); expect(wrapper.innerHTML).toBe("

" + newList.split(' ').join(', ') + "

"); } } it("the list widget with counter-last should update correctly when list is appended", testCounterLast("1 2 3 4", "1 2 3 4 5")); it("the list widget with counter-last should update correctly when last item is removed", testCounterLast("1 2 3 4", "1 2 3")); it("the list widget with counter-last should update correctly when first item is inserted", testCounterLast("1 2 3 4", "0 1 2 3 4")); it("the list widget with counter-last should update correctly when first item is removed", testCounterLast("1 2 3 4", "2 3 4")); it("should deal with the list widget followed by other widgets", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "Worldly Old Jelly"}, {title: "TiddlerThree", text: "Golly Gosh"}, {title: "TiddlerFour", text: "Lemon Squash"} ]); // Construct the widget node var text = "<$list><$view field='title'/>Something"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

TiddlerFourTiddlerOneTiddlerThreeTiddlerTwoSomething

"); // Check the next siblings of each of the list elements var listWidget = widgetNode.children[0].children[0]; // Add another tiddler wiki.addTiddler({title: "TiddlerFive", text: "Jalapeno Peppers"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerFive"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwoSomething

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(7); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(4); expect(wrapper.children[0].children[4].sequenceNumber).toBe(5); // Remove a tiddler wiki.deleteTiddler("TiddlerThree"); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerTwoSomething

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(7); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(5); // Add it back a tiddler wiki.addTiddler({title: "TiddlerThree", text: "Something"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerThree"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwoSomething

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(7); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(8); expect(wrapper.children[0].children[4].sequenceNumber).toBe(5); // Add another a tiddler to the end of the list wiki.addTiddler({title: "YetAnotherTiddler", text: "Something"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["YetAnotherTiddler"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

TiddlerFiveTiddlerFourTiddlerOneTiddlerThreeTiddlerTwoYetAnotherTiddlerSomething

"); // Test the sequence numbers in the DOM expect(wrapper.sequenceNumber).toBe(0); expect(wrapper.children[0].sequenceNumber).toBe(1); expect(wrapper.children[0].children[0].sequenceNumber).toBe(7); expect(wrapper.children[0].children[1].sequenceNumber).toBe(2); expect(wrapper.children[0].children[2].sequenceNumber).toBe(3); expect(wrapper.children[0].children[3].sequenceNumber).toBe(8); expect(wrapper.children[0].children[4].sequenceNumber).toBe(5); }); it("should deal with the list widget and external templates", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "$:/myTemplate", text: "(<$view field='title'/>)"}, {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "Worldly Old Jelly"}, {title: "TiddlerThree", text: "Golly Gosh"}, {title: "TiddlerFour", text: "Lemon Squash"} ]); // Construct the widget node var text = "<$list template='$:/myTemplate'>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); //console.log(require("util").inspect(widgetNode,{depth:8,colors:true})); // Test the rendering expect(wrapper.innerHTML).toBe("

(TiddlerFour)(TiddlerOne)(TiddlerThree)(TiddlerTwo)

"); }); it("should deal with the list widget and empty lists", function() { var wiki = new $tw.Wiki(); // Construct the widget node var text = "<$list emptyMessage='nothing'><$view field='title'/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

nothing

"); }); it("should refresh lists that become empty", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "TiddlerOne", text: "Jolly Old World"}, {title: "TiddlerTwo", text: "Worldly Old Jelly"}, {title: "TiddlerThree", text: "Golly Gosh"}, {title: "TiddlerFour", text: "Lemon Squash"} ]); // Construct the widget node var text = "<$list emptyMessage='nothing'><$view field='title'/>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

TiddlerFourTiddlerOneTiddlerThreeTiddlerTwo

"); // Get rid of the tiddlers wiki.deleteTiddler("TiddlerOne"); wiki.deleteTiddler("TiddlerTwo"); wiki.deleteTiddler("TiddlerThree"); wiki.deleteTiddler("TiddlerFour"); // Refresh refreshWidgetNode(widgetNode,wrapper,["TiddlerOne","TiddlerTwo","TiddlerThree","TiddlerFour"]); // Test the refreshing expect(wrapper.innerHTML).toBe("

nothing

"); }); /**This test confirms that imported set variables properly refresh * if they use transclusion for their value. This relates to PR #4108. */ it("should refresh imported <$set> widgets", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "Raw", text: "Initial value"}, {title: "Macro", text: "<$set name='test' value={{Raw}}>\n\ndummy text"}, {title: "Caller", text: text} ]); var text = "\\import Macro\n<>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Initial value

"); wiki.addTiddler({title: "Raw", text: "New value"}); // Refresh refreshWidgetNode(widgetNode,wrapper,["Raw"]); expect(wrapper.innerHTML).toBe("

New value

"); }); it("should support mixed setWidgets and macros when importing", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "A", text: "\\define A() Aval"}, {title: "B", text: "<$set name='B' value='Bval'>\n\ndummy text"}, {title: "C", text: "\\define C() Cval"} ]); var text = "\\import A B C\n<> <> <>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Aval Bval Cval

"); }); it("should skip parameters widgets when importing", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ {title: "B", text: "<$parameters bee=nothing><$set name='B' value='Bval'>\n\ndummy text"}, ]); var text = "\\import B\n<>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Bval

"); }); it("can have more than one macroDef variable imported", function() { var wiki = new $tw.Wiki(); wiki.addTiddlers([ {title: "ABC", text: "<$set name=A value=A>\n\n<$set name=B value=B>\n\n<$set name=C value=C>\n\ndummy text"}, {title: "D", text: "\\define D() D"}]); // A and B shouldn't chew up C just cause it's a macroDef var text = "\\import ABC D\n<
> <> <> <>"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

A B C D

"); }); it("import doesn't hold onto dead variables", function() { var wiki = new $tw.Wiki(); wiki.addTiddlers([ {title: "ABC", text: "\\define A() A\n\\define B() B\n<$set name=C value=C>\n\n"}, {title: "DE", text: "\\define D() D\n\\define E() E"}]); var text = "\\import ABC DE\ncontent"; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM renderWidgetNode(widgetNode); var childNode = widgetNode; while(childNode.children.length > 0) { childNode = childNode.children[0]; } // First make sure A and B imported expect(childNode.getVariable("A")).toBe("A"); expect(childNode.getVariable("B")).toBe("B"); expect(childNode.getVariable("C")).toBe("C"); expect(childNode.getVariable("D")).toBe("D"); expect(childNode.getVariable("E")).toBe("E"); // Then change A and remove B wiki.addTiddler({title: "ABC", text: "\\define A() A2\n<$set name=C value=C2>\n\n"}); wiki.addTiddler({title: "DE", text: "\\define D() D2"}); widgetNode.refresh({"ABC": {modified: true}, "DE": {modified: true}}); var childNode = widgetNode; while(childNode.children.length > 0) { childNode = childNode.children[0]; } // Make sure \import recognized changes and deletions expect(childNode.getVariable("A")).toBe("A2"); expect(childNode.getVariable("B")).toBe(undefined); expect(childNode.getVariable("C")).toBe("C2"); expect(childNode.getVariable("D")).toBe("D2"); expect(childNode.getVariable("E")).toBe(undefined); }); /** Special case. <$importvariables> has different handling if * it doesn't end up importing any variables. Make sure it * doesn't forget its childrenNodes. */ it("should work when import widget imports nothing", function() { var wiki = new $tw.Wiki(); var text = "\\import [prefix[XXX]]\nDon't forget me."; var widgetNode = createWidgetNode(parseText(text,wiki),wiki); // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering expect(wrapper.innerHTML).toBe("

Don't forget me.

"); }); /** Special case. \import should parse correctly, even if it's * the only line in the tiddler. Technically doesn't cause a * visual difference, but may affect plugins if it doesn't. */ it("should work when import pragma is standalone", function() { var wiki = new $tw.Wiki(); var text = "\\import [prefix[XXX]]"; var parseTreeNode = parseText(text,wiki); // Test the resulting parse tree node, since there is no // rendering which may expose a problem. expect(parseTreeNode.children[0].attributes.filter.value).toBe('[prefix[XXX]]'); }); /** This test reproduces issue #4504. * * The importvariable widget was creating redundant copies into * itself of variables in widgets higher up in the tree. Normally, * this caused no errors, but it would mess up qualify-macros. * They would find multiple instances of the same transclusion * variable if a transclusion occured higher up in the widget tree * than an importvariables AND that importvariables was importing * at least ONE variable. */ it("adding imported variables doesn't change qualifyers", function() { var wiki = new $tw.Wiki(); function wikiparse(text) { var tree = parseText(text,wiki); var widgetNode = createWidgetNode(tree,wiki); var wrapper = renderWidgetNode(widgetNode); return wrapper.innerHTML; }; wiki.addTiddlers([ {title: "body", text: "\\import A\n<>"}, {title: "A", text: "\\define unused() ignored"} ]); // This transclude wraps "body", which has an // importvariable widget in it. var withA = wikiparse("{{body}}"); wiki.addTiddler({title: "A", text: ""}); var withoutA = wikiparse("{{body}}"); // Importing two different version of "A" shouldn't cause // the <> widget to spit out something different. expect(withA).toBe(withoutA); }); }); })();