diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89639e2..b0aadf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,10 +36,4 @@ jobs: - name: Run tests run: | - npm run unit -- --reporter spec --reporter md:report.md --reporter gh - - - name: Upload report - shell: bash - if: success() || failure() - run: | - cat report.md >> "$GITHUB_STEP_SUMMARY" + npm run unit -- --reporter spec diff --git a/README.md b/README.md index df24869..22135fd 100644 --- a/README.md +++ b/README.md @@ -100,65 +100,12 @@ Note the use of `incremental: true`, which speed up compilation massively. Here are the available reporters: -* `md`: creates a markdown table, useful for setting up a Summary in your GitHub Action -* `gh`: emits `::error` workflow commands for GitHub Actions to show inlined error. Enabled by default when running on GHA. +* `gh`: emits `::error` workflow commands for GitHub Actions to show inlined errors. Enabled by default when running on GHA. * `tap`: outputs the test results in the TAP format. * `spec`: outputs the test results in a human-readable format. * `dot`: outputs the test results in a compact format, where each passing test is represented by a ., and each failing test is represented by a X. * `junit`: outputs test results in a jUnit XML format -## GitHub Action Summary - -The following will automatically show the summary of the test run in the summary page of GitHub Actions. - -```yaml -name: ci - -on: - push: - paths-ignore: - - 'docs/**' - - '*.md' - pull_request: - paths-ignore: - - 'docs/**' - - '*.md' - -jobs: - test: - runs-on: ${{matrix.os}} - - strategy: - matrix: - node-version: [18.x, 20.x, 21.x] - os: [ubuntu-latest, windows-latest] - steps: - - uses: actions/checkout@v4 - - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Install - run: | - npm install - - - name: Lint - run: | - npm run lint - - - name: Run tests - run: | - npm run unit -- --reporter spec --reporter md:report.md - - - name: Upload report - shell: bash - if: success() || failure() - run: | - cat report.md >> "$GITHUB_STEP_SUMMARY" -``` - ## License MIT diff --git a/borp.js b/borp.js index e525ab6..ff27f37 100755 --- a/borp.js +++ b/borp.js @@ -8,7 +8,7 @@ import { finished } from 'node:stream/promises' import { join, relative } from 'node:path' import posix from 'node:path/posix' import runWithTypeScript from './lib/run.js' -import { MarkdownReporter, GithubWorkflowFailuresReporter } from './lib/reporters.js' +import githubReporter from '@reporters/github' import { Report } from 'c8' import os from 'node:os' import { execa } from 'execa' @@ -90,8 +90,7 @@ try { const reporters = { ...Reporters, - md: new MarkdownReporter(config), - gh: new GithubWorkflowFailuresReporter(config), + gh: githubReporter, /* eslint new-cap: "off" */ spec: new Reporters.spec() } diff --git a/lib/reporters.js b/lib/reporters.js deleted file mode 100644 index 0f50859..0000000 --- a/lib/reporters.js +++ /dev/null @@ -1,108 +0,0 @@ -import { Transform } from 'node:stream' -import { fileURLToPath } from 'node:url' - -function normalizeFile (file, cwd) { - let res = file - if (file.startsWith('file://')) { - res = fileURLToPath(file) - } - res = res.replace(cwd, '') - if (res.startsWith('/') || res.startsWith('\\')) { - res = res.slice(1) - } - return res -} - -function eventToLine (event) { - return `* __${event.data.name}__, duration ${event.data.details.duration_ms}ms, line ${event.data.line}\n` -} - -export class MarkdownReporter extends Transform { - constructor (opts) { - super({ - ...opts, - objectMode: true - }) - - this._files = {} - this._cwd = opts?.cwd - } - - getFile (path) { - const file = this._files[path] || { - pass: [], - fail: [] - } - this._files[path] = file - return file - } - - _transform (event, encoding, callback) { - if (!event.data.file) { - callback() - return - } - - const path = normalizeFile(event.data.file, this._cwd) - const file = this.getFile(path) - switch (event.type) { - case 'test:pass': - file.pass.push(event) - break - case 'test:fail': - file.fail.push(event) - break - } - - callback() - } - - _flush (callback) { - this.push('# Summary\n') - for (const [path, file] of Object.entries(this._files)) { - this.push(`## ${path}\n`) - if (file.pass.length > 0) { - this.push('### :white_check_mark: Pass\n') - for (const event of file.pass) { - this.push(eventToLine(event)) - } - } - if (file.fail.length > 0) { - this.push('### :x: Fail\n') - for (const event of file.fail) { - this.push(eventToLine(event)) - } - } - } - this.push(null) - callback() - } -} - -export class GithubWorkflowFailuresReporter extends Transform { - constructor (opts) { - super({ - ...opts, - objectMode: true - }) - - this._files = {} - this._cwd = opts?.cwd - } - - _transform (event, encoding, callback) { - if (!event.data.file) { - callback() - return - } - - const path = normalizeFile(event.data.file, this._cwd) - switch (event.type) { - case 'test:fail': - this.push(`::error file=${path},line=${event.data.line}::${event.data.name}\n`) - break - } - - callback() - } -} diff --git a/package.json b/package.json index 062c1d1..780ac99 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "typescript": "^5.3.2" }, "dependencies": { + "@reporters/github": "^1.5.4", "c8": "^9.0.0", "execa": "^8.0.1", "find-up": "^7.0.0", diff --git a/test/reporters.test.js b/test/reporters.test.js deleted file mode 100644 index 754c934..0000000 --- a/test/reporters.test.js +++ /dev/null @@ -1,172 +0,0 @@ -import { MarkdownReporter, GithubWorkflowFailuresReporter } from '../lib/reporters.js' -import { test, describe } from 'node:test' -import { strictEqual } from 'node:assert' - -const cwd = process.platform === 'win32' ? 'C:\\foo' : '/foo' -const base = process.platform === 'win32' ? 'file://C:\\foo\\test\\' : 'file:///foo/test/' - -describe('MarkdownReporter', async () => { - test('should write a report', async () => { - const reporter = new MarkdownReporter({ cwd }) - - // This is skipped - reporter.write({ - type: 'test:start', - data: {} - }) - - reporter.write({ - type: 'test:pass', - data: { - name: 'add', - file: base + 'add.test.ts', - line: 1, - details: { - duration_ms: 100 - } - } - }) - - reporter.write({ - type: 'test:pass', - data: { - name: 'add2', - file: base + 'add.test.ts', - line: 2, - details: { - duration_ms: 100 - } - } - }) - - reporter.write({ - type: 'test:fail', - data: { - name: 'add3', - file: base + 'add.test.ts', - line: 10, - details: { - duration_ms: 100 - } - } - }) - reporter.end() - - let output = '' - for await (const chunk of reporter) { - output += chunk - } - - strictEqual(output.replaceAll('\\', '/'), `# Summary -## test/add.test.ts -### :white_check_mark: Pass -* __add__, duration 100ms, line 1 -* __add2__, duration 100ms, line 2 -### :x: Fail -* __add3__, duration 100ms, line 10 -`) - }) - - test('skip fail heading if no failing tests', async () => { - const reporter = new MarkdownReporter({ cwd }) - - reporter.write({ - type: 'test:pass', - data: { - name: 'add', - file: base + 'add.test.ts', - line: 1, - details: { - duration_ms: 100 - } - } - }) - - reporter.write({ - type: 'test:pass', - data: { - name: 'add2', - file: base + 'add.test.ts', - line: 2, - details: { - duration_ms: 100 - } - } - }) - - reporter.end() - - let output = '' - for await (const chunk of reporter) { - output += chunk - } - - strictEqual(output.replaceAll('\\', '/'), `# Summary -## test/add.test.ts -### :white_check_mark: Pass -* __add__, duration 100ms, line 1 -* __add2__, duration 100ms, line 2 -`) - }) -}) - -describe('GithubWorkflowFailuresReporter', async () => { - test('should write error in github format', async () => { - const reporter = new GithubWorkflowFailuresReporter({ cwd }) - - // This is skipped - reporter.write({ - type: 'test:start', - data: {} - }) - - reporter.write({ - type: 'test:pass', - data: { - name: 'add', - file: base + 'add.test.ts', - line: 1, - details: { - duration_ms: 100 - } - } - }) - - reporter.write({ - type: 'test:fail', - data: { - name: 'add2', - file: base + 'add.test.ts', - line: 2, - details: { - duration_ms: 100 - } - } - }) - - reporter.write({ - type: 'test:fail', - data: { - name: 'add3', - file: base + 'add.test.ts', - line: 10, - details: { - duration_ms: 100 - } - } - }) - reporter.end() - - let output = '' - for await (const chunk of reporter) { - output += chunk - } - - const expected = [ - '::error file=test/add.test.ts,line=2::add2\n', - '::error file=test/add.test.ts,line=10::add3\n' - ].join('') - - strictEqual(output.replaceAll('\\', '/'), expected) - }) -})