Repository: testem/testem (js)

Issue:
reporter output cruft
I just upgraded from v2 to v3, and the browser log output is now in JSON.stringify() format, which for my purposes is far from ideal -- namely due to character escaping necessary of the log message.

I know I can create my own reporter, but I want 99% of what the default TAP reporter does and would really prefer not to duplicate all that code and then have to manually keep it up-to-date.

I have created a draft PR #1463 which adds a `log_processor` config to address this.  I didn't want to do anything more extensive without consulting the project maintainer(s) on the best way to go about this, but this is working as expected.  Thoughts?

Just add the config function to `testem.js`, for example:

```
  log_processor(log) {
    return log.text;
  },
```

Pull request description:
🔧 add log_processor config option
resolves #1464

Patch:
diff --git a/README.md b/README.md
index f79761d..1174f27 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,14 @@ For improved ergonomics, TAP reporter does not actually strictly adhere to the S
 }
 ```
 
+By default, the TAP reporter outputs the result of `JSON.stringify()` for any log content that is not a `String`. You can override this behavior by specifying a function for `tap_log_processor`.
+
+```js
+{
+  "tap_log_processor": function(log) { return log.toString(); }
+}
+```
+
 ## Other Test Reporters
 
 Testem has other test reporters besides TAP: `dot`, `xunit` and `teamcity`. You can use the `-R` to specify them
diff --git a/lib/reporters/tap_reporter.js b/lib/reporters/tap_reporter.js
index 5be3654..9d4fac7 100644
--- a/lib/reporters/tap_reporter.js
+++ b/lib/reporters/tap_reporter.js
@@ -18,6 +18,7 @@ module.exports = class TapReporter {
     this.results = [];
     this.errors = [];
     this.logs = [];
+    this.logProcessor = config.get('tap_log_processor');
   }
 
   report(prefix, data) {
@@ -56,7 +57,7 @@ module.exports = class TapReporter {
    */
   display(prefix, result) {
     if (this.willDisplay(result)) {
-      this.out.write(displayutils.resultString(this.id++, prefix, result, this.quietLogs, this.strictSpecCompliance));
+      this.out.write(displayutils.resultString(this.id++, prefix, result, this.quietLogs, this.strictSpecCompliance, this.logProcessor));
     }
   }
 
diff --git a/lib/utils/displayutils.js b/lib/utils/displayutils.js
index fea3701..6c4703f 100644
--- a/lib/utils/displayutils.js
+++ b/lib/utils/displayutils.js
@@ -57,7 +57,7 @@ function resultDisplay(id, prefix, result, strictSpecCompliance) {
   return output;
 }
 
-function yamlDisplay(err, logs) {
+function yamlDisplay(err, logs, logProcessor) {
   let testLogs;
   let failed = Object.keys(err || {})
     .filter(key => key !== 'passed')
@@ -68,7 +68,11 @@ function yamlDisplay(err, logs) {
         let logLine;
         if (strutils.isString(log)) {
           logLine = log;
-        } else {
+        }
+        else if (logProcessor) {
+          logLine = logProcessor(log);
+        }
+        else {
           logLine = JSON.stringify(log);
         }
         return strutils.indent(logLine);
@@ -84,10 +88,10 @@ function yamlDisplay(err, logs) {
   ].join('\n'));
 }
 
-function resultString(id, prefix, result, quietLogs, strictSpecCompliance) {
+function resultString(id, prefix, result, quietLogs, strictSpecCompliance, logProcessor) {
   let string = resultDisplay(id, prefix, result, strictSpecCompliance) + '\n';
   if (result.error || (!quietLogs && result.logs && result.logs.length)) {
-    string += yamlDisplay(result.error, result.logs) + '\n';
+    string += yamlDisplay(result.error, result.logs, logProcessor) + '\n';
   }
   return string;
 }
diff --git a/package-lock.json b/package-lock.json
index 801c4b5..eafc6bf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5619,9 +5619,9 @@
       }
     },
     "node_modules/qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "version": "6.5.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+      "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
       "dev": true,
       "engines": {
         "node": ">=0.6"
@@ -12023,9 +12023,9 @@
       "dev": true
     },
     "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "version": "6.5.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+      "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
       "dev": true
     },
     "querystring": {


Test patch:
diff --git a/tests/ci/reporter_tests.js b/tests/ci/reporter_tests.js
index 680802c..7d96aaf 100644
--- a/tests/ci/reporter_tests.js
+++ b/tests/ci/reporter_tests.js
@@ -74,6 +74,12 @@ describe('test reporters', function() {
             logs: [],
             runDuration: 0,
           });
+          reporter.report('phantomjs', {
+            name: 'it logs something other than a string',
+            passed: true,
+            logs: [{text: 'ye olde texte'}],
+            runDuration: 3,
+          });
           reporter.finish();
           assert.deepEqual(stream.read().toString().split('\n'), [
             'ok 1 phantomjs - [3 ms] - it does stuff',
@@ -82,10 +88,15 @@ describe('test reporters', function() {
             '            some log',
             '    ...',
             'skip 2 phantomjs - [0 ms] - it is skipped',
+            'ok 3 phantomjs - [3 ms] - it logs something other than a string',
+            '    ---',
+            '        browser log: |',
+            '            {"text":"ye olde texte"}',
+            '    ...',
             '',
-            '1..2',
-            '# tests 2',
-            '# pass  1',
+            '1..3',
+            '# tests 3',
+            '# pass  2',
             '# skip  1',
             '# todo  0',
             '# fail  0',
@@ -615,6 +626,61 @@ describe('test reporters', function() {
         assert.equal(reporter.willDisplay({error: true}), true);
       });
     });
+
+    context('with log processor', function() {
+      beforeEach(function() {
+        config = new Config('ci', {
+          tap_log_processor: function(log) {
+            return `yee ${log.text}`; } });
+      });
+
+      it('uses log processor', function() {
+        var reporter = new TapReporter(false, stream, config);
+        reporter.report('phantomjs', {
+          name: 'it does stuff',
+          passed: true,
+          logs: ['some log'],
+          runDuration: 3,
+        });
+        reporter.report('phantomjs', {
+          name: 'it is skipped',
+          skipped: true,
+          logs: [],
+          runDuration: 0,
+        });
+        reporter.report('phantomjs', {
+          name: 'it logs something other than a string',
+          passed: true,
+          logs: [{text: 'ye olde texte'}],
+          runDuration: 3,
+        });
+        reporter.finish();
+        assert.deepEqual(stream.read().toString().split('\n'), [
+          'ok 1 phantomjs - [3 ms] - it does stuff',
+          '    ---',
+          '        browser log: |',
+          '            some log',
+          '    ...',
+          'skip 2 phantomjs - [0 ms] - it is skipped',
+          'ok 3 phantomjs - [3 ms] - it logs something other than a string',
+          '    ---',
+          '        browser log: |',
+          '            yee ye olde texte',
+          '    ...',
+          '',
+          '1..3',
+          '# tests 3',
+          '# pass  2',
+          '# skip  1',
+          '# todo  0',
+          '# fail  0',
+          '',
+          '# ok',
+          ''
+        ]);
+      });
+    });
+
   });
 
   describe('dot reporter', function() {