Port html_scanner tests; detect duplicate templates

Reintroduce a Template.__define__ (like on devel)

Remove old compiler tests
This commit is contained in:
David Greenspan
2013-12-05 16:06:20 -08:00
parent 54ead3fca2
commit 6397fec3dc
6 changed files with 44 additions and 176 deletions

View File

@@ -332,151 +332,6 @@ Tinytest.add("spacebars - parser", function (test) {
{"type":"EndTag","name":"div"}]});
});
Tinytest.add("spacebars - compiler", function (test) {
var run = function (input/*, expectedLines*/) {
var expectedLines = Array.prototype.slice.call(arguments, 1);
var expected = expectedLines.join('\n');
if (arguments[1].fail) {
var expectedMessage = arguments[1].fail;
// test for error starting with expectedMessage
var msg = '';
test.throws(function () {
try {
Spacebars.compile(input);
} catch (e) {
msg = e.message;
throw e;
}
});
test.equal(msg.slice(0, expectedMessage.length),
expectedMessage);
} else {
var output = Spacebars.compile(input);
test.equal(output, expected);
}
};
run('abc',
'function (buf) {',
' buf.write("abc");',
'}');
run('<a foo=bar>abc</a>',
'function (buf) {',
' buf.write("<a",',
' {attrs: {"foo": "bar"}},',
' ">abc</a>");',
'}');
// NOTE: These are old tests of code generation from various previous versions
// of the compiler. Once the form of generated code stabilizes, it would be
// nice to have these tests as a way of seeing that code generation is working
// as intended and pretty-printing remains correct, as well as as a form of
// documentation.
/*
run('<a foo={{bar}}>',
'function (buf) {',
' var self = this;',
' buf.write("<a",',
' {attrs: function () { return {"foo": Spacebars.dstache(self.lookup("bar"))}; }},',
' ">");',
'}');
run('<a name={{foo bar}}>',
'function (buf) {',
' var self = this;',
' buf.write("<a",',
' {attrs: function () { return {"name": Spacebars.dstache(self.lookup("foo"), self.lookup("bar"))}; }},',
' ">");',
'}');
run('<a foo={{bar.baz}}>',
'function (buf) {',
' var self = this;',
' buf.openTag("a", {"foo": function () { return String(Spacebars.call(Spacebars.index(self.lookup("bar"), "baz")) || ""); }});',
'}');
run('foo {{bar}} baz',
'function (buf) {',
' var self = this;',
' buf.text("foo ");',
' buf.text(function () { return String(Spacebars.call(self.lookup("bar")) || ""); });',
' buf.text(" baz");',
'}');
run('foo {{{bar}}} baz',
'function (buf) {',
' var self = this;',
' buf.text("foo ");',
' buf.rawHtml(function () { return String(Spacebars.call(self.lookup("bar")) || ""); });',
' buf.text(" baz");',
'}');
run('foo {{bar "hello"}} baz',
'function (buf) {',
' var self = this;',
' buf.text("foo ");',
' buf.text(function () { return String(Spacebars.call(self.lookup("bar"), "hello") || ""); });',
' buf.text(" baz");',
'}');
run('foo {{bar hello}} baz',
'function (buf) {',
' var self = this;',
' buf.text("foo ");',
' buf.text(function () { return String(Spacebars.call(self.lookup("bar"), Spacebars.call(self.lookup("hello"))) || ""); });',
' buf.text(" baz");',
'}');
run('{{foo.bar x.y abc=z.w 0 null "hi" z=123.4}}',
'function (buf) {',
' var self = this;',
' buf.text(function () { return String(Spacebars.call(Spacebars.index(self.lookup("foo"), "bar"), Spacebars.call(Spacebars.index(self.lookup("x"), "y")), 0, null, "hi", {"abc": Spacebars.call(Spacebars.index(self.lookup("z"), "w")), "z": 123.4}) || ""); });',
'}');
run('{{> foo bar baz=x.y}}',
'function (buf) {',
' var self = this;',
' buf.component(function () { return ((self.lookup("foo")) || Component).create({"data": Spacebars.call(self.lookup("bar")), "baz": Spacebars.call(Spacebars.index(self.lookup("x"), "y"))}); });',
'}');
run('{{#foo.bar}}{{/foo.baz}}', {fail: 'Close tag'});
run('{{/foo.bar}}{{#foo.bar}}', {fail: 'Unexpected close tag'});
run('{{#if foo}}bar{{/if}}',
'function (buf) {',
' var self = this;',
' buf.component(function () { return ((self.lookup("if")) || Component).create({"data": Spacebars.call(self.lookup("foo")), "content": Component.extend({render: function (buf) {',
' buf.text("bar");',
' }})}); });',
'}');
run('{{#if foo}}bar{{else}}baz{{/if}}',
'function (buf) {',
' var self = this;',
' buf.component(function () { return ((self.lookup("if")) || Component).create({"data": Spacebars.call(self.lookup("foo")), "content": Component.extend({render: function (buf) {',
' buf.text("bar");',
' }}), "elseContent": Component.extend({render: function (buf) {',
' buf.text("baz");',
' }})}); });',
'}');
*/
});
Tinytest.add("spacebars - Spacebars.index", function (test) {
test.equal(Spacebars.index(null, 'foo'), null);
test.equal(Spacebars.index('foo', 'foo'), undefined);

View File

@@ -1,3 +1,13 @@
// Create an empty template object. Packages and apps add templates on
// to this object.
Template = {};
Template = {};
Template.__define__ = function (templateName, renderFunc) {
if (Template.hasOwnProperty(templateName))
throw new Error("Can't have two templates named: " + templateName);
Template[templateName] = UI.Component.extend({
kind: "Template_" + templateName,
render: renderFunc
});
};

View File

@@ -45,7 +45,7 @@ Package.on_test(function (api) {
'templating_tests.html2'
], 'client');
api.add_files([
'plugin/html_scanner.js',
'plugin/html2_scanner.js',
'scanner_tests.js'
], 'server');
});

