can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
903 lines (781 loc) • 27.4 kB
JavaScript
steal("can/view/callbacks",
"can/view",
"can/view/ejs",
"can/view/mustache",
"can/view/stache",
"can/observe",
"can/test",
"can/util/fixture",
"steal-qunit",
function () {
var restoreInfo = [];
var copy = function(source){
var copied = can.isArray(source) ? source.slice(0) : can.extend({}, source);
restoreInfo.push({source: source, copy: copied});
};
var restore = function(){
can.each(restoreInfo, function(data){
if(can.isArray(data.source) ) {
data.source.splice(0, data.source.length);
data.source.push.apply(data.source, data.copy);
} else {
for(var prop in data.source) {
delete data.source[prop];
}
can.extend(data.source, data.copy);
}
});
};
QUnit.module('can/view', {
setup: function () {
copy(can.view.callbacks._attributes);
copy(can.view.callbacks._regExpAttributes);
copy(can.view.callbacks._tags);
},
teardown: function () {
restore();
}
});
test('basic loading', function(){
var data = {message: "hello"},
expected = "<h1>hello</h1>",
templates= {
"ejs" : "<h1><%= message %></h1>",
"mustache" : "<h1>{{message}}</h1>",
"stache": "<h1>{{message}}</h1>"
},
templateUrl = function(ext){
return can.test.path('view/test/basic_loading.' + ext);
};
can.each([
'ejs',
'mustache',
'stache'
], function (ext) {
var result = can.view( templateUrl(ext), data );
equal(result.childNodes[0].nodeName.toLowerCase(), "h1", ext+" can.view(url,data) "+"got an h1");
equal(result.childNodes[0].innerHTML, "hello", ext+" can.view(url,data) "+"innerHTML");
result = can.view( templateUrl(ext) )(data);
equal(result.childNodes[0].nodeName.toLowerCase(), "h1", ext+" can.view(url)(data) "+"got an h1");
equal(result.childNodes[0].innerHTML, "hello", ext+" can.view(url)(data) "+"innerHTML");
result = can.view( templateUrl(ext) )(data);
equal(result.childNodes[0].nodeName.toLowerCase(), "h1", ext+" can.view(url)(data) "+"got an h1");
equal(result.childNodes[0].innerHTML, "hello", ext+" can.view(url)(data) "+"innerHTML");
result = can[ext]( templates[ext ])(data);
equal(result.childNodes[0].nodeName.toLowerCase(), "h1", ext+" can."+ext+"(template)(data) "+"got an h1");
equal(result.childNodes[0].innerHTML, "hello", ext+" can."+ext+"(template)(data) "+"innerHTML");
if(ext !== "stache") {
result = can.view( templateUrl(ext) ).render( data );
equal(result, expected, ext+" can.view(url).renderer(data) "+"result");
result = can[ext]( templates[ext ] ).render( data );
equal(result, expected, ext+" can."+ext+"(template).renderer(data) "+"result");
}
});
});
test('helpers work', function () {
var expected = '<h3>helloworld</h3><div>foo</div>';
can.each([
'ejs',
'mustache'
], function (ext) {
var actual = can.view.render(can.test.path('view/test/helpers.' + ext), {
'message': 'helloworld'
}, {
helper: function () {
return 'foo';
}
});
equal(can.trim(actual), expected, 'Text rendered');
});
});
test('buildFragment works right', function () {
can.append(can.$('#qunit-fixture'), can.view(can.test.path('view/test//plugin.ejs'), {}));
ok(/something/.test(can.$('#something span')[0].firstChild.nodeValue), 'something has something');
can.remove(can.$('#something'));
});
test('async templates, and caching work', function () {
stop();
var i = 0;
can.view.render(can.test.path('view/test//temp.ejs'), {
'message': 'helloworld'
}, function (text) {
ok(/helloworld\s*/.test(text), 'we got a rendered template');
i++;
equal(i, 2, 'Ajax is not synchronous');
start();
});
i++;
equal(i, 1, 'Ajax is not synchronous');
});
test('caching works', function () {
// this basically does a large ajax request and makes sure
// that the second time is always faster
stop();
var first;
can.view.render(can.test.path('view/test/large.ejs'), {
'message': 'helloworld'
}, function (text) {
first = new Date();
ok(text, 'we got a rendered template');
can.view.render(can.test.path('view/test/large.ejs'), {
'message': 'helloworld'
}, function (text) {
/*
var lap2 = new Date() - first,
lap1 = first - startT;
ok( lap1 > lap2, "faster this time "+(lap1 - lap2) )
*/
start();
});
});
});
test('hookup', function () {
can.view(can.test.path('view/test/hookup.ejs'), {});
equal(window.hookedUp, 'dummy', 'Hookup ran and got element');
});
test('inline templates other than \'tmpl\' like ejs', function () {
var script = document.createElement('script');
script.setAttribute('type', 'test/ejs');
script.setAttribute('id', 'test_ejs');
script.text = '<span id="new_name"><%= name %></span>';
document.getElementById('qunit-fixture')
.appendChild(script);
var div = document.createElement('div');
div.appendChild(can.view('test_ejs', {
name: 'Henry'
}));
equal(div.getElementsByTagName('span')[0].firstChild.nodeValue, 'Henry');
});
//canjs issue #31
test('render inline templates with a #', function () {
var script = document.createElement('script');
script.setAttribute('type', 'test/ejs');
script.setAttribute('id', 'test_ejs');
script.text = '<span id="new_name"><%= name %></span>';
document.getElementById('qunit-fixture')
.appendChild(script);
var div = document.createElement('div');
div.appendChild(can.view('#test_ejs', {
name: 'Henry'
}));
//make sure we aren't returning the current document as the template
equal(div.getElementsByTagName('script')
.length, 0, 'Current document was not used as template');
if (div.getElementsByTagName('span')
.length === 1) {
equal(div.getElementsByTagName('span')[0].firstChild.nodeValue, 'Henry');
}
});
test('object of deferreds', function () {
var foo = new can.Deferred(),
bar = new can.Deferred();
stop();
can.view.render(can.test.path('view/test/deferreds.ejs'), {
foo: typeof foo.promise === 'function' ? foo.promise() : foo,
bar: bar
})
.then(function (result) {
equal(result, 'FOO and BAR');
start();
});
setTimeout(function () {
foo.resolve('FOO');
}, 100);
bar.resolve('BAR');
});
test('deferred', function () {
var foo = new can.Deferred();
stop();
can.view.render(can.test.path('view/test//deferred.ejs'), foo)
.then(function (result) {
equal(result, 'FOO');
start();
});
setTimeout(function () {
foo.resolve({
foo: 'FOO'
});
}, 100);
});
test('hyphen in type', function () {
var script = document.createElement('script');
script.setAttribute('type', 'text/x-ejs');
script.setAttribute('id', 'hyphenEjs');
script.text = '\nHyphen\n';
document.getElementById('qunit-fixture')
.appendChild(script);
var div = document.createElement('div');
div.appendChild(can.view('hyphenEjs', {}));
ok(/Hyphen/.test(div.innerHTML), 'has hyphen');
});
test('create template with string', function () {
can.view.ejs('fool', 'everybody plays the <%= who %> <%= howOften %>');
var div = document.createElement('div');
div.appendChild(can.view('fool', {
who: 'fool',
howOften: 'sometimes'
}));
ok(/fool sometimes/.test(div.innerHTML), 'has fool sometimes' + div.innerHTML);
});
test('return renderer', function () {
var directResult = can.view.ejs('renderer_test', 'This is a <%= test %>');
var renderer = can.view('renderer_test');
ok(can.isFunction(directResult), 'Renderer returned directly');
ok(can.isFunction(renderer), 'Renderer is a function');
equal(renderer.render({
test: 'working test'
}), 'This is a working test', 'Rendered');
renderer = can.view(can.test.path('view/test/template.ejs'));
ok(can.isFunction(renderer), 'Renderer is a function');
equal(renderer.render({
message: 'Rendered!'
}), '<h3>Rendered!</h3>', 'Synchronous template loaded and rendered'); // TODO doesn't get caught in Zepto for whatever reason
// raises(function() {
// can.view('jkflsd.ejs');
// }, 'Nonexistent template throws error');
});
test('nameless renderers (#162, #195)', 8, function () {
// EJS
var nameless = can.view.ejs('<h2><%= message %></h2>'),
data = {
message: 'HI!'
}, result = nameless(data),
node = result.childNodes[0];
ok('ownerDocument' in result, 'Result is a document fragment');
equal(node.tagName.toLowerCase(), 'h2', 'Got h2 rendered');
equal(node.innerHTML, data.message, 'Got EJS result rendered');
equal(nameless.render(data), '<h2>HI!</h2>', '.render EJS works and returns HTML');
// Mustache
nameless = can.view.mustache('<h3>{{message}}</h3>');
data = {
message: 'MUSTACHE!'
};
result = nameless(data);
node = result.childNodes[0];
ok('ownerDocument' in result, 'Result is a document fragment');
equal(node.tagName.toLowerCase(), 'h3', 'Got h3 rendered');
equal(node.innerHTML, data.message, 'Got Mustache result rendered');
equal(nameless.render(data), '<h3>MUSTACHE!</h3>', '.render Mustache works and returns HTML');
});
test('deferred resolves with data (#183, #209)', function () {
var foo = new can.Deferred();
var bar = new can.Deferred();
var original = {
foo: foo,
bar: bar
};
stop();
ok(can.isPromise(original.foo), 'Original foo property is a Deferred');
can.view(can.test.path('view/test//deferred.ejs'), original)
.then(function (result, data) {
ok(data, 'Data exists');
equal(data.foo, 'FOO', 'Foo is resolved');
equal(data.bar, 'BAR', 'Bar is resolved');
ok(can.isPromise(original.foo), 'Original property did not get modified');
start();
});
setTimeout(function () {
foo.resolve('FOO');
}, 100);
setTimeout(function () {
bar.resolve('BAR');
}, 50);
});
test('Empty model displays __!!__ as input values (#196)', function () {
can.view.ejs('test196', 'User id: <%= user.attr(\'id\') || \'-\' %>' + ' User name: <%= user.attr(\'name\') || \'-\' %>');
var frag = can.view('test196', {
user: new can.Map()
});
var div = document.createElement('div');
div.appendChild(frag);
equal(div.innerHTML, 'User id: - User name: -', 'Got expected HTML content');
can.view('test196', {
user: new can.Map()
}, function (frag) {
div = document.createElement('div');
div.appendChild(frag);
equal(div.innerHTML, 'User id: - User name: -', 'Got expected HTML content in callback as well');
});
});
test('Select live bound options don\'t contain __!!__', function () {
var domainList = new can.List([{
id: 1,
name: 'example.com'
}, {
id: 2,
name: 'google.com'
}, {
id: 3,
name: 'yahoo.com'
}, {
id: 4,
name: 'microsoft.com'
}]),
frag = can.view(can.test.path('view/test/select.ejs'), {
domainList: domainList
}),
div = document.createElement('div');
div.appendChild(frag);
can.append(can.$('#qunit-fixture'), div);
equal(div.outerHTML.match(/__!!__/g), null, 'No __!!__ contained in HTML content');
});
test('Live binding on number inputs', function () {
var template = can.view.ejs('<input id="candy" type="number" value="<%== state.attr("number") %>" />');
var observe = new can.Map({
number: 2
});
var frag = template({
state: observe
});
can.append(can.$('#qunit-fixture'), frag);
var input = document.getElementById('candy');
equal(input.getAttribute('value'), 2, 'render workered');
observe.attr('number', 5);
equal(input.getAttribute('value'), 5, 'update workered');
});
test('live binding textNodes before a table', function(){
var data = new can.Map({
loading: true
}),
templates = {
"ejs" : "<% if (state.attr('loading')) { %>Loading<% }else{ %>Loaded<% } %><table><tbody><tr></tr></tbody></table>",
"mustache" : "{{#if state.loading}}Loading{{else}}Loaded{{/if}}<table><tbody><tr></tr></tbody></table>",
"stache": "{{#if state.loading}}Loading{{else}}Loaded{{/if}}<table><tbody><tr></tr></tbody></table>"
};
can.each([
'ejs',
'mustache',
'stache'
], function (ext) {
var result = can[ext]( templates[ext])({state: data});
equal(result.childNodes.length, 2, "can."+ext+"(template)(data) "+"proper number of nodes");
equal(result.childNodes[0].nodeType, 3, "can."+ext+"(template)(data) "+"got text node");
equal(result.childNodes[0].nodeValue, "Loading", "can."+ext+"(template)(data) "+"got live bound text value");
equal(result.childNodes[1].nodeName.toLowerCase(), "table", ext+" can."+ext+"(template)(data) "+"innerHTML");
});
});
test('Resetting a live-bound <textarea> changes its value to __!!__ (#223)', function () {
var template = can.view.ejs('<form><textarea><%= this.attr(\'test\') %></textarea></form>'),
frag = template(new can.Map({
test: 'testing'
})),
form, textarea;
can.append(can.$('#qunit-fixture'), frag);
form = document.getElementById('qunit-fixture')
.getElementsByTagName('form')[0];
textarea = form.children[0];
equal(textarea.value, 'testing', 'Textarea value set');
textarea.value = 'blabla';
equal(textarea.value, 'blabla', 'Textarea value updated');
form.reset();
equal(form.children[0].value, 'testing', 'Textarea value set back to original live-bound value');
});
test('Deferred fails (#276)', function () {
var foo = new can.Deferred();
stop();
can.view.render(can.test.path('view/test/deferred.ejs'), foo)
.fail(function (error) {
equal(error.message, 'Deferred error');
start();
});
setTimeout(function () {
foo.reject({
message: 'Deferred error'
});
}, 100);
});
test('Object of deferreds fails (#276)', function () {
var foo = new can.Deferred(),
bar = new can.Deferred();
stop();
can.view.render(can.test.path('view/test//deferreds.ejs'), {
foo: typeof foo.promise === 'function' ? foo.promise() : foo,
bar: bar
})
.fail(function (error) {
equal(error.message, 'foo error');
start();
});
setTimeout(function () {
foo.reject({
message: 'foo error'
});
}, 100);
bar.resolve('Bar done');
});
test('Using \'=\' in attribute does not truncate the value', function () {
var template = can.view.ejs('<img id=\'equalTest\' <%= this.attr(\'class\') %> src="<%= this.attr(\'src\') %>">'),
obs = new can.Map({
'class': 'class="someClass"',
'src': 'http://canjs.us/scripts/static/img/canjs_logo_yellow_small.png'
}),
frag = template(obs),
img;
can.append(can.$('#qunit-fixture'), frag);
img = document.getElementById('equalTest');
obs.attr('class', 'class="do=not=truncate=me"');
obs.attr('src', 'http://canjs.us/scripts/static/img/canjs_logo_yellow_small.png?wid=100&wid=200');
equal(img.className, 'do=not=truncate=me', 'class is right');
equal(img.src, 'http://canjs.us/scripts/static/img/canjs_logo_yellow_small.png?wid=100&wid=200', 'attribute is right');
});
test("basic scanner custom tags", function () {
can.view.tag("panel", function (el, tagData) {
ok(tagData.options.attr('helpers.myhelper')(), "got a helper");
equal(tagData.scope.attr('foo'), "bar", "got scope and can read from it");
equal(tagData.subtemplate(tagData.scope.add({
message: "hi"
}), tagData.options), "<p>sub says hi</p>");
});
var template = can.view.mustache("<panel title='foo'><p>sub says {{message}}</p></panel>");
template({
foo: "bar"
}, {
myhelper: function () {
return true;
}
});
});
test("custom tags without subtemplate", function () {
can.view.tag("empty-tag", function (el, tagData) {
ok(!tagData.subtemplate, "There is no subtemplate");
});
var template = can.view.mustache("<empty-tag title='foo'></empty-tag>");
template({
foo: "bar"
});
});
test("sub hookup", function () {
var tabs = document.createElement("tabs");
document.body.appendChild(tabs);
var panel = document.createElement("panel");
document.body.appendChild(panel);
can.view.tag("tabs", function (el, tagData) {
var frag = can.view.frag(tagData.subtemplate(tagData.scope, tagData.options));
var div = document.createElement("div");
div.appendChild(frag);
var panels = div.getElementsByTagName("panel");
equal(panels.length, 1, "there is one panel");
equal(panels[0].nodeName.toUpperCase(), "PANEL");
equal(panels[0].getAttribute("title"), "Fruits", "attribute left correctly");
equal(panels[0].innerHTML, "oranges, apples", "innerHTML");
});
can.view.tag("panel", function (el, tagData) {
ok(tagData.scope, "got scope");
return tagData.scope;
});
var template = can.view.mustache("<tabs>" +
"{{#each foodTypes}}" +
"<panel title='{{title}}'>{{content}}</panel>" +
"{{/each}}" +
"</tabs>");
var foodTypes = new can.List([{
title: "Fruits",
content: "oranges, apples"
} //,
//{title: "Breads", content: "pasta, cereal"},
//{title: "Sweets", content: "ice cream, candy"}
]);
template({
foodTypes: foodTypes
});
});
test("sub hookup passes helpers", function () {
can.view.tag("tabs", function (el, tagData) {
var optionsScope = tagData.options.add({
tabsHelper: function () {
return "TabsHelper";
}
});
var frag = can.view.frag(tagData.subtemplate(tagData.scope, optionsScope));
var div = document.createElement("div");
div.appendChild(frag);
var panels = div.getElementsByTagName('panel');
equal(panels.length, 1, 'there is one panel');
equal(panels[0].nodeName.toUpperCase(), 'PANEL');
equal(panels[0].getAttribute('title'), 'Fruits', 'attribute left correctly');
equal(panels[0].innerHTML, 'TabsHelperoranges, apples', 'innerHTML');
});
can.view.tag("panel", function (el, tagData) {
ok(tagData.scope, "got scope");
return tagData.scope;
});
var template = can.view.mustache("<tabs>" +
"{{#each foodTypes}}" +
"<panel title='{{title}}'>{{tabsHelper}}{{content}}</panel>" +
"{{/each}}" +
"</tabs>");
var foodTypes = new can.List([{
title: "Fruits",
content: "oranges, apples"
} //,
//{title: "Breads", content: "pasta, cereal"},
//{title: "Sweets", content: "ice cream, candy"}
]);
template({
foodTypes: foodTypes
});
});
test('attribute matching', function () {
var item = 0;
can.view.attr("on-click", function (el, attrData) {
ok(true, "attribute called");
equal(attrData.attributeName, "on-click", "attr is on click");
equal(el.nodeName.toLowerCase(), "p", "got a paragraph");
var cur = attrData.scope.attr(".");
equal(foodTypes[item], cur, "can get the current scope");
var attr = el.getAttribute("on-click");
equal(attrData.scope.get(attr,{proxyMethods: false}), doSomething, "can call a parent's function");
item++;
});
var template = can.view.mustache('<div>' + '{{#each foodTypes}}' + '<p on-click=\'doSomething\'>{{content}}</p>' + '{{/each}}' + '</div>');
var foodTypes = new can.List([{
title: 'Fruits',
content: 'oranges, apples'
}, {
title: 'Breads',
content: 'pasta, cereal'
}, {
title: 'Sweets',
content: 'ice cream, candy'
}]);
var doSomething = function () {};
template({
foodTypes: foodTypes,
doSomething: doSomething
});
});
test('regex attribute matching', function () {
var item = 0;
can.view.attr(/on-[\w\.]+/, function (el, attrData) {
ok(true, "attribute called");
equal(attrData.attributeName, "on-click", "attr is on click");
equal(el.nodeName.toLowerCase(), "p", "got a paragraph");
var cur = attrData.scope.attr(".");
equal(foodTypes[item], cur, "can get the current scope");
var attr = el.getAttribute("on-click");
equal(attrData.scope.get(attr,{proxyMethods: false}), doSomething, "can call a parent's function");
item++;
});
var template = can.view.mustache('<div>' + '{{#each foodTypes}}' + '<p on-click=\'doSomething\'>{{content}}</p>' + '{{/each}}' + '</div>');
var foodTypes = new can.List([{
title: 'Fruits',
content: 'oranges, apples'
}, {
title: 'Breads',
content: 'pasta, cereal'
}, {
title: 'Sweets',
content: 'ice cream, candy'
}]);
var doSomething = function () {};
template({
foodTypes: foodTypes,
doSomething: doSomething
});
});
test('content element', function () {
var template = can.view.mustache('{{#foo}}<content></content>{{/foo}}');
var context = new can.Map({
foo: 'bar'
});
var frag = template(context, {
tags: {
content: function (el, options) {
equal(el.nodeName.toLowerCase(), 'content', 'got an element');
equal(options.scope.attr('.'), 'bar', 'got the context of content');
el.innerHTML = 'updated';
}
}
});
equal(frag.childNodes[0].nodeName.toLowerCase(), 'content', "found content element");
equal(frag.childNodes[0].innerHTML, 'updated', 'content is updated');
context.removeAttr('foo');
equal(frag.childNodes[0].nodeType, 3, 'only a text element remains');
context.attr('foo', 'bar');
equal(frag.childNodes[0].nodeName.toLowerCase(), 'content');
equal(frag.childNodes[0].innerHTML, 'updated', 'content is updated');
});
test("content element inside tbody", function () {
var template = can.view.mustache("<table><tbody><content></content></tbody></table>");
var context = new can.Map({
foo: "bar"
});
template(context, {
tags: {
content: function (el, options) {
equal(el.parentNode.nodeName.toLowerCase(), "tbody", "got an element in a tbody");
equal(options.scope.attr('.'), context, "got the context of content");
}
}
});
});
test('extensionless views, enforcing engine (#193)', 1, function () {
var path = can.test.path('view/test/extensionless');
// Because we don't have an extension and if we are using Steal we will get
// view/test/extensionless/extensionless.js which we need to fix in this case
if (path.indexOf('.js', this.length - 3) !== -1) {
path = path.substring(0, path.lastIndexOf('/'));
}
var frag = can.view({
url: path,
engine: 'mustache'
}, {
message: 'Hi test'
});
var div = document.createElement('div');
div.appendChild(frag);
equal(div.getElementsByTagName('h3')[0].innerHTML, 'Hi test', 'Got expected test from extensionless template');
});
test('can.view[engine] always returns fragment renderers (#485)', 2, function () {
var template = '<h1>{{message}}</h1>';
var withId = can.view.mustache('test-485', template);
var withoutId = can.view.mustache(template);
ok(withoutId({
message: 'Without id'
})
.nodeType === 11, 'View without id returned document fragment');
ok(withId({
message: 'With id'
})
.nodeType === 11, 'View with id returned document fragment');
});
test('create a template before the custom element works with slash and colon', function () {
if (window.html5) {
// Calling this here causes odd syntax errors in old IE
// window.html5.elements += ' ignore-this';
// window.html5.shivDocument();
// Skip instead for now
ok(true, 'Old IE');
return;
}
can.view.mustache("theid", "<unique-name></unique-name><can:something></can:something><ignore-this>content</ignore-this>");
can.view.tag("unique-name", function (el, tagData) {
ok(true, "unique-name called!");
});
can.view.tag("can:something", function (el, tagData) {
ok(true, "can:something called!");
});
can.view('theid', {});
});
test('loaded live element test', function () {
// all custom elements must be registered for IE to work
if (window.html5) {
window.html5.elements += ' my-el';
window.html5.shivDocument();
}
var t = can.view.mustache('<div><my-el {{#if foo}}checked{{/if}} class=\'{{bar}}\' >inner</my-el></div>');
t();
ok(true);
});
test('content within non-component tags gets rendered with context', function () {
// all custom elements must be registered for IE to work
if (window.html5) {
window.html5.elements += ' unique-element-name';
window.html5.shivDocument();
}
var tmp = can.view.mustache('<div><unique-element-name>{{name}}</unique-element-name></div>');
var frag = tmp({
name: 'Josh M'
});
equal(frag.childNodes[0].childNodes[0].innerHTML, 'Josh M', 'correctly retrieved scope data');
});
test('empty non-component tags', function () {
// all custom elements must be registered for IE to work
if (window.html5) {
window.html5.elements += ' unique-element-name';
window.html5.shivDocument();
}
var tmp = can.view.mustache('<div><unique-element-name></unique-element-name></div>');
tmp();
ok(true, 'no error');
});
if (window.require) {
if (window.require.config && window.require.toUrl) {
test('template files relative to requirejs baseUrl (#647)', function () {
can.view.ext = '.mustache';
var oldBaseUrl = window.requirejs.s.contexts._.config.baseUrl;
window.require.config({
baseUrl: oldBaseUrl + '/view/test/'
});
ok(can.isFunction(can.view('template')));
window.require.config({
baseUrl: oldBaseUrl
});
});
}
}
test('should not error with IE conditional compilation turned on (#679)', function(){
var pass = true;
/*@cc_on @*/
var template = can.view.mustache('Hello World');
try {
template({});
} catch(e) {
pass = false;
}
ok(pass);
});
test('renderer passed with Deferred gets executed (#1139)', 1, function() {
// See http://jsfiddle.net/a35ZH/1/
var template = can.view.mustache('<h1>Value is {{value}}!</h1>');
var def = can.Deferred();
stop();
setTimeout(function() {
def.resolve({
value: 'Test'
});
}, 50);
can.view(template, def, function (frag) {
equal(frag.childNodes[0].innerHTML, 'Value is Test!');
start();
});
});
test('live lists are rendered properly when batch updated (#680)', function () {
var div1 = document.createElement('div'),
div2 = document.createElement('div'),
template = "{{#if items.length}}<ul>{{#each items}}<li>{{.}}</li>{{/each}}</ul>{{/if}}",
stacheTempl = can.stache(template),
mustacheTempl = can.mustache(template);
var data = {
items: new can.List()
};
div1.appendChild(stacheTempl(data));
div2.appendChild(mustacheTempl(data));
can.batch.start();
for (var i=1; i<=3; i++) {
data.items.push(i);
}
can.batch.stop();
var getLIText = function(el) {
var items = el.querySelectorAll('li');
var text = '';
can.each(items, function(item) {
text += item.firstChild.data;
});
return text;
};
equal(getLIText(div1), "123", "Batched lists rendered properly with stache.");
equal(getLIText(div2), "123", "Batched lists rendered properly with mustache.");
});
test('hookups on self-closing elements do not leave orphaned @@!!@@ text content (#1113)', function(){
var
list = new can.List([{},{}]),
templates = {
"ejs" : "<table><colgroup><% list.each( function() { %><col /><% }) %></colgroup></table>",
"mustache" : "<table><colgroup>{{#list}}<col/>{{/list}}</colgroup></table>",
"stache" : "<table><colgroup>{{#list}}<col/>{{/list}}</colgroup></table>"
};
can.each([
"ejs",
"mustache",
"stache"
], function (ext) {
var
frag = can[ext](templates[ext])({ list : list }),
div = document.createElement("div");
div.appendChild(frag);
equal(div.querySelectorAll("col").length, 2, "Hookup with self-closing tag rendered properly with " + ext );
equal(div.innerHTML.indexOf("@@!!@@"), -1, "Hookup with self-closing tag did not leave orphaned @@!!@@ text content with " + ext );
});
});
});