diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e22375 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz + +pids +logs +results + +node_modules + +reports + +npm-debug.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f74264 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +mochawesome +======================== + +1. Clone +2. `npm install` +3. `npm link` +4. `npm link mochawesome` +5. `gulp` \ No newline at end of file diff --git a/fiveby-config.json b/fiveby-config.json new file mode 100644 index 0000000..3008d98 --- /dev/null +++ b/fiveby-config.json @@ -0,0 +1,7 @@ +{ + "implicitWait": 5000, + "hubUrl": null, + "browsers": { + "chrome": 1 + } +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..541a06d --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,13 @@ +//TODO: feeding in configs, logins, selenium servers, browsers.. tests should do this so they can run in isolation but that's a lot more maintenance... +var gulp = require('gulp'); +var mocha = require('gulp-spawn-mocha'); +var options = {reporter: 'mochawesome', timeout: 30000, slow: 1}; + + +gulp.task('test', function () { + return gulp.src('test/*.js') + .pipe(mocha(options)) + .on("error", console.warn.bind(console)); +}); + +gulp.task('default', ['test']); \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..a841837 --- /dev/null +++ b/index.js @@ -0,0 +1,6 @@ +/** + * Export lib/ + * + */ + +module.exports = require('./lib'); \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..5834e11 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,184 @@ +var util = require('util'), + fs = require('fs'), + path = require('path'), + mocha = require('mocha'), + _ = require('lodash'), + hogan = require('hogan.js'), + moment = require('moment'); + +var Base = mocha.reporters.Base, + utils = mocha.utils, + cursor = Base.cursor, + color = Base.color; + +var reportsDir = path.join(process.cwd(), 'reports'), + reportJsonFile = path.join(reportsDir, 'reports.json'), + reportHtmlFile = path.join(reportsDir, 'reportCard.html'); + +module.exports = Mochawesome; + +// Load template +var reportCard = fs.readFileSync(path.join(__dirname, '..', 'templates', 'reportCard.mu'), { + encoding: 'utf8' +}); + +// Compile template +var reportCardTmp = hogan.compile(reportCard); + +/** + * Initialize a new reporter. + * + * @param {Runner} runner + * @api public + */ + +function Mochawesome (runner) { + var self = this; + Base.call(this, runner); + + var allSuites = {}, + allTests = [], + allFailures = [], + allPasses = []; + + runner.on('suite', function (suite) { + // ignore the root suite + if (!suite.root) { + // top level suite + if (suite.parent.root) { + allSuites[suite.title] = {}; + } + + // nested suite 1 level deep only + if (!suite.parent.root && suite.parent.parent.root) { + parentSuite = allSuites[suite.parent.title]; + if (!parentSuite.childSuites) { + parentSuite.childSuites = {}; + } + parentSuite.childSuites[suite.title] = {}; + } + } + }); + + runner.on('suite end', function (suite) { + // ignore the root suite + if (!suite.root) { + + var allTests = suite.tests.map(cleanTest); + + var passingTests = allTests.filter(function (test) { + return test.state === 'passed'; + }); + + var failingTests = allTests.filter(function (test) { + return test.state === 'failed'; + }); + + var suiteObj = { + title: suite.title, + fullTitle: suite.fullTitle(), + tests: allTests, + passes: passingTests, + failures: failingTests, + totalTests: suite.tests.length, + totalPasses: passingTests.length, + totalFailures: failingTests.length + }; + + // top level suite + if (suite.parent.root) { + allSuites[suite.title] = _.merge(allSuites[suite.title], suiteObj); + } + + // nested suite 1 level deep only + if (!suite.parent.root && suite.parent.parent.root) { + parentSuite = allSuites[suite.parent.title]; + parentSuite.childSuites[suite.title] = suiteObj; + } + } + }); + + runner.on('test end', function (test) { + allTests.push(test); + }); + + runner.on('pass', function (test) { + allPasses.push(test); + }); + + runner.on('fail', function (test){ + allFailures.push(test); + }); + + runner.on('end', function () { + + // unless we render client-side, this has to be updated client-side + // self.stats.endDateStr = moment(self.stats.end).fromNow(); + + var obj = { + stats: self.stats, + suites: allSuites, + tests: allTests.map(cleanTest), + passes: allPasses.map(cleanTest), + failures: allFailures.map(cleanTest) + }; + + saveToFile('json', obj); + saveToFile('html', obj); + }); +} + +/** + * Return a plain-object representation of `test` + * free of cyclic properties etc. + * + * @param {Object} test + * @return {Object} + * @api private + */ + +function cleanTest (test) { + return { + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + state: test.state, + pass: test.state === 'passed', + fail: test.state === 'failed', + file: test.file, + code: utils.clean(test.fn.toString()), + err: test.err + } +} + +/** + * Save data out to files + */ + +function saveToFile (filetype, inData) { + var outData, outFile, writeFile; + switch (filetype) { + case 'json': + outData = JSON.stringify(inData, null, 2); + outFile = reportJsonFile; + break; + case 'html': + outData = reportCardTmp.render(inData); + outFile = reportHtmlFile; + break; + } + + try { + if (!fs.existsSync(reportsDir)) { + fs.mkdirSync(reportsDir); + }; + writeFile = fs.openSync(outFile, 'w'); + fs.writeSync(writeFile, outData); + fs.close(writeFile); + util.print("\nSaved " + outFile + "\n"); + } catch (err) { + console.log(err); + util.print("\nError: Unable to save " + outFile + "\n"); + } + +} \ No newline at end of file diff --git a/lib/sample-report.json b/lib/sample-report.json new file mode 100644 index 0000000..57bf38e --- /dev/null +++ b/lib/sample-report.json @@ -0,0 +1,34 @@ +"allSuites" = { + "suite 1": { + "title": "suite title", + "fullTitle": "suite full title", + "tests": [{}, {}, {}], + "passes": [{}, {}], + "failures": [{}], + "totalTests": 3, + "totalPasses": 2, + "totalFailures": 1, + "childSuites": { + "child1": { + "title": "test title", + "fullTitle": "test full title", + "tests": [{}, {}, {}], + "passes": [{}, {}], + "failures": [{}], + "totalTests": 3, + "totalPasses": 2, + "totalFailures": 1 + }, + "child2": { + "title": "test title", + "fullTitle": "test full title", + "tests": [{}, {}, {}], + "passes": [{}, {}], + "failures": [{}], + "totalTests": 3, + "totalPasses": 2, + "totalFailures": 1 + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a04a50 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "mochawesome", + "description": "An awesome mocha json html file reporter", + "author": "Adam Gruber ", + "version": "0.0.1", + "main": "index.js", + "dependencies": { + "hogan.js": "^3.0.2", + "lodash": "^2.4.1", + "mocha": "", + "moment": "^2.7.0" + }, + "devDependencies": { + "fiveby": "git+https://github.dowjones.net/institutional/fiveby.git", + "gulp": "^3.8.1", + "gulp-spawn-mocha": "^0.1.5", + "should": "^3.3.2", + "mochawesome": "" + }, + "scripts": { + "test": "mocha" + }, + "engine": "node >= 0.8.0", + "keywords": [ + "mocha", + "reporter", + "json", + "mustache" + ], + "license": "ISC" +} \ No newline at end of file diff --git a/templates/reportCard.mu b/templates/reportCard.mu new file mode 100644 index 0000000..c728ac6 --- /dev/null +++ b/templates/reportCard.mu @@ -0,0 +1,68 @@ + + + + + + + Mocha Report Card + + + + + + + + + + + +
+ {{#suites}} +
+

{{title}}

+
{{endDateStr}}
+ {{#tests}} +
+

{{title}}

+

{{file}}

+

Duration: {{duration}}ms

+
{{code}}
+ {{err}} + {{#err}} + Expected:{{expected}} + Actual:{{actual}} + Message:{{message}} +
{{stack}}
+ {{/err}} +
+ {{/tests}} +
+ {{/suites}} +
+ + + + diff --git a/test/dummy.js b/test/dummy.js new file mode 100644 index 0000000..9a590d3 --- /dev/null +++ b/test/dummy.js @@ -0,0 +1,31 @@ +var fiveby = require('fiveby'); + +new fiveby(function (browser) { + + return describe('Dummy Test 1', function () { + + before(function () { + console.log("i did something before"); + }); + + it('does nothing', function (done) { + true.should.be.ok; + done(); + }); + + it('fails', function (done) { + false.should.be.ok; + done(); + }); + + it('fails also', function (done) { + 'adam'.should.equal('someone'); + done(); + }); + + after(function () { + console.log("i did something after"); + }); + + }); +}); \ No newline at end of file diff --git a/test/dummy2.js b/test/dummy2.js new file mode 100644 index 0000000..4c9e5a3 --- /dev/null +++ b/test/dummy2.js @@ -0,0 +1,31 @@ +var fiveby = require('fiveby'); + +new fiveby(function (browser) { + + return describe('Dummy Test 2', function () { + + before(function () { + console.log("i did something before"); + }); + + it('does something', function (done) { + true.should.be.ok; + done(); + }); + + it('fails 2', function (done) { + false.should.be.ok; + done(); + }); + + it('fails also 2', function (done) { + 'adam'.should.equal('someone'); + done(); + }); + + after(function () { + console.log("i did something after"); + }); + + }); +}); \ No newline at end of file diff --git a/test/dummy3.js b/test/dummy3.js new file mode 100644 index 0000000..838c5d1 --- /dev/null +++ b/test/dummy3.js @@ -0,0 +1,20 @@ +var fiveby = require('fiveby'); + +new fiveby(function (browser) { + + return describe('Dummy Test 3', function () { + + describe("i'm nested!", function() { + it('does something', function (done) { + true.should.be.ok; + done(); + }); + }) + + it('fails 3', function (done) { + false.should.be.ok; + done(); + }); + + }); +}); \ No newline at end of file