Skip to content

Instantly share code, notes, and snippets.

@craigmulligan
Last active February 7, 2026 09:22
Show Gist options
  • Select an option

  • Save craigmulligan/00aec33f5ca427f236763b12245949f7 to your computer and use it in GitHub Desktop.

Select an option

Save craigmulligan/00aec33f5ca427f236763b12245949f7 to your computer and use it in GitHub Desktop.
Node:test Custom reporter to only print failed test console.logs
import { spec } from "node:test/reporters";
import { Transform } from 'node:stream';
class Reporter extends Transform {
// This is a custom test reporter for node:test
// it'll only log console.logs that occur during
// failed tests. For everything else it delegates
// to the default "spec" reporter.
constructor() {
super({ writableObjectMode: true })
this.specReporter = new spec()
this.logs = []
}
addToFailedLogs = (_, data) => {
this.logs.push(data)
}
_transform(event, encoding, callback) {
switch (event.type) {
case 'test:stderr':
case 'test:stdout':
this.logs.push(this.specReporter._transform(event, encoding, this.addToFailedLogs));
callback(null)
break;
case 'test:pass':
this.specReporter._transform(event, encoding, callback);
this.logs = [];
break;
case 'test:fail':
this.specReporter._transform(event, encoding, this.addToFailedLogs);
callback(null, this.logs.join(''))
this.logs = []
break;
default:
this.specReporter._transform(event, encoding, callback);
break;
}
}
_flush(callback) {
this.specReporter._flush(callback)
}
}
export default new Reporter()
@Greenheart
Copy link

Thanks for sharing this, and your blog post!

Here's a TypeScript version if anyone is interested :)

import { spec } from 'node:test/reporters'
import { type Stream, Transform } from 'node:stream'

/**
 * Custom test reporter for `node:test`
 *
 * Only output logs for failed tests to get cleaner test results and simplify debugging.
 *
 * Adapted from https://hobochild.com/posts/node-suppress-test-logs/
 */
class FailedTestsReporter extends Transform {
  specReporter: ReturnType<typeof spec>
  logs: unknown[]

  constructor() {
    super({ writableObjectMode: true })
    this.specReporter = new spec()
    this.logs = []
  }

  addToFailedLogs: Stream.TransformCallback = (_, data: unknown) => {
    this.logs.push(data)
  }

  _transform(
    event: { type: string; data: unknown },
    encoding: BufferEncoding,
    callback: Stream.TransformCallback,
  ) {
    switch (event.type) {
      case 'test:stderr':
      case 'test:stdout':
        // Save any output from the spec reporter internally
        this.logs.push(
          this.specReporter._transform(event, encoding, this.addToFailedLogs),
        )
        callback(null)
        break
      case 'test:pass':
        this.specReporter._transform(event, encoding, callback)
        this.logs = []
        break
      case 'test:fail':
        this.specReporter._transform(event, encoding, this.addToFailedLogs)
        // Only return logs if test failed
        callback(null, this.logs.join(''))
        this.logs = []
        break
      default:
        // Fall back to spec reporter for other events
        this.specReporter._transform(event, encoding, callback)
        break
    }
  }

  _flush(callback: Stream.TransformCallback) {
    this.specReporter._flush(callback)
  }
}

export default new FailedTestsReporter()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment