Commit 51cf09e6 authored by Scott Hampton's avatar Scott Hampton

Switch to backend a11y comp

The frontend previously handled the
comparison of the accessiblity reports. We
are switching this to happen on the backend.

To handle this, we need to change our endpoint
to be a single endpoint, poll the endpoint while
the comparison happens, and update the
schema to match the backend response.
parent a1b737af
...@@ -13,11 +13,7 @@ export default { ...@@ -13,11 +13,7 @@ export default {
IssuesList, IssuesList,
}, },
props: { props: {
baseEndpoint: { endpoint: {
type: String,
required: true,
},
headEndpoint: {
type: String, type: String,
required: true, required: true,
}, },
...@@ -34,15 +30,12 @@ export default { ...@@ -34,15 +30,12 @@ export default {
]), ]),
}, },
created() { created() {
this.setEndpoints({ this.setEndpoint(this.endpoint);
baseEndpoint: this.baseEndpoint,
headEndpoint: this.headEndpoint,
});
this.fetchReport(); this.fetchReport();
}, },
methods: { methods: {
...mapActions(['fetchReport', 'setEndpoints']), ...mapActions(['fetchReport', 'setEndpoint']),
}, },
}; };
</script> </script>
......
import Visibility from 'visibilityjs';
import Poll from '~/lib/utils/poll';
import httpStatusCodes from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { parseAccessibilityReport, compareAccessibilityReports } from './utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export const setEndpoints = ({ commit }, { baseEndpoint, headEndpoint }) => let eTagPoll;
commit(types.SET_ENDPOINTS, { baseEndpoint, headEndpoint });
export const clearEtagPoll = () => {
eTagPoll = null;
};
export const stopPolling = () => {
if (eTagPoll) eTagPoll.stop();
};
export const restartPolling = () => {
if (eTagPoll) eTagPoll.restart();
};
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
/**
* We need to poll the report endpoint while they are being parsed in the Backend.
* This can take up to one minute.
*
* Poll.js will handle etag response.
* While http status code is 204, it means it's parsing, and we'll keep polling
* When http status code is 200, it means parsing is done, we can show the results & stop polling
* When http status code is 500, it means parsing went wrong and we stop polling
*/
export const fetchReport = ({ state, dispatch, commit }) => { export const fetchReport = ({ state, dispatch, commit }) => {
commit(types.REQUEST_REPORT); commit(types.REQUEST_REPORT);
// If we don't have both endpoints, throw an error. eTagPoll = new Poll({
if (!state.baseEndpoint || !state.headEndpoint) { resource: {
commit( getReport(endpoint) {
types.RECEIVE_REPORT_ERROR, return axios.get(endpoint);
s__('AccessibilityReport|Accessibility report artifact not found'), },
); },
return; data: state.endpoint,
method: 'getReport',
successCallback: ({ status, data }) => dispatch('receiveReportSuccess', { status, data }),
errorCallback: () => dispatch('receiveReportError'),
});
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
} else {
axios
.get(state.endpoint)
.then(({ status, data }) => dispatch('receiveReportSuccess', { status, data }))
.catch(() => dispatch('receiveReportError'));
} }
Promise.all([ Visibility.change(() => {
axios.get(state.baseEndpoint).then(response => ({ if (!Visibility.hidden()) {
...response.data, dispatch('restartPolling');
isHead: false, } else {
})), dispatch('stopPolling');
axios.get(state.headEndpoint).then(response => ({ }
...response.data, });
isHead: true, };
})),
]) export const receiveReportSuccess = ({ commit }, response) => {
.then(responses => dispatch('receiveReportSuccess', responses)) if (response.status === httpStatusCodes.OK) {
.catch(() => const report = response.data;
commit( commit(types.RECEIVE_REPORT_SUCCESS, report);
types.RECEIVE_REPORT_ERROR, }
s__('AccessibilityReport|Failed to retrieve accessibility report'),
),
);
}; };
export const receiveReportSuccess = ({ commit }, responses) => { export const receiveReportError = ({ commit }) => {
const parsedReports = responses.map(response => ({ commit(
isHead: response.isHead, types.RECEIVE_REPORT_ERROR,
issues: parseAccessibilityReport(response), s__('AccessibilityReport|Failed to retrieve accessibility report'),
})); );
const report = compareAccessibilityReports(parsedReports);
commit(types.RECEIVE_REPORT_SUCCESS, report);
}; };
// prevent babel-plugin-rewire from generating an invalid default during karma tests // prevent babel-plugin-rewire from generating an invalid default during karma tests
......
...@@ -10,8 +10,7 @@ export const groupedSummaryText = state => { ...@@ -10,8 +10,7 @@ export const groupedSummaryText = state => {
return s__('Reports|Accessibility scanning failed loading results'); return s__('Reports|Accessibility scanning failed loading results');
} }
const numberOfResults = const numberOfResults = state.report?.summary?.errored || 0;
(state.report?.summary?.errors || 0) + (state.report?.summary?.warnings || 0);
if (numberOfResults === 0) { if (numberOfResults === 0) {
return s__('Reports|Accessibility scanning detected no issues for the source branch only'); return s__('Reports|Accessibility scanning detected no issues for the source branch only');
} }
......
export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_ENDPOINT = 'SET_ENDPOINT';
export const REQUEST_REPORT = 'REQUEST_REPORT'; export const REQUEST_REPORT = 'REQUEST_REPORT';
export const RECEIVE_REPORT_SUCCESS = 'RECEIVE_REPORT_SUCCESS'; export const RECEIVE_REPORT_SUCCESS = 'RECEIVE_REPORT_SUCCESS';
......
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
[types.SET_ENDPOINTS](state, { baseEndpoint, headEndpoint }) { [types.SET_ENDPOINT](state, endpoint) {
state.baseEndpoint = baseEndpoint; state.endpoint = endpoint;
state.headEndpoint = headEndpoint;
}, },
[types.REQUEST_REPORT](state) { [types.REQUEST_REPORT](state) {
state.isLoading = true; state.isLoading = true;
......
export default (initialState = {}) => ({ export default (initialState = {}) => ({
baseEndpoint: initialState.baseEndpoint || '', endpoint: initialState.endpoint || '',
headEndpoint: initialState.headEndpoint || '',
isLoading: initialState.isLoading || false, isLoading: initialState.isLoading || false,
hasError: initialState.hasError || false, hasError: initialState.hasError || false,
...@@ -11,9 +10,8 @@ export default (initialState = {}) => ({ ...@@ -11,9 +10,8 @@ export default (initialState = {}) => ({
* status: {String}, * status: {String},
* summary: { * summary: {
* total: {Number}, * total: {Number},
* notes: {Number}, * resolved: {Number},
* warnings: {Number}, * errored: {Number},
* errors: {Number},
* }, * },
* existing_errors: {Array.<Object>}, * existing_errors: {Array.<Object>},
* existing_notes: {Array.<Object>}, * existing_notes: {Array.<Object>},
......
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 () => {};
...@@ -143,11 +143,7 @@ export default { ...@@ -143,11 +143,7 @@ export default {
}); });
}, },
shouldShowAccessibilityReport() { shouldShowAccessibilityReport() {
return ( return this.mr.accessibilityReportPath && this.glFeatures.accessibilityMergeRequestWidget;
this.accessibilility?.base_path &&
this.accessibilility?.head_path &&
this.glFeatures.accessibilityMergeRequestWidget
);
}, },
}, },
watch: { watch: {
...@@ -393,8 +389,7 @@ export default { ...@@ -393,8 +389,7 @@ export default {
<grouped-accessibility-reports-app <grouped-accessibility-reports-app
v-if="shouldShowAccessibilityReport" v-if="shouldShowAccessibilityReport"
:base-endpoint="mr.accessibility.base_path" :endpoint="mr.accessibilityReportPath"
:head-endpoint="mr.accessibility.head_path"
/> />
<div class="mr-widget-section"> <div class="mr-widget-section">
......
...@@ -103,7 +103,7 @@ export default class MergeRequestStore { ...@@ -103,7 +103,7 @@ export default class MergeRequestStore {
this.ciStatusFaviconPath = pipelineStatus ? pipelineStatus.favicon : null; this.ciStatusFaviconPath = pipelineStatus ? pipelineStatus.favicon : null;
this.terraformReportsPath = data.terraform_reports_path; this.terraformReportsPath = data.terraform_reports_path;
this.testResultsPath = data.test_reports_path; this.testResultsPath = data.test_reports_path;
this.accessibility = data.accessibility || {}; this.accessibilityReportPath = data.accessibility_report_path;
this.exposedArtifactsPath = data.exposed_artifacts_path; this.exposedArtifactsPath = data.exposed_artifacts_path;
this.cancelAutoMergePath = data.cancel_auto_merge_path; this.cancelAutoMergePath = data.cancel_auto_merge_path;
this.canCancelAutomaticMerge = Boolean(data.cancel_auto_merge_path); this.canCancelAutomaticMerge = Boolean(data.cancel_auto_merge_path);
......
...@@ -352,8 +352,7 @@ export default { ...@@ -352,8 +352,7 @@ export default {
<grouped-accessibility-reports-app <grouped-accessibility-reports-app
v-if="shouldShowAccessibilityReport" v-if="shouldShowAccessibilityReport"
:base-endpoint="mr.accessibility.base_endpoint" :endpoint="mr.accessibilityReportPath"
:head-endpoint="mr.accessibility.head_endpoint"
/> />
<div class="mr-widget-section"> <div class="mr-widget-section">
......
...@@ -1063,9 +1063,6 @@ msgstr "" ...@@ -1063,9 +1063,6 @@ msgstr ""
msgid "AccessTokens|reset it" msgid "AccessTokens|reset it"
msgstr "" msgstr ""
msgid "AccessibilityReport|Accessibility report artifact not found"
msgstr ""
msgid "AccessibilityReport|Failed to retrieve accessibility report" msgid "AccessibilityReport|Failed to retrieve accessibility report"
msgstr "" msgstr ""
......
...@@ -3,7 +3,7 @@ import Vuex from 'vuex'; ...@@ -3,7 +3,7 @@ import Vuex from 'vuex';
import GroupedAccessibilityReportsApp from '~/reports/accessibility_report/grouped_accessibility_reports_app.vue'; import GroupedAccessibilityReportsApp from '~/reports/accessibility_report/grouped_accessibility_reports_app.vue';
import AccessibilityIssueBody from '~/reports/accessibility_report/components/accessibility_issue_body.vue'; import AccessibilityIssueBody from '~/reports/accessibility_report/components/accessibility_issue_body.vue';
import store from '~/reports/accessibility_report/store'; import store from '~/reports/accessibility_report/store';
import { comparedReportResult } from './mock_data'; import { mockReport } from './mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -18,8 +18,7 @@ describe('Grouped accessibility reports app', () => { ...@@ -18,8 +18,7 @@ describe('Grouped accessibility reports app', () => {
store: mockStore, store: mockStore,
localVue, localVue,
propsData: { propsData: {
baseEndpoint: 'base_endpoint.json', endpoint: 'endpoint.json',
headEndpoint: 'head_endpoint.json',
}, },
methods: { methods: {
fetchReport: () => {}, fetchReport: () => {},
...@@ -66,8 +65,7 @@ describe('Grouped accessibility reports app', () => { ...@@ -66,8 +65,7 @@ describe('Grouped accessibility reports app', () => {
beforeEach(() => { beforeEach(() => {
mockStore.state.report = { mockStore.state.report = {
summary: { summary: {
errors: 0, errored: 0,
warnings: 0,
}, },
}; };
}); });
...@@ -83,8 +81,7 @@ describe('Grouped accessibility reports app', () => { ...@@ -83,8 +81,7 @@ describe('Grouped accessibility reports app', () => {
beforeEach(() => { beforeEach(() => {
mockStore.state.report = { mockStore.state.report = {
summary: { summary: {
errors: 0, errored: 1,
warnings: 1,
}, },
}; };
}); });
...@@ -100,8 +97,7 @@ describe('Grouped accessibility reports app', () => { ...@@ -100,8 +97,7 @@ describe('Grouped accessibility reports app', () => {
beforeEach(() => { beforeEach(() => {
mockStore.state.report = { mockStore.state.report = {
summary: { summary: {
errors: 1, errored: 2,
warnings: 1,
}, },
}; };
}); });
...@@ -115,17 +111,15 @@ describe('Grouped accessibility reports app', () => { ...@@ -115,17 +111,15 @@ describe('Grouped accessibility reports app', () => {
describe('with issues to show', () => { describe('with issues to show', () => {
beforeEach(() => { beforeEach(() => {
mockStore.state.report = comparedReportResult; mockStore.state.report = mockReport;
}); });
it('renders custom accessibility issue body', () => { it('renders custom accessibility issue body', () => {
const issueBody = wrapper.find(AccessibilityIssueBody); const issueBody = wrapper.find(AccessibilityIssueBody);
expect(issueBody.props('issue').name).toEqual(comparedReportResult.new_errors[0].name); expect(issueBody.props('issue').name).toEqual(mockReport.new_errors[0].name);
expect(issueBody.props('issue').code).toEqual(comparedReportResult.new_errors[0].code); expect(issueBody.props('issue').code).toEqual(mockReport.new_errors[0].code);
expect(issueBody.props('issue').message).toEqual( expect(issueBody.props('issue').message).toEqual(mockReport.new_errors[0].message);
comparedReportResult.new_errors[0].message,
);
expect(issueBody.props('isNew')).toEqual(true); expect(issueBody.props('isNew')).toEqual(true);
}); });
}); });
......
export const baseReport = { export const mockReport = {
results: {
'http://about.gitlab.com/users/sign_in': [
{
code: 'WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail',
type: 'error',
typeCode: 1,
message:
'This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 2.82:1. Recommendation: change background to #d1470c.',
context:
'<a class="btn btn-nav-cta btn-nav-link-cta" href="/free-trial">\nGet free trial\n</a>',
selector: '#main-nav > div:nth-child(2) > ul > div:nth-child(8) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
'https://about.gitlab.com': [
{
code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
type: 'error',
typeCode: 1,
message:
'Anchor element found with a valid href attribute, but no link content has been supplied.',
context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
selector: '#main-nav > div:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
},
};
export const parsedBaseReport = [
'{"code":"WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail","type":"error","typeCode":1,"message":"This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 2.82:1. Recommendation: change background to #d1470c.","context":"<a class=\\"btn btn-nav-cta btn-nav-link-cta\\" href=\\"/free-trial\\">\\nGet free trial\\n</a>","selector":"#main-nav > div:nth-child(2) > ul > div:nth-child(8) > a","runner":"htmlcs","runnerExtras":{}}',
'{"code":"WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent","type":"error","typeCode":1,"message":"Anchor element found with a valid href attribute, but no link content has been supplied.","context":"<a href=\\"/\\" class=\\"navbar-brand animated\\"><svg height=\\"36\\" viewBox=\\"0 0 1...</a>","selector":"#main-nav > div:nth-child(1) > a","runner":"htmlcs","runnerExtras":{}}',
];
export const headReport = {
results: {
'http://about.gitlab.com/users/sign_in': [
{
code: 'WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail',
type: 'error',
typeCode: 1,
message:
'This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 3.84:1. Recommendation: change text colour to #767676.',
context: '<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>',
selector: '#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
'https://about.gitlab.com': [
{
code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
type: 'error',
typeCode: 1,
message:
'Anchor element found with a valid href attribute, but no link content has been supplied.',
context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
selector: '#main-nav > div:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
},
};
export const comparedReportResult = {
status: 'failed', status: 'failed',
summary: { summary: {
total: 2, total: 2,
notes: 0, resolved: 0,
errors: 2, errored: 2,
warnings: 0,
}, },
new_errors: [headReport.results['http://about.gitlab.com/users/sign_in'][0]], new_errors: [
{
code: 'WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail',
type: 'error',
typeCode: 1,
message:
'This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 3.84:1. Recommendation: change text colour to #767676.',
context: '<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>',
selector: '#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
new_notes: [], new_notes: [],
new_warnings: [], new_warnings: [],
resolved_errors: [baseReport.results['http://about.gitlab.com/users/sign_in'][0]], resolved_errors: [
{
code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
type: 'error',
typeCode: 1,
message:
'Anchor element found with a valid href attribute, but no link content has been supplied.',
context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
selector: '#main-nav > div:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
resolved_notes: [], resolved_notes: [],
resolved_warnings: [], resolved_warnings: [],
existing_errors: [headReport.results['https://about.gitlab.com'][0]], existing_errors: [
{
code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
type: 'error',
typeCode: 1,
message:
'Anchor element found with a valid href attribute, but no link content has been supplied.',
context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
selector: '#main-nav > div:nth-child(1) > a',
runner: 'htmlcs',
runnerExtras: {},
},
],
existing_notes: [], existing_notes: [],
existing_warnings: [], existing_warnings: [],
}; };
export default () => {};
...@@ -5,7 +5,7 @@ import * as types from '~/reports/accessibility_report/store/mutation_types'; ...@@ -5,7 +5,7 @@ import * as types from '~/reports/accessibility_report/store/mutation_types';
import createStore from '~/reports/accessibility_report/store'; import createStore from '~/reports/accessibility_report/store';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { baseReport, headReport, comparedReportResult } from '../mock_data'; import { mockReport } from '../mock_data';
describe('Accessibility Reports actions', () => { describe('Accessibility Reports actions', () => {
let localState; let localState;
...@@ -18,14 +18,13 @@ describe('Accessibility Reports actions', () => { ...@@ -18,14 +18,13 @@ describe('Accessibility Reports actions', () => {
describe('setEndpoints', () => { describe('setEndpoints', () => {
it('should commit SET_ENDPOINTS mutation', done => { it('should commit SET_ENDPOINTS mutation', done => {
const baseEndpoint = 'base_endpoint.json'; const endpoint = 'endpoint.json';
const headEndpoint = 'head_endpoint.json';
testAction( testAction(
actions.setEndpoints, actions.setEndpoint,
{ baseEndpoint, headEndpoint }, endpoint,
localState, localState,
[{ type: types.SET_ENDPOINTS, payload: { baseEndpoint, headEndpoint } }], [{ type: types.SET_ENDPOINT, payload: endpoint }],
[], [],
done, done,
); );
...@@ -36,8 +35,7 @@ describe('Accessibility Reports actions', () => { ...@@ -36,8 +35,7 @@ describe('Accessibility Reports actions', () => {
let mock; let mock;
beforeEach(() => { beforeEach(() => {
localState.baseEndpoint = `${TEST_HOST}/endpoint.json`; localState.endpoint = `${TEST_HOST}/endpoint.json`;
localState.headEndpoint = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
}); });
...@@ -45,30 +43,6 @@ describe('Accessibility Reports actions', () => { ...@@ -45,30 +43,6 @@ describe('Accessibility Reports actions', () => {
mock.restore(); mock.restore();
}); });
describe('when no endpoints are given', () => {
beforeEach(() => {
localState.baseEndpoint = null;
localState.headEndpoint = null;
});
it('should commit REQUEST_REPORT and RECEIVE_REPORT_ERROR mutations', done => {
testAction(
actions.fetchReport,
null,
localState,
[
{ type: types.REQUEST_REPORT },
{
type: types.RECEIVE_REPORT_ERROR,
payload: 'Accessibility report artifact not found',
},
],
[],
done,
);
});
});
describe('success', () => { describe('success', () => {
it('should commit REQUEST_REPORT mutation and dispatch receiveReportSuccess', done => { it('should commit REQUEST_REPORT mutation and dispatch receiveReportSuccess', done => {
const data = { report: { summary: {} } }; const data = { report: { summary: {} } };
...@@ -81,7 +55,7 @@ describe('Accessibility Reports actions', () => { ...@@ -81,7 +55,7 @@ describe('Accessibility Reports actions', () => {
[{ type: types.REQUEST_REPORT }], [{ type: types.REQUEST_REPORT }],
[ [
{ {
payload: [{ ...data, isHead: false }, { ...data, isHead: true }], payload: { status: 200, data },
type: 'receiveReportSuccess', type: 'receiveReportSuccess',
}, },
], ],
...@@ -98,14 +72,8 @@ describe('Accessibility Reports actions', () => { ...@@ -98,14 +72,8 @@ describe('Accessibility Reports actions', () => {
actions.fetchReport, actions.fetchReport,
null, null,
localState, localState,
[ [{ type: types.REQUEST_REPORT }],
{ type: types.REQUEST_REPORT }, [{ type: 'receiveReportError' }],
{
type: types.RECEIVE_REPORT_ERROR,
payload: 'Failed to retrieve accessibility report',
},
],
[],
done, done,
); );
}); });
...@@ -116,9 +84,22 @@ describe('Accessibility Reports actions', () => { ...@@ -116,9 +84,22 @@ describe('Accessibility Reports actions', () => {
it('should commit RECEIVE_REPORT_SUCCESS mutation', done => { it('should commit RECEIVE_REPORT_SUCCESS mutation', done => {
testAction( testAction(
actions.receiveReportSuccess, actions.receiveReportSuccess,
[{ ...baseReport, isHead: false }, { ...headReport, isHead: true }], { status: 200, data: mockReport },
localState,
[{ type: types.RECEIVE_REPORT_SUCCESS, payload: mockReport }],
[],
done,
);
});
});
describe('receiveReportError', () => {
it('should commit RECEIVE_REPORT_ERROR mutation', done => {
testAction(
actions.receiveReportError,
null,
localState, localState,
[{ type: types.RECEIVE_REPORT_SUCCESS, payload: comparedReportResult }], [{ type: types.RECEIVE_REPORT_ERROR, payload: 'Failed to retrieve accessibility report' }],
[], [],
done, done,
); );
......
...@@ -67,8 +67,7 @@ describe('Accessibility reports store getters', () => { ...@@ -67,8 +67,7 @@ describe('Accessibility reports store getters', () => {
it('returns summary message containing number of errors', () => { it('returns summary message containing number of errors', () => {
localState.report = { localState.report = {
summary: { summary: {
errors: 1, errored: 2,
warnings: 1,
}, },
}; };
const result = 'Accessibility scanning detected 2 issues for the source branch only'; const result = 'Accessibility scanning detected 2 issues for the source branch only';
...@@ -81,8 +80,7 @@ describe('Accessibility reports store getters', () => { ...@@ -81,8 +80,7 @@ describe('Accessibility reports store getters', () => {
it('returns summary message containing no errors', () => { it('returns summary message containing no errors', () => {
localState.report = { localState.report = {
summary: { summary: {
errors: 0, errored: 0,
warnings: 0,
}, },
}; };
const result = 'Accessibility scanning detected no issues for the source branch only'; const result = 'Accessibility scanning detected no issues for the source branch only';
...@@ -108,7 +106,7 @@ describe('Accessibility reports store getters', () => { ...@@ -108,7 +106,7 @@ describe('Accessibility reports store getters', () => {
it('returns false', () => { it('returns false', () => {
localState.report = { localState.report = {
status: 'success', status: 'success',
summary: { errors: 0, warnings: 0 }, summary: { errored: 0 },
}; };
expect(getters.shouldRenderIssuesList(localState)).toEqual(false); expect(getters.shouldRenderIssuesList(localState)).toEqual(false);
......
...@@ -10,17 +10,12 @@ describe('Accessibility Reports mutations', () => { ...@@ -10,17 +10,12 @@ describe('Accessibility Reports mutations', () => {
localState = localStore.state; localState = localStore.state;
}); });
describe('SET_ENDPOINTS', () => { describe('SET_ENDPOINT', () => {
it('sets base and head endpoints to give values', () => { it('sets endpoint to given value', () => {
const baseEndpoint = 'base_endpoint.json'; const endpoint = 'endpoint.json';
const headEndpoint = 'head_endpoint.json'; mutations.SET_ENDPOINT(localState, endpoint);
mutations.SET_ENDPOINTS(localState, {
baseEndpoint, expect(localState.endpoint).toEqual(endpoint);
headEndpoint,
});
expect(localState.baseEndpoint).toEqual(baseEndpoint);
expect(localState.headEndpoint).toEqual(headEndpoint);
}); });
}); });
......
import * as utils from '~/reports/accessibility_report/store/utils';
import { baseReport, headReport, parsedBaseReport, comparedReportResult } from '../mock_data';
describe('Accessibility Report store utils', () => {
describe('parseAccessibilityReport', () => {
it('returns array of stringified issues', () => {
const result = utils.parseAccessibilityReport(baseReport);
expect(result).toEqual(parsedBaseReport);
});
});
describe('compareAccessibilityReports', () => {
let reports;
beforeEach(() => {
reports = [
{
isHead: false,
issues: utils.parseAccessibilityReport(baseReport),
},
{
isHead: true,
issues: utils.parseAccessibilityReport(headReport),
},
];
});
it('returns the comparison report with a new, resolved, and existing error', () => {
const result = utils.compareAccessibilityReports(reports);
expect(result).toEqual(comparedReportResult);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment