From f37294d13c3398f8f1654c6ec421c61ba29c3cdd Mon Sep 17 00:00:00 2001 From: Gael Girodon Date: Thu, 23 May 2024 12:22:08 +0200 Subject: [PATCH] Report skipped tests and clean code - Report skipped tests for JUnit & Go test formats - Generate a JUnit badge only if at least one file matches - Improve tests, types and naming --- .github/workflows/main.yml | 3 ++- README.md | 10 ++++----- jsconfig.json | 9 ++++++-- package.json | 2 +- src/badges/coverage.js | 4 ++-- src/badges/index.js | 15 +++++-------- src/badges/index.test.js | 2 +- src/badges/tests.js | 10 ++++++--- src/badges/tests.test.js | 10 ++++++--- src/gist/index.js | 10 ++++----- src/index.js | 3 ++- src/reports/cobertura.js | 19 ++++++++-------- src/reports/cobertura.test.js | 2 +- src/reports/go.js | 34 +++++++++++++++-------------- src/reports/go.test.js | 35 ++++++++++++++++++++++++------ src/reports/index.js | 11 +++------- src/reports/jacoco.js | 23 ++++++++++---------- src/reports/jacoco.test.js | 2 +- src/reports/junit.js | 27 ++++++++++++----------- src/reports/junit.test.js | 10 +++++++-- src/types.js | 24 ++++++++++++++++++++ test/data/go/failed-test.out | 15 +++++++++++++ test/data/go/only-test.out | 5 +++++ test/data/junit/test-results-1.xml | 10 ++++----- test/data/junit/test-results-2.xml | 6 ++--- 25 files changed, 190 insertions(+), 111 deletions(-) create mode 100644 src/types.js create mode 100644 test/data/go/failed-test.out create mode 100644 test/data/go/only-test.out diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b53b8c1..bceec12 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,10 +19,11 @@ jobs: node-version: 20 - run: npm ci - run: npm run lint - - run: npm run build - run: npm run test:ci env: GIST_TOKEN: ${{ secrets.GIST_TOKEN }} + - run: npm run build + - run: sed -i '0,/index.js/s//dist\/index.js/' action.yml package.json - run: rm -rf test/data - uses: ./ with: diff --git a/README.md b/README.md index 4e4202b..38ebf48 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ advantage of Shields customization features (through the query string). ![tests](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fgist.githubusercontent.com%2FGaelGirodon%2F715c62717519f634185af0ebde234992%2Fraw%2Frepo-go-tests.json) ![tests](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fgist.githubusercontent.com%2FGaelGirodon%2F715c62717519f634185af0ebde234992%2Fraw%2Frepo-junit-tests.json) -This badge displays the number of passed and failed tests extracted from a test -report. +This badge displays the number of passed, failed and skipped tests extracted +from test report(s). ```json {"schemaVersion":1,"label":"tests","message":"3 passed","color":"brightgreen"} @@ -89,7 +89,7 @@ Only matched report formats will get a file uploaded to the Gist. Write the verbose test output (`>` or `tee`) with coverage enabled to a single `test*.{out,txt}` file next to the `go.mod` file: -- `RUN`, `PASS` and `FAIL` flags will be used to count tests +- `RUN`, `PASS`, `FAIL` and `SKIP` flags will be used to count tests - The last percentage will be used as the coverage value `go tool cover -func=cover.out` output may be appended to the above file to make @@ -118,8 +118,8 @@ support this format too, natively or using an additional reporter: - **Deno**: `deno test --junit-path=report.xml` - **PHPUnit**: `phpunit --log-junit report.xml` -The number of tests and failures will be extracted from top-level `` -tags, from all matching and valid report files. +The number of tests (total, failed and skipped) will be extracted from +top-level `` tags, from all matching and valid report files. ➡️ `{repo}-[{ref}-]junit-tests.json` diff --git a/jsconfig.json b/jsconfig.json index f9feea1..e8f87a7 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -4,7 +4,12 @@ "target": "esnext", "moduleResolution": "node", "strict": true, - "checkJs": true + "checkJs": true, + "lib": ["esnext"] }, - "include": [ "*.js", "src/**/*.js" ] + "include": [ + "*.js", + "src/**/*.js", + "test/**/*.js" + ] } diff --git a/package.json b/package.json index 241cfb0..b10aaaa 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "lint": "eslint", "test": "mocha src", "test:e2e": "mocha test", - "test:ci": "c8 --reporter=cobertura --all --include \"src/**/*.js\" mocha --reporter mocha-junit-reporter src test || exit 0" + "test:ci": "c8 -r cobertura --all --src src -x \"**/*.test.js\" -x \"**/types.js\" mocha -R mocha-junit-reporter src test || exit 0" }, "repository": { "type": "git", diff --git a/src/badges/coverage.js b/src/badges/coverage.js index bac782c..f80f37f 100644 --- a/src/badges/coverage.js +++ b/src/badges/coverage.js @@ -1,7 +1,7 @@ /** * Build a coverage badge. - * @param {*} data Badge data - * @returns {import('./index.js').Badge} Badge content + * @param {CoverageReportData} data Coverage report data + * @returns {BadgeContent} Badge content */ export function buildBadge(data) { const content = {}; diff --git a/src/badges/index.js b/src/badges/index.js index 03c65a0..35e3156 100644 --- a/src/badges/index.js +++ b/src/badges/index.js @@ -2,21 +2,16 @@ import * as core from '@actions/core'; import * as tests from './tests.js'; import * as coverage from './coverage.js'; -/** - * @typedef {{schemaVersion?: number, label?: string, message?: string, color?: string}} Badge A generated badge - * @typedef {(data: any) => Badge} BadgeGenerator A badge generator - */ - /** * Available badge generators - * @type {{[key: string]: {buildBadge: BadgeGenerator}}} + * @type {{ [key: string]: { buildBadge: BadgeGenerator } }} */ const generators = { tests, coverage }; /** * Build a badge file from a report. - * @param {import('../reports/index.js').Report} report Input report - * @returns {{name: string, content: Badge}} Badge file name and content + * @param {Report} report Input report + * @returns {NamedBadge} Badge name and content */ export function buildBadge(report) { let name = `${report.format}-${report.type}.json`; @@ -32,10 +27,10 @@ export function buildBadge(report) { name = `${prefix}-${name}`; } core.info(`Build badge ${name}`); - const content = { + const badge = { schemaVersion: 1, label: report.type, ...generators[report.type].buildBadge(report.data) }; - return { name, content }; + return { name, badge }; } diff --git a/src/badges/index.test.js b/src/badges/index.test.js index 228b3e2..dda9f71 100644 --- a/src/badges/index.test.js +++ b/src/badges/index.test.js @@ -43,7 +43,7 @@ describe('badges/index', () => { const actual = buildBadge({ type: 'coverage', format: 'go', data: { coverage: 0 } }); assert.deepEqual(actual, { name: expected.name, - content: { schemaVersion: 1, label: 'coverage', message: '0%', color: 'red' } + badge: { schemaVersion: 1, label: 'coverage', message: '0%', color: 'red' } }); }); } diff --git a/src/badges/tests.js b/src/badges/tests.js index 4fe6a8a..8f2e6bb 100644 --- a/src/badges/tests.js +++ b/src/badges/tests.js @@ -1,14 +1,18 @@ /** * Build a tests badge. - * @param {*} data Badge data - * @returns {import('./index.js').Badge} Badge content + * @param {TestReportData} data Test report data + * @returns {BadgeContent} Badge content */ export function buildBadge(data) { const content = {}; content.message = `${data.passed} passed`; + content.color = data.passed > 0 ? 'brightgreen' : 'lightgrey'; if (data.failed > 0) { content.message += `, ${data.failed} failed`; + content.color = 'red'; + } + if (data.skipped > 0) { + content.message += `, ${data.skipped} skipped`; } - content.color = data.failed === 0 ? 'brightgreen' : 'red'; return content; } diff --git a/src/badges/tests.test.js b/src/badges/tests.test.js index 7ef6471..b0a98e1 100644 --- a/src/badges/tests.test.js +++ b/src/badges/tests.test.js @@ -4,11 +4,15 @@ import { buildBadge } from './tests.js'; describe('badges/tests', () => { describe('#buildBadge()', () => { const tests = [ - { data: { passed: 5, failed: 0 }, expected: { message: '5 passed', color: 'brightgreen' } }, - { data: { passed: 4, failed: 1 }, expected: { message: '4 passed, 1 failed', color: 'red' } } + { data: { passed: 0, failed: 0, skipped: 0 }, expected: { message: '0 passed', color: 'lightgrey' } }, + { data: { passed: 0, failed: 0, skipped: 1 }, expected: { message: '0 passed, 1 skipped', color: 'lightgrey' } }, + { data: { passed: 5, failed: 0, skipped: 0 }, expected: { message: '5 passed', color: 'brightgreen' } }, + { data: { passed: 4, failed: 0, skipped: 1 }, expected: { message: '4 passed, 1 skipped', color: 'brightgreen' } }, + { data: { passed: 4, failed: 1, skipped: 0 }, expected: { message: '4 passed, 1 failed', color: 'red' } }, + { data: { passed: 3, failed: 1, skipped: 1 }, expected: { message: '3 passed, 1 failed, 1 skipped', color: 'red' } } ]; for (const { data, expected } of tests) { - it(`should return a ${expected.color} "${expected.message}" badge for ${data.passed}/${data.failed} tests`, () => { + it(`should return a ${expected.color} "${expected.message}" badge for ${data.passed}/${data.failed}/${data.skipped} tests`, () => { const actual = buildBadge(data); assert.deepEqual(actual, expected); }); diff --git a/src/gist/index.js b/src/gist/index.js index 51ad620..061bb28 100644 --- a/src/gist/index.js +++ b/src/gist/index.js @@ -3,16 +3,14 @@ import * as github from '@actions/github'; /** * Update the Gist. - * @param {{name: string, content: import('../badges/index.js').Badge}[]} badges Badges to add to the Gist + * @param {NamedBadge[]} badges Badges to add to the Gist */ export async function update(badges) { core.info(`Update Gist with ${badges.length} file(s)`); const octokit = github.getOctokit(core.getInput('token')); - const files = badges - .reduce((result, b) => { - result[b.name] = { content: JSON.stringify(b.content) }; - return result; - }, {}); + const files = Object.fromEntries( + badges.map((b) => [b.name, { content: JSON.stringify(b.badge) }]) + ); await octokit.rest.gists.update({ gist_id: core.getInput('gist-id'), files diff --git a/src/index.js b/src/index.js index c71930f..c0ab60d 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ export async function main() { .map(b => buildBadge(b)); await gist.update(badges); } catch (error) { - core.warning(`An error occurred: ${error.message}`); + const msg = error instanceof Error ? error.message : error; + core.warning(`An error occurred: ${msg}`); } } diff --git a/src/reports/cobertura.js b/src/reports/cobertura.js index 55dd42c..ac76398 100644 --- a/src/reports/cobertura.js +++ b/src/reports/cobertura.js @@ -14,21 +14,22 @@ export async function getReports(root) { join(root, '**/*cobertura*.xml'), join(root, '**/*coverage*.xml') ]; - const reports = await globNearest(patterns); - const badges = []; - for (const r of reports) { - core.info(`Load Cobertura report '${r}'`); - const report = await fs.readFile(r, { encoding: 'utf8' }); - const coverageMatches = report + const files = await globNearest(patterns); + /** @type {Omit[]} */ + const reports = []; + for (const f of files) { + core.info(`Load Cobertura report '${f}'`); + const contents = await fs.readFile(f, { encoding: 'utf8' }); + const coverageMatches = contents .match(/(?<=]+line-rate=")[0-9.]+(?=")/); if (coverageMatches?.length !== 1) { core.info('Report is not a valid Cobertura report'); continue; // Invalid report file, trying the next one } const coverage = parseFloat(coverageMatches[0]) * 100; - badges.push({ type: 'coverage', data: { coverage } }); + reports.push({ type: 'coverage', data: { coverage } }); break; // Successfully loaded a report file, can return now } - core.info(`Loaded ${badges.length} Cobertura report(s)`); - return badges; + core.info(`Loaded ${reports.length} Cobertura report(s)`); + return reports; } diff --git a/src/reports/cobertura.test.js b/src/reports/cobertura.test.js index cce2a7e..c224883 100644 --- a/src/reports/cobertura.test.js +++ b/src/reports/cobertura.test.js @@ -4,7 +4,7 @@ import { getReports } from './cobertura.js'; describe('reports/cobertura', () => { describe('#getReports()', () => { - it('should return coverage report', async () => { + it('should return a coverage report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/cobertura')); assert.equal(reports.length, 1); assert.deepEqual(reports, [ diff --git a/src/reports/go.js b/src/reports/go.js index c1dbfcc..b68b12f 100644 --- a/src/reports/go.js +++ b/src/reports/go.js @@ -4,12 +4,12 @@ import { dirname, join } from 'path'; import { globNearest } from '../util/index.js'; /** - * Load reports using Go tests and coverage formats. + * Load reports using Go test and coverage formats. * @param {string} root Root search directory - * @returns {Promise} Go tests and coverage reports + * @returns Go test and coverage reports */ export async function getReports(root) { - core.info('Load Go tests and coverage report'); + core.info('Load Go test and coverage report'); const goMods = await globNearest([join(root, '**/go.mod')]); if (goMods.length === 0) { core.info('go.mod file not found, skipping'); @@ -17,26 +17,28 @@ export async function getReports(root) { } const dir = dirname(goMods[0]); core.info(`Search Go reports in '${dir}'`); - const badges = []; + /** @type {Omit[]} */ + const reports = []; const patterns = ['test*.out', 'test*.txt'].map(t => join(dir, t)); - const reports = await globNearest(patterns); - for (const r of reports) { - core.info(`Load Go report '${r}'`); - const report = await fs.readFile(r, { encoding: 'utf8' }); - const tests = (report.match(/=== RUN/g) || []).length; + const files = await globNearest(patterns); + for (const f of files) { + core.info(`Load Go report '${f}'`); + const contents = await fs.readFile(f, { encoding: 'utf8' }); + const tests = (contents.match(/=== RUN/g) || []).length; if (tests === 0) { continue; // Invalid report file, trying the next one } - const passed = (report.match(/--- PASS/g) || []).length; - const failed = (report.match(/--- FAIL/g) || []).length; - badges.push({ type: 'tests', data: { passed, failed, tests } }); - const percentages = report.match(/(?<=\s)[0-9.]+(?=%)/g); + const passed = (contents.match(/--- PASS/g) || []).length; + const failed = (contents.match(/--- FAIL/g) || []).length; + const skipped = (contents.match(/--- SKIP/g) || []).length; + reports.push({ type: 'tests', data: { tests, passed, failed, skipped } }); + const percentages = contents.match(/(?<=\s)[0-9.]+(?=%)/g); if (percentages && percentages.length >= 1) { const coverage = parseFloat(percentages.slice(-1)[0]); - badges.push({ type: 'coverage', data: { coverage } }); + reports.push({ type: 'coverage', data: { coverage } }); } break; // Successfully loaded a report file, can return now } - core.info(`Loaded ${badges.length} Go report(s)`); - return badges; + core.info(`Loaded ${reports.length} Go report(s)`); + return reports; } diff --git a/src/reports/go.test.js b/src/reports/go.test.js index c36047f..bf5f4e4 100644 --- a/src/reports/go.test.js +++ b/src/reports/go.test.js @@ -1,16 +1,37 @@ import assert from 'assert/strict'; +import { copyFile, mkdir, rm, writeFile } from 'fs/promises'; import { join } from 'path'; import { getReports } from './go.js'; describe('reports/go', () => { describe('#getReports()', () => { - it('should return tests and coverage reports', async () => { - const reports = await getReports(join(process.cwd(), 'test/data/go')); - assert.equal(reports.length, 2); - assert.deepEqual(reports, [ - { type: 'tests', data: { passed: 3, failed: 0, tests: 3 } }, - { type: 'coverage', data: { coverage: 96.5 } } - ]); + before(async () => { + await mkdir('test/data/go/project'); + await writeFile('test/data/go/project/go.mod', ''); + }); + const coverage = { type: 'coverage', data: { coverage: 96.5 } }; + const tests = [ + { file: 'test.out', expected: [{ type: 'tests', data: { tests: 3, passed: 3, failed: 0, skipped: 0 } }, coverage] }, + { file: 'only-test.out', expected: [{ type: 'tests', data: { tests: 2, passed: 2, failed: 0, skipped: 0 } }] }, + { file: 'failed-test.out', expected: [{ type: 'tests', data: { tests: 6, passed: 3, failed: 2, skipped: 1 } }, coverage] }, + { file: 'go.mod', expected: [] } + ]; + for (const { file, expected } of tests) { + it(`should return expected report(s) for file ${file}`, async () => { + await copyFile(join('test/data/go', file), 'test/data/go/project/test.out'); + const reports = await getReports(join(process.cwd(), 'test/data/go/project')); + assert.equal(reports.length, expected.length); + assert.deepEqual(reports, expected); + }); + } + after(async () => { + await rm('test/data/go/project', { recursive: true }); + }); + + it('should not return any report when the go.mod file is missing', async () => { + const reports = await getReports(join(process.cwd(), 'test/data/junit')); + assert.equal(reports.length, 0); + assert.deepEqual(reports, []); }); }); }); diff --git a/src/reports/index.js b/src/reports/index.js index 0cf2aee..71e14c5 100644 --- a/src/reports/index.js +++ b/src/reports/index.js @@ -4,20 +4,15 @@ import * as junit from './junit.js'; import * as cobertura from './cobertura.js'; import * as jacoco from './jacoco.js'; -/** - * @typedef {{ type: string, format?: string, data: any }} Report A loaded report - * @typedef {(root: string) => Promise} ReportsLoader A reports loader - */ - /** * Available report loaders - * @type {{[key: string]: {getReports: ReportsLoader}}} + * @type {{ [key: string]: { getReports: ReportsLoader } }} */ const loaders = { go, junit, cobertura, jacoco }; /** * Load all available reports in the current workspace. - * @returns Loaded reports + * @returns {Promise} Loaded reports */ export async function getReports() { core.info('Load reports'); @@ -25,7 +20,7 @@ export async function getReports() { for (const id of Object.keys(loaders)) { try { const reports = await loaders[id].getReports(process.cwd()); - all.push(...reports.map(r => ({ format: id, ...r }))); + all.push(...reports.map(r => ({ ...r, format: id }))); } catch (error) { core.warning(`Skipping ${id} report format: ${error}`); } diff --git a/src/reports/jacoco.js b/src/reports/jacoco.js index 9b614d6..43f8642 100644 --- a/src/reports/jacoco.js +++ b/src/reports/jacoco.js @@ -6,7 +6,7 @@ import { globNearest } from '../util/index.js'; /** * Load coverage reports using JaCoCo format. * @param {string} root Root search directory - * @returns {Promise} JaCoCo coverage report + * @returns JaCoCo coverage report */ export async function getReports(root) { core.info('Load JaCoCo coverage report'); @@ -14,14 +14,15 @@ export async function getReports(root) { join(root, '**/*jacoco*.xml'), join(root, '**/*coverage*.xml') ]; - const reports = await globNearest(patterns); - const badges = []; - for (const r of reports) { - core.info(`Load JaCoCo report '${r}'`); - const report = await fs.readFile(r, { encoding: 'utf8' }); - const missedMatches = report + const files = await globNearest(patterns); + /** @type {Omit[]} */ + const reports = []; + for (const f of files) { + core.info(`Load JaCoCo report '${f}'`); + const contents = await fs.readFile(f, { encoding: 'utf8' }); + const missedMatches = contents .match(/(?<=]+type="LINE"[^>]+missed=")[0-9.]+(?=")/); - const coveredMatches = report + const coveredMatches = contents .match(/(?<=]+type="LINE"[^>]+covered=")[0-9.]+(?=")/); if (!missedMatches?.length || !coveredMatches?.length) { core.info('Report is not a valid JaCoCo report'); @@ -30,9 +31,9 @@ export async function getReports(root) { const missed = parseInt(missedMatches.slice(-1)[0]); const covered = parseInt(coveredMatches.slice(-1)[0]); const coverage = covered * 100 / (covered + missed); - badges.push({ type: 'coverage', data: { coverage } }); + reports.push({ type: 'coverage', data: { coverage } }); break; // Successfully loaded a report file, can return now } - core.info(`Loaded ${badges.length} JaCoCo report(s)`); - return badges; + core.info(`Loaded ${reports.length} JaCoCo report(s)`); + return reports; } diff --git a/src/reports/jacoco.test.js b/src/reports/jacoco.test.js index fb70d51..4d07b7e 100644 --- a/src/reports/jacoco.test.js +++ b/src/reports/jacoco.test.js @@ -4,7 +4,7 @@ import { getReports } from './jacoco.js'; describe('reports/jacoco', () => { describe('#getReports()', () => { - it('should return coverage report', async () => { + it('should return a coverage report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/jacoco')); assert.equal(reports.length, 1); assert.deepEqual(reports, [ diff --git a/src/reports/junit.js b/src/reports/junit.js index a69acab..033459b 100644 --- a/src/reports/junit.js +++ b/src/reports/junit.js @@ -4,38 +4,39 @@ import { join } from 'path'; import { globNearest } from '../util/index.js'; /** - * Load tests reports using JUnit format. + * Load test reports using JUnit format. * @param {string} root Root search directory - * @returns {Promise} JUnit tests report + * @returns {Promise[]>} JUnit test report */ export async function getReports(root) { - core.info('Load JUnit tests report'); + core.info('Load JUnit test report'); const patterns = [ join(root, '**/TEST-*.xml'), join(root, '**/report.xml'), join(root, '**/*test*.xml'), join(root, '**/*junit*.xml') ]; - const reports = await globNearest(patterns); - const data = { passed: 0, failed: 0, tests: 0 }; + const files = await globNearest(patterns); + const data = { tests: 0, passed: 0, failed: 0, skipped: 0 }; let count = 0; - for (const r of reports) { - core.info(`Load JUnit report '${r}'`); - const testSuites = await getTestSuiteTags(r); + for (const f of files) { + core.info(`Load JUnit report '${f}'`); + const testSuites = await getTestSuiteTags(f); if (testSuites.length === 0) { core.info('Report is not a valid JUnit report'); continue; // Invalid report file, trying the next one } for (const ts of testSuites) { + data.tests += parseInt(ts.match(/tests="([0-9]+)"/)?.[1] ?? '0'); data.failed += parseInt(ts.match(/failures="([0-9]+)"/)?.[1] ?? '0'); data.failed += parseInt(ts.match(/errors="([0-9]+)"/)?.[1] ?? '0'); - data.tests += parseInt(ts.match(/tests="([0-9]+)"/)?.[1] ?? '0'); + data.skipped += parseInt(ts.match(/skipped="([0-9]+)"/)?.[1] ?? '0'); } count++; } - data.passed = data.tests - data.failed; + data.passed = data.tests - (data.failed + data.skipped); core.info(`Loaded ${count} JUnit report(s)`); - return [{ type: 'tests', data }]; + return count > 0 ? [{ type: 'tests', data }] : []; } /** @@ -49,8 +50,8 @@ export async function getReports(root) { async function getTestSuiteTags(path) { const testSuites = []; let depth = 0; - const report = await fs.readFile(path, { encoding: 'utf8' }); - const tags = report.match(/<\/?testsuite(?:[^s>][^>]+|\s*)>/g) ?? []; + const contents = await fs.readFile(path, { encoding: 'utf8' }); + const tags = contents.match(/<\/?testsuite(?:[^s>][^>]+|\s*)>/g) ?? []; for (const tag of tags) { if (tag.startsWith(' { describe('#getReports()', () => { - it('should return tests report', async () => { + it('should return a test report', async () => { const reports = await getReports(join(process.cwd(), 'test/data/junit')); assert.equal(reports.length, 1); assert.deepEqual(reports, [ - { type: 'tests', data: { passed: 8, failed: 4, tests: 12 } } + { type: 'tests', data: { tests: 12, passed: 7, failed: 4, skipped: 1 } } ]); }); + + it('should not return a test report when no file matches', async () => { + const reports = await getReports(join(process.cwd(), 'test/data/jacoco')); + assert.equal(reports.length, 0); + assert.deepEqual(reports, []); + }); }); }); diff --git a/src/types.js b/src/types.js new file mode 100644 index 0000000..5dd1b7e --- /dev/null +++ b/src/types.js @@ -0,0 +1,24 @@ +/* + * Common types + */ + +/* Reports loading */ + +/** + * @typedef {{ type: string, format: string, data: ReportData }} Report A loaded report + * @typedef {{ type: 'tests', data: TestReportData } & Report} TestReport A loaded test report + * @typedef {{ type: 'coverage', data: CoverageReportData } & Report} CoverageReport A loaded coverage report + * @typedef {{ [key: string]: number }} ReportData Report data + * @typedef {{ tests?: number, passed: number, failed: number, skipped: number }} TestReportData Test report data + * @typedef {{ coverage: number }} CoverageReportData Coverage report data + * @typedef {(root: string) => Promise[]>} ReportsLoader A report(s) loader + */ + +/* Badges generation */ + +/** + * @typedef {{ name: string, badge: Badge }} NamedBadge A badge with a name + * @typedef {{ schemaVersion: number, label: string } & BadgeContent} Badge A Shields badge + * @typedef {{ message: string, color: string }} BadgeContent The generated content of a badge + * @typedef {(data: any) => BadgeContent} BadgeGenerator A badge generator + */ diff --git a/test/data/go/failed-test.out b/test/data/go/failed-test.out new file mode 100644 index 0000000..2cf4271 --- /dev/null +++ b/test/data/go/failed-test.out @@ -0,0 +1,15 @@ +=== RUN Test +=== RUN Test/example/1 +=== RUN Test/example/2 +=== RUN Test/example/3 +=== RUN Test/example/4 +=== RUN Test/example/5 +--- FAIL: Test (0.01s) + --- PASS: Test/example/1 (0.00s) + --- PASS: Test/example/2 (0.01s) + --- SKIP: Test/example/3 (0.00s) + --- FAIL: Test/example/4 (0.01s) + --- PASS: Test/example/5 (0.01s) +FAIL +coverage: 96.5% of statements in ./... +ok github.com/example/test 0.111s coverage: 96.5% of statements in ./... diff --git a/test/data/go/only-test.out b/test/data/go/only-test.out new file mode 100644 index 0000000..7a3c4c9 --- /dev/null +++ b/test/data/go/only-test.out @@ -0,0 +1,5 @@ +=== RUN Test +=== RUN Test/example/1 +--- PASS: Test (0.01s) + --- PASS: Test/example/1 (0.01s) +PASS diff --git a/test/data/junit/test-results-1.xml b/test/data/junit/test-results-1.xml index 061b702..992106f 100644 --- a/test/data/junit/test-results-1.xml +++ b/test/data/junit/test-results-1.xml @@ -1,19 +1,19 @@ - - + + - + - + - + diff --git a/test/data/junit/test-results-2.xml b/test/data/junit/test-results-2.xml index 0d98714..eb43853 100644 --- a/test/data/junit/test-results-2.xml +++ b/test/data/junit/test-results-2.xml @@ -1,7 +1,7 @@ - - - + + +