View File

@@ -154,10 +154,8 @@ html2_scanner = {
sourceName: 'Template "' + name + '"'
});
results.js += "\nTemplate[" + JSON.stringify(name) +
"] = UI.Component.extend({kind: " +
JSON.stringify("Template_" + name) + ", render: " +
renderFuncCode + "});\n";
results.js += "\nTemplate.__define__(" + JSON.stringify(name) +
", " + renderFuncCode + ");\n";
} else {
// <body>
if (hasAttribs)

View File

@@ -22,12 +22,17 @@ Tinytest.add("templating - html scanner", function (test) {
test.fail("Parse error didn't throw exception");
};
var BODY_PREAMBLE = "Meteor.startup(function(){" +
"document.body.appendChild(Spark.render(" +
"Template.__define__(null,";
var BODY_POSTAMBLE = ")));});";
var TEMPLATE_PREAMBLE = "Template.__define__(";
var TEMPLATE_POSTAMBLE = ");\n";
// returns the appropriate code to put content in the body,
// where content is something simple like the string "Hello"
// (passed in as a source string including the quotes).
var simpleBody = function (content) {
return "\nUI.body2.contentParts.push(UI.Component.extend({render: (function() {\n var self = this;\n return " + content + ";\n})}));\nMeteor.startup(function () { if (! UI.body2.INSTANTIATED) { UI.materialize(UI.body2, document.body); } });\n";
};
// arguments are quoted strings like '"hello"'
var simpleTemplate = function (templateName, content) {
return '\nTemplate.__define__(' + templateName + ', (function() {\n var self = this;\n var __content = self.__content, __elseContent = self.__elseContent;\n return ' + content + ';\n}));\n';
};
var checkResults = function(results, expectJs, expectHead) {
test.equal(results.body, '');
@@ -35,6 +40,8 @@ Tinytest.add("templating - html scanner", function (test) {
test.equal(results.head, expectHead || '');
};
var html_scanner = html2_scanner;
checkError(function() {
return html_scanner.scan("asdf");
}, "formatting in HTML template", 1);
@@ -42,73 +49,65 @@ Tinytest.add("templating - html scanner", function (test) {
// body all on one line
checkResults(
html_scanner.scan("<body>Hello</body>"),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["Hello"])'+BODY_POSTAMBLE);
simpleBody('"Hello"'));
// multi-line body, contents trimmed
checkResults(
html_scanner.scan("\n\n\n<body>\n\nHello\n\n</body>\n\n\n"),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["Hello"])'+BODY_POSTAMBLE);
simpleBody('"Hello"'));
// same as previous, but with various HTML comments
checkResults(
html_scanner.scan("\n<!--\n\nfoo\n-->\n<!-- -->\n"+
"<body>\n\nHello\n\n</body>\n\n<!----\n>\n\n"),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["Hello"])'+BODY_POSTAMBLE);
simpleBody('"Hello"'));
// head and body
checkResults(
html_scanner.scan("<head>\n<title>Hello</title>\n</head>\n\n<body>World</body>\n\n"),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["World"])'+BODY_POSTAMBLE,
simpleBody('"World"'),
"<title>Hello</title>");
// head and body with tag whitespace
checkResults(
html_scanner.scan("<head\n>\n<title>Hello</title>\n</head >\n\n<body>World</body\n\n>\n\n"),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["World"])'+BODY_POSTAMBLE,
simpleBody('"World"'),
"<title>Hello</title>");
// head, body, and template
checkResults(
html_scanner.scan("<head>\n<title>Hello</title>\n</head>\n\n<body>World</body>\n\n"+
'<template name="favoritefood">\n pizza\n</template>\n'),
BODY_PREAMBLE+'Package.handlebars.Handlebars.json_ast_to_func(["World"])'+BODY_POSTAMBLE+
TEMPLATE_PREAMBLE+'"favoritefood",Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE,
simpleBody('"World"') + simpleTemplate('"favoritefood"', '"pizza"'),
"<title>Hello</title>");
// one-line template
checkResults(
html_scanner.scan('<template name="favoritefood">pizza</template>'),
TEMPLATE_PREAMBLE+'"favoritefood",Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE);
simpleTemplate('"favoritefood"', '"pizza"'));
// template with other attributes
checkResults(
html_scanner.scan('<template foo="bar" name="favoritefood" baz="qux">'+
'pizza</template>'),
TEMPLATE_PREAMBLE+'"favoritefood",Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE);
simpleTemplate('"favoritefood"', '"pizza"'));
// whitespace around '=' in attributes and at end of tag
checkResults(
html_scanner.scan('<template foo = "bar" name ="favoritefood" baz= "qux" >'+
'pizza</template\n\n>'),
TEMPLATE_PREAMBLE+'"favoritefood",Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE);
simpleTemplate('"favoritefood"', '"pizza"'));
// whitespace around template name
checkResults(
html_scanner.scan('<template name=" favoritefood ">pizza</template>'),
TEMPLATE_PREAMBLE+'"favoritefood",Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE);
simpleTemplate('"favoritefood"', '"pizza"'));
// single quotes around template name
checkResults(
html_scanner.scan('<template name=\'the "cool" template\'>'+
'pizza</template>'),
TEMPLATE_PREAMBLE+'"the \\"cool\\" template",'+
'Package.handlebars.Handlebars.json_ast_to_func(["pizza"])'+
TEMPLATE_POSTAMBLE);
simpleTemplate('"the \\"cool\\" template"', '"pizza"'));
// error cases; exact line numbers are not critical, these just reflect
// the current implementation

View File

@@ -556,3 +556,9 @@ Tinytest.add('templating - each falsy Issue #801', function (test) {
test.equal(canonicalizeHtml(div.innerHTML), "12null");
});
Tinytest.add('templating - duplicate template error', function (test) {
Template.__define__("foo", function () {});
test.throws(function () {
Template.__define__("foo", function () {});
});
});