marko/test/util/runRenderTest.js

225 lines
7.3 KiB
JavaScript

'use strict';
const path = require('path');
const fs = require('fs');
const marko = require('marko');
const fsExtra = require('fs-extra');
const domToHTML = require('./domToHTML');
const domToString = require('./domToString');
const jsdom = require("jsdom").jsdom;
const expect = require('chai').expect;
const defaultDocument = jsdom('<html><body></body></html>');
require('../../').setDocument(defaultDocument); // We need this to parse HTML fragments on the server
function createAsyncVerifier(main, helpers, out) {
var events = [];
var eventsByAwaitInstance = {};
var addEventListener = function(event) {
out.on(event, function(arg) {
var name = arg.name;
if (!eventsByAwaitInstance[name]) {
eventsByAwaitInstance[name] = [];
}
eventsByAwaitInstance[name].push(event);
events.push({
event: event,
arg: arg
});
});
};
addEventListener('await:begin');
addEventListener('await:beforeRender');
addEventListener('await:finish');
return {
verify() {
if (main.checkEvents) {
main.checkEvents(events, helpers, out);
}
// Make sure all of the await instances were correctly ended
Object.keys(eventsByAwaitInstance).forEach(function(name) {
var events = eventsByAwaitInstance[name];
expect(events).to.deep.equal([
'await:begin',
'await:beforeRender',
'await:finish'
]);
});
}
};
}
module.exports = function runRenderTest(dir, helpers, done, options) {
let output = options.output || 'html';
require('marko/compiler').configure({output: output});
let isVDOM = output === 'vdom';
let checkAsyncEvents = options.checkAsyncEvents === true;
let actualDir;
if (isVDOM) {
actualDir = path.join(dir, '../~vdom.skip/' + path.basename(dir));
fsExtra.removeSync(actualDir);
fsExtra.copySync(dir, actualDir, {
filter: function(file) {
if (file.endsWith('.marko.js') || file.indexOf('.generated.') !== -1) {
return false;
}
return true;
}
});
} else {
actualDir = dir;
}
let templatePath = path.join(actualDir, 'template.marko');
let mainPath = path.join(actualDir, 'test.js');
let main = fs.existsSync(mainPath) ? require(mainPath) : {};
let loadOptions = main && main.loadOptions;
if (isVDOM && main.vdomSkip) {
return done();
}
if (main.writeToDisk === false) {
require('marko/compiler').defaultOptions.writeToDisk = false;
}
if (main.preserveWhitespaceGlobal === true) {
require('marko/compiler').defaultOptions.preserveWhitespace = true;
}
var oldDone = done;
done = function(err) {
require('marko/compiler').configure();
if (err) {
return oldDone(err);
}
return oldDone();
};
try {
if (main.checkError) {
let e;
try {
marko.load(templatePath, loadOptions);
} catch(_e) {
e = _e;
let errorFile = path.join(actualDir, 'error.txt');
fs.writeFileSync(errorFile, e.stack.toString(), { encoding: 'utf8' });
}
if (!e) {
throw new Error('Error expected');
}
main.checkError(e);
return done();
} else {
let template = marko.load(templatePath, loadOptions);
let templateData = Object.assign({}, main.templateData || {});
let out = template.createOut();
let asyncEventsVerifier;
if (checkAsyncEvents) {
asyncEventsVerifier = createAsyncVerifier(main, helpers, out);
}
template.render(templateData, out, function(err, renderOutput) {
if (err) {
return done(err);
}
if (isVDOM) {
let vdomTree = renderOutput;
var getExpectedHtml = function(callback) {
var expectedHtml;
try {
expectedHtml = fs.readFileSync(path.join(dir, 'vdom-expected.html'), { encoding: 'utf8'});
} catch(e) {}
if (expectedHtml) {
return callback(null, expectedHtml);
}
require('marko/compiler').configure({ output: 'html' });
let htmlTemplatePath = path.join(dir, 'template.marko');
let htmlTemplate = marko.load(htmlTemplatePath);
let htmlMainPath = path.join(dir, 'test.js');
let htmlMain = fs.existsSync(htmlMainPath) ? require(htmlMainPath) : {};
htmlTemplate.render(htmlMain.templateData || {}, function(err, html) {
if (err) {
return callback(err);
}
let document = jsdom('<html><body>' + html + '</body></html>');
let expectedHtml = domToString(document.body, { childrenOnly: true });
callback(null, expectedHtml);
});
};
getExpectedHtml(function(err, expectedHtml) {
fs.writeFileSync(path.join(dir, 'vdom-expected.generated.html'), expectedHtml, { encoding: 'utf8' });
let actualizedDom = vdomTree.actualize(defaultDocument);
// NOTE: We serialie the virtual DOM tree into an HTML string and reparse so that we can
// normalize the text
let vdomHtml = domToHTML(actualizedDom);
let vdomRealDocument = jsdom('<html><body>' + vdomHtml + '</body></html>');
let vdomString = domToString(vdomRealDocument.body, { childrenOnly: true });
helpers.compare(vdomString, 'vdom-', '.generated.html');
if (checkAsyncEvents) {
asyncEventsVerifier.verify();
}
done();
});
} else {
let html = renderOutput;
if (main.checkHtml) {
fs.writeFileSync(path.join(dir, 'actual.html'), html, { encoding: 'utf8' });
main.checkHtml(html);
} else {
helpers.compare(html, '.html');
}
if (checkAsyncEvents) {
asyncEventsVerifier.verify();
}
done();
}
});
out.end();
}
} finally {
if (main.writeToDisk === false) {
delete require('marko/compiler').defaultOptions.writeToDisk;
}
if (main.preserveWhitespaceGlobal === true) {
delete require('marko/compiler').defaultOptions.preserveWhitespace;
}
}
};