diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-21 18:21:10 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-21 18:21:10 +0300 |
commit | e33f87ac0fabaab468ce4b457996cc0f1b1bb648 (patch) | |
tree | 8bf0de72a9acac014cfdaddab7d463b208294af2 /app/assets/javascripts/reports | |
parent | 5baf990db20a75078684702782c24399ef9eb0fa (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/reports')
10 files changed, 214 insertions, 15 deletions
diff --git a/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue b/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue index 6aae9195be1..256b0e33e79 100644 --- a/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue +++ b/app/assets/javascripts/reports/accessibility_report/components/accessibility_issue_body.vue @@ -26,18 +26,11 @@ export default { * The TECHS code is the "G18", "G168", "H91", etc. from the code which is used for the documentation. * Here we simply split the string on `.` and get the code in the 5th position */ - if (this.issue.code === undefined) { - return null; - } - - return this.issue.code.split('.')[4] || null; + return this.issue.code?.split('.')[4]; }, learnMoreUrl() { - if (this.parsedTECHSCode === null) { - return 'https://www.w3.org/TR/WCAG20-TECHS/Overview.html'; - } - - return `https://www.w3.org/TR/WCAG20-TECHS/${this.parsedTECHSCode}.html`; + // eslint-disable-next-line @gitlab/require-i18n-strings + return `https://www.w3.org/TR/WCAG20-TECHS/${this.parsedTECHSCode || 'Overview'}.html`; }, }, }; diff --git a/app/assets/javascripts/reports/accessibility_report/store/actions.js b/app/assets/javascripts/reports/accessibility_report/store/actions.js new file mode 100644 index 00000000000..f145b352e7d --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/actions.js @@ -0,0 +1,47 @@ +import axios from '~/lib/utils/axios_utils'; +import * as types from './mutation_types'; +import { parseAccessibilityReport, compareAccessibilityReports } from './utils'; +import { s__ } from '~/locale'; + +export const fetchReport = ({ state, dispatch, commit }) => { + commit(types.REQUEST_REPORT); + + // If we don't have both endpoints, throw an error. + if (!state.baseEndpoint || !state.headEndpoint) { + commit( + types.RECEIVE_REPORT_ERROR, + s__('AccessibilityReport|Accessibility report artifact not found'), + ); + return; + } + + Promise.all([ + axios.get(state.baseEndpoint).then(response => ({ + ...response.data, + isHead: false, + })), + axios.get(state.headEndpoint).then(response => ({ + ...response.data, + isHead: true, + })), + ]) + .then(responses => dispatch('receiveReportSuccess', responses)) + .catch(() => + commit( + types.RECEIVE_REPORT_ERROR, + s__('AccessibilityReport|Failed to retrieve accessibility report'), + ), + ); +}; + +export const receiveReportSuccess = ({ commit }, responses) => { + const parsedReports = responses.map(response => ({ + isHead: response.isHead, + issues: parseAccessibilityReport(response), + })); + const report = compareAccessibilityReports(parsedReports); + commit(types.RECEIVE_REPORT_SUCCESS, report); +}; + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/reports/accessibility_report/store/index.js b/app/assets/javascripts/reports/accessibility_report/store/index.js new file mode 100644 index 00000000000..c1413499802 --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/index.js @@ -0,0 +1,14 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default initialState => + new Vuex.Store({ + actions, + mutations, + state: state(initialState), + }); diff --git a/app/assets/javascripts/reports/accessibility_report/store/mutation_types.js b/app/assets/javascripts/reports/accessibility_report/store/mutation_types.js new file mode 100644 index 00000000000..381736bbd38 --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/mutation_types.js @@ -0,0 +1,3 @@ +export const REQUEST_REPORT = 'REQUEST_REPORT'; +export const RECEIVE_REPORT_SUCCESS = 'RECEIVE_REPORT_SUCCESS'; +export const RECEIVE_REPORT_ERROR = 'RECEIVE_REPORT_ERROR'; diff --git a/app/assets/javascripts/reports/accessibility_report/store/mutations.js b/app/assets/javascripts/reports/accessibility_report/store/mutations.js new file mode 100644 index 00000000000..66cf9f3d69d --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/mutations.js @@ -0,0 +1,18 @@ +import * as types from './mutation_types'; + +export default { + [types.REQUEST_REPORT](state) { + state.isLoading = true; + }, + [types.RECEIVE_REPORT_SUCCESS](state, report) { + state.hasError = false; + state.isLoading = false; + state.report = report; + }, + [types.RECEIVE_REPORT_ERROR](state, message) { + state.isLoading = false; + state.hasError = true; + state.errorMessage = message; + state.report = {}; + }, +}; diff --git a/app/assets/javascripts/reports/accessibility_report/store/state.js b/app/assets/javascripts/reports/accessibility_report/store/state.js new file mode 100644 index 00000000000..7d560a9f419 --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/state.js @@ -0,0 +1,30 @@ +export default (initialState = {}) => ({ + baseEndpoint: initialState.baseEndpoint || '', + headEndpoint: initialState.headEndpoint || '', + + isLoading: initialState.isLoading || false, + hasError: initialState.hasError || false, + + /** + * Report will have the following format: + * { + * status: {String}, + * summary: { + * total: {Number}, + * notes: {Number}, + * warnings: {Number}, + * errors: {Number}, + * }, + * existing_errors: {Array.<Object>}, + * existing_notes: {Array.<Object>}, + * existing_warnings: {Array.<Object>}, + * new_errors: {Array.<Object>}, + * new_notes: {Array.<Object>}, + * new_warnings: {Array.<Object>}, + * resolved_errors: {Array.<Object>}, + * resolved_notes: {Array.<Object>}, + * resolved_warnings: {Array.<Object>}, + * } + */ + report: initialState.report || {}, +}); diff --git a/app/assets/javascripts/reports/accessibility_report/store/utils.js b/app/assets/javascripts/reports/accessibility_report/store/utils.js new file mode 100644 index 00000000000..f2de65445b0 --- /dev/null +++ b/app/assets/javascripts/reports/accessibility_report/store/utils.js @@ -0,0 +1,83 @@ +import { difference, intersection } from 'lodash'; +import { + STATUS_FAILED, + STATUS_SUCCESS, + ACCESSIBILITY_ISSUE_ERROR, + ACCESSIBILITY_ISSUE_WARNING, +} from '../../constants'; + +export const parseAccessibilityReport = data => { + // Combine all issues into one array + return Object.keys(data.results) + .map(key => [...data.results[key]]) + .flat() + .map(issue => JSON.stringify(issue)); // stringify to help with comparisons +}; + +export const compareAccessibilityReports = reports => { + const result = { + status: '', + summary: { + total: 0, + notes: 0, + errors: 0, + warnings: 0, + }, + new_errors: [], + new_notes: [], + new_warnings: [], + resolved_errors: [], + resolved_notes: [], + resolved_warnings: [], + existing_errors: [], + existing_notes: [], + existing_warnings: [], + }; + + const headReport = reports.filter(report => report.isHead)[0]; + const baseReport = reports.filter(report => !report.isHead)[0]; + + // existing issues are those that exist in both the head report and the base report + const existingIssues = intersection(headReport.issues, baseReport.issues); + // new issues are those that exist in only the head report + const newIssues = difference(headReport.issues, baseReport.issues); + // resolved issues are those that exist in only the base report + const resolvedIssues = difference(baseReport.issues, headReport.issues); + + const parseIssues = (issue, issueType, shouldCount) => { + const parsedIssue = JSON.parse(issue); + switch (parsedIssue.type) { + case ACCESSIBILITY_ISSUE_ERROR: + result[`${issueType}_errors`].push(parsedIssue); + if (shouldCount) { + result.summary.errors += 1; + } + break; + case ACCESSIBILITY_ISSUE_WARNING: + result[`${issueType}_warnings`].push(parsedIssue); + if (shouldCount) { + result.summary.warnings += 1; + } + break; + default: + result[`${issueType}_notes`].push(parsedIssue); + if (shouldCount) { + result.summary.notes += 1; + } + break; + } + }; + + existingIssues.forEach(issue => parseIssues(issue, 'existing', true)); + newIssues.forEach(issue => parseIssues(issue, 'new', true)); + resolvedIssues.forEach(issue => parseIssues(issue, 'resolved', false)); + + result.summary.total = result.summary.errors + result.summary.warnings + result.summary.notes; + const hasErrorsOrWarnings = result.summary.errors > 0 || result.summary.warnings > 0; + result.status = hasErrorsOrWarnings ? STATUS_FAILED : STATUS_SUCCESS; + + return result; +}; + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue index 88d174f96ed..0f7a0e60dc0 100644 --- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue +++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue @@ -1,6 +1,6 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; +import { sprintf, s__ } from '~/locale'; import { componentNames } from './issue_body'; import ReportSection from './report_section.vue'; import SummaryRow from './summary_row.vue'; @@ -52,8 +52,17 @@ export default { methods: { ...mapActions(['setEndpoint', 'fetchReports']), reportText(report) { - const summary = report.summary || {}; - return reportTextBuilder(report.name, summary); + const { name, summary } = report || {}; + + if (report.status === 'error') { + return sprintf(s__('Reports|An error occurred while loading %{name} results'), { name }); + } + + if (!report.name) { + return s__('Reports|An error occured while loading report'); + } + + return reportTextBuilder(name, summary); }, getReportIcon(report) { return statusIcon(report.status); diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js index 1845b51e6b2..b3905cbfcfb 100644 --- a/app/assets/javascripts/reports/constants.js +++ b/app/assets/javascripts/reports/constants.js @@ -22,3 +22,6 @@ export const status = { ERROR: 'ERROR', SUCCESS: 'SUCCESS', }; + +export const ACCESSIBILITY_ISSUE_ERROR = 'error'; +export const ACCESSIBILITY_ISSUE_WARNING = 'warning'; diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js index 68f6de3a7ee..35ab72bf694 100644 --- a/app/assets/javascripts/reports/store/mutations.js +++ b/app/assets/javascripts/reports/store/mutations.js @@ -8,8 +8,7 @@ export default { state.isLoading = true; }, [types.RECEIVE_REPORTS_SUCCESS](state, response) { - // Make sure to clean previous state in case it was an error - state.hasError = false; + state.hasError = response.suites.some(suite => suite.status === 'error'); state.isLoading = false; |