'use strict'; const jsdom = require("jsdom").jsdom; const defaultDocument = jsdom(''); 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 expect = require('chai').expect; 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); } out.on('error', done); out.on('finish', function(result) { var renderOutput = result.getOutput(); 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' }); require('marko/runtime/vdom/AsyncVDOMBuilder').prototype.$__document = defaultDocument; global.document = defaultDocument; 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 + ''); 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('' + vdomHtml + ''); 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(); } }); template.render(templateData, out).end(); } } finally { if (main.writeToDisk === false) { delete require('marko/compiler').defaultOptions.writeToDisk; } if (main.preserveWhitespaceGlobal === true) { delete require('marko/compiler').defaultOptions.preserveWhitespace; } } };