Files
meteor/packages/templating-tools/html-scanner-tests.js
Sashko Stubailo ec33215a7f Static HTML package and templating refactor
1. Make a package called `static-html` that just compiles `<head>` and `<body>` tags inside `.html` files. Much like templating but without the templates.
2. Refactor `templating` to avoid duplicating code.

Split out code that would be shared between `templating` and `static-html` into a new `templating-tools` package. These tools could also be used to simplify implementation of other build plugins, like `simple:markdown-templating`.

This also has the added benefit of moving as much code as humanly possible out of the `templating` package, so that it can be reused in other packages.

1. `templating-tools` package and its README
2. `static-html` package and its README
3. `caching-html-compiler` is not new code; it is just code factored out of the batch plugin version of `templating`, but the README and some comments are new.

1. `tools/tests/static-html.js` tests static html and error handling
2. `templating-tools/html-scanner-tests.js` tests `scanHtmlForTags` and `compileTagsWithSpacebars` together.

All unit tests pass on this branch.
2015-07-31 10:40:23 -07:00

190 lines
6.0 KiB
JavaScript

Tinytest.add("templating-tools - html scanner", function (test) {
var testInString = function(actualStr, wantedContents) {
if (actualStr.indexOf(wantedContents) >= 0)
test.ok();
else
test.fail("Expected "+JSON.stringify(wantedContents)+
" in "+JSON.stringify(actualStr));
};
var checkError = function(f, msgText, lineNum) {
try {
f();
} catch (e) {
if (! e instanceof TemplatingTools.CompileError) {
throw e;
}
if (e.line === lineNum)
test.ok();
else
test.fail("Error should have been on line " + lineNum + ", not " +
e.line);
testInString(e.message, msgText);
return;
}
test.fail("Parse error didn't throw exception");
};
// 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 "\nTemplate.body.addContent((function() {\n var view = this;\n return " + content + ";\n}));\nMeteor.startup(Template.body.renderToDocument);\n";
};
// arguments are quoted strings like '"hello"'
var simpleTemplate = function (templateName, content) {
// '"hello"' into '"Template.hello"'
var viewName = templateName.slice(0, 1) + 'Template.' + templateName.slice(1);
return '\nTemplate.__checkName(' + templateName + ');\nTemplate[' + templateName +
'] = new Template(' + viewName +
', (function() {\n var view = this;\n return ' + content + ';\n}));\n';
};
var checkResults = function(results, expectJs, expectHead, expectBodyAttrs) {
test.equal(results.body, '');
test.equal(results.js, expectJs || '');
test.equal(results.head, expectHead || '');
test.equal(results.bodyAttrs, expectBodyAttrs || {});
};
function scanForTest(contents) {
const tags = TemplatingTools.scanHtmlForTags({
sourceName: "",
contents: contents,
tagNames: ["body", "head", "template"]
});
return TemplatingTools.compileTagsWithSpacebars(tags);
}
checkError(function() {
return scanForTest("asdf");
}, "Expected one of: <body>, <head>, <template>", 1);
// body all on one line
checkResults(
scanForTest("<body>Hello</body>"),
simpleBody('"Hello"'));
// multi-line body, contents trimmed
checkResults(
scanForTest("\n\n\n<body>\n\nHello\n\n</body>\n\n\n"),
simpleBody('"Hello"'));
// same as previous, but with various HTML comments
checkResults(
scanForTest("\n<!--\n\nfoo\n-->\n<!-- -->\n"+
"<body>\n\nHello\n\n</body>\n\n<!----\n>\n\n"),
simpleBody('"Hello"'));
// head and body
checkResults(
scanForTest("<head>\n<title>Hello</title>\n</head>\n\n<body>World</body>\n\n"),
simpleBody('"World"'),
"<title>Hello</title>");
// head and body with tag whitespace
checkResults(
scanForTest("<head\n>\n<title>Hello</title>\n</head >\n\n<body>World</body\n\n>\n\n"),
simpleBody('"World"'),
"<title>Hello</title>");
// head, body, and template
checkResults(
scanForTest("<head>\n<title>Hello</title>\n</head>\n\n<body>World</body>\n\n"+
'<template name="favoritefood">\n pizza\n</template>\n'),
simpleBody('"World"') + simpleTemplate('"favoritefood"', '"pizza"'),
"<title>Hello</title>");
// one-line template
checkResults(
scanForTest('<template name="favoritefood">pizza</template>'),
simpleTemplate('"favoritefood"', '"pizza"'));
// template with other attributes
checkResults(
scanForTest('<template foo="bar" name="favoritefood" baz="qux">'+
'pizza</template>'),
simpleTemplate('"favoritefood"', '"pizza"'));
// whitespace around '=' in attributes and at end of tag
checkResults(
scanForTest('<template foo = "bar" name ="favoritefood" baz= "qux" >'+
'pizza</template\n\n>'),
simpleTemplate('"favoritefood"', '"pizza"'));
// whitespace around template name
checkResults(
scanForTest('<template name=" favoritefood ">pizza</template>'),
simpleTemplate('"favoritefood"', '"pizza"'));
// single quotes around template name
checkResults(
scanForTest('<template name=\'the "cool" template\'>'+
'pizza</template>'),
simpleTemplate('"the \\"cool\\" template"', '"pizza"'));
checkResults(scanForTest('<body foo="bar">\n Hello\n</body>'), simpleBody('"Hello"'), "", {foo: "bar"});
// error cases; exact line numbers are not critical, these just reflect
// the current implementation
// unclosed body (error mentions body)
checkError(function() {
return scanForTest("\n\n<body>\n Hello\n</body");
}, "body", 3);
// bad open tag
checkError(function() {
return scanForTest("\n\n\n<bodyd>\n Hello\n</body>");
}, "Expected one of: <body>, <head>, <template>", 4);
checkError(function() {
return scanForTest("\n\n\n\n<body foo=>\n Hello\n</body>");
}, "error in tag", 5);
// unclosed tag
checkError(function() {
return scanForTest("\n<body>Hello");
}, "nclosed", 2);
// unnamed template
checkError(function() {
return scanForTest(
"\n\n<template>Hi</template>\n\n<template>Hi</template>");
}, "name", 3);
// helpful doctype message
checkError(function() {
return scanForTest(
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '+
'"http://www.w3.org/TR/html4/strict.dtd">'+
'\n\n<head>\n</head>');
}, "DOCTYPE", 1);
// lowercase basic doctype
checkError(function() {
return scanForTest(
'<!doctype html>');
}, "DOCTYPE", 1);
// attributes on head not supported
checkError(function() {
return scanForTest('<head foo="bar">\n Hello\n</head>');
}, "<head>", 1);
// can't mismatch quotes
checkError(function() {
return scanForTest('<template name="foo\'>'+
'pizza</template>');
}, "error in tag", 1);
// unexpected <html> at top level
checkError(function() {
return scanForTest('\n<html>\n</html>');
}, "Expected one of: <body>, <head>, <template>", 2);
});