System.register(['reactive-jsx', 'src/client/ContextJS/src/contextjs.js', 'src/components/widgets/lively-morph.js'], function (_export, _context) {
  "use strict";

  var _addSourceLocation, cop, Morph;

  return {
    setters: [function (_reactiveJsx) {
      _addSourceLocation = _reactiveJsx.addSourceLocation;
    }, function (_srcClientContextJSSrcContextjsJs) {
      cop = _srcClientContextJSSrcContextjsJs;
    }, function (_srcComponentsWidgetsLivelyMorphJs) {
      Morph = _srcComponentsWidgetsLivelyMorphJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_components_tools_lively_testrunner_js = _recorder_._src_components_tools_lively_testrunner_js || {};
      Object.defineProperty(_recorder_._src_components_tools_lively_testrunner_js, 'isTestFile', {
        get() {
          return isTestFile;
        },

        set(thisIsVererySecretVariableName) {
          _export('isTestFile', isTestFile = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_testrunner_js, 'cop', {
        get() {
          return cop;
        },

        set(thisIsVererySecretVariableName) {
          cop = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_tools_lively_testrunner_js, 'Morph', {
        get() {
          return Morph;
        },

        set(thisIsVererySecretVariableName) {
          Morph = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });


      /*MD # Testrunner 
      
      [doc](browse://doc/tools/test-runner.md)
      
      ![](../../../doc/tools/media/test-runner.png){width=400px}
      
      MD*/

      //     Mocha.utils.parseQuery()

      // var parseQueryLayer = cop.create("MochaParseQueryLayer");
      // parseQueryLayer.layerObject(Mocha.utils, {

      // });

      function isTestFile(fileName) {
        return fileName.match(/(-|\.)(spec|test)\.js$/);
      }

      _export('isTestFile', isTestFile);

      class TestRunner extends Morph {
        get testDir() {
          return this.get('#testDir');
        }
        setTestPath(path) {
          this.testDir.value = path;
        }

        initialize() {

          this.windowTitle = "Test Runner";
          this.registerButtons();
          // lively.html.registerInputs(this)
          this.testDir.addEventListener('keydown', event => {
            if (event.keyCode == 13) {
              // ENTER
              this.onRunButton();
              event.stopPropagation();
              event.preventDefault();
            }
          });

          if (!this.querySelector("#mocha")) {
            this.mochadiv = _addSourceLocation(document.createElement("div"), {
              file: 'https://lively-kernel.org/lively4/composed-offset/src/components/tools/lively-testrunner.js',
              end: {
                column: 51,
                line: 44
              },
              start: {
                column: 22,
                line: 44
              }
            });
            this.mochadiv.id = "mocha";
            this.appendChild(this.mochadiv);
          } else {
            this.mochadiv = this.querySelector("#mocha");
          }
          this.querySelector("#mocha").innerHTML = "";

          lively.loadCSSThroughDOM("mochaCSS", lively4url + "/src/external/mocha.css");

          this.resetMocha();

          var testDir = this.getAttribute('testDir');
          if (testDir) {
            this.testDir.value = testDir;
          }
          this.testDir.addEventListener("input", e => this.testDirChanged(e));
        }

        async findTestFilesInDir(dir) {
          try {
            var files = await lively.files.walkDir(lively4url + dir);
          } catch (e) {
            console.error(e);
            files = [];
          }
          return files.filter(isTestFile);
        }

        async findTestFiles() {
          const dirs = this.testDir.value;

          // single file?
          if (isTestFile(dirs)) {
            try {
              var file = dirs.startsWith('/') ? dirs.substring(1) : dirs;
              file = new URL(file, lively4url + '/').href;
              const infoString = await lively.files.statFile(file);
              const { type } = JSON.parse(infoString);
              if (type === 'file') {
                return [file];
              }
            } catch (e) {}
          }

          var files = [];
          var list = dirs.split(",");
          console.log("[testrunner] findTestFiles: " + list

          // await Promise.all(list.map((dir) => {
          //   console.log("find test file in dir: " + dir)
          //   return this.findTestFilesInDir(dir).then(newFiles => {
          //     files = files.concat(newFiles)
          //   })
          // }));

          );for (let dir of list) {
            let newFiles = await this.findTestFilesInDir(dir);
            files = files.concat(newFiles);
          }

          return files;
          // #WhyNotThis
          // return ["/test/", "/test/templates/"].reduce(async (sum, ea) => {
          //     return sum.concat(await this.findTestFilesInDir(ea))
          // }, [])
        }
        // await that.findTestFilesInDir( "/test/templates/")

        clearTests() {
          if (mocha.suite) {
            mocha.suite.tests.length = 0;
            mocha.suite.suites.length = 0;
          }
          this.querySelector("#mocha").innerHTML = "";
        }

        async loadTestsInOrder() {

          var testFiles = await this.findTestFiles();
          for (var url of testFiles) {
            var name = url.replace(/.*\//, "").replace(/\/\.[^\.]*/, "");
            // lively.notify("loadTest: " + name)
            await lively.reloadModule(url);
            await System.import(url);
          }
        }

        async loadTests() {
          var testFiles = await this.findTestFiles();
          return Promise.all(testFiles.map(async url => {
            var name = url.replace(/.*\//, "").replace(/\/\.[^\.]*/, "");
            await lively.reloadModule(url);
            await System.import(url);
            this.get("#log").textContent += "loaded Test: " + name + "\n";
          }));
        }

        runTests() {
          this.get("#log").textContent = "";
          var self = this;
          mocha.run(failures => {
            if (self.prevState) {
              this.isRunning = false;
              window.history.pushState(self.prevState, '', self.prevLocation + "&grep=.*");
              self.prevState = self.prevLocation = undefined;
            }
            self.fixHTML();
          });
        }

        async onRunButton() {
          this.clearTests();

          await this.loadTests();
          this.runTests();
        }

        async onResetButton() {
          this.clearTests();
          this.resetMocha();
        }

        // some tests, e.g. ContextJS manage to break mocha, so that they can be only once... without this
        resetMocha() {
          return lively.loadJavaScriptThroughDOM("mochaJS", lively4url + "/src/external/mocha.js", true).then(() => {
            mocha.setup("bdd");
          });
        }

        //  window.history.pushState({ mochastate: true }, '',        window.location);
        fixHTML() {
          var self = this;
          this.mochadiv.querySelectorAll(".replay").forEach(ea => {
            ea.innerHTML = "R";
            ea.onclick = evt => {
              evt.preventDefault();
              self.prevState = window.history.state;
              self.prevLocation = window.location.toString().replace(/&grep=[^&]+/, '');
              var grep = ea.href.replace(/.*(&grep.*)/, '$1');
              window.history.pushState({ mochastate: true }, '', self.prevLocation + grep);
              self.onRunButton();
              return false;
            };
          });
        }

        async onTestDirChanged() {
          this.onRunButton();
        }

        runMocha() {
          // #TODO port this to Lively4
          var self = this;

          // this.runTestFile("http://localhost:9001/lively-mocha-tester/tests/test-test.js")

          return new Promise((resolve, reject) => {
            var files = mocha.suite.suites.compact().pluck("file");
            var tests = this.state.loadedTests.filter(ea => files.include(ea.file)).pluck("tests").flatten();

            if (!tests || !tests.length) return reject(new Error(`Trying to run tests of ${files.join(", ")} but cannot find them in loaded tests!`));

            mocha.reporter(function Reporter(runner) {
              // this.done = (failures) => show("done " + failures)
              // runner.on("suite", function (x) { show("suite %s", x) });
              // runner.on("pending", function (x) { show("pending %s", x) });

              runner.on("test", test => {
                try {
                  var t = tests.detect(ea => ea.fullTitle === test.fullTitle());
                  t.state = "running";
                  self.update();
                } catch (e) {
                  self.showError("runner on test error: " + e.stack);
                }
              });

              // runner.on("test end", test => {
              //   try {
              //     var t = tests.tests.detect(ea => ea.fullTitle === test.fullTitle();
              //     t.state = "finished";
              // self.update();
              //   } catch (e) { self.showError("error: " + e.stack); }
              // });

              runner.on("pass", test => {
                try {
                  var t = tests.detect(ea => ea.fullTitle === test.fullTitle());
                  t.state = "succeeded";
                  t.duration = test.duration;
                  self.update();
                } catch (e) {
                  self.showError("runner on pass error: " + e.stack);
                }
              });

              runner.on("fail", (test, error) => {
                try {
                  var t = tests.detect(ea => ea.fullTitle === test.fullTitle());
                  if (t) attachErrorToTest(t, error, test.duration);else {
                    // "test" is a hook...
                    var parentTests = test.parent.tests.invoke("fullTitle");
                    tests.filter(ea => parentTests.include(ea.fullTitle)).forEach(ea => attachErrorToTest(ea, error, test.duration));
                  }

                  self.update();

                  function attachErrorToTest(test, error, duration) {
                    test.state = "failed";
                    test.duration = test.duration;
                    test.error = error;
                  }
                } catch (e) {
                  self.showError("runner on fail error: " + e.stack);
                }
              });

              // runner.on("start", test => { show("START %o", lively.printInspect(test ,1)) });
              // runner.on("end", test => { show("end %o", lively.printInspect(test ,1)) });

              // runner.on("hook end", function (x) { show("hook end %s", x) });
              // runner.on("suite end", function (x) { show("suite end %s", x) });
            });

            mocha.run(failures => resolve());
          });
        }

        livelyMigrate(other) {
          this.testDir.value = other.testDir.value;
        }

        livelyPrepareSave() {
          this.setAttribute('testDir', this.testDir.value);
        }

        testDirChanged(e) {
          this.setAttribute('testDir', this.testDir.value);
        }
      }

      _export('default', TestRunner);

      Object.defineProperty(_recorder_._src_components_tools_lively_testrunner_js, 'TestRunner', {
        get() {
          return TestRunner;
        },

        set(thisIsVererySecretVariableName) {
          _export('default', TestRunner = thisIsVererySecretVariableName);

          return true;
        },

        enumerable: true,
        configurable: true
      });
    }
  };
});