text: 'use strict';
  const jsdom = require("jsdom").jsdom;
  const defaultDocument = jsdom('<html><body></body></html>');
  
  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><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();
                  }
              });
  
              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;
          }
      }
  };