"use strict"; require("../__util__/test-init"); const fs = require("fs"); const path = require("path"); const marko = require("marko"); const autotest = require("../autotest"); const domToString = require("../__util__/domToString"); const createBrowserWithMarko = require("../__util__/create-marko-jsdom-module"); const expect = require("chai").expect; const toDiffableHtml = require("diffable-html"); const browser = createBrowserWithMarko(__dirname, ""); autotest("fixtures", { html: testRunner, vdom: testRunner, "html ≅ vdom": compareNormalized }); autotest("fixtures-deprecated", { html: testRunner, vdom: testRunner, "html ≅ vdom": compareNormalized }); autotest("fixtures-async-callback", { html: testRunner }); autotest("fixtures-async-deprecated", { html: testRunner }); function testRunner(fixture) { fixture.test(() => runRenderTest(fixture)); } function compareNormalized({ test, context }) { test(function() { if ( !context.hasOwnProperty("html") || !context.hasOwnProperty("vdom") ) { this.skip(); } else { expect(context.html).to.equal(context.vdom); } }); } async function runRenderTest(fixture) { let dir = fixture.dir; let output = fixture.mode || "html"; let snapshot = fixture.snapshot; let isVDOM = output === "vdom"; let templatePath = path.join(dir, "template.marko"); let mainPath = path.join(dir, "test.js"); let main = !fs.existsSync(mainPath) ? {} : isVDOM ? browser.require(mainPath) : require(mainPath); let loadOptions = main && main.loadOptions; try { var compilerOptions = { output: output, writeToDisk: main.writeToDisk !== false, preserveWhitespace: main.preserveWhitespaceGlobal === true, ignoreUnrecognizedTags: main.ignoreUnrecognizedTags === true }; require("marko/compiler").configure(compilerOptions); if (main.checkError) { let e; try { isVDOM ? browser.require(templatePath) : marko.load(templatePath, loadOptions); } catch (_e) { e = _e; let errorFile = path.join(dir, "error.txt"); fs.writeFileSync(errorFile, e.stack.toString(), { encoding: "utf8" }); } if (!e) { throw new Error("Error expected"); } main.checkError(e); return; } else { let template = isVDOM ? browser.require(templatePath) : marko.load(templatePath, loadOptions); let templateData = Object.assign({}, main.templateData || {}); if (template.default) { template = template.default; } let out = template.createOut(); let asyncEventsVerifier = createAsyncVerifier( main, snapshot, out, main.noFlushComment, isVDOM ); template.render(templateData, out).end(); if (isVDOM) { let document = browser.window.document; let actualNode = document.createDocumentFragment(); if (out.___state.___finished) { out.___getResult().replaceChildrenOf(actualNode); } else { (await out).replaceChildrenOf(actualNode); } actualNode.normalize(); let vdomString = domToString(actualNode, { childrenOnly: true }); snapshot(vdomString, { name: "vdom", ext: ".html" }); fixture.context.vdom = normalizeHtml(actualNode); } else { let html = (await out).getOutput(); if (main.checkHtml) { fs.writeFileSync(path.join(dir, "actual.html"), html, { encoding: "utf8" }); main.checkHtml(html); } else { snapshot(html, { ext: ".html", format: toDiffableHtml }); } fixture.context.html = normalizeHtml(html); } asyncEventsVerifier.verify(); } } finally { require("marko/compiler").configure(); } } function normalizeHtml(htmlOrNode) { let document = browser.window.document; if (typeof htmlOrNode === "string") { document.open(); document.write(htmlOrNode); document.close(); } else { document.documentElement.innerHTML = ""; document.body.appendChild(htmlOrNode); } const treeWalker = document.createTreeWalker(document.body); const nodesToRemove = []; while (treeWalker.nextNode()) { const node = treeWalker.currentNode; if ( node.nodeType === 8 || isIgnoredTag(node) || isClientReorderFragment(node) ) { nodesToRemove.push(node); } else if (node.tagName === "TEXTAREA") { node.textContent = node.value; } } nodesToRemove.forEach(n => n.remove()); document.body.innerHTML = document.body.innerHTML; document.body.normalize(); return document.body.innerHTML.trim(); } function isIgnoredTag(node) { switch (node.tagName) { case "LINK": case "TITLE": case "STYLE": case "SCRIPT": return true; default: return false; } } function isClientReorderFragment(node) { return /^af\d+$/.test(node.id); } function createAsyncVerifier(main, snapshot, out, noFlushComment, isVDOM) { 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: Object.assign({}, arg) }); }); }; addEventListener("await:begin"); addEventListener("await:beforeRender"); addEventListener("await:finish"); if (!isVDOM && !noFlushComment) { var _flush = out.flush; out.flush = function() { try { out.comment("FLUSH"); } catch (e) { // we may try to flush after the out has ended // if this is the case, trying to add a comment // will throw an error. we can safely ignore this } _flush && _flush.apply(out, arguments); }; } return { verify() { if (main.checkEvents && !isVDOM) { main.checkEvents(events, snapshot, 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" ]); }); } }; }