diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/extensions')
4 files changed, 179 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js index e52f2c2c666..6ca0ea9c4e7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js @@ -48,6 +48,9 @@ export default { { text: 'Full report', href: this.conflictsDocsPath, target: '_blank' }, ]; }, + shouldCollapse() { + return true; + }, }, methods: { // Fetches the collapsed data 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 new file mode 100644 index 00000000000..cd5cfb6837c --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/constants.js @@ -0,0 +1,39 @@ +import { __, n__, s__, sprintf } from '~/locale'; + +const digitText = (bold = false) => (bold ? '%{strong_start}%d%{strong_end}' : '%d'); +const noText = (bold = false) => (bold ? '%{strong_start}no%{strong_end}' : 'no'); + +export const TESTS_FAILED_STATUS = 'failed'; +export const ERROR_STATUS = 'error'; + +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'), + fullReport: s__('Reports|Full report'), + + noChanges: (bold) => s__(`Reports|${noText(bold)} changed test results`), + resultsString: (combinedString, resolvedString) => + sprintf(s__('Reports|%{combinedString} and %{resolvedString}'), { + combinedString, + resolvedString, + }), + + summaryText: (name, resultsString) => + sprintf(__('%{name}: %{resultsString}'), { name, resultsString }), + + failedClause: (failed, bold) => + n__(`${digitText(bold)} failed`, `${digitText(bold)} failed`, failed), + erroredClause: (errored, bold) => + n__(`${digitText(bold)} error`, `${digitText(bold)} errors`, errored), + resolvedClause: (resolved, bold) => + n__(`${digitText(bold)} fixed test result`, `${digitText(bold)} fixed test results`, resolved), + totalClause: (total, bold) => + n__(`${digitText(bold)} total test`, `${digitText(bold)} total tests`, total), + + reportError: s__('Reports|An error occurred while loading report'), + reportErrorWithName: (name) => + 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:'), +}; 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 new file mode 100644 index 00000000000..65d9257903f --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js @@ -0,0 +1,82 @@ +import { uniqueId } from 'lodash'; +import axios from '~/lib/utils/axios_utils'; +import { EXTENSION_ICONS } from '../../constants'; +import { summaryTextBuilder, reportTextBuilder, reportSubTextBuilder } from './utils'; +import { i18n, TESTS_FAILED_STATUS, ERROR_STATUS } from './constants'; + +export default { + name: 'WidgetTestSummary', + enablePolling: true, + i18n, + expandEvent: 'i_testing_summary_widget_total', + props: ['testResultsPath', 'headBlobPath', 'pipeline'], + computed: { + summary(data) { + if (data.parsingInProgress) { + return this.$options.i18n.loading; + } + if (data.hasSuiteError) { + return this.$options.i18n.error; + } + return summaryTextBuilder(this.$options.i18n.label, data.summary); + }, + statusIcon(data) { + if (data.parsingInProgress) { + return null; + } + if (data.status === TESTS_FAILED_STATUS) { + return EXTENSION_ICONS.warning; + } + if (data.hasSuiteError) { + return EXTENSION_ICONS.failed; + } + return EXTENSION_ICONS.success; + }, + tertiaryButtons() { + return [ + { + text: this.$options.i18n.fullReport, + href: `${this.pipeline.path}/test_report`, + target: '_blank', + }, + ]; + }, + }, + methods: { + fetchCollapsedData() { + return axios.get(this.testResultsPath).then(({ data = {}, status }) => { + return { + data: { + hasSuiteError: data.suites?.some((suite) => suite.status === ERROR_STATUS), + parsingInProgress: status === 204, + ...data, + }, + }; + }); + }, + fetchFullData() { + return Promise.resolve(this.prepareReports()); + }, + suiteIcon(suite) { + if (suite.status === ERROR_STATUS) { + return EXTENSION_ICONS.error; + } + if (suite.status === TESTS_FAILED_STATUS) { + return EXTENSION_ICONS.failed; + } + return EXTENSION_ICONS.success; + }, + prepareReports() { + return this.collapsedData.suites.map((suite) => { + return { + id: uniqueId('suite-'), + text: reportTextBuilder(suite), + subtext: reportSubTextBuilder(suite), + icon: { + name: this.suiteIcon(suite), + }, + }; + }); + }, + }, +}; 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 new file mode 100644 index 00000000000..a74ed20362f --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/utils.js @@ -0,0 +1,55 @@ +import { i18n } from './constants'; + +const textBuilder = (results, boldNumbers = false) => { + const { failed, errored, resolved, total } = results; + + const failedOrErrored = (failed || 0) + (errored || 0); + const failedString = failed ? i18n.failedClause(failed, boldNumbers) : null; + const erroredString = errored ? i18n.erroredClause(errored, boldNumbers) : null; + const combinedString = + failed && errored ? `${failedString}, ${erroredString}` : failedString || erroredString; + const resolvedString = resolved ? i18n.resolvedClause(resolved, boldNumbers) : null; + const totalString = total ? i18n.totalClause(total, boldNumbers) : null; + + let resultsString = i18n.noChanges(boldNumbers); + + if (failedOrErrored) { + if (resolved) { + resultsString = i18n.resultsString(combinedString, resolvedString); + } else { + resultsString = combinedString; + } + } else if (resolved) { + resultsString = resolvedString; + } + + return `${resultsString}, ${totalString}`; +}; + +export const summaryTextBuilder = (name = '', results = {}) => { + const resultsString = textBuilder(results, true); + return i18n.summaryText(name, resultsString); +}; + +export const reportTextBuilder = ({ name = '', summary = {}, status }) => { + if (!name) { + return i18n.reportError; + } + if (status === 'error') { + return i18n.reportErrorWithName(name); + } + + const resultsString = textBuilder(summary); + 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}`); + } + return errors.join('<br />'); +}; |