diff --git a/doc/api/test.md b/doc/api/test.md index e59889fff78731..165d38f620b737 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -520,6 +520,10 @@ test('spies on an object method', (t) => { added: - v19.6.0 - v18.15.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/47238 + description: Reporters are now exposed at `node:test/reporters`. --> The `node:test` module supports passing [`--test-reporter`][] @@ -546,6 +550,16 @@ Node.js, and should not be relied on programmatically. If programmatic access to the test runner's output is required, use the events emitted by the {TestsStream}. +The reporters are available via the `node:test/reporters` module: + +```mjs +import { tap, spec, dot } from 'node:test/reporters'; +``` + +```cjs +const { tap, spec, dot } = require('node:test/reporters'); +``` + ### Custom reporters [`--test-reporter`][] can be used to specify a path to custom reporter. @@ -739,8 +753,20 @@ added: **Default:** `undefined`. * Returns: {TestsStream} -```js +```mjs +import { tap } from 'node:test/reporters'; +import process from 'node:process'; + +run({ files: [path.resolve('./tests/test.js')] }) + .compose(tap) + .pipe(process.stdout); +``` + +```cjs +const { tap } = require('node:test/reporters'); + run({ files: [path.resolve('./tests/test.js')] }) + .compose(tap) .pipe(process.stdout); ``` diff --git a/lib/test/reporters.js b/lib/test/reporters.js new file mode 100644 index 00000000000000..287c07510bc13a --- /dev/null +++ b/lib/test/reporters.js @@ -0,0 +1,38 @@ +'use strict'; + +const { ObjectDefineProperties } = primordials; + +let dot; +let spec; +let tap; + +ObjectDefineProperties(module.exports, { + __proto__: null, + dot: { + __proto__: null, + configurable: true, + enumerable: true, + get() { + dot ??= require('internal/test_runner/reporter/dot'); + return dot; + }, + }, + spec: { + __proto__: null, + configurable: true, + enumerable: true, + get() { + spec ??= require('internal/test_runner/reporter/spec'); + return spec; + }, + }, + tap: { + __proto__: null, + configurable: true, + enumerable: true, + get() { + tap ??= require('internal/test_runner/reporter/tap'); + return tap; + }, + }, +}); diff --git a/test/parallel/test-runner-run.mjs b/test/parallel/test-runner-run.mjs index 6ac007bfb5dd6c..b37bdf94f38c02 100644 --- a/test/parallel/test-runner-run.mjs +++ b/test/parallel/test-runner-run.mjs @@ -2,6 +2,7 @@ import * as common from '../common/index.mjs'; import * as fixtures from '../common/fixtures.mjs'; import { join } from 'node:path'; import { describe, it, run } from 'node:test'; +import { dot, spec, tap } from 'node:test/reporters'; import assert from 'node:assert'; const testFixtures = fixtures.path('test-runner'); @@ -65,4 +66,39 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { code: 'ERR_INVALID_ARG_TYPE' })); }); + + it('should be piped with dot', async () => { + const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(dot).toArray(); + assert.deepStrictEqual(result, [ + '.', + '\n', + ]); + }); + + it('should be piped with spec', async () => { + const specReporter = new spec(); + const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(specReporter).toArray(); + const stringResults = result.map((bfr) => bfr.toString()); + assert.match(stringResults[0], /this should pass/); + assert.match(stringResults[1], /tests 1/); + assert.match(stringResults[1], /pass 1/); + }); + + it('should be piped with tap', async () => { + const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(tap).toArray(); + assert.strictEqual(result.length, 13); + assert.strictEqual(result[0], 'TAP version 13\n'); + assert.strictEqual(result[1], '# Subtest: this should pass\n'); + assert.strictEqual(result[2], 'ok 1 - this should pass\n'); + assert.match(result[3], /duration_ms: \d+\.?\d*/); + assert.strictEqual(result[4], '1..1\n'); + assert.strictEqual(result[5], '# tests 1\n'); + assert.strictEqual(result[6], '# suites 0\n'); + assert.strictEqual(result[7], '# pass 1\n'); + assert.strictEqual(result[8], '# fail 0\n'); + assert.strictEqual(result[9], '# cancelled 0\n'); + assert.strictEqual(result[10], '# skipped 0\n'); + assert.strictEqual(result[11], '# todo 0\n'); + assert.match(result[12], /# duration_ms \d+\.?\d*/); + }); });