OLD | NEW |
(Empty) | |
| 1 // Copyright 2006 Google Inc. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 12 // implied. See the License for the specific language governing |
| 13 // permissions and limitations under the License. |
| 14 /** |
| 15 * @author Steffen Meschkat (mesch@google.com) |
| 16 * @fileoverview Unittest and examples for jstemplates. |
| 17 */ |
| 18 |
| 19 function jstWrap(data, template) { |
| 20 return jstProcess(new JsEvalContext(data), template); |
| 21 } |
| 22 |
| 23 function testJstSelect() { |
| 24 // Template cardinality from jsselect. |
| 25 var t = document.getElementById('t1'); |
| 26 var d = { |
| 27 items: [ 'A', 'B', 'C', '' ] |
| 28 } |
| 29 jstWrap(d, t); |
| 30 |
| 31 var h = t.innerHTML; |
| 32 var clone = domCloneNode(t); |
| 33 assertTrue(/>A<\/div>/.test(h)); |
| 34 assertTrue(/>B<\/div>/.test(h)); |
| 35 assertTrue(/>C<\/div>/.test(h)); |
| 36 assertTrue(/><\/div>/.test(h)); |
| 37 |
| 38 // Reprocessing with identical data. |
| 39 jstWrap(d, t); |
| 40 assertAttributesMatch(t, clone); |
| 41 |
| 42 // Reprocessing with changed data. |
| 43 d.items[1] = 'BB'; |
| 44 jstWrap(d, t); |
| 45 |
| 46 h = t.innerHTML; |
| 47 assertTrue(/>A<\/div>/.test(h)); |
| 48 assertFalse(/>B<\/div>/.test(h)); |
| 49 assertTrue(/>BB<\/div>/.test(h)); |
| 50 assertTrue(/>C<\/div>/.test(h)); |
| 51 |
| 52 // Reprocessing with dropped data. |
| 53 d.items.pop(); |
| 54 d.items.pop(); |
| 55 jstWrap(d, t); |
| 56 h = t.innerHTML; |
| 57 assertTrue(/>A<\/div>/.test(h)); |
| 58 assertTrue(/>BB<\/div>/.test(h)); |
| 59 assertFalse(/>C<\/div>/.test(h)); |
| 60 assertFalse(/><\/div>/.test(h)); |
| 61 |
| 62 // Reprocessing with dropped data, once more. |
| 63 d.items.pop(); |
| 64 jstWrap(d, t); |
| 65 h = t.innerHTML; |
| 66 assertTrue(/>A<\/div>/.test(h)); |
| 67 assertFalse(/>BB<\/div>/.test(h)); |
| 68 assertFalse(/>C<\/div>/.test(h)); |
| 69 |
| 70 // Reprocessing with empty data -- the last template instance is |
| 71 // preserved, and only hidden. |
| 72 d.items.pop(); |
| 73 jstWrap(d, t); |
| 74 |
| 75 assertTrue(/>A<\/div>/.test(h)); |
| 76 assertFalse(/>BB<\/div>/.test(h)); |
| 77 assertFalse(/>C<\/div>/.test(h)); |
| 78 |
| 79 // Reprocessing with added data. |
| 80 d.items.push('D'); |
| 81 jstWrap(d, t); |
| 82 h = t.innerHTML; |
| 83 assertFalse(/>A<\/div>/.test(h)); |
| 84 assertTrue(/>D<\/div>/.test(h)); |
| 85 } |
| 86 |
| 87 function testJstDisplay() { |
| 88 var t = document.getElementById('t2'); |
| 89 var d = { |
| 90 display: true |
| 91 } |
| 92 jstWrap(d, t); |
| 93 |
| 94 var h = t.innerHTML; |
| 95 assertFalse(/display:\s*none/.test(h)); |
| 96 |
| 97 d.display = false; |
| 98 jstWrap(d, t); |
| 99 |
| 100 h = t.innerHTML; |
| 101 assertTrue(/display:\s*none/.test(h)); |
| 102 |
| 103 // Check that 'this' within js expressions is the template node |
| 104 t = document.getElementById('t2a'); |
| 105 d = { |
| 106 showId: 'x' |
| 107 }; |
| 108 jstWrap(d, t); |
| 109 |
| 110 h = t.innerHTML; |
| 111 assertFalse(/display:\s*none/.test(h)); |
| 112 |
| 113 d.showId = 'y'; |
| 114 jstWrap(d, t); |
| 115 |
| 116 h = t.innerHTML; |
| 117 assertTrue(/display:\s*none/.test(h)); |
| 118 } |
| 119 |
| 120 function stringContains(str, sub) { |
| 121 return str.indexOf(sub) != -1; |
| 122 } |
| 123 |
| 124 function testJseval() { |
| 125 var data = {}; |
| 126 |
| 127 var counter = 0; |
| 128 var ctx = new JsEvalContext(data); |
| 129 ctx.setVariable("callback1", function() { |
| 130 ++counter; |
| 131 }); |
| 132 ctx.setVariable("callback2", function() { |
| 133 counter *= 2; |
| 134 }); |
| 135 |
| 136 jstProcess(ctx, document.getElementById('testJseval1')); |
| 137 assertEquals("testJseval1", 1, counter); |
| 138 |
| 139 jstProcess(ctx, document.getElementById('testJseval2')); |
| 140 assertEquals("testJseval2", 4, counter); |
| 141 } |
| 142 |
| 143 function testJstValues() { |
| 144 var t = document.getElementById('t3'); |
| 145 var d = {}; |
| 146 jstWrap(d, t); |
| 147 var h = t.innerHTML; |
| 148 assertTrue(stringContains(h, 'http://maps.google.com/')); |
| 149 var t3a = document.getElementById('t3a'); |
| 150 assertEquals('http://maps.google.com/', t3a.foo.bar.baz); |
| 151 assertEquals('http://maps.google.com/', t3a.bar); |
| 152 assertEquals('red', t3a.style.backgroundColor); |
| 153 } |
| 154 |
| 155 function testJstTransclude() { |
| 156 var t = document.getElementById('t4'); |
| 157 var p = document.getElementById('parent'); |
| 158 var d = {}; |
| 159 jstWrap(d, t); |
| 160 var h = p.innerHTML; |
| 161 assertTrue(h, stringContains(h, 'http://maps.google.com/')); |
| 162 } |
| 163 |
| 164 function assertAttributesMatch(first, second) { |
| 165 assertEquals('assertAttributesMatch: number of child nodes', |
| 166 jsLength(first.childNodes), jsLength(second.childNodes)); |
| 167 var b = second.firstChild; |
| 168 for (var a = first.firstChild; a; a = a.nextSibling) { |
| 169 var att = a.attributes; |
| 170 if (att) { |
| 171 assertTrue(b.attributes != null); |
| 172 assertEquals('assertAttributesMatch: number of attribute nodes', |
| 173 att.length, b.attributes.length); |
| 174 for (var i = 0; i < jsLength(att); i++) { |
| 175 var a = att[i]; |
| 176 assertEquals('assertAttributesMatch: value of attribute ' + a.name, |
| 177 a.value, b.getAttribute(a.name)); |
| 178 } |
| 179 } else { |
| 180 assertNull(b.attributes); |
| 181 } |
| 182 b = b.nextSibling; |
| 183 } |
| 184 } |
| 185 |
| 186 function testJsskip() { |
| 187 var div = domCreateElement(document, "DIV"); |
| 188 div.innerHTML = [ |
| 189 '<div jseval="outercallback()" jsskip="1">', |
| 190 '<div jseval="innercallback()">', |
| 191 '</div>', |
| 192 '</div>' |
| 193 ].join(''); |
| 194 |
| 195 var data = {}; |
| 196 var ctx = new JsEvalContext(data); |
| 197 var outerCalled = false; |
| 198 ctx.setVariable("outercallback", function() { |
| 199 outerCalled = true; |
| 200 }); |
| 201 var innerCalled = false; |
| 202 ctx.setVariable("innercallback", function() { |
| 203 innerCalled = true; |
| 204 }); |
| 205 jstProcess(ctx, div); |
| 206 |
| 207 assertTrue(outerCalled); |
| 208 assertFalse(innerCalled); |
| 209 } |
| 210 |
| 211 function testScalarContext() { |
| 212 var t = document.getElementById('testScalarContext'); |
| 213 |
| 214 jstWrap(true, t); |
| 215 assertTrue(/>true</.test(t.innerHTML)); |
| 216 |
| 217 jstWrap(false, t); |
| 218 assertTrue(/>false</.test(t.innerHTML)); |
| 219 |
| 220 jstWrap(0, t); |
| 221 assertTrue(/>0</.test(t.innerHTML)); |
| 222 |
| 223 jstWrap("foo", t); |
| 224 assertTrue(/>foo</.test(t.innerHTML)); |
| 225 |
| 226 jstWrap(undefined, t); |
| 227 assertTrue(/>undefined</.test(t.innerHTML)); |
| 228 |
| 229 jstWrap(null, t); |
| 230 assertTrue(/>null</.test(t.innerHTML)); |
| 231 } |
| 232 |
| 233 function testJstLoadTemplate() { |
| 234 var wrapperId = 'testJstLoadTemplateWrapper'; |
| 235 var id = 'testJstLoadTemplate'; |
| 236 jstLoadTemplate_(document, '<div id="' + id + '">content</div>', wrapperId); |
| 237 var wrapperElem = document.getElementById(wrapperId); |
| 238 assertTrue('Expected wrapper element to be in document', |
| 239 !!wrapperElem); |
| 240 var newTemplate = document.getElementById(id); |
| 241 assertTrue('Expected newly loaded template to be in document', |
| 242 !!newTemplate); |
| 243 assertTrue('Expected wrapper to be grandparent of template', |
| 244 newTemplate.parentNode.parentNode == wrapperElem); |
| 245 |
| 246 // Make sure the next template loaded with the same wrapper id re-uses the |
| 247 // wrapper element. |
| 248 var id2 = 'testJstLoadTemplate2'; |
| 249 jstLoadTemplate_(document, '<div id="' + id2 + '">content</div>', wrapperId); |
| 250 var newTemplate2 = document.getElementById(id2); |
| 251 assertTrue('Expected newly loaded template to be in document', |
| 252 !!newTemplate2); |
| 253 assertTrue('Expected wrapper to be grandparent of template', |
| 254 newTemplate2.parentNode.parentNode == wrapperElem); |
| 255 } |
| 256 |
| 257 function testJstGetTemplateFromDom() { |
| 258 var element; |
| 259 // Get by id a template in the document |
| 260 // Success |
| 261 element = jstGetTemplate('t1'); |
| 262 assertTrue("Asserted jstGetTemplate('t1') to return a dom element", |
| 263 !!element); |
| 264 // Failure |
| 265 element = jstGetTemplate('asdf'); |
| 266 assertFalse("Asserted jstGetTemplate('asdf') to return null", |
| 267 !!element); |
| 268 } |
| 269 |
| 270 function testJstGetTemplateFromFunction() { |
| 271 var element; |
| 272 // Fetch a jstemplate by id from within a html string, passed via a function. |
| 273 function returnHtmlWithId(id) { |
| 274 var html = |
| 275 '<div>' + |
| 276 '<div id="' + id + '">Here is the template</div>' + |
| 277 '</div>'; |
| 278 return html; |
| 279 } |
| 280 // Success |
| 281 element = jstGetTemplate('template', |
| 282 partial(returnHtmlWithId, 'template')); |
| 283 assertTrue("Expected jstGetTemplate('template') to return a dom element", |
| 284 !!element); |
| 285 |
| 286 // Failure |
| 287 element = jstGetTemplate('asdf', |
| 288 partial(returnHtmlWithId, 'zxcv')); |
| 289 assertFalse("Expected jstGetTemplate('zxcv') to return null", |
| 290 !!element); |
| 291 } |
| 292 |
| 293 function testPrepareNode() { |
| 294 var id, node; |
| 295 // Reset the cache so we're testing from a known state. |
| 296 JstProcessor.jstCache_ = {}; |
| 297 JstProcessor.jstCache_[0] = {}; |
| 298 |
| 299 // Skip pre-processed nodes. Preprocessed nodes are those with a |
| 300 // PROP_jstcache property. |
| 301 var t = document.getElementById('t1'); |
| 302 var caches = []; |
| 303 caches.push(JstProcessor.prepareNode_(t)); |
| 304 caches.push(JstProcessor.prepareNode_(t)); |
| 305 assertEquals('The same cache should be returned on each call to prepareNode', |
| 306 caches[0], caches[1]); |
| 307 |
| 308 // Preprocessing a node with a jst attribute should return a valid struct |
| 309 id = 'testPrepareNodeWithAttributes'; |
| 310 jstLoadTemplate_(document, '<div id="' + id + '" jsskip="1"></div>'); |
| 311 node = document.getElementById(id); |
| 312 var cache = JstProcessor.prepareNode_(node); |
| 313 try { |
| 314 var jsskip = cache['jsskip']({}, {}); |
| 315 } catch (e) { |
| 316 fail('Exception when evaluating jsskip from cache'); |
| 317 } |
| 318 assertEquals(1, jsskip); |
| 319 } |
| 320 |
| 321 |
| 322 function testPrepareNodeWithNoAttributes() { |
| 323 // Preprocessing a node with no jst attributes should return null |
| 324 var id = 'testPrepareNodeNoAttributes'; |
| 325 jstLoadTemplate_(document, '<div id="' + id + '"></div>'); |
| 326 var node = document.getElementById(id); |
| 327 assertEquals('prepareNode with no jst attributes should return default', |
| 328 JstProcessor.jstcache_[0], JstProcessor.prepareNode_(node)); |
| 329 } |
| 330 |
| 331 |
| 332 function testJsVars() { |
| 333 var template = document.createElement('div'); |
| 334 document.body.appendChild(template); |
| 335 template.innerHTML = '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>'; |
| 336 |
| 337 var context = new JsEvalContext; |
| 338 jstProcess(context, template); |
| 339 |
| 340 assertEquals('foo', context.getVariable('foo')); |
| 341 assertEquals(1, context.getVariable('$baz')); |
| 342 assertTrue(context.getVariable('bar')); |
| 343 assertUndefined(context.getVariable('foobar')); |
| 344 } |
| 345 |
| 346 |
| 347 function testCacheReuse() { |
| 348 var template = document.createElement('div'); |
| 349 document.body.appendChild(template); |
| 350 template.innerHTML = |
| 351 '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>' + |
| 352 '<span jsvars="foo:\'foo\';bar:true;$baz:1"></span>'; |
| 353 JstProcessor.prepareTemplate_(template); |
| 354 assertEquals(template.firstChild.getAttribute(ATT_jstcache), |
| 355 template.lastChild.getAttribute(ATT_jstcache)); |
| 356 } |
| 357 |
OLD | NEW |