// 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));
}