tests: Compile Handlebars templates with source maps.

This allows us to collect coverage for Handlebars templates, and also
improves the readability of Handlebars-related stack traces.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2020-02-10 03:32:56 -08:00 committed by showell
parent 36a8e61e67
commit 4889a0486d
2 changed files with 48 additions and 42 deletions

View File

@ -1,32 +1,24 @@
const fs = require("fs"); const fs = require("fs");
const Handlebars = require("handlebars/dist/cjs/handlebars.js");
const path = require("path"); const path = require("path");
const { SourceMapConsumer, SourceNode } = require("source-map");
const templates_path = path.resolve(__dirname, "../../static/templates"); const templates_path = path.resolve(__dirname, "../../static/templates");
let render;
exports.make_handlebars = () => { exports.make_handlebars = () => Handlebars.create();
// Create a new Handlebars instance.
const Handlebars = require("handlebars/dist/cjs/handlebars.js");
const hb = Handlebars.create();
const compiled = new Set(); exports.stub_templates = stub => {
const compileFile = filename => { window.template_stub = stub;
const name = "$" + path.relative(templates_path, filename);
if (!compiled.has(name)) {
compiled.add(name);
hb.registerPartial(
name,
hb.compile(fs.readFileSync(filename, "utf-8"), { preventIndent: true, zjsFilename: filename })
);
}
return name;
}; };
const hb = Handlebars.create();
class ZJavaScriptCompiler extends hb.JavaScriptCompiler { class ZJavaScriptCompiler extends hb.JavaScriptCompiler {
nameLookup(parent, name, type) { nameLookup(parent, name, type) {
// Auto-register partials with relative paths, like handlebars-loader. // Auto-register partials with relative paths, like handlebars-loader.
if (type === "partial" && name !== "@partial-block") { if (type === "partial" && name !== "@partial-block") {
name = compileFile(path.resolve(path.dirname(this.options.zjsFilename), name + ".hbs")); const filename = path.resolve(path.dirname(this.options.srcName), name + ".hbs");
return ["require(", JSON.stringify(filename), ")"];
} }
return super.nameLookup(parent, name, type); return super.nameLookup(parent, name, type);
} }
@ -35,18 +27,32 @@ exports.make_handlebars = () => {
ZJavaScriptCompiler.prototype.compiler = ZJavaScriptCompiler; ZJavaScriptCompiler.prototype.compiler = ZJavaScriptCompiler;
hb.JavaScriptCompiler = ZJavaScriptCompiler; hb.JavaScriptCompiler = ZJavaScriptCompiler;
render = (filename, ...args) => hb.partials[compileFile(filename)](...args);
return hb;
};
exports.stub_templates = stub => {
render = (filename, ...args) => {
const name = path.relative(templates_path, filename).slice(0, -".hbs".length);
return stub(name, ...args);
};
};
require.extensions[".hbs"] = (module, filename) => { require.extensions[".hbs"] = (module, filename) => {
module.exports = (...args) => render(filename, ...args); const code = fs.readFileSync(filename, "utf-8");
const name = path.relative(templates_path, filename).slice(0, -".hbs".length);
const pc = hb.precompile(code, { preventIndent: true, srcName: filename });
const node = new SourceNode();
node.add([
"let hb, template;\n",
"module.exports = (...args) => {\n",
" if (window.template_stub !== undefined) {\n",
" return window.template_stub(",
JSON.stringify(name),
", ...args);\n",
" }\n",
" if (hb !== Handlebars) {\n",
" template = (hb = Handlebars).template(",
SourceNode.fromStringWithSourceMap(pc.code, new SourceMapConsumer(pc.map)),
");\n",
" }\n",
" return template(...args);\n",
"};\n",
]);
const out = node.toStringWithSourceMap();
module._compile(
out.code +
"\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," +
Buffer.from(out.map.toString()).toString("base64"),
filename
);
}; };

View File

@ -202,7 +202,7 @@ def run_tests_via_node_js() -> int:
coverage_lcov_file = os.path.join(coverage_dir, 'lcov.info') coverage_lcov_file = os.path.join(coverage_dir, 'lcov.info')
nyc = os.path.join(ROOT_DIR, 'node_modules/.bin/nyc') nyc = os.path.join(ROOT_DIR, 'node_modules/.bin/nyc')
command = [nyc, '--extension', '.ts'] command = [nyc, '--extension', '.hbs', '--extension', '.ts']
command += ['--report-dir', coverage_dir] command += ['--report-dir', coverage_dir]
command += ['--temp-directory', coverage_dir, '-r=text-summary'] command += ['--temp-directory', coverage_dir, '-r=text-summary']
command += node_tests_cmd command += node_tests_cmd