| // Copyright 2006 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| // implied. See the License for the specific language governing |
| // permissions and limitations under the License. |
| /** |
| * @author Steffen Meschkat (mesch@google.com) |
| * @fileoverview Unittest and examples for jstemplates. |
| */ |
| |
| function jstWrap(data, template) { |
| return jstProcess(new JsEvalContext(data), template); |
| } |
| |
| function testJstSelect() { |
| // Template cardinality from jsselect. |
| var t = document.getElementById('t1'); |
| var d = { |
| items: [ 'A', 'B', 'C', '' ] |
| } |
| jstWrap(d, t); |
| |
| var h = t.innerHTML; |
| var clone = domCloneNode(t); |
| assertTrue(/>A<\/div>/.test(h)); |
| assertTrue(/>B<\/div>/.test(h)); |
| assertTrue(/>C<\/div>/.test(h)); |
| assertTrue(/><\/div>/.test(h)); |
| |
| // Reprocessing with identical data. |
| jstWrap(d, t); |
| assertAttributesMatch(t, clone); |
| |
| // Reprocessing with changed data. |
| d.items[1] = 'BB'; |
| jstWrap(d, t); |
| |
| h = t.innerHTML; |
| assertTrue(/>A<\/div>/.test(h)); |
| assertFalse(/>B<\/div>/.test(h)); |
| assertTrue(/>BB<\/div>/.test(h)); |
| assertTrue(/>C<\/div>/.test(h)); |
| |
| // Reprocessing with dropped data. |
| d.items.pop(); |
| d.items.pop(); |
| jstWrap(d, t); |
| h = t.innerHTML; |
| assertTrue(/>A<\/div>/.test(h)); |
| assertTrue(/>BB<\/div>/.test(h)); |
| assertFalse(/>C<\/div>/.test(h)); |
| assertFalse(/><\/div>/.test(h)); |
| |
| // Reprocessing with dropped data, once more. |
| d.items.pop(); |
| jstWrap(d, t); |
| h = t.innerHTML; |
| assertTrue(/>A<\/div>/.test(h)); |
| assertFalse(/>BB<\/div>/.test(h)); |
| assertFalse(/>C<\/div>/.test(h)); |
| |
| // Reprocessing with empty data -- the last template instance is |
| // preserved, and only hidden. |
| d.items.pop(); |
| jstWrap(d, t); |
| |
| assertTrue(/>A<\/div>/.test(h)); |
| assertFalse(/>BB<\/div>/.test(h)); |
| assertFalse(/>C<\/div>/.test(h)); |
| |
| // Reprocessing with added data. |
| d.items.push('D'); |
| jstWrap(d, t); |
| h = t.innerHTML; |
| assertFalse(/>A<\/div>/.test(h)); |
| assertTrue(/>D<\/div>/.test(h)); |
| } |
| |
| function testJstDisplay() { |
| var t = document.getElementById('t2'); |
| var d = { |
| display: true |
| } |
| jstWrap(d, t); |
| |
| var h = t.innerHTML; |
| assertFalse(/display:\s*none/.test(h)); |
| |
| d.display = false; |
| jstWrap(d, t); |
| |
| h = t.innerHTML; |
| assertTrue(/display:\s*none/.test(h)); |
| |
| // Check that 'this' within js expressions is the template node |
| t = document.getElementById('t2a'); |
| d = { |
| showId: 'x' |
| }; |
| jstWrap(d, t); |
| |
| h = t.innerHTML; |
| assertFalse(/display:\s*none/.test(h)); |
| |
| d.showId = 'y'; |
| jstWrap(d, t); |
| |
| h = t.innerHTML; |
| assertTrue(/display:\s*none/.test(h)); |
| } |
| |
| function stringContains(str, sub) { |
| return str.indexOf(sub) != -1; |
| } |
| |
| function testJseval() { |
| var data = {}; |
| |
| var counter = 0; |
| var ctx = new JsEvalContext(data); |
| ctx.setVariable("callback1", function() { |
| ++counter; |
| }); |
| ctx.setVariable("callback2", function() { |
| counter *= 2; |
| }); |
| |
| jstProcess(ctx, document.getElementById('testJseval1')); |
| assertEquals("testJseval1", 1, counter); |
| |
| jstProcess(ctx, document.getElementById('testJseval2')); |
| assertEquals("testJseval2", 4, counter); |
| } |
| |
| function testJstValues() { |
| var t = document.getElementById('t3'); |
| var d = {}; |
| jstWrap(d, t); |
| var h = t.innerHTML; |
| assertTrue(stringContains(h, 'http://maps.google.com/')); |
| var t3a = document.getElementById('t3a'); |
| assertEquals('http://maps.google.com/', t3a.foo.bar.baz); |
| assertEquals('http://maps.google.com/', t3a.bar); |
| assertEquals('red', t3a.style.backgroundColor); |
| } |
| |
| function testJstTransclude() { |
| var t = document.getElementById('t4'); |
| var p = document.getElementById('parent'); |
| var d = {}; |
| jstWrap(d, t); |
| var h = p.innerHTML; |
| assertTrue(h, stringContains(h, 'http://maps.google.com/')); |
| } |
| |
| function assertAttributesMatch(first, second) { |
| assertEquals('assertAttributesMatch: number of child nodes', |
| jsLength(first.childNodes), jsLength(second.childNodes)); |
| var b = second.firstChild; |
| for (var a = first.firstChild; a; a = a.nextSibling) { |
| var att = a.attributes; |
| if (att) { |
| assertTrue(b.attributes != null); |
| assertEquals('assertAttributesMatch: number of attribute nodes', |
| att.length, b.attributes.length); |
| for (var i = 0; i < jsLength(att); i++) { |
| var a = att[i]; |
| assertEquals('assertAttributesMatch: value of attribute ' + a.name, |
| a.value, b.getAttribute(a.name)); |
| } |
| } else { |
| assertNull(b.attributes); |
| } |
| b = b.nextSibling; |
| } |
| } |
| |
| function testJsskip() { |
| var div = domCreateElement(document, "DIV"); |
| div.innerHTML = [ |
| '<div jseval="outercallback()" jsskip="1">', |
| '<div jseval="innercallback()">', |
| '</div>', |
| '</div>' |
| ].join(''); |
| |
| var data = {}; |
| var ctx = new JsEvalContext(data); |
| var outerCalled = false; |
| ctx.setVariable("outercallback", function() { |
| outerCalled = true; |
| }); |
| var innerCalled = false; |
| ctx.setVariable("innercallback", function() { |
| innerCalled = true; |
| }); |
| jstProcess(ctx, div); |
| |
| assertTrue(outerCalled); |
| assertFalse(innerCalled); |
| } |
| |
| function testScalarContext() { |
| var t = document.getElementById('testScalarContext'); |
| |
| jstWrap(true, t); |
| assertTrue(/>true</.test(t.innerHTML)); |
| |
| jstWrap(false, t); |
| assertTrue(/>false</.test(t.innerHTML)); |
| |
| jstWrap(0, t); |
| assertTrue(/>0</.test(t.innerHTML)); |
| |
| jstWrap("foo", t); |
| assertTrue(/>foo</.test(t.innerHTML)); |
| |
| jstWrap(undefined, t); |
| assertTrue(/>undefined</.test(t.innerHTML)); |
| |
| jstWrap(null, t); |
| assertTrue(/>null</.test(t.innerHTML)); |
| } |
| |
| function testJstLoadTemplate() { |
| var wrapperId = 'testJstLoadTemplateWrapper'; |
| var id = 'testJstLoadTemplate'; |
| jstLoadTemplate_(document, '<div id="' + id + '">content</div>', wrapperId); |
| var wrapperElem = document.getElementById(wrapperId); |
| assertTrue('Expected wrapper element to be in document', |
| !!wrapperElem); |
| var newTemplate = document.getElementById(id); |
| assertTrue('Expected newly loaded template to be in document', |
| !!newTemplate); |
| assertTrue('Expected wrapper to be grandparent of template', |
| newTemplate.parentNode.parentNode == wrapperElem); |
| |
| // Make sure the next template loaded with the same wrapper id re-uses the |
| // wrapper element. |
| var id2 = 'testJstLoadTemplate2'; |
| jstLoadTemplate_(document, '<div id="' + id2 + '">content</div>', wrapperId); |
| var newTemplate2 = document.getElementById(id2); |
| assertTrue('Expected newly loaded template to be in document', |
| !!newTemplate2); |
| assertTrue('Expected wrapper to be grandparent of template', |
| newTemplate2.parentNode.parentNode == wrapperElem); |
| } |
| |
| function testJstGetTemplateFromDom() { |
| var element; |
| // Get by id a template in the document |
| // Success |
| element = jstGetTemplate('t1'); |
| assertTrue("Asserted jstGetTemplate('t1') to return a dom element", |
| !!element); |
| // Failure |
| element = jstGetTemplate('asdf'); |
| assertFalse("Asserted jstGetTemplate('asdf') to return null", |
| !!element); |
| } |
| |
| function testJstGetTemplateFromFunction() { |
| var element; |
| // Fetch a jstemplate by id from within a html string, passed via a function. |
| function returnHtmlWithId(id) { |
| var html = |
| '<div>' + |
| '<div id="' + id + '">Here is the template</div>' + |
| '</div>'; |
| return html; |
| } |
| // Success |
| element = jstGetTemplate('template', |
| partial(returnHtmlWithId, 'template')); |
| assertTrue("Expected jstGetTemplate('template') to return a dom element", |
| !!element); |
| |
| // Failure |
| element = jstGetTemplate('asdf', |
| partial(returnHtmlWithId, 'zxcv')); |
| assertFalse("Expected jstGetTemplate('zxcv') to return null", |
| !!element); |
| } |
| |
| function testPrepareNode() { |
| var id, node; |
| // Reset the cache so we're testing from a known state. |
| JstProcessor.jstCache_ = {}; |
| JstProcessor.jstCache_[0] = {}; |
| |
| // Skip pre-processed nodes. Preprocessed nodes are those with a |
| // PROP_jstcache property. |
| var t = document.getElementById('t1'); |
| var caches = []; |
| caches.push(JstProcessor.prepareNode_(t)); |
| caches.push(JstProcessor.prepareNode_(t)); |
| assertEquals('The same cache should be returned on each call to prepareNode', |
| caches[0], caches[1]); |
| |
| // Preprocessing a node with a jst attribute should return a valid struct |
| id = 'testPrepareNodeWithAttributes'; |
| jstLoadTemplate_(document, '<div id="' + id + '" jsskip="1"></div>'); |
| node = document.getElementById(id); |
| var cache = JstProcessor.prepareNode_(node); |
| try { |
| var jsskip = cache['jsskip']({}, {}); |
| } catch (e) { |
| fail('Exception when evaluating jsskip from cache'); |
| } |
| assertEquals(1, jsskip); |
| } |
| |
| |
| function testPrepareNodeWithNoAttributes() { |
| // Preprocessing a node with no jst attributes should return null |
| var id = 'testPrepareNodeNoAttributes'; |
| jstLoadTemplate_(document, '<div id="' + id + '"></div>'); |
| var node = document.getElementById(id); |
| assertEquals('prepareNode with no jst attributes should return default', |
| JstProcessor.jstcache_[0], JstProcessor.prepareNode_(node)); |
| } |
| |
| |
| function testJsVars() { |
| var template = document.createElement('div'); |
| document.body.appendChild(template); |
| template.innerHTML = '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>'; |
| |
| var context = new JsEvalContext; |
| jstProcess(context, template); |
| |
| assertEquals('foo', context.getVariable('foo')); |
| assertEquals(1, context.getVariable('$baz')); |
| assertTrue(context.getVariable('bar')); |
| assertUndefined(context.getVariable('foobar')); |
| } |
| |
| |
| function testCacheReuse() { |
| var template = document.createElement('div'); |
| document.body.appendChild(template); |
| template.innerHTML = |
| '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>' + |
| '<span jsvars="foo:\'foo\';bar:true;$baz:1"></span>'; |
| JstProcessor.prepareTemplate_(template); |
| assertEquals(template.firstChild.getAttribute(ATT_jstcache), |
| template.lastChild.getAttribute(ATT_jstcache)); |
| } |