Home | History | Annotate | Download | only in jstemplate
      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 (a] 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