Commits

Stephen McKamey committed 1e3780a

- splitting tests out into individual files
- adding Result.toDOM tests
- fixing markup data in toDOM

Comments (0)

Files changed (6)

 	/**
 	 * @constant
 	 */
+	CALL = "$call",
+	/**
+	 * @constant
+	 */
 	INIT = "$init",
 	/**
 	 * @constant
 	}
 
 	/**
+	 * Calls into another view
+	 * 
+	 * @param {Array|Object|string|function(*,number,number):Array|Object|string} node The template subtree root
+	 * @param {*} model The data item being bound
+	 * @param {number|string} index The index of the current data item
+	 * @param {number} count The total number of data items
+	 * @returns {Array|Object|string}
+	 */
+	function call(node, model, index, count) {
+		var args = node[1];
+		return "TODO";
+	}
+
+	/**
 	 * Binds the node to model
 	 * 
-	 * @param {Array|Object|string|function(*,number,number):Array|Object|string} node The template subtree root
+	 * @param {Array|Object|string|function(*,number,number):Array|Object|string|View|Result} node The template subtree root
 	 * @param {*} model The data item being bound
 	 * @param {number|string} index The index of the current data item
 	 * @param {number} count The total number of data items
 					case ELSE:
 						result = choose([CHOOSE, node], model, index, count);
 						break;
+					case CALL:
+						result = call(node, model, index, count);
+						break;
 					default:
 						// element array, first item is name
 						result = [tag];
 					appendChild(elem, build(child));
 					break;
 				case OBJ:
-					if (child instanceof Markup) {
-						appendChild(elem, toDOM(child));
-					} else if (elem.nodeType === 1) {
+					if (elem.nodeType === 1) {
 						// add attributes
 						elem = addAttributes(elem, child);
 					}
 					break;
+				case RAW:
+					appendChild(elem, toDOM(child));
+					break;
 			}
 		}
 

src/duel.tests.html

 
 	<link href="qunit/qunit.css" rel="stylesheet" type="text/css" />
 
+</head>
+<body>
 	<script src="qunit/qunit.js" type="text/javascript"></script>
 	<script src="duel.js" type="text/javascript"></script>
-</head>
-<body>
-<h1 id="qunit-header">duel.js tests</h1>  
-<h2 id="qunit-banner"></h2>  
-<h2 id="qunit-userAgent"></h2>  
-<ol id="qunit-tests"></ol> 
+	<script src="tests/domUtils.js" type="text/javascript"></script>
 
-<!-- Binding -->
-<script type="text/javascript">
+	<h1 id="qunit-header">duel.js tests</h1>  
+	<h2 id="qunit-banner"></h2>  
+	<h2 id="qunit-userAgent"></h2>  
+	<ol id="qunit-tests"></ol> 
 
-	module("Binding");
-
-	test("bind() static", function() {
-
-		var expected =
-			["div", { "class" : "list", "style" : "color:blue" },
-				["h2", "This is the title"],
-				["ul",
-					["li", { "class" : "item" },
-						["b", "Example"],
-						": ",
-						["i", "First!"]
-					],
-					["li", { "class" : "item" },
-						["b", "Sample"],
-						": ",
-						["i", "Last!"]
-					]
-				]
-			];
-
-		var actual = duel(expected).bind().value;
-
-		same(actual, expected, "Static input should result in same output.");
-	});
-
-	test("bind() simple", function() {
-
-		var model = {
-		        name: "Foo.js",
-		        url: "http://example.com/foo.js",
-		        size: 5.87,
-		        details: "Lorem ipsum dolor sit amet"
-		    };
-
-		var view = duel(
-			["div", { "class" : "download" },
-				["h2",
-				 	"Filename: ",
-					function(model, index, count) { return model.name; }
-				],
-				["p",
-				 	"URL: ",
-				 	["a", { "href" : function(model, index, count) { return model.url; }, "target" : "_blank" },
-				 	 	function(model, index, count) { return model.url ;}
-				 	],
-				 	" (",
-				 	function(model, index, count) { return model.size ;},
-				 	"KB)"
-			 	],
-				["p",
-				 	"Description: ",
-				 	function(model, index, count) { return model.details; }
-				]
-			]);
-
-		var actual = view.bind(model).value;
-
-		var expected = 
-			["div", { "class" : "download" },
-				["h2",
-				 	"Filename: Foo.js"
-				],
-				["p",
-				 	"URL: ",
-				 	["a", { "href" : "http://example.com/foo.js", "target" : "_blank" },
-				 		"http://example.com/foo.js"
-			 		],
-				 	" (5.87KB)"
-			 	],
-				["p",
-				 	"Description: Lorem ipsum dolor sit amet"
-				]
-			];
-
-		same(actual, expected, "Binding of simple values.");
-	});
-
-	test("bind() simple if", function() {
-
-		var view = duel(
-			["",
-			 	["$if", { "test" : function(model, index, count) { return model.name === "Example";} },
-			 	 	["p", "True: Example === ", function(model, index, count) { return model.name;} ]
-			 	],
-			 	["$if", { "test" : function(model, index, count) { return model.name !== "Example";} },
-			 	 	["p", "False: Example !== ", function(model, index, count) { return model.name;} ]
-			 	],
-			 	["$else",
-			 	 	["p", "Both: orphaned else always executes" ]
-			 	]
-			]);
-
-		var model1 = { name: "Example" };
-		var actual1 = view.bind(model1).value;
-		var expected1 =
-			["",
-			 	["p", "True: Example === Example"],
-			 	["p", "Both: orphaned else always executes" ]
-			];
-
-		same(actual1, expected1, "Binding with simple if statements.");
-
-		var model2 = { name: "Sample" };
-		var actual2 = view.bind(model2).value;
-		var expected2 =
-			["",
-			 	["p", "False: Example !== Sample"],
-			 	["p", "Both: orphaned else always executes" ]
-			];
-
-		same(actual2, expected2, "Binding with simple if statements.");
-	});
-
-	test("bind() choose", function() {
-
-		var view = duel(
-		 	["$choose",
-			 	["$if", {"test":function(model, index, count){return !model.children || !model.children.length;}},
-			 	 	["p", "Has no items."]
-			 	],
-			 	["$if", {"test":function(model, index, count){return model.children && model.children.length === 1;}},
-			 	 	["p", "Has only one item."]
-			 	],
-			 	["$else",
-			 	 	["p", "Has ", function(model, index, count){ return model.children.length; }, " items."]
-			 	]
-		 	]);
-
-		var model1 = { name: "Three", children: [0,2,4] };
-		var actual1 = view.bind(model1).value;
-		var expected1 = ["p", "Has 3 items."];
-
-		same(actual1, expected1, "Binding with choose block.");
-
-		var model2 = { name: "One", children: [42] };
-		var actual2 = view.bind(model2).value;
-		var expected2 = ["p", "Has only one item."];
-
-		same(actual2, expected2, "Binding with choose block.");
-
-		var model3 = { name: "Zero", children: [] };
-		var actual3 = view.bind(model3).value;
-		var expected3 = ["p", "Has no items."];
-
-		same(actual3, expected3, "Binding with choose block.");
-	});
-
-	test("bind() foreach array", function() {
-
-		var model = {
-		        title: "This is the title",
-		        items: [
-		            { name: "One" },
-		            { name: "Two" },
-		            { name: "Three" },
-		            { name: "Four" },
-		            { name: "Five" }
-		        ]
-		    };
-
-		var view = duel(
-			["div", { "class" : "list", "style" : "color:blue" },
-				["h2",
-				 	function(model, index, count) { return model.title; }
-				],
-				["ul",
-				 	["$for", { "each" : function(model, index, count) { return model.items; } },
-						["li", { "class" : "item" },
-							["b",
-							 	function(model, index, count) { return model.name; }
-							],
-							": ",
-							["i",
-							 	function(model, index, count) { return index + 1; },
-								" of ",
-								function(model, index, count) { return count; }
-							]
-						]
-				 	]
-				]
-			]);
-
-		var actual = view.bind(model).value;
-
-		var expected =
-			["div", { "class" : "list", "style" : "color:blue" },
-				["h2", "This is the title"],
-				["ul",
-					["li", { "class" : "item" },
-						["b", "One"],
-						": ",
-						["i", "1 of 5"]
-					],
-					["li", { "class" : "item" },
-						["b", "Two"],
-						": ",
-						["i", "2 of 5" ]
-					],
-					["li", { "class" : "item" },
-						["b", "Three"],
-						": ",
-						["i", "3 of 5"]
-					],
-					["li", { "class" : "item" },
-						["b", "Four"],
-						": ",
-						["i", "4 of 5"]
-					],
-					["li", { "class" : "item" },
-						["b", "Five"],
-						": ",
-						["i", "5 of 5"]
-					]
-				]
-			];
-
-		same(actual, expected, "Binding of looped array items.");
-	});
-
-	test("bind() foreach object", function() {
-		var model = {
-		        name: "List of items",
-		        total: 5,
-		        items: [
-		            "One",
-		            "Two",
-		            "Three",
-		            "Four",
-		            "Five"
-		        ]
-		    };
-
-		var view = duel(
-			["",
-			 	"model => ",
-			 	["dl",
-					["$for", { "each" : function(model, index, count) { return model; } },
-					 	["dt",
-						 	function(model, index, count) { return index; },
-						 	" : "],
-						["dd",
-						 	"(",
-						 	function(model, index, count) { return (model instanceof Array) ? "array" : typeof model; },
-						 	") ",
-						 	function(model, index, count) { return JSON.stringify(model); }
-					 	]
-				 	]
-			 	]
-		 	]);
-
-		var actual = view.bind(model).value;
-
-		var expected =
-			["",
-			 	"model => ",
-			 	["dl",
-			 	 	["dt", "name : "],
-			 	 	["dd", '(string) "List of items"'],
-			 	 	["dt", "total : "],
-			 	 	["dd", '(number) 5'],
-			 	 	["dt", "items : "],
-					["dd", '(array) ["One","Two","Three","Four","Five"]']
-			 	]
-		 	];
-
-		same(actual, expected, "Binding of looped object properties.");
-	});
-
-	test("bind() markup data", function() {
-
-		var model = {
-		        details: "<blink>Lorem ipsum dolor sit amet</blink>"
-		    };
-
-		var view = duel(
-			["div", { "class" : "test" },
-				["p",
-				 	"Description: ",
-				 	function(model, index, count) { return duel.raw(model.details); }
-				]
-			]);
-
-		var actual = view.bind(model).value;
-
-		var expected = 
-			["div", { "class" : "test" },
-				["p",
-				 	"Description: ",
-				 	duel.raw("<blink>Lorem ipsum dolor sit amet</blink>")
-				]
-			];
-
-		same(actual, expected, "Binding of data containing markup.");
-	});
-
-</script>
-
-<!-- Rendering -->
-<script type="text/javascript">
-
-	module("Rendering");
-
-	test("toString() nested with attributes", function() {
-
-		var view = duel(
-			["div", { "class" : "download" },
-				["h2",
-				 	"Filename: Foo.js"
-				],
-				["p",
-				 	"URL: ",
-				 	["a", { "href" : "http://example.com/foo.js", "target" : "_blank", "title" : "Lorem ipsum dolor sit amet" },
-				 		"http://example.com/foo.js"
-			 		],
-				 	" (5.87KB)"
-			 	],
-				["p",
-				 	"Description: Lorem ipsum dolor sit amet"
-				]
-			]);
-
-		var actual = view.bind().toString();
-
-		var expected =
-			'<div class="download"><h2>Filename: Foo.js</h2>'+
-			'<p>URL: <a href="http://example.com/foo.js" target="_blank" title="Lorem ipsum dolor sit amet">http://example.com/foo.js</a> (5.87KB)</p>'+
-			'<p>Description: Lorem ipsum dolor sit amet</p>'+
-			'</div>';
-
-		same(actual, expected, "Rendering of nested elements with varying number of attributes.");
-	});
-
-	test("toString() root docFrag", function() {
-
-		var view = duel(
-			["",
-			 	["p", "Inner child one."],
-			 	["p", "Inner child two." ]
-			]);
-
-		var actual = view.bind().toString();
-		var expected = '<p>Inner child one.</p><p>Inner child two.</p>';
-
-		same(actual, expected, "Binding with root document fragment.");
-	});
-
-	test("toString() inner docFrag", function() {
-
-		var view = duel(
-			["div",
-				["",
-				 	["p", "Inner child one."],
-				 	["p", "Inner child two." ]
-				]
-			]);
-
-		var actual = view.bind().toString();
-		var expected = '<div><p>Inner child one.</p><p>Inner child two.</p></div>';
-
-		same(actual, expected, "Binding with root document fragment.");
-	});
-
-	test("bind() markup data", function() {
-
-		var view = duel(
-			["div", { "class" : "test" },
-				["p",
-				 	"Description: ",
-				 	duel.raw("<b>Lorem </b>"),
-				 	duel.raw("<blink>ipsum</blink>"),
-				 	" ",
-				 	duel.raw("<i>dolor sit amet</i>")
-				]
-			]);
-
-		var actual = view.bind().toString();
-
-		var expected = 
-			'<div class="test">'+
-			'<p>Description: <b>Lorem </b><blink>ipsum</blink> <i>dolor sit amet</i></p>'+
-			'</div>';
-		same(actual, expected, "Binding of data containing markup.");
-	});
-
-</script>
-
-<!-- DOM Building -->
-<script type="text/javascript">
-
-	module("DOM Building");
-
-</script>
+	<script src="tests/bind.js" type="text/javascript"></script>
+	<script src="tests/render.js" type="text/javascript"></script>
+	<script src="tests/build.js" type="text/javascript"></script>
 
 </body>
 </html>

src/tests/bind.js

+module("View.bind()");
+
+test("static view", function() {
+
+	var expected =
+		["div", { "class" : "list", "style" : "color:blue" },
+			["h2", "This is the title"],
+			["ul",
+				["li", { "class" : "item" },
+					["b", "Example"],
+					": ",
+					["i", "First!"]
+				],
+				["li", { "class" : "item" },
+					["b", "Sample"],
+					": ",
+					["i", "Last!"]
+				]
+			]
+		];
+
+	var actual = duel(expected).bind().value;
+
+	same(actual, expected, "");
+});
+
+test("simple expressions", function() {
+
+	var model = {
+	        name: "Foo.js",
+	        url: "http://example.com/foo.js",
+	        size: 5.87,
+	        details: "Lorem ipsum dolor sit amet"
+	    };
+
+	var view = duel(
+		["div", { "class" : "download" },
+			["h2",
+			 	"Filename: ",
+				function(model, index, count) { return model.name; }
+			],
+			["p",
+			 	"URL: ",
+			 	["a", { "href" : function(model, index, count) { return model.url; }, "target" : "_blank" },
+			 	 	function(model, index, count) { return model.url ;}
+			 	],
+			 	" (",
+			 	function(model, index, count) { return model.size ;},
+			 	"KB)"
+		 	],
+			["p",
+			 	"Description: ",
+			 	function(model, index, count) { return model.details; }
+			]
+		]);
+
+	var actual = view.bind(model).value;
+
+	var expected = 
+		["div", { "class" : "download" },
+			["h2",
+			 	"Filename: Foo.js"
+			],
+			["p",
+			 	"URL: ",
+			 	["a", { "href" : "http://example.com/foo.js", "target" : "_blank" },
+			 		"http://example.com/foo.js"
+		 		],
+			 	" (5.87KB)"
+		 	],
+			["p",
+			 	"Description: Lorem ipsum dolor sit amet"
+			]
+		];
+
+	same(actual, expected, "");
+});
+
+test("simple orphaned if/else", function() {
+
+	var view = duel(
+		["",
+		 	["$if", { "test" : function(model, index, count) { return model.name === "Example"; } },
+		 	 	["p", "True: Example === ", function(model, index, count) { return model.name; } ]
+		 	],
+		 	["$if", { "test" : function(model, index, count) { return model.name !== "Example"; } },
+		 	 	["p", "False: Example !== ", function(model, index, count) { return model.name; } ]
+		 	],
+		 	["$else",
+		 	 	["p", "Both: orphaned else always executes" ]
+		 	]
+		]);
+
+	var model1 = { name: "Example" };
+	var actual1 = view.bind(model1).value;
+	var expected1 =
+		["",
+		 	["p", "True: Example === Example"],
+		 	["p", "Both: orphaned else always executes" ]
+		];
+
+	same(actual1, expected1, "Binding with simple if statements.");
+
+	var model2 = { name: "Sample" };
+	var actual2 = view.bind(model2).value;
+	var expected2 =
+		["",
+		 	["p", "False: Example !== Sample"],
+		 	["p", "Both: orphaned else always executes" ]
+		];
+
+	same(actual2, expected2, "");
+});
+
+test("choose", function() {
+
+	var view = duel(
+	 	["$choose",
+		 	["$if", { "test" : function(model, index, count) { return !model.children || !model.children.length; } },
+		 	 	["p", "Has no items."]
+		 	],
+		 	["$if", { "test" : function(model, index, count) { return model.children && model.children.length === 1; } },
+		 	 	["p", "Has only one item."]
+		 	],
+		 	["$else",
+		 	 	["p", "Has ", function(model, index, count) { return model.children.length; }, " items."]
+		 	]
+	 	]);
+
+	var model1 = { name: "Three", children: [0,2,4] };
+	var actual1 = view.bind(model1).value;
+	var expected1 = ["p", "Has 3 items."];
+
+	same(actual1, expected1, "Binding with choose block.");
+
+	var model2 = { name: "One", children: [42] };
+	var actual2 = view.bind(model2).value;
+	var expected2 = ["p", "Has only one item."];
+
+	same(actual2, expected2, "Binding with choose block.");
+
+	var model3 = { name: "Zero", children: [] };
+	var actual3 = view.bind(model3).value;
+	var expected3 = ["p", "Has no items."];
+
+	same(actual3, expected3, "");
+});
+
+test("foreach array", function() {
+
+	var model = {
+	        title: "This is the title",
+	        items: [
+	            { name: "One" },
+	            { name: "Two" },
+	            { name: "Three" },
+	            { name: "Four" },
+	            { name: "Five" }
+	        ]
+	    };
+
+	var view = duel(
+		["div", { "class" : "list", "style" : "color:blue" },
+			["h2",
+			 	function(model, index, count) { return model.title; }
+			],
+			["ul",
+			 	["$for", { "each" : function(model, index, count) { return model.items; } },
+					["li", { "class" : "item" },
+						["b",
+						 	function(model, index, count) { return model.name; }
+						],
+						": ",
+						["i",
+						 	function(model, index, count) { return index + 1; },
+							" of ",
+							function(model, index, count) { return count; }
+						]
+					]
+			 	]
+			]
+		]);
+
+	var actual = view.bind(model).value;
+
+	var expected =
+		["div", { "class" : "list", "style" : "color:blue" },
+			["h2", "This is the title"],
+			["ul",
+				["li", { "class" : "item" },
+					["b", "One"],
+					": ",
+					["i", "1 of 5"]
+				],
+				["li", { "class" : "item" },
+					["b", "Two"],
+					": ",
+					["i", "2 of 5" ]
+				],
+				["li", { "class" : "item" },
+					["b", "Three"],
+					": ",
+					["i", "3 of 5"]
+				],
+				["li", { "class" : "item" },
+					["b", "Four"],
+					": ",
+					["i", "4 of 5"]
+				],
+				["li", { "class" : "item" },
+					["b", "Five"],
+					": ",
+					["i", "5 of 5"]
+				]
+			]
+		];
+
+	same(actual, expected, "");
+});
+
+test("foreach object", function() {
+	var model = {
+	        name: "List of items",
+	        total: 5,
+	        items: [
+	            "One",
+	            "Two",
+	            "Three",
+	            "Four",
+	            "Five"
+	        ]
+	    };
+
+	var view = duel(
+		["",
+		 	"model => ",
+		 	["dl",
+				["$for", { "each" : function(model, index, count) { return model; } },
+				 	["dt",
+					 	function(model, index, count) { return index; },
+					 	" : "],
+					["dd",
+					 	"(",
+					 	function(model, index, count) { return (model instanceof Array) ? "array" : typeof model; },
+					 	") ",
+					 	function(model, index, count) { return "" + model; }
+				 	]
+			 	]
+		 	]
+	 	]);
+
+	var actual = view.bind(model).value;
+
+	var expected =
+		["",
+		 	"model => ",
+		 	["dl",
+		 	 	["dt", "name : "],
+		 	 	["dd", "(string) List of items"],
+		 	 	["dt", "total : "],
+		 	 	["dd", "(number) 5"],
+		 	 	["dt", "items : "],
+				["dd", "(array) One,Two,Three,Four,Five"]
+		 	]
+	 	];
+
+	same(actual, expected, "");
+});
+
+test("markup data", function() {
+
+	var model = {
+	        details: "<blink>Lorem ipsum dolor sit amet</blink>"
+	    };
+
+	var view = duel(
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	function(model, index, count) { return duel.raw(model.details); }
+			]
+		]);
+
+	var actual = view.bind(model).value;
+
+	var expected = 
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	duel.raw("<blink>Lorem ipsum dolor sit amet</blink>")
+			]
+		];
+
+	same(actual, expected, "");
+});
+
+test("call view", function() {
+
+	var model = {
+	        details: "<blink>Lorem ipsum dolor sit amet</blink>"
+	    };
+
+	var view = duel(
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	function(model, index, count) { return duel.raw(model.details); }
+			]
+		]);
+
+	var actual = view.bind(model).value;
+
+	var expected = 
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	duel.raw("<blink>Lorem ipsum dolor sit amet</blink>")
+			]
+		];
+
+	same(actual, expected, "");
+});

src/tests/build.js

+module("Result.toDOM()");
+
+test("nested elements with attributes", function() {
+
+	var view = duel(
+		["div", { "class" : "download" },
+			["h2",
+			 	"Filename: Foo.js"
+			],
+			["p",
+			 	"URL: ",
+			 	["a", { "href" : "http://example.com/foo.js", "target" : "_blank", "title" : "Lorem ipsum dolor sit amet" },
+			 		"http://example.com/foo.js"
+		 		],
+			 	" (5.87KB)"
+		 	],
+			["p",
+			 	"Description: Lorem ipsum dolor sit amet"
+			]
+		]);
+
+	var actual = view.bind().toDOM();
+
+	var temp, expected = document.createElement("div");
+	expected.setAttribute("class", "download");
+
+	temp = document.createElement("h2");
+	temp.appendChild(document.createTextNode("Filename: Foo.js"));
+	expected.appendChild(temp);
+
+	temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("URL: "));
+	var temp2 = document.createElement("a");
+	temp2.setAttribute("href", "http://example.com/foo.js");
+	temp2.setAttribute("target", "_blank");
+	temp2.setAttribute("title", "Lorem ipsum dolor sit amet");
+	temp2.appendChild(document.createTextNode("http://example.com/foo.js"));
+	temp.appendChild(temp2);
+	temp.appendChild(document.createTextNode(" (5.87KB)"));
+	expected.appendChild(temp);
+
+	temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Description: Lorem ipsum dolor sit amet"));
+	expected.appendChild(temp);
+
+	same(toHTML(actual), toHTML(expected), "");
+});
+
+test("docFrag root", function() {
+
+	var view = duel(
+		["",
+		 	["p", "Inner child one."],
+		 	["p", "Inner child two." ]
+		]);
+
+	var actual = view.bind().toDOM();
+
+	var expected = document.createDocumentFragment();
+
+	var temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Inner child one."));
+	expected.appendChild(temp);
+
+	temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Inner child two."));
+	expected.appendChild(temp);
+
+	same(toHTML(actual), toHTML(expected), "");
+});
+
+test("docFrag inner", function() {
+
+	var view = duel(
+		["div",
+			["",
+			 	["p", "Inner child one."],
+			 	["p", "Inner child two." ]
+			]
+		]);
+
+	var actual = view.bind().toDOM();
+
+	var expected = document.createElement("div");
+
+	var temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Inner child one."));
+	expected.appendChild(temp);
+
+	temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Inner child two."));
+	expected.appendChild(temp);
+
+	same(toHTML(actual), toHTML(expected), "");
+});
+
+test("markup data", function() {
+
+	var view = duel(
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	duel.raw("<b>Lorem </b>"),
+			 	duel.raw("<blink>ipsum</blink>"),
+			 	" ",
+			 	duel.raw("<i>dolor sit amet</i>")
+			]
+		]);
+
+	var actual = view.bind().toDOM();
+
+	var expected = document.createElement("div");
+	expected.className = "test";
+
+	var temp = document.createElement("p");
+	temp.appendChild(document.createTextNode("Description: "));
+
+	var temp2 = document.createElement("div");
+	temp2.innerHTML = "<b>Lorem </b>";
+	while (temp2.firstChild) {
+		temp.appendChild(temp2.firstChild);
+	}
+	temp2.innerHTML = "<blink>ipsum</blink>";
+	while (temp2.firstChild) {
+		temp.appendChild(temp2.firstChild);
+	}
+
+	temp.appendChild(document.createTextNode(" "));
+	temp2.innerHTML = "<i>dolor sit amet</i>";
+	while (temp2.firstChild) {
+		temp.appendChild(temp2.firstChild);
+	}
+
+	expected.appendChild(temp);
+
+	same(toHTML(actual), toHTML(expected), "");
+});

src/tests/domUtils.js

+function toHTML(node) {
+	var root = document.createElement("div");
+	root.appendChild(node);
+	return root.innerHTML;
+}

src/tests/render.js

+module("Result.toString()");
+
+test("nested elements with attributes", function() {
+
+	var view = duel(
+		["div", { "class" : "download" },
+			["h2",
+			 	"Filename: Foo.js"
+			],
+			["p",
+			 	"URL: ",
+			 	["a", { "href" : "http://example.com/foo.js", "target" : "_blank", "title" : "Lorem ipsum dolor sit amet" },
+			 		"http://example.com/foo.js"
+		 		],
+			 	" (5.87KB)"
+		 	],
+			["p",
+			 	"Description: Lorem ipsum dolor sit amet"
+			]
+		]);
+
+	var actual = view.bind().toString();
+
+	var expected =
+		'<div class="download"><h2>Filename: Foo.js</h2>'+
+		'<p>URL: <a href="http://example.com/foo.js" target="_blank" title="Lorem ipsum dolor sit amet">http://example.com/foo.js</a> (5.87KB)</p>'+
+		'<p>Description: Lorem ipsum dolor sit amet</p>'+
+		'</div>';
+
+	same(actual, expected, "");
+});
+
+test("docFrag root", function() {
+
+	var view = duel(
+		["",
+		 	["p", "Inner child one."],
+		 	["p", "Inner child two." ]
+		]);
+
+	var actual = view.bind().toString();
+	var expected = '<p>Inner child one.</p><p>Inner child two.</p>';
+
+	same(actual, expected, "");
+});
+
+test("docFrag inner", function() {
+
+	var view = duel(
+		["div",
+			["",
+			 	["p", "Inner child one."],
+			 	["p", "Inner child two." ]
+			]
+		]);
+
+	var actual = view.bind().toString();
+	var expected = '<div><p>Inner child one.</p><p>Inner child two.</p></div>';
+
+	same(actual, expected, "");
+});
+
+test("markup data", function() {
+
+	var view = duel(
+		["div", { "class" : "test" },
+			["p",
+			 	"Description: ",
+			 	duel.raw("<b>Lorem </b>"),
+			 	duel.raw("<blink>ipsum</blink>"),
+			 	" ",
+			 	duel.raw("<i>dolor sit amet</i>")
+			]
+		]);
+
+	var actual = view.bind().toString();
+
+	var expected = 
+		'<div class="test">'+
+		'<p>Description: <b>Lorem </b><blink>ipsum</blink> <i>dolor sit amet</i></p>'+
+		'</div>';
+	same(actual, expected, "");
+});