Repository: mochajs/mocha

Problem statement:
reporter xunit output report incomplete
### Prerequisites

- [X] Checked that your issue hasn't already been filed by cross-referencing [issues with the `faq` label](https://github.com/mochajs/mocha/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Afaq%20)
- [X] Checked next-gen ES issues and syntax problems by using the same environment and/or transpiler configuration without Mocha to ensure it isn't just a feature that actually isn't supported in the environment in question or a bug in your code.
- [X] 'Smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, your usage of Mocha, or Mocha itself
- [X] Ensured that there is no discrepancy between the locally and globally installed versions of Mocha. You can find them with: `node node_modules/.bin/mocha --version`(Local) and `mocha --version`(Global). We recommend that you _not_ install Mocha globally.

### Description

We are using hippie to test our components. During the request assertion, if the test fails, the output does not display the entire report. Not event the failed test nor the next tests.
```
<testsuite name="Mocha Tests" tests="3" failures="0" errors="1" skipped="0" timestamp="Tue, 17 Sep 2019 13:26:17 GMT" time="0.378">
<testcase classname="test" name="test ok" time="0.001"/>
// missing testsuite end tag.
```
### Steps to Reproduce

```js
"use strict";

const {expect} = require("chai");
const hippie = require("hippie");

describe("test", function() {
	it("test ok", function(){
		expect(true).to.be.equal(true);
	})
	it("test ko", function(){
		return hippie()
			.base("https://mochajs.org")
			.get("/not_found")
			.expectStatus(200)
			.end();
	})
	it("test ok", function(){
		expect(true).to.be.equal(true);
	})
})
```

Test report with spec reporter shows succesfully:

`$ npx mocha test.js --reporter spec`

```
    test
    ✓ test ok
    1) test ko
    ✓ test ok


  2 passing (204ms)
  1 failing

  1) test
       test ko:
     AssertionError: Status code

    Actual:   404
    Expected: 200

      at assert (node_modules/hippie/lib/hippie/assert.js:36:10)
      at statusCode (node_modules/hippie/lib/hippie/expect.js:23:10)
      at verify (node_modules/hippie/lib/hippie/client.js:476:5)
      at Client.verify (node_modules/hippie/lib/hippie/client.js:477:5)
      at /home/sbesson/GIT/TMP/node_modules/hippie/lib/hippie/client.js:437:12
      at Client.exports.raw [as parse] (node_modules/hippie/lib/hippie/parsers.js:33:3)
      at Request._callback (node_modules/hippie/lib/hippie/client.js:435:10)
      at Request.self.callback (node_modules/request/request.js:185:22)
      at Request.<anonymous> (node_modules/request/request.js:1157:10)
      at IncomingMessage.<anonymous> (node_modules/request/request.js:1079:12)
      at endReadableNT (_stream_readable.js:1129:12)
      at process._tickCallback (internal/process/next_tick.js:63:19)
```

But with xunit reporter:
`npx mocha test.js --reporter xunit`

```
<testsuite name="Mocha Tests" tests="3" failures="0" errors="1" skipped="0" timestamp="Tue, 17 Sep 2019 13:33:12 GMT" time="0.195">
<testcase classname="test" name="test ok" time="0.001"/>
```

**Expected behavior:** [What you expect to happen]
We expect an output looking like this:
```
<testsuite name="Mocha Tests" tests="3" failures="0" errors="1" skipped="0" timestamp="Tue, 17 Sep 2019 13:37:42 GMT" time="0.251">
<testcase classname="test" name="test ok" time="0.001"/>
<testcase classname="test" name="test ko" time="0"><failure>expected 404 to equal 200

      + expected - actual

      -404
      +200
      
AssertionError: expected 404 to equal 200
    at /home/sbesson/GIT/TMP/test.js:16:31
    at verify (node_modules/hippie/lib/hippie/client.js:475:32)
    at Client.verify (node_modules/hippie/lib/hippie/client.js:477:5)
    at /home/sbesson/GIT/TMP/node_modules/hippie/lib/hippie/client.js:437:12
    at Client.exports.raw [as parse] (node_modules/hippie/lib/hippie/parsers.js:33:3)
    at Request._callback (node_modules/hippie/lib/hippie/client.js:435:10)
    at Request.self.callback (node_modules/request/request.js:185:22)
    at Request.&#x3C;anonymous&#x3E; (node_modules/request/request.js:1157:10)
    at IncomingMessage.&#x3C;anonymous&#x3E; (node_modules/request/request.js:1079:12)
    at endReadableNT (_stream_readable.js:1129:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)</failure></testcase>
<testcase classname="test" name="test ok" time="0"/>
</testsuite>
```

**Actual behavior:** [What actually happens]
```
<testsuite name="Mocha Tests" tests="3" failures="0" errors="1" skipped="0" timestamp="Tue, 17 Sep 2019 13:33:12 GMT" time="0.195">
<testcase classname="test" name="test ok" time="0.001"/>
```

**Reproduces how often:** [What percentage of the time does it reproduce?]
100%

### Versions

* node : 10.16.0
* mocha : 6.2.0
* hippie : 0.5.2

* OS : Linux Mint 19.1 Tessa
* architecture : 64-bit
* shell : bash

### Additional Information

It works just fine with mocha@5.2.0.


Best regards.


Hints:
Please run the same test again and write the output to a file, see [docu](https://mochajs.org/#xunit). It's important to know wether the file output is correct or truncated as well.
Hi, I am Serge's colleague.

The output is truncated as well.

[report-output.zip](https://github.com/mochajs/mocha/files/3626331/report-output.zip)

It appears the output doesn't show up because the reporter crashes at [this line](https://github.com/mochajs/mocha/blob/master/lib/reporters/base.js#L424), which means it's actually a problem in the `diff` package. The exception that is thrown is `TypeError: value.split is not a function`.

Edit: the expected result is achieved when casting both the expected and actual values to strings first. Seems `diff` was having trouble with `number` type values.

Patch:
diff --git a/lib/reporters/base.js b/lib/reporters/base.js
--- a/lib/reporters/base.js
+++ b/lib/reporters/base.js
@@ -154,14 +154,14 @@ exports.cursor = {
   }
 };
 
-function showDiff(err) {
+var showDiff = (exports.showDiff = function(err) {
   return (
     err &&
     err.showDiff !== false &&
     sameType(err.actual, err.expected) &&
     err.expected !== undefined
   );
-}
+});
 
 function stringifyDiffObjs(err) {
   if (!utils.isString(err.actual) || !utils.isString(err.expected)) {
@@ -182,9 +182,19 @@ function stringifyDiffObjs(err) {
  * @return {string} Diff
  */
 var generateDiff = (exports.generateDiff = function(actual, expected) {
-  return exports.inlineDiffs
-    ? inlineDiff(actual, expected)
-    : unifiedDiff(actual, expected);
+  try {
+    return exports.inlineDiffs
+      ? inlineDiff(actual, expected)
+      : unifiedDiff(actual, expected);
+  } catch (err) {
+    var msg =
+      '\n      ' +
+      color('diff added', '+ expected') +
+      ' ' +
+      color('diff removed', '- actual:  failed to generate Mocha diff') +
+      '\n';
+    return msg;
+  }
 });
 
 /**
diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js
--- a/lib/reporters/xunit.js
+++ b/lib/reporters/xunit.js
@@ -163,9 +163,9 @@ XUnit.prototype.test = function(test) {
   if (test.state === STATE_FAILED) {
     var err = test.err;
     var diff =
-      Base.hideDiff || !err.actual || !err.expected
-        ? ''
-        : '\n' + Base.generateDiff(err.actual, err.expected);
+      !Base.hideDiff && Base.showDiff(err)
+        ? '\n' + Base.generateDiff(err.actual, err.expected)
+        : '';
     this.write(
       tag(
         'testcase',


Test patch:
diff --git a/test/reporters/xunit.spec.js b/test/reporters/xunit.spec.js
--- a/test/reporters/xunit.spec.js
+++ b/test/reporters/xunit.spec.js
@@ -350,6 +350,42 @@ describe('XUnit reporter', function() {
           '</failure></testcase>';
         expect(expectedWrite, 'to be', expectedTag);
       });
+
+      it('should handle non-string diff values', function() {
+        var runner = new EventEmitter();
+        createStatsCollector(runner);
+        var xunit = new XUnit(runner);
+
+        var expectedTest = {
+          state: STATE_FAILED,
+          title: expectedTitle,
+          parent: {
+            fullTitle: function() {
+              return expectedClassName;
+            }
+          },
+          duration: 1000,
+          err: {
+            actual: 1,
+            expected: 2,
+            message: expectedMessage,
+            stack: expectedStack
+          }
+        };
+
+        sandbox.stub(xunit, 'write').callsFake(function(str) {
+          expectedWrite += str;
+        });
+
+        runner.emit(EVENT_TEST_FAIL, expectedTest, expectedTest.err);
+        runner.emit(EVENT_RUN_END);
+        sandbox.restore();
+
+        var expectedDiff =
+          '\n      + expected - actual\n\n      -1\n      +2\n      ';
+
+        expect(expectedWrite, 'to contain', expectedDiff);
+      });
     });
 
     describe('on test pending', function() {