mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
620 lines
18 KiB
JavaScript
620 lines
18 KiB
JavaScript
|
|
// for events to bubble an element needs to be in the DOM.
|
|
// @return {Function} call this for cleanup
|
|
var addToBody = function (el) {
|
|
el.style.display = "none";
|
|
document.body.appendChild(el);
|
|
return function () {
|
|
document.body.removeChild(el);
|
|
};
|
|
};
|
|
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - assembly", function (test) {
|
|
|
|
// Test for a bug that made it to production -- after a replacement,
|
|
// we need to also check the newly replaced node for replacements
|
|
var div = renderToDiv(Template.test_assembly_a0);
|
|
test.equal(canonicalizeHtml(div.innerHTML),
|
|
"Hi");
|
|
|
|
// Another production bug -- we must use LiveRange to replace the
|
|
// placeholder, or risk breaking other LiveRanges
|
|
Session.set("stuff", true); // XXX bad form to use Session in a test?
|
|
Template.test_assembly_b1.helpers({
|
|
stuff: function () {
|
|
return Session.get("stuff");
|
|
}
|
|
});
|
|
var onscreen = renderToDiv(Template.test_assembly_b0);
|
|
test.equal(canonicalizeHtml(onscreen.innerHTML), "xyhi");
|
|
Session.set("stuff", false);
|
|
Tracker.flush();
|
|
test.equal(canonicalizeHtml(onscreen.innerHTML), "xhi");
|
|
Tracker.flush();
|
|
});
|
|
|
|
// Test that if a template throws an error, then pending_partials is
|
|
// cleaned up properly (that template rendering doesn't break..)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - table assembly", function(test) {
|
|
var childWithTag = function(node, tag) {
|
|
return _.find(node.childNodes, function(n) {
|
|
return n.nodeName === tag;
|
|
});
|
|
};
|
|
|
|
// The table.rows test would fail when TR/TD tags are stripped due
|
|
// to improper html-to-fragment
|
|
var table = childWithTag(renderToDiv(Template.test_table_b0), "TABLE");
|
|
test.equal(table.rows.length, 3);
|
|
|
|
var c = new LocalCollection();
|
|
c.insert({bar:'a'});
|
|
c.insert({bar:'b'});
|
|
c.insert({bar:'c'});
|
|
var onscreen = renderToDiv(Template.test_table_each, {foo: c.find()});
|
|
table = childWithTag(onscreen, "TABLE");
|
|
|
|
test.equal(table.rows.length, 3, table.parentNode.innerHTML);
|
|
var tds = onscreen.getElementsByTagName("TD");
|
|
test.equal(tds.length, 3);
|
|
test.equal(canonicalizeHtml(tds[0].innerHTML), "a");
|
|
test.equal(canonicalizeHtml(tds[1].innerHTML), "b");
|
|
test.equal(canonicalizeHtml(tds[2].innerHTML), "c");
|
|
|
|
Tracker.flush();
|
|
});
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - event handler this", function(test) {
|
|
|
|
Template.test_event_data_with.helpers({
|
|
ONE: {str: "one"},
|
|
TWO: {str: "two"},
|
|
THREE: {str: "three"}
|
|
});
|
|
|
|
Template.test_event_data_with.events({
|
|
'click': function(event, template) {
|
|
test.isTrue(this.str);
|
|
test.equal(template.data.str, "one");
|
|
event_buf.push(this.str);
|
|
}
|
|
});
|
|
|
|
var event_buf = [];
|
|
var containerDiv = renderToDiv(Template.test_event_data_with, {str: "one"});
|
|
var cleanupDiv = addToBody(containerDiv);
|
|
|
|
var divs = containerDiv.getElementsByTagName("div");
|
|
test.equal(3, divs.length);
|
|
|
|
clickElement(divs[0]);
|
|
test.equal(event_buf, ['one']);
|
|
event_buf.length = 0;
|
|
|
|
clickElement(divs[1]);
|
|
test.equal(event_buf, ['two']);
|
|
event_buf.length = 0;
|
|
|
|
clickElement(divs[2]);
|
|
test.equal(event_buf, ['three']);
|
|
event_buf.length = 0;
|
|
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
});
|
|
|
|
|
|
if (document.addEventListener) {
|
|
// Only run this test on browsers with support for event
|
|
// capturing. A more detailed analysis can be found at
|
|
// https://www.meteor.com/blog/2013/09/06/browser-events-bubbling-capturing-and-delegation
|
|
|
|
// This is related to issue at https://gist.github.com/mquandalle/8157017
|
|
// Tests two situations related to events that can only be captured, not bubbled:
|
|
// 1. Event should only fire the handler that matches the selector given
|
|
// 2. Event should work on every element in the selector and not just the first element
|
|
// This test isn't written against mouseenter because it is synthesized by jQuery,
|
|
// the bug also happened with the play event
|
|
Tinytest.add("spacebars-tests - templating_tests - capturing events", function (test) {
|
|
var video1Played = 0,
|
|
video2Played = 0;
|
|
|
|
Template.test_capture_events.events({
|
|
'play .video1': function () {
|
|
video1Played++;
|
|
},
|
|
'play .video2': function () {
|
|
video2Played++;
|
|
}
|
|
});
|
|
|
|
// add to body or else events don't actually fire
|
|
var containerDiv = renderToDiv(Template.test_capture_events);
|
|
var cleanupDiv = addToBody(containerDiv);
|
|
|
|
var checkAndResetEvents = function(video1, video2) {
|
|
test.equal(video1Played, video1);
|
|
test.equal(video2Played, video2);
|
|
|
|
video1Played = 0;
|
|
video2Played = 0;
|
|
};
|
|
|
|
simulateEvent($(containerDiv).find(".video1").get(0),
|
|
"play", {}, {bubbles: false});
|
|
checkAndResetEvents(1, 0);
|
|
|
|
simulateEvent($(containerDiv).find(".video2").get(0),
|
|
"play", {}, {bubbles: false});
|
|
checkAndResetEvents(0, 1);
|
|
|
|
simulateEvent($(containerDiv).find(".video2").get(1),
|
|
"play", {}, {bubbles: false});
|
|
checkAndResetEvents(0, 1);
|
|
|
|
// clean up DOM
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
});
|
|
}
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - safestring", function(test) {
|
|
|
|
Template.test_safestring_a.helpers({
|
|
foo: function() {
|
|
return "<br>";
|
|
},
|
|
bar: function() {
|
|
return new Spacebars.SafeString("<hr>");
|
|
}
|
|
});
|
|
|
|
var obj = {fooprop: "<br>",
|
|
barprop: new Spacebars.SafeString("<hr>")};
|
|
var html = canonicalizeHtml(
|
|
renderToDiv(Template.test_safestring_a, obj).innerHTML);
|
|
|
|
test.equal(html,
|
|
"<br><br><hr><hr>"+
|
|
"<br><br><hr><hr>");
|
|
|
|
});
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(test) {
|
|
Template.registerHelper("platypus", function() {
|
|
return "eggs";
|
|
});
|
|
Template.registerHelper("watermelon", function() {
|
|
return "seeds";
|
|
});
|
|
|
|
Template.registerHelper("daisygetter", function() {
|
|
return this.daisy;
|
|
});
|
|
|
|
// XXX for debugging
|
|
Template.registerHelper("debugger", function() {
|
|
debugger;
|
|
});
|
|
|
|
var getFancyObject = function() {
|
|
return {
|
|
foo: 'bar',
|
|
apple: {banana: 'smoothie'},
|
|
currentFruit: function() {
|
|
return 'guava';
|
|
},
|
|
currentCountry: function() {
|
|
return {name: 'Iceland',
|
|
_pop: 321007,
|
|
population: function() {
|
|
return this._pop;
|
|
},
|
|
unicorns: 0, // falsy value
|
|
daisyGetter: function() {
|
|
return this.daisy;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
};
|
|
|
|
Template.registerHelper("fancyhelper", getFancyObject);
|
|
|
|
Template.test_helpers_a.helpers({
|
|
platypus: 'bill',
|
|
warthog: function() {
|
|
return 'snout';
|
|
}
|
|
});
|
|
|
|
var listFour = function(a, b, c, d, options) {
|
|
test.isTrue(options instanceof Spacebars.kw);
|
|
var keywordArgs = _.map(_.keys(options.hash), function(k) {
|
|
var val = options.hash[k];
|
|
return k+':'+val;
|
|
});
|
|
return [a, b, c, d].concat(keywordArgs).join(' ');
|
|
};
|
|
|
|
var dataObj = {
|
|
zero: 0,
|
|
platypus: 'weird',
|
|
watermelon: 'rind',
|
|
daisy: 'petal',
|
|
tree: function() { return 'leaf'; },
|
|
thisTest: function() { return this.tree(); },
|
|
getNull: function() { return null; },
|
|
getUndefined: function () { return; },
|
|
fancy: getFancyObject(),
|
|
methodListFour: listFour
|
|
};
|
|
|
|
var html;
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_a, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
'platypus=bill', // helpers on Template object take first priority
|
|
'watermelon=seeds', // global helpers take second priority
|
|
'daisy=petal', // unshadowed object property
|
|
'tree=leaf', // function object property
|
|
'warthog=snout' // function Template property
|
|
]);
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_b, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
// unknown properties silently fail
|
|
'unknown=',
|
|
// falsy property comes through
|
|
'zero=0'
|
|
]);
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_c, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
// property gets are supposed to silently fail
|
|
'platypus.X=',
|
|
'watermelon.X=',
|
|
'daisy.X=',
|
|
'tree.X=',
|
|
'warthog.X=',
|
|
'getNull.X=',
|
|
'getUndefined.X=',
|
|
'getUndefined.X.Y='
|
|
]);
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_d, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
// helpers should get current data context in `this`
|
|
'daisygetter=petal',
|
|
// object methods should get object in `this`
|
|
'thisTest=leaf',
|
|
// nesting inside {{#with fancy}} shouldn't affect
|
|
// method
|
|
'../thisTest=leaf',
|
|
// combine .. and .
|
|
'../fancy.currentFruit=guava'
|
|
]);
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_e, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
'fancy.foo=bar',
|
|
'fancy.apple.banana=smoothie',
|
|
'fancy.currentFruit=guava',
|
|
'fancy.currentCountry.name=Iceland',
|
|
'fancy.currentCountry.population=321007',
|
|
'fancy.currentCountry.unicorns=0'
|
|
]);
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_f, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
'fancyhelper.foo=bar',
|
|
'fancyhelper.apple.banana=smoothie',
|
|
'fancyhelper.currentFruit=guava',
|
|
'fancyhelper.currentCountry.name=Iceland',
|
|
'fancyhelper.currentCountry.population=321007',
|
|
'fancyhelper.currentCountry.unicorns=0'
|
|
]);
|
|
|
|
// test significance of 'this', which prevents helper from
|
|
// shadowing property
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_g, dataObj).innerHTML);
|
|
test.equal(html.match(/\S+/g), [
|
|
'platypus=eggs',
|
|
'this.platypus=weird'
|
|
]);
|
|
|
|
// test interpretation of arguments
|
|
|
|
Template.test_helpers_h.helpers({helperListFour: listFour});
|
|
|
|
html = canonicalizeHtml(
|
|
renderToDiv(Template.test_helpers_h, dataObj).innerHTML);
|
|
var trials =
|
|
html.match(/\(.*?\)/g);
|
|
test.equal(trials[0],
|
|
'(methodListFour 6 7 8 9=6 7 8 9)');
|
|
test.equal(trials[1],
|
|
'(methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=eggs leaf guava 0)');
|
|
test.equal(trials[2],
|
|
'(methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=eggs leaf guava 0 a:eggs b:leaf c:guava d:0)');
|
|
test.equal(trials[3],
|
|
'(helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=eggs leaf guava 0)');
|
|
test.equal(trials[4],
|
|
'(helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=eggs leaf guava 0 a:eggs b:leaf c:guava d:0)');
|
|
test.equal(trials.length, 5);
|
|
|
|
});
|
|
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - rendered template", function(test) {
|
|
var R = ReactiveVar('foo');
|
|
Template.test_render_a.helpers({
|
|
foo: function() {
|
|
R.get();
|
|
return this.x + 1;
|
|
}
|
|
});
|
|
|
|
var div = renderToDiv(Template.test_render_a, {x: 123});
|
|
test.equal($(div).text().match(/\S+/)[0], "124");
|
|
|
|
var br1 = div.getElementsByTagName('br')[0];
|
|
var hr1 = div.getElementsByTagName('hr')[0];
|
|
test.isTrue(br1);
|
|
test.isTrue(hr1);
|
|
|
|
R.set('bar');
|
|
Tracker.flush();
|
|
var br2 = div.getElementsByTagName('br')[0];
|
|
var hr2 = div.getElementsByTagName('hr')[0];
|
|
test.isTrue(br2);
|
|
test.isTrue(br1 === br2);
|
|
test.isTrue(hr2);
|
|
test.isTrue(hr1 === hr2);
|
|
|
|
Tracker.flush();
|
|
|
|
/////
|
|
|
|
R = ReactiveVar('foo');
|
|
|
|
Template.test_render_b.helpers({foo: function() {
|
|
R.get();
|
|
return (+this) + 1;
|
|
}});
|
|
|
|
div = renderToDiv(Template.test_render_b, {x: 123});
|
|
test.equal($(div).text().match(/\S+/)[0], "201");
|
|
|
|
var br1 = div.getElementsByTagName('br')[0];
|
|
var hr1 = div.getElementsByTagName('hr')[0];
|
|
test.isTrue(br1);
|
|
test.isTrue(hr1);
|
|
|
|
R.set('bar');
|
|
Tracker.flush();
|
|
var br2 = div.getElementsByTagName('br')[0];
|
|
var hr2 = div.getElementsByTagName('hr')[0];
|
|
test.isTrue(br2);
|
|
test.isTrue(br1 === br2);
|
|
test.isTrue(hr2);
|
|
test.isTrue(hr1 === hr2);
|
|
|
|
Tracker.flush();
|
|
|
|
});
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - template arg", function (test) {
|
|
Template.test_template_arg_a.events({
|
|
click: function (event, template) {
|
|
template.firstNode.innerHTML = 'Hello';
|
|
template.lastNode.innerHTML = 'World';
|
|
template.find('i').innerHTML =
|
|
(template.findAll('*').length)+"-element";
|
|
template.lastNode.innerHTML += ' (the secret is '+
|
|
template.secret+')';
|
|
}
|
|
});
|
|
|
|
Template.test_template_arg_a.created = function() {
|
|
var self = this;
|
|
test.isFalse(self.firstNode);
|
|
test.isFalse(self.lastNode);
|
|
test.throws(function () { return self.find("*"); });
|
|
test.throws(function () { return self.findAll("*"); });
|
|
};
|
|
|
|
Template.test_template_arg_a.rendered = function () {
|
|
var template = this;
|
|
template.firstNode.innerHTML = 'Greetings';
|
|
template.lastNode.innerHTML = 'Line';
|
|
template.find('i').innerHTML =
|
|
(template.findAll('b').length)+"-bold";
|
|
template.secret = "strawberry "+template.data.food;
|
|
};
|
|
|
|
Template.test_template_arg_a.destroyed = function() {
|
|
var self = this;
|
|
test.isFalse(self.firstNode);
|
|
test.isFalse(self.lastNode);
|
|
test.throws(function () { return self.find("*"); });
|
|
test.throws(function () { return self.findAll("*"); });
|
|
};
|
|
|
|
var div = renderToDiv(Template.test_template_arg_a, {food: "pie"});
|
|
var cleanupDiv = addToBody(div);
|
|
Tracker.flush(); // cause `rendered` to be called
|
|
test.equal($(div).text(), "Greetings 1-bold Line");
|
|
clickElement(div.querySelector('i'));
|
|
test.equal($(div).text(), "Hello 3-element World (the secret is strawberry pie)");
|
|
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
});
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - helpers", function (test) {
|
|
var tmpl = Template.test_template_helpers_a;
|
|
|
|
tmpl._NOWARN_OLDSTYLE_HELPERS = true;
|
|
tmpl.foo = 'z';
|
|
tmpl.helpers({bar: 'b'});
|
|
// helpers(...) takes precendence of assigned helper
|
|
tmpl.helpers({foo: 'a', baz: function() { return 'c'; }});
|
|
|
|
var div = renderToDiv(tmpl);
|
|
test.equal($(div).text().match(/\S+/)[0], 'abc');
|
|
Tracker.flush();
|
|
|
|
tmpl = Template.test_template_helpers_b;
|
|
|
|
tmpl.helpers({
|
|
'name': 'A',
|
|
'arity': 'B',
|
|
'toString': 'C',
|
|
'length': 4,
|
|
'var': 'D'
|
|
});
|
|
|
|
div = renderToDiv(tmpl);
|
|
var txt = $(div).text();
|
|
txt = txt.replace('[object Object]', 'X'); // IE 8
|
|
txt = txt.match(/\S+/)[0];
|
|
test.isTrue(txt.match(/^AB[CX]4D$/));
|
|
// We don't make helpers with names like toString work in IE 8.
|
|
test.expect_fail();
|
|
test.equal(txt, 'ABC4D');
|
|
Tracker.flush();
|
|
|
|
// test that helpers don't "leak"
|
|
tmpl = Template.test_template_helpers_c;
|
|
div = renderToDiv(tmpl);
|
|
test.equal($(div).text(), 'x');
|
|
Tracker.flush();
|
|
});
|
|
|
|
Tinytest.add("spacebars-tests - templating_tests - events", function (test) {
|
|
var tmpl = Template.test_template_events_a;
|
|
|
|
var buf = [];
|
|
|
|
// old style
|
|
tmpl.events = {
|
|
'click b': function () { buf.push('b'); }
|
|
};
|
|
|
|
var div = renderToDiv(tmpl);
|
|
var cleanupDiv = addToBody(div);
|
|
clickElement($(div).find('b')[0]);
|
|
test.equal(buf, ['b']);
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
|
|
///
|
|
|
|
tmpl = Template.test_template_events_b;
|
|
buf = [];
|
|
// new style
|
|
tmpl.events({
|
|
'click u': function () { buf.push('u'); }
|
|
});
|
|
tmpl.events({
|
|
'click i': function () { buf.push('i'); }
|
|
});
|
|
|
|
div = renderToDiv(tmpl);
|
|
cleanupDiv = addToBody(div);
|
|
clickElement($(div).find('u')[0]);
|
|
clickElement($(div).find('i')[0]);
|
|
test.equal(buf, ['u', 'i']);
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
|
|
//Test for identical callbacks for issue #650
|
|
tmpl = Template.test_template_events_c;
|
|
buf = [];
|
|
tmpl.events({
|
|
'click u': function () { buf.push('a'); }
|
|
});
|
|
tmpl.events({
|
|
'click u': function () { buf.push('b'); }
|
|
});
|
|
|
|
div = renderToDiv(tmpl);
|
|
cleanupDiv = addToBody(div);
|
|
clickElement($(div).find('u')[0]);
|
|
test.equal(buf.length, 2);
|
|
test.isTrue(_.contains(buf, 'a'));
|
|
test.isTrue(_.contains(buf, 'b'));
|
|
cleanupDiv();
|
|
Tracker.flush();
|
|
});
|
|
|
|
|
|
Tinytest.add('spacebars-tests - templating_tests - helper typecast Issue #617', function (test) {
|
|
|
|
Template.registerHelper('testTypeCasting', function (/*arguments*/) {
|
|
// Return a string representing the arguments passed to this
|
|
// function, including types. eg:
|
|
// (1, true) -> "[number,1][boolean,true]"
|
|
return _.reduce(_.toArray(arguments), function (memo, arg) {
|
|
if (typeof arg === 'object')
|
|
return memo + "[object]";
|
|
return memo + "[" + typeof arg + "," + arg + "]";
|
|
}, "");
|
|
return x;
|
|
});
|
|
|
|
var div = renderToDiv(Template.test_type_casting);
|
|
var result = canonicalizeHtml(div.innerHTML);
|
|
test.equal(
|
|
result,
|
|
// This corresponds to entries in templating_tests.html.
|
|
// true/faslse
|
|
"[string,true][string,false][boolean,true][boolean,false]" +
|
|
// numbers
|
|
"[number,0][number,1][number,-1][number,10][number,-10]" +
|
|
// handlebars 'options' argument. appended to args of all helpers.
|
|
"[object]");
|
|
});
|
|
|
|
Tinytest.add('spacebars-tests - templating_tests - each falsy Issue #801', function (test) {
|
|
//Minor test for issue #801 (#each over array containing nulls)
|
|
Template.test_template_issue801.helpers({
|
|
values: function() { return [0,1,2,null,undefined,false]; }});
|
|
var div = renderToDiv(Template.test_template_issue801);
|
|
test.equal(canonicalizeHtml(div.innerHTML), "012");
|
|
});
|
|
|
|
Tinytest.add('spacebars-tests - templating_tests - duplicate template error', function (test) {
|
|
Template.__checkName("test_duplicate_template");
|
|
Template.test_duplicate_template = new Template(
|
|
"dup", function () { return null; });
|
|
|
|
test.throws(function () {
|
|
Template.__checkName("test_duplicate_template");
|
|
});
|
|
});
|
|
|
|
Tinytest.add('spacebars-tests - templating_tests - reserved template name error', function (test) {
|
|
|
|
_.each('length __proto__ prototype name body currentData instance'.split(' '),
|
|
function (name) {
|
|
test.throws(function () {
|
|
Template.__checkName(name);
|
|
}, /This template name is reserved: /);
|
|
});
|
|
});
|