Skip to content

Commit

Permalink
Feature/inline diff (#167)
Browse files Browse the repository at this point in the history
* support mocha inline-diffs option
  • Loading branch information
adamgruber authored Jun 28, 2017
1 parent 1568d25 commit 4680aef
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 28 deletions.
16 changes: 13 additions & 3 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const boolOpts = [
'enableCode',
'overwrite',
'quiet',
'useInlineDiffs',
'dev'
];

Expand All @@ -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;
}

[
Expand All @@ -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);
});
Expand Down
15 changes: 7 additions & 8 deletions src/mochawesome.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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
Expand All @@ -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 = [];
Expand Down Expand Up @@ -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,
Expand Down
35 changes: 24 additions & 11 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,27 @@ 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
*
* @param {Error} err Error object
*
* @return {Object} normalized error
*/
function normalizeErr(err) {
function normalizeErr(err, config) {
const { name, message, actual, expected, stack, showDiff } = err;
let errMessage;
let errDiff;
Expand All @@ -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
Expand All @@ -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;

Expand All @@ -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,
Expand All @@ -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 });
Expand Down Expand Up @@ -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);
});
}
Expand Down
12 changes: 12 additions & 0 deletions test/reporter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
28 changes: 28 additions & 0 deletions test/sample-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
20 changes: 14 additions & 6 deletions test/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ describe('Mochawesome Utils', () => {
});

describe('cleanTest', () => {
const config = {};
const expectedProps = [
'title',
'fullTitle',
Expand All @@ -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',
Expand Down Expand Up @@ -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);
});
Expand Down

0 comments on commit 4680aef

Please sign in to comment.