{"version":3,"sources":["https://lively-kernel.org/lively4/lively4-RP19-4/src/components/tools/lively-testrunner.js"],"names":["cop","Morph","TestRunner","testDir","get","initialize","windowTitle","registerButtons","addEventListener","event","keyCode","onRunButton","stopPropagation","preventDefault","querySelector","mochadiv","document","createElement","id","appendChild","innerHTML","lively","loadCSSThroughDOM","lively4url","resetMocha","getAttribute","value","e","testDirChanged","findTestFilesInDir","dir","files","walkDir","console","error","filter","fileName","match","findTestFiles","list","split","log","newFiles","concat","clearTests","mocha","suite","tests","length","suites","loadTestsInOrder","testFiles","url","name","replace","reloadModule","System","import","loadTests","Promise","all","map","textContent","runTests","self","run","failures","prevState","isRunning","window","history","pushState","prevLocation","undefined","fixHTML","onResetButton","loadJavaScriptThroughDOM","then","setup","querySelectorAll","forEach","ea","onclick","evt","state","location","toString","grep","href","mochastate","onTestDirChanged","runMocha","resolve","reject","compact","pluck","loadedTests","include","file","flatten","Error","join","reporter","Reporter","runner","on","test","t","detect","fullTitle","update","showError","stack","duration","attachErrorToTest","parentTests","parent","invoke","livelyMigrate","other","livelyPrepareSave","setAttribute"],"mappings":"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","file":"lively-testrunner.js","sourcesContent":["import * as cop from \"src/client/ContextJS/src/contextjs.js\"\nimport Morph from 'src/components/widgets/lively-morph.js';\n\n\n/*MD # Testrunner \n\n[doc](browse://doc/tools/test-runner.md)\n\n![](../../../doc/tools/media/test-runner.png){width=400px}\n\nMD*/\n\n//     Mocha.utils.parseQuery()\n\n// var parseQueryLayer = cop.create(\"MochaParseQueryLayer\");\n// parseQueryLayer.layerObject(Mocha.utils, {\n\n// });\n\nexport default class TestRunner extends Morph {\n  get testDir() { return this.get('#testDir'); }\n\n  initialize() {\n    \n    this.windowTitle = \"Test Runner\"\n    this.registerButtons();\n    // lively.html.registerInputs(this)\n    this.testDir.addEventListener('keydown', event => {\n      if (event.keyCode == 13) { // ENTER\n        this.onRunButton();\n        event.stopPropagation();\n        event.preventDefault();\n      }\n    });\n\n    if (!this.querySelector(\"#mocha\")) {\n      this.mochadiv = document.createElement(\"div\");\n      this.mochadiv.id = \"mocha\";\n      this.appendChild(this.mochadiv);\n    } else {\n      this.mochadiv = this.querySelector(\"#mocha\");\n    }\n    this.querySelector(\"#mocha\").innerHTML = \"\";\n\n    lively.loadCSSThroughDOM(\"mochaCSS\", lively4url + \"/src/external/mocha.css\");\n\n    this.resetMocha();\n\n    var testDir =  this.getAttribute('testDir');\n    if (testDir) {\n      this.testDir.value = testDir;\n    }\n    this.testDir.addEventListener(\"input\", e => this.testDirChanged(e));\n  }\n\n  async findTestFilesInDir(dir) {\n    try {\n      var files = await lively.files.walkDir(lively4url + dir);\n    } catch(e) {\n      console.error(e)\n      files = []\n    }\n    return files\n      .filter(fileName => fileName.match(/(-|\\.)(spec|test)\\.js$/));\n  }\n\n  async findTestFiles() {\n    var files = []\n    var list = this.testDir.value.split(\",\")\n    console.log(\"[testrunner] findTestFiles: \" + list)\n\n    // await Promise.all(list.map((dir) => {\n    //   console.log(\"find test file in dir: \" + dir)\n    //   return this.findTestFilesInDir(dir).then(newFiles => {\n    //     files = files.concat(newFiles)\n    //   })\n    // }));\n\n    for (let dir of list) {\n      let newFiles = await this.findTestFilesInDir(dir)\n      files = files.concat(newFiles)\n    }\n\n    return files\n    // #WhyNotThis\n    // return [\"/test/\", \"/test/templates/\"].reduce(async (sum, ea) => {\n    //     return sum.concat(await this.findTestFilesInDir(ea))\n    // }, [])\n  }\n  // await that.findTestFilesInDir( \"/test/templates/\")\n\n  clearTests() {\n    if (mocha.suite) {\n      mocha.suite.tests.length = 0; \n      mocha.suite.suites.length = 0; \n    }\n    this.querySelector(\"#mocha\").innerHTML= \"\";\n  }\n\n  async loadTestsInOrder() {\n    \n    var testFiles = await this.findTestFiles()\n    for(var url of testFiles) {\n      var name = url.replace(/.*\\//,\"\").replace(/\\/\\.[^\\.]*/,\"\");\n      // lively.notify(\"loadTest: \" + name)\n      await lively.reloadModule(url);\n      await System.import(url);\n    }\n  }\n\n  \n  async loadTests() {\n    var testFiles = await this.findTestFiles()\n    return Promise.all(testFiles.map(async url => {\n      var name = url.replace(/.*\\//,\"\").replace(/\\/\\.[^\\.]*/,\"\");\n      await lively.reloadModule(url);\n      await System.import(url);\n      this.get(\"#log\").textContent += \"loaded Test: \" + name + \"\\n\"\n    }))\n  }\n\n  runTests() {\n    this.get(\"#log\").textContent = \"\"\n    var self = this;\n    mocha.run(failures => {\n      if (self.prevState) {\n        this.isRunning = false\n        window.history.pushState(self.prevState, '', self.prevLocation + \"&grep=.*\");\n        self.prevState = self.prevLocation = undefined;\n      }\n      self.fixHTML();\n    });\n  }\n\n  async onRunButton() {\n    this.clearTests();\n    \n    await this.loadTests();\n    this.runTests();\n  }\n\n  async onResetButton() {\n    this.clearTests();\n    this.resetMocha();\n  }\n\n  // some tests, e.g. ContextJS manage to break mocha, so that they can be only once... without this\n  resetMocha() {\n    return lively.loadJavaScriptThroughDOM(\"mochaJS\", lively4url + \"/src/external/mocha.js\", true).then(() => {\n      mocha.setup(\"bdd\");\n    });\n  }\n\n  //  window.history.pushState({ mochastate: true }, '',        window.location);\n  fixHTML() {\n    var self = this;\n    this.mochadiv.querySelectorAll(\".replay\").forEach(ea => {\n      ea.innerHTML = \"R\";\n      ea.onclick = (evt) => {\n        evt.preventDefault();\n        self.prevState = window.history.state;\n        self.prevLocation = window.location.toString().replace(/&grep=[^&]+/, '');\n        var grep = ea.href.replace(/.*(&grep.*)/, '$1');\n        window.history.pushState({ mochastate: true }, '', self.prevLocation + grep);\n        self.onRunButton();\n        return false;\n      };\n    });\n  }\n\n  async onTestDirChanged() {\n    this.onRunButton()\n  }\n\n  runMocha() {\n    // #TODO port this to Lively4\n  var self = this;\n\n// this.runTestFile(\"http://localhost:9001/lively-mocha-tester/tests/test-test.js\")\n\n  return new Promise((resolve, reject) => {\n      var files = mocha.suite.suites.compact().pluck(\"file\")\n      var tests = this.state.loadedTests.filter(ea => files.include(ea.file)).pluck(\"tests\").flatten();\n\n      if (!tests || !tests.length)\n        return reject(new Error(`Trying to run tests of ${files.join(\", \")} but cannot find them in loaded tests!`));\n\n      mocha.reporter(function Reporter(runner) {\n        // this.done = (failures) => show(\"done \" + failures)\n        // runner.on(\"suite\", function (x) { show(\"suite %s\", x) });\n        // runner.on(\"pending\", function (x) { show(\"pending %s\", x) });\n\n        runner.on(\"test\", test => {\n          try {\n            var t = tests.detect(ea => ea.fullTitle === test.fullTitle());\n            t.state = \"running\";\n            self.update();\n          } catch (e) { self.showError(\"runner on test error: \" + e.stack); }\n        });\n\n        // runner.on(\"test end\", test => {\n        //   try {\n        //     var t = tests.tests.detect(ea => ea.fullTitle === test.fullTitle();\n        //     t.state = \"finished\";\n        // self.update();\n        //   } catch (e) { self.showError(\"error: \" + e.stack); }\n        // });\n\n        runner.on(\"pass\", test => {\n          try {\n            var t = tests.detect(ea => ea.fullTitle === test.fullTitle());\n            t.state = \"succeeded\";\n            t.duration = test.duration;\n            self.update();\n          } catch (e) { self.showError(\"runner on pass error: \" + e.stack); }\n        });\n\n        runner.on(\"fail\", (test, error) => {\n          try {\n            var t = tests.detect(ea => ea.fullTitle === test.fullTitle());\n            if (t) attachErrorToTest(t, error, test.duration);\n            else { // \"test\" is a hook...\n              var parentTests = test.parent.tests.invoke(\"fullTitle\")\n              tests\n                .filter(ea => parentTests.include(ea.fullTitle))\n                .forEach(ea => attachErrorToTest(ea, error, test.duration))\n            }\n\n            self.update();\n\n            function attachErrorToTest(test, error, duration) {\n              test.state = \"failed\";\n              test.duration = test.duration;\n              test.error = error;\n            }\n\n          } catch (e) { self.showError(\"runner on fail error: \" + e.stack); }\n        });\n\n        // runner.on(\"start\", test => { show(\"START %o\", lively.printInspect(test ,1)) });\n        // runner.on(\"end\", test => { show(\"end %o\", lively.printInspect(test ,1)) });\n\n        // runner.on(\"hook end\", function (x) { show(\"hook end %s\", x) });\n        // runner.on(\"suite end\", function (x) { show(\"suite end %s\", x) });\n      });\n\n      mocha.run(failures => resolve());\n    });\n  }\n\n  livelyMigrate(other) {\n    this.testDir.value = other.testDir.value;\n  }\n\n  livelyPrepareSave() {\n    this.setAttribute('testDir', this.testDir.value);\n  }\n\n  testDirChanged(e) {\n    this.setAttribute('testDir', this.testDir.value);\n  }\n}\n"]}