diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget')
4 files changed, 145 insertions, 21 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/constants.js b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/constants.js index cd5cfb6837c..23f14bea4e1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/constants.js +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/constants.js @@ -10,6 +10,8 @@ export const i18n = { label: s__('Reports|Test summary'), loading: s__('Reports|Test summary results are loading'), error: s__('Reports|Test summary failed to load results'), + newHeader: s__('Reports|New'), + fixedHeader: s__('Reports|Fixed'), fullReport: s__('Reports|Full report'), noChanges: (bold) => s__(`Reports|${noText(bold)} changed test results`), @@ -36,4 +38,32 @@ export const i18n = { sprintf(s__('Reports|An error occurred while loading %{name} results'), { name }), headReportParsingError: s__('Reports|Head report parsing error:'), baseReportParsingError: s__('Reports|Base report parsing error:'), + + recentFailureSummary: (recentlyFailed, failed) => { + if (failed < 2) { + return sprintf( + s__( + 'Reports|%{recentlyFailed} out of %{failed} failed test has failed more than once in the last 14 days', + ), + { recentlyFailed, failed }, + ); + } + return sprintf( + n__( + 'Reports|%{recentlyFailed} out of %{failed} failed tests has failed more than once in the last 14 days', + 'Reports|%{recentlyFailed} out of %{failed} failed tests have failed more than once in the last 14 days', + recentlyFailed, + ), + { recentlyFailed, failed }, + ); + }, + recentFailureCount: (recentFailures) => + sprintf( + n__( + 'Reports|Failed %{count} time in %{base_branch} in the last 14 days', + 'Reports|Failed %{count} times in %{base_branch} in the last 14 days', + recentFailures.count, + ), + recentFailures, + ), }; diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js index 65d9257903f..577b2cbfc5c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js @@ -1,7 +1,13 @@ import { uniqueId } from 'lodash'; import axios from '~/lib/utils/axios_utils'; import { EXTENSION_ICONS } from '../../constants'; -import { summaryTextBuilder, reportTextBuilder, reportSubTextBuilder } from './utils'; +import { + summaryTextBuilder, + reportTextBuilder, + reportSubTextBuilder, + countRecentlyFailedTests, + recentFailuresTextBuilder, +} from './utils'; import { i18n, TESTS_FAILED_STATUS, ERROR_STATUS } from './constants'; export default { @@ -18,7 +24,10 @@ export default { if (data.hasSuiteError) { return this.$options.i18n.error; } - return summaryTextBuilder(this.$options.i18n.label, data.summary); + return { + subject: summaryTextBuilder(this.$options.i18n.label, data.summary), + meta: recentFailuresTextBuilder(data.summary), + }; }, statusIcon(data) { if (data.parsingInProgress) { @@ -50,6 +59,10 @@ export default { hasSuiteError: data.suites?.some((suite) => suite.status === ERROR_STATUS), parsingInProgress: status === 204, ...data, + summary: { + recentlyFailed: countRecentlyFailedTests(data.suites), + ...data.summary, + }, }, }; }); @@ -66,17 +79,66 @@ export default { } return EXTENSION_ICONS.success; }, - prepareReports() { - return this.collapsedData.suites.map((suite) => { + testHeader(test, sectionHeader, index) { + const headers = []; + if (index === 0) { + headers.push(sectionHeader); + } + if (test.recent_failures?.count && test.recent_failures?.base_branch) { + headers.push(i18n.recentFailureCount(test.recent_failures)); + } + return headers; + }, + mapTestAsChild({ iconName, sectionHeader }) { + return (test, index) => { return { - id: uniqueId('suite-'), - text: reportTextBuilder(suite), - subtext: reportSubTextBuilder(suite), - icon: { - name: this.suiteIcon(suite), - }, + id: uniqueId('test-'), + header: this.testHeader(test, sectionHeader, index), + icon: { name: iconName }, + text: test.name, }; - }); + }; + }, + prepareReports() { + return this.collapsedData.suites + .map((suite) => { + return { + ...suite, + summary: { + recentlyFailed: countRecentlyFailedTests(suite), + ...suite.summary, + }, + }; + }) + .map((suite) => { + return { + id: uniqueId('suite-'), + text: reportTextBuilder(suite), + subtext: reportSubTextBuilder(suite), + icon: { + name: this.suiteIcon(suite), + }, + children: [ + ...[...suite.new_failures, ...suite.new_errors].map( + this.mapTestAsChild({ + sectionHeader: i18n.newHeader, + iconName: EXTENSION_ICONS.failed, + }), + ), + ...[...suite.existing_failures, ...suite.existing_errors].map( + this.mapTestAsChild({ + iconName: EXTENSION_ICONS.failed, + }), + ), + ...[...suite.resolved_failures, ...suite.resolved_errors].map( + this.mapTestAsChild({ + sectionHeader: i18n.fixedHeader, + iconName: EXTENSION_ICONS.success, + }), + ), + ], + }; + }); }, }, }; diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/utils.js b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/utils.js index a74ed20362f..9e4b0ac581c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/utils.js +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/utils.js @@ -43,13 +43,42 @@ export const reportTextBuilder = ({ name = '', summary = {}, status }) => { return i18n.summaryText(name, resultsString); }; -export const reportSubTextBuilder = ({ suite_errors }) => { - const errors = []; - if (suite_errors?.head) { - errors.push(`${i18n.headReportParsingError} ${suite_errors.head}`); - } - if (suite_errors?.base) { - errors.push(`${i18n.baseReportParsingError} ${suite_errors.base}`); +export const recentFailuresTextBuilder = (summary = {}) => { + const { failed, recentlyFailed } = summary; + if (!failed || !recentlyFailed) return ''; + + return i18n.recentFailureSummary(recentlyFailed, failed); +}; + +export const reportSubTextBuilder = ({ suite_errors, summary }) => { + if (suite_errors?.head || suite_errors?.base) { + const errors = []; + if (suite_errors?.head) { + errors.push(`${i18n.headReportParsingError} ${suite_errors.head}`); + } + if (suite_errors?.base) { + errors.push(`${i18n.baseReportParsingError} ${suite_errors.base}`); + } + return errors.join('<br />'); } - return errors.join('<br />'); + return recentFailuresTextBuilder(summary); +}; + +export const countRecentlyFailedTests = (subject) => { + // handle either a single report or an array of reports + const reports = !subject.length ? [subject] : subject; + + return reports + .map((report) => { + return ( + [report.new_failures, report.existing_failures, report.resolved_failures] + // only count tests which have failed more than once + .map( + (failureArray) => + failureArray.filter((failure) => failure.recent_failures?.count > 1).length, + ) + .reduce((total, count) => total + count, 0) + ); + }) + .reduce((total, count) => total + count, 0); }; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 4b3ad288768..04f71e2b185 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -195,6 +195,9 @@ export default { shouldRenderTestReport() { return Boolean(this.mr?.testResultsPath); }, + shouldRenderRefactoredTestReport() { + return window.gon?.features?.refactorMrWidgetTestSummary; + }, mergeError() { let { mergeError } = this.mr; @@ -512,7 +515,7 @@ export default { } }, registerTestReportExtension() { - if (this.shouldRenderTestReport && this.shouldShowExtension) { + if (this.shouldRenderTestReport && this.shouldRenderRefactoredTestReport) { registerExtension(testReportExtension); } }, @@ -588,7 +591,7 @@ export default { /> <grouped-test-reports-app - v-if="mr.testResultsPath && !shouldShowExtension" + v-if="shouldRenderTestReport && !shouldRenderRefactoredTestReport" class="js-reports-container" :endpoint="mr.testResultsPath" :head-blob-path="mr.headBlobPath" |