From 961d8d956578a3666a4ed11b2e663f633d409dd9 Mon Sep 17 00:00:00 2001 From: Adam Gruber Date: Wed, 28 Jun 2017 14:21:32 -0400 Subject: [PATCH 1/2] support mocha inline-diffs option --- src/config.js | 16 +++++++++++++--- src/mochawesome.js | 15 +++++++-------- src/utils.js | 35 ++++++++++++++++++++++++----------- test/reporter.test.js | 12 ++++++++++++ test/sample-tests.js | 28 ++++++++++++++++++++++++++++ test/utils.test.js | 20 ++++++++++++++------ 6 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/config.js b/src/config.js index 4fd81e9..ef70b36 100644 --- a/src/config.js +++ b/src/config.js @@ -13,6 +13,7 @@ const boolOpts = [ 'enableCode', 'overwrite', 'quiet', + 'useInlineDiffs', 'dev' ]; @@ -37,11 +38,12 @@ function _getOption(optToGet, options, isBool) { module.exports = function (opts) { const options = {}; + const reporterOpts = (opts && opts.reporterOptions) || {}; // Added for compatibility. enableTestCode option is deprecated as of 2.0.3 - if (Object.hasOwnProperty.call(opts, 'enableTestCode')) { - opts.enableCode = opts.enableTestCode; - delete opts.enableTestCode; + if (Object.hasOwnProperty.call(reporterOpts, 'enableTestCode')) { + reporterOpts.enableCode = reporterOpts.enableTestCode; + delete reporterOpts.enableTestCode; } [ @@ -56,7 +58,15 @@ module.exports = function (opts) { 'timestamp', 'overwrite', 'quiet', + 'inlineDiffs', 'dev' + ].forEach(optName => { + options[optName] = _getOption(optName, reporterOpts, boolOpts.indexOf(optName) >= 0); + }); + + // Transfer options from mocha + [ + 'useInlineDiffs' ].forEach(optName => { options[optName] = _getOption(optName, opts, boolOpts.indexOf(optName) >= 0); }); diff --git a/src/mochawesome.js b/src/mochawesome.js index 738df71..4ff8451 100755 --- a/src/mochawesome.js +++ b/src/mochawesome.js @@ -1,4 +1,5 @@ -const mocha = require('mocha'); +const Base = require('mocha/lib/reporters/Base'); +const Spec = require('mocha/lib/reporters/Spec'); const uuid = require('uuid'); const stringify = require('json-stringify-safe'); const conf = require('./config'); @@ -48,7 +49,6 @@ function done(output, config, failures, exit) { * @param {Runner} runner * @api public */ - function Mochawesome(runner, options) { // Done function will be called before mocha exits // This is where we will save JSON and generate the HTML report @@ -57,15 +57,14 @@ function Mochawesome(runner, options) { // Reset total tests counter totalTestsRegistered.total = 0; - // Create/Save necessary report dirs/files - const reporterOpts = (options && options.reporterOptions) || {}; - this.config = conf(reporterOpts); + // Set the config options + this.config = conf(options); // Call the Base mocha reporter - mocha.reporters.Base.call(this, runner); + Base.call(this, runner); // Show the Spec Reporter in the console - new mocha.reporters.Spec(runner); // eslint-disable-line + new Spec(runner); // eslint-disable-line const allTests = []; const allPending = []; @@ -102,7 +101,7 @@ function Mochawesome(runner, options) { const allSuites = this.runner.suite; - traverseSuites(allSuites, totalTestsRegistered); + traverseSuites(allSuites, totalTestsRegistered, this.config); const obj = { stats: this.stats, diff --git a/src/utils.js b/src/utils.js index b8fb7ce..77aada3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -106,6 +106,19 @@ function createUnifiedDiff({ actual, expected }) { .join('\n'); } +/** + * Create an inline diff between two strings + * + * @param {Error} err Error object + * @param {string} err.actual Actual result returned + * @param {string} err.expected Result expected + * + * @return {array} diff string objects + */ +function createInlineDiff({ actual, expected }) { + return diff.diffWordsWithSpace(actual, expected); +} + /** * Return a normalized error object * @@ -113,7 +126,7 @@ function createUnifiedDiff({ actual, expected }) { * * @return {Object} normalized error */ -function normalizeErr(err) { +function normalizeErr(err, config) { const { name, message, actual, expected, stack, showDiff } = err; let errMessage; let errDiff; @@ -133,7 +146,7 @@ function normalizeErr(err) { err.actual = mochaUtils.stringify(actual); err.expected = mochaUtils.stringify(expected); } - errDiff = createUnifiedDiff(err); + errDiff = config.useInlineDiffs ? createInlineDiff(err) : createUnifiedDiff(err); } // Assertion libraries do not output consitent error objects so in order to @@ -159,7 +172,7 @@ function normalizeErr(err) { * * @return {Object} cleaned test */ -function cleanTest(test) { +function cleanTest(test, config) { /* istanbul ignore next: test.fn exists prior to mocha 2.4.0 */ const code = test.fn ? test.fn.toString() : test.body; @@ -175,7 +188,7 @@ function cleanTest(test) { pending: test.pending, context: stringify(test.context, null, 2), code: code && cleanCode(code), - err: (test.err && normalizeErr(test.err)) || {}, + err: (test.err && normalizeErr(test.err, config)) || {}, isRoot: test.parent && test.parent.root, uuid: test.uuid || /* istanbul ignore next: default */uuid.v4(), parentUUID: test.parent && test.parent.uuid, @@ -195,11 +208,11 @@ function cleanTest(test) { * @param {Object} totalTestsRegistered * @param {Integer} totalTestsRegistered.total */ -function cleanSuite(suite, totalTestsRegistered) { +function cleanSuite(suite, totalTestsRegistered, config) { suite.uuid = uuid.v4(); - const beforeHooks = _.map([].concat(suite._beforeAll, suite._beforeEach), cleanTest); - const afterHooks = _.map([].concat(suite._afterAll, suite._afterEach), cleanTest); - const cleanTests = _.map(suite.tests, cleanTest); + const beforeHooks = _.map([].concat(suite._beforeAll, suite._beforeEach), test => cleanTest(test, config)); + const afterHooks = _.map([].concat(suite._afterAll, suite._afterEach), test => cleanTest(test, config)); + const cleanTests = _.map(suite.tests, test => cleanTest(test, config)); const passingTests = _.filter(cleanTests, { state: 'passed' }); const failingTests = _.filter(cleanTests, { state: 'failed' }); const pendingTests = _.filter(cleanTests, { pending: true }); @@ -278,16 +291,16 @@ function cleanSuite(suite, totalTestsRegistered) { * @param {Object} totalTestsRegistered * @param {Integer} totalTestsRegistered.total */ -function traverseSuites(suite, totalTestsRegistered) { +function traverseSuites(suite, totalTestsRegistered, config) { const queue = []; let next = suite; while (next) { if (next.root) { - cleanSuite(next, totalTestsRegistered); + cleanSuite(next, totalTestsRegistered, config); } if (next.suites.length) { _.each(next.suites, (nextSuite, i) => { - cleanSuite(nextSuite, totalTestsRegistered); + cleanSuite(nextSuite, totalTestsRegistered, config); queue.push(nextSuite); }); } diff --git a/test/reporter.test.js b/test/reporter.test.js index 87358fb..c9190bc 100644 --- a/test/reporter.test.js +++ b/test/reporter.test.js @@ -234,6 +234,18 @@ describe('Mochawesome Reporter', () => { done(); }); }); + + it('should transfer mocha options', done => { + mochaReporter = new mocha._reporter(runner, { useInlineDiffs: true }); + + const test = makeTest('test', () => {}); + subSuite.addTest(test); + + runner.run(failureCount => { + mochaReporter.config.useInlineDiffs.should.equal(true); + done(); + }); + }); }); describe('Reporter Done Function', () => { diff --git a/test/sample-tests.js b/test/sample-tests.js index 6afddce..ff4c196 100644 --- a/test/sample-tests.js +++ b/test/sample-tests.js @@ -203,6 +203,34 @@ module.exports = { parentUUID: '56508f44-b4e6-40f0-bae8-b15e0720f120', skipped: false, isHook: false + }, + cleanedWithInlineDiff: { + title: 'failing test', + fullTitle: 'failing test', + timedOut: false, + duration: 2, + state: 'failed', + speed: undefined, + pass: false, + fail: true, + pending: false, + context: undefined, + code: 'return tDone(new Assert(error));', + err: { + message: 'AssertionError: { a: 2 } undefined { a: 1 }', + estack: 'AssertionError: { a: 2 } undefined { a: 1 }', + diff: [ + { count: 6, value: '{\n "a": ' }, + { count: 1, added: undefined, removed: true, value: '2' }, + { count: 1, added: true, removed: undefined, value: '1' }, + { count: 2, value: '\n}' } + ] + }, + isRoot: false, + uuid: '2bcbe88c-acd6-4a53-ba3a-61a59188d5b0', + parentUUID: '56508f44-b4e6-40f0-bae8-b15e0720f120', + skipped: false, + isHook: false } }, pending: { diff --git a/test/utils.test.js b/test/utils.test.js index 26a4211..c811493 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -129,6 +129,7 @@ describe('Mochawesome Utils', () => { }); describe('cleanTest', () => { + const config = {}; const expectedProps = [ 'title', 'fullTitle', @@ -150,31 +151,38 @@ describe('Mochawesome Utils', () => { ]; it('returns cleaned passing test', () => { - const cleaned = cleanTest(sampleTests.passing.raw); + const cleaned = cleanTest(sampleTests.passing.raw, config); cleaned.should.have.properties(expectedProps); cleaned.should.deepEqual(sampleTests.passing.cleaned); }); it('returns cleaned failing test', () => { - const cleaned = cleanTest(sampleTests.failing.raw); + const cleaned = cleanTest(sampleTests.failing.raw, config); cleaned.should.have.properties(expectedProps); cleaned.should.deepEqual(sampleTests.failing.cleaned); }); + it('returns cleaned failing test with inline diff', () => { + const cleaned = cleanTest(sampleTests.failing.raw, { useInlineDiffs: true }); + cleaned.should.have.properties(expectedProps); + cleaned.should.deepEqual(sampleTests.failing.cleanedWithInlineDiff); + }); + it('returns cleaned pending test', () => { - const cleaned = cleanTest(sampleTests.pending.raw); + const cleaned = cleanTest(sampleTests.pending.raw, config); cleaned.should.have.properties(expectedProps); cleaned.should.deepEqual(sampleTests.pending.cleaned); }); it('returns cleaned hook', () => { - const cleaned = cleanTest(sampleTests.hook.raw); + const cleaned = cleanTest(sampleTests.hook.raw, config); cleaned.should.have.properties(expectedProps); cleaned.should.deepEqual(sampleTests.hook.cleaned); }); }); describe('cleanSuite', () => { + const config = {}; const totalTestsRegistered = { total: 0 }; const expectedProps = [ 'title', @@ -210,14 +218,14 @@ describe('Mochawesome Utils', () => { it('cleans a root suite', () => { const s = cloneDeep(sampleSuite.one.raw); - cleanSuite(s, totalTestsRegistered); + cleanSuite(s, totalTestsRegistered, config); s.should.have.properties(expectedProps); s.should.deepEqual(sampleSuite.one.cleaned); }); it('cleans a non-root suite', () => { const s = cloneDeep(sampleSuite.two.raw); - cleanSuite(s, totalTestsRegistered); + cleanSuite(s, totalTestsRegistered, config); s.should.have.properties(expectedProps); s.should.deepEqual(sampleSuite.two.cleaned); }); From e428ff15f0be808cf89c8d906f0edad421c3f218 Mon Sep 17 00:00:00 2001 From: Adam Gruber Date: Wed, 28 Jun 2017 16:04:11 -0400 Subject: [PATCH 2/2] fix casing in mocha reporter requires --- src/mochawesome.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mochawesome.js b/src/mochawesome.js index 4ff8451..9ba0878 100755 --- a/src/mochawesome.js +++ b/src/mochawesome.js @@ -1,5 +1,5 @@ -const Base = require('mocha/lib/reporters/Base'); -const Spec = require('mocha/lib/reporters/Spec'); +const Base = require('mocha/lib/reporters/base'); +const Spec = require('mocha/lib/reporters/spec'); const uuid = require('uuid'); const stringify = require('json-stringify-safe'); const conf = require('./config');