diff --git a/CHANGELOG.md b/CHANGELOG.md index 9163f49..79915a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2.6.0 +* Feature: Add `sort-suites` input to order test suites by execution time (descending) + ## 2.5.0 * Feature: Add Nette Tester support with `tester-junit` reporter https://github.com/dorny/test-reporter/pull/707 * Maintenance: Bump actions/upload-artifact from 5 to 6 https://github.com/dorny/test-reporter/pull/695 diff --git a/action.yml b/action.yml index 8dbc85c..e98fc93 100644 --- a/action.yml +++ b/action.yml @@ -46,6 +46,13 @@ inputs: - none required: false default: 'all' + sort-suites: + description: | + Sort order for test suites. Supported options: + - name: Sort alphabetically by name (default) + - time-desc: Sort by execution time, slowest first + required: false + default: 'name' list-tests: description: | Limits which test cases are listed. Supported options: diff --git a/dist/index.js b/dist/index.js index b24a507..1eebaea 100644 --- a/dist/index.js +++ b/dist/index.js @@ -303,6 +303,7 @@ class TestReporter { pathReplaceBackslashes = core.getInput('path-replace-backslashes', { required: false }) === 'true'; reporter = core.getInput('reporter', { required: true }); listSuites = core.getInput('list-suites', { required: true }); + sortSuites = core.getInput('sort-suites', { required: false }); listTests = core.getInput('list-tests', { required: true }); maxAnnotations = parseInt(core.getInput('max-annotations', { required: true })); failOnError = core.getInput('fail-on-error', { required: true }) === 'true'; @@ -322,6 +323,10 @@ class TestReporter { core.setFailed(`Input parameter 'list-suites' has invalid value`); return; } + if (this.sortSuites !== 'name' && this.sortSuites !== 'time-desc') { + core.setFailed(`Input parameter 'sort-suites' has invalid value`); + return; + } if (this.listTests !== 'all' && this.listTests !== 'failed' && this.listTests !== 'none') { core.setFailed(`Input parameter 'list-tests' has invalid value`); return; @@ -409,7 +414,7 @@ class TestReporter { throw error; } } - const { listSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed } = this; + const { listSuites, sortSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed } = this; const passed = results.reduce((sum, tr) => sum + tr.passed, 0); const failed = results.reduce((sum, tr) => sum + tr.failed, 0); const skipped = results.reduce((sum, tr) => sum + tr.skipped, 0); @@ -418,6 +423,7 @@ class TestReporter { if (this.useActionsSummary) { const summary = (0, get_report_1.getReport)(results, { listSuites, + sortSuites, listTests, baseUrl, onlySummary, @@ -446,6 +452,7 @@ class TestReporter { baseUrl = createResp.data.html_url; const summary = (0, get_report_1.getReport)(results, { listSuites, + sortSuites, listTests, baseUrl, onlySummary, @@ -2446,6 +2453,7 @@ const MAX_ACTIONS_SUMMARY_LENGTH = 1048576; exports.DEFAULT_OPTIONS = { listSuites: 'all', listTests: 'all', + sortSuites: 'name', baseUrl: '', onlySummary: false, useActionsSummary: true, @@ -2454,7 +2462,7 @@ exports.DEFAULT_OPTIONS = { collapsed: 'auto' }; function getReport(results, options = exports.DEFAULT_OPTIONS, shortSummary = '') { - applySort(results); + applySort(results, options); const opts = { ...options }; let lines = renderReport(results, opts, shortSummary); let report = lines.join('\n'); @@ -2502,10 +2510,15 @@ function trimReport(lines, options) { reportLines.push(errorMsg); return reportLines.join('\n'); } -function applySort(results) { +function applySort(results, options) { results.sort((a, b) => a.path.localeCompare(b.path, node_utils_1.DEFAULT_LOCALE)); for (const res of results) { - res.suites.sort((a, b) => a.name.localeCompare(b.name, node_utils_1.DEFAULT_LOCALE)); + if (options.sortSuites === 'time-desc') { + res.suites.sort((a, b) => b.time - a.time); + } + else { + res.suites.sort((a, b) => a.name.localeCompare(b.name, node_utils_1.DEFAULT_LOCALE)); + } } } function getByteLength(text) { diff --git a/package-lock.json b/package-lock.json index ad74b6f..79f1120 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "test-reporter", - "version": "2.5.0", + "version": "2.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "test-reporter", - "version": "2.5.0", + "version": "2.6.0", "license": "MIT", "dependencies": { "@actions/core": "^1.11.1", diff --git a/package.json b/package.json index 8c390b0..ac751c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "test-reporter", - "version": "2.5.0", + "version": "2.6.0", "private": true, "description": "Presents test results from popular testing frameworks as Github check run", "main": "lib/main.js", diff --git a/src/main.ts b/src/main.ts index 9b24d38..9c15e9b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,6 +42,7 @@ class TestReporter { readonly pathReplaceBackslashes = core.getInput('path-replace-backslashes', {required: false}) === 'true' readonly reporter = core.getInput('reporter', {required: true}) readonly listSuites = core.getInput('list-suites', {required: true}) as 'all' | 'failed' | 'none' + readonly sortSuites = core.getInput('sort-suites', {required: false}) as 'name' | 'time-desc' readonly listTests = core.getInput('list-tests', {required: true}) as 'all' | 'failed' | 'none' readonly maxAnnotations = parseInt(core.getInput('max-annotations', {required: true})) readonly failOnError = core.getInput('fail-on-error', {required: true}) === 'true' @@ -64,6 +65,11 @@ class TestReporter { return } + if (this.sortSuites !== 'name' && this.sortSuites !== 'time-desc') { + core.setFailed(`Input parameter 'sort-suites' has invalid value`) + return + } + if (this.listTests !== 'all' && this.listTests !== 'failed' && this.listTests !== 'none') { core.setFailed(`Input parameter 'list-tests' has invalid value`) return @@ -174,7 +180,7 @@ class TestReporter { } } - const {listSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed} = this + const {listSuites, sortSuites, listTests, onlySummary, useActionsSummary, badgeTitle, reportTitle, collapsed} = this const passed = results.reduce((sum, tr) => sum + tr.passed, 0) const failed = results.reduce((sum, tr) => sum + tr.failed, 0) @@ -187,6 +193,7 @@ class TestReporter { results, { listSuites, + sortSuites, listTests, baseUrl, onlySummary, @@ -218,6 +225,7 @@ class TestReporter { baseUrl = createResp.data.html_url as string const summary = getReport(results, { listSuites, + sortSuites, listTests, baseUrl, onlySummary, diff --git a/src/report/get-report.ts b/src/report/get-report.ts index 02b9d49..015589c 100644 --- a/src/report/get-report.ts +++ b/src/report/get-report.ts @@ -11,6 +11,7 @@ const MAX_ACTIONS_SUMMARY_LENGTH = 1048576 export interface ReportOptions { listSuites: 'all' | 'failed' | 'none' listTests: 'all' | 'failed' | 'none' + sortSuites: 'name' | 'time-desc' baseUrl: string onlySummary: boolean useActionsSummary: boolean @@ -22,6 +23,7 @@ export interface ReportOptions { export const DEFAULT_OPTIONS: ReportOptions = { listSuites: 'all', listTests: 'all', + sortSuites: 'name', baseUrl: '', onlySummary: false, useActionsSummary: true, @@ -35,7 +37,7 @@ export function getReport( options: ReportOptions = DEFAULT_OPTIONS, shortSummary = '' ): string { - applySort(results) + applySort(results, options) const opts = {...options} let lines = renderReport(results, opts, shortSummary) @@ -94,10 +96,14 @@ function trimReport(lines: string[], options: ReportOptions): string { return reportLines.join('\n') } -function applySort(results: TestRunResult[]): void { +function applySort(results: TestRunResult[], options: ReportOptions): void { results.sort((a, b) => a.path.localeCompare(b.path, DEFAULT_LOCALE)) for (const res of results) { - res.suites.sort((a, b) => a.name.localeCompare(b.name, DEFAULT_LOCALE)) + if (options.sortSuites === 'time-desc') { + res.suites.sort((a, b) => b.time - a.time) + } else { + res.suites.sort((a, b) => a.name.localeCompare(b.name, DEFAULT_LOCALE)) + } } }