Commit ec9d067d authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch '327751-code-quality-widget-status-not-found' into 'master'

Render base path error based on report status

See merge request gitlab-org/gitlab!67370
parents ef4d93f8 6c70491d
...@@ -12,19 +12,10 @@ export default { ...@@ -12,19 +12,10 @@ export default {
ReportSection, ReportSection,
}, },
props: { props: {
headPath: {
type: String,
required: true,
},
headBlobPath: { headBlobPath: {
type: String, type: String,
required: true, required: true,
}, },
basePath: {
type: String,
required: false,
default: null,
},
baseBlobPath: { baseBlobPath: {
type: String, type: String,
required: false, required: false,
...@@ -52,8 +43,6 @@ export default { ...@@ -52,8 +43,6 @@ export default {
}, },
created() { created() {
this.setPaths({ this.setPaths({
basePath: this.basePath,
headPath: this.headPath,
baseBlobPath: this.baseBlobPath, baseBlobPath: this.baseBlobPath,
headBlobPath: this.headBlobPath, headBlobPath: this.headBlobPath,
reportsPath: this.codequalityReportsPath, reportsPath: this.codequalityReportsPath,
......
import pollUntilComplete from '~/lib/utils/poll_until_complete'; import pollUntilComplete from '~/lib/utils/poll_until_complete';
import { STATUS_NOT_FOUND } from '../../constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { parseCodeclimateMetrics } from './utils/codequality_parser'; import { parseCodeclimateMetrics } from './utils/codequality_parser';
...@@ -7,11 +8,11 @@ export const setPaths = ({ commit }, paths) => commit(types.SET_PATHS, paths); ...@@ -7,11 +8,11 @@ export const setPaths = ({ commit }, paths) => commit(types.SET_PATHS, paths);
export const fetchReports = ({ state, dispatch, commit }) => { export const fetchReports = ({ state, dispatch, commit }) => {
commit(types.REQUEST_REPORTS); commit(types.REQUEST_REPORTS);
if (!state.basePath) {
return dispatch('receiveReportsError');
}
return pollUntilComplete(state.reportsPath) return pollUntilComplete(state.reportsPath)
.then(({ data }) => { .then(({ data }) => {
if (data.status === STATUS_NOT_FOUND) {
return dispatch('receiveReportsError', data);
}
return dispatch('receiveReportsSuccess', { return dispatch('receiveReportsSuccess', {
newIssues: parseCodeclimateMetrics(data.new_errors, state.headBlobPath), newIssues: parseCodeclimateMetrics(data.new_errors, state.headBlobPath),
resolvedIssues: parseCodeclimateMetrics(data.resolved_errors, state.baseBlobPath), resolvedIssues: parseCodeclimateMetrics(data.resolved_errors, state.baseBlobPath),
......
import { spriteIcon } from '~/lib/utils/common_utils'; import { spriteIcon } from '~/lib/utils/common_utils';
import { sprintf, __, s__, n__ } from '~/locale'; import { sprintf, __, s__, n__ } from '~/locale';
import { LOADING, ERROR, SUCCESS } from '../../constants'; import { LOADING, ERROR, SUCCESS, STATUS_NOT_FOUND } from '../../constants';
export const hasCodequalityIssues = (state) => export const hasCodequalityIssues = (state) =>
Boolean(state.newIssues?.length || state.resolvedIssues?.length); Boolean(state.newIssues?.length || state.resolvedIssues?.length);
...@@ -42,7 +42,7 @@ export const codequalityText = (state) => { ...@@ -42,7 +42,7 @@ export const codequalityText = (state) => {
}; };
export const codequalityPopover = (state) => { export const codequalityPopover = (state) => {
if (state.headPath && !state.basePath) { if (state.status === STATUS_NOT_FOUND) {
return { return {
title: s__('ciReport|Base pipeline codequality artifact not found'), title: s__('ciReport|Base pipeline codequality artifact not found'),
content: sprintf( content: sprintf(
......
...@@ -2,8 +2,6 @@ import * as types from './mutation_types'; ...@@ -2,8 +2,6 @@ import * as types from './mutation_types';
export default { export default {
[types.SET_PATHS](state, paths) { [types.SET_PATHS](state, paths) {
state.basePath = paths.basePath;
state.headPath = paths.headPath;
state.baseBlobPath = paths.baseBlobPath; state.baseBlobPath = paths.baseBlobPath;
state.headBlobPath = paths.headBlobPath; state.headBlobPath = paths.headBlobPath;
state.reportsPath = paths.reportsPath; state.reportsPath = paths.reportsPath;
...@@ -14,6 +12,7 @@ export default { ...@@ -14,6 +12,7 @@ export default {
}, },
[types.RECEIVE_REPORTS_SUCCESS](state, data) { [types.RECEIVE_REPORTS_SUCCESS](state, data) {
state.hasError = false; state.hasError = false;
state.status = '';
state.statusReason = ''; state.statusReason = '';
state.isLoading = false; state.isLoading = false;
state.newIssues = data.newIssues; state.newIssues = data.newIssues;
...@@ -22,6 +21,7 @@ export default { ...@@ -22,6 +21,7 @@ export default {
[types.RECEIVE_REPORTS_ERROR](state, error) { [types.RECEIVE_REPORTS_ERROR](state, error) {
state.isLoading = false; state.isLoading = false;
state.hasError = true; state.hasError = true;
state.status = error?.status || '';
state.statusReason = error?.response?.data?.status_reason; state.statusReason = error?.response?.data?.status_reason;
}, },
}; };
export default () => ({ export default () => ({
basePath: null,
headPath: null,
reportsPath: null, reportsPath: null,
baseBlobPath: null, baseBlobPath: null,
...@@ -8,6 +6,7 @@ export default () => ({ ...@@ -8,6 +6,7 @@ export default () => ({
isLoading: false, isLoading: false,
hasError: false, hasError: false,
status: '',
statusReason: '', statusReason: '',
newIssues: [], newIssues: [],
......
...@@ -12,6 +12,7 @@ export const SUCCESS = 'SUCCESS'; ...@@ -12,6 +12,7 @@ export const SUCCESS = 'SUCCESS';
export const STATUS_FAILED = 'failed'; export const STATUS_FAILED = 'failed';
export const STATUS_SUCCESS = 'success'; export const STATUS_SUCCESS = 'success';
export const STATUS_NEUTRAL = 'neutral'; export const STATUS_NEUTRAL = 'neutral';
export const STATUS_NOT_FOUND = 'not_found';
export const ICON_WARNING = 'warning'; export const ICON_WARNING = 'warning';
export const ICON_SUCCESS = 'success'; export const ICON_SUCCESS = 'success';
......
...@@ -150,7 +150,7 @@ export default { ...@@ -150,7 +150,7 @@ export default {
); );
}, },
shouldRenderCodeQuality() { shouldRenderCodeQuality() {
return this.mr?.codeclimate?.head_path; return this.mr?.codequalityReportsPath;
}, },
shouldRenderRelatedLinks() { shouldRenderRelatedLinks() {
return Boolean(this.mr.relatedLinks) && !this.mr.isNothingToMergeState; return Boolean(this.mr.relatedLinks) && !this.mr.isNothingToMergeState;
...@@ -496,8 +496,6 @@ export default { ...@@ -496,8 +496,6 @@ export default {
<!-- <extensions-container :mr="mr" /> --> <!-- <extensions-container :mr="mr" /> -->
<grouped-codequality-reports-app <grouped-codequality-reports-app
v-if="shouldRenderCodeQuality" v-if="shouldRenderCodeQuality"
:base-path="mr.codeclimate.base_path"
:head-path="mr.codeclimate.head_path"
:head-blob-path="mr.headBlobPath" :head-blob-path="mr.headBlobPath"
:base-blob-path="mr.baseBlobPath" :base-blob-path="mr.baseBlobPath"
:codequality-reports-path="mr.codequalityReportsPath" :codequality-reports-path="mr.codequalityReportsPath"
......
...@@ -261,7 +261,6 @@ export default class MergeRequestStore { ...@@ -261,7 +261,6 @@ export default class MergeRequestStore {
this.baseBlobPath = blobPath.base_path || ''; this.baseBlobPath = blobPath.base_path || '';
this.codequalityReportsPath = data.codequality_reports_path; this.codequalityReportsPath = data.codequality_reports_path;
this.codequalityHelpPath = data.codequality_help_path; this.codequalityHelpPath = data.codequality_help_path;
this.codeclimate = data.codeclimate;
// Security reports // Security reports
this.sastComparisonPath = data.sast_comparison_path; this.sastComparisonPath = data.sast_comparison_path;
......
...@@ -307,8 +307,6 @@ export default { ...@@ -307,8 +307,6 @@ export default {
<blocking-merge-requests-report :mr="mr" /> <blocking-merge-requests-report :mr="mr" />
<grouped-codequality-reports-app <grouped-codequality-reports-app
v-if="shouldRenderCodeQuality" v-if="shouldRenderCodeQuality"
:base-path="mr.codeclimate.base_path"
:head-path="mr.codeclimate.head_path"
:head-blob-path="mr.headBlobPath" :head-blob-path="mr.headBlobPath"
:base-blob-path="mr.baseBlobPath" :base-blob-path="mr.baseBlobPath"
:codequality-reports-path="mr.codequalityReportsPath" :codequality-reports-path="mr.codequalityReportsPath"
......
...@@ -3,6 +3,7 @@ import Vuex from 'vuex'; ...@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue'; import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue';
import GroupedCodequalityReportsApp from '~/reports/codequality_report/grouped_codequality_reports_app.vue'; import GroupedCodequalityReportsApp from '~/reports/codequality_report/grouped_codequality_reports_app.vue';
import { getStoreConfig } from '~/reports/codequality_report/store'; import { getStoreConfig } from '~/reports/codequality_report/store';
import { STATUS_NOT_FOUND } from '~/reports/constants';
import { parsedReportIssues } from './mock_data'; import { parsedReportIssues } from './mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -14,8 +15,6 @@ describe('Grouped code quality reports app', () => { ...@@ -14,8 +15,6 @@ describe('Grouped code quality reports app', () => {
const PATHS = { const PATHS = {
codequalityHelpPath: 'codequality_help.html', codequalityHelpPath: 'codequality_help.html',
basePath: 'base.json',
headPath: 'head.json',
baseBlobPath: 'base/blob/path/', baseBlobPath: 'base/blob/path/',
headBlobPath: 'head/blob/path/', headBlobPath: 'head/blob/path/',
}; };
...@@ -127,21 +126,6 @@ describe('Grouped code quality reports app', () => { ...@@ -127,21 +126,6 @@ describe('Grouped code quality reports app', () => {
}); });
}); });
describe('when there is a head report but no base report', () => {
beforeEach(() => {
mockStore.state.basePath = null;
mockStore.state.hasError = true;
});
it('renders error text', () => {
expect(findWidget().text()).toContain('Failed to load codeclimate report');
});
it('renders a help icon with more information', () => {
expect(findWidget().find('[data-testid="question-icon"]').exists()).toBe(true);
});
});
describe('on error', () => { describe('on error', () => {
beforeEach(() => { beforeEach(() => {
mockStore.state.hasError = true; mockStore.state.hasError = true;
...@@ -154,5 +138,15 @@ describe('Grouped code quality reports app', () => { ...@@ -154,5 +138,15 @@ describe('Grouped code quality reports app', () => {
it('does not render a help icon', () => { it('does not render a help icon', () => {
expect(findWidget().find('[data-testid="question-icon"]').exists()).toBe(false); expect(findWidget().find('[data-testid="question-icon"]').exists()).toBe(false);
}); });
describe('when base report was not found', () => {
beforeEach(() => {
mockStore.state.status = STATUS_NOT_FOUND;
});
it('renders a help icon with more information', () => {
expect(findWidget().find('[data-testid="question-icon"]').exists()).toBe(true);
});
});
}); });
}); });
...@@ -5,6 +5,7 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -5,6 +5,7 @@ import axios from '~/lib/utils/axios_utils';
import createStore from '~/reports/codequality_report/store'; import createStore from '~/reports/codequality_report/store';
import * as actions from '~/reports/codequality_report/store/actions'; import * as actions from '~/reports/codequality_report/store/actions';
import * as types from '~/reports/codequality_report/store/mutation_types'; import * as types from '~/reports/codequality_report/store/mutation_types';
import { STATUS_NOT_FOUND } from '~/reports/constants';
import { reportIssues, parsedReportIssues } from '../mock_data'; import { reportIssues, parsedReportIssues } from '../mock_data';
const pollInterval = 123; const pollInterval = 123;
...@@ -24,8 +25,6 @@ describe('Codequality Reports actions', () => { ...@@ -24,8 +25,6 @@ describe('Codequality Reports actions', () => {
describe('setPaths', () => { describe('setPaths', () => {
it('should commit SET_PATHS mutation', (done) => { it('should commit SET_PATHS mutation', (done) => {
const paths = { const paths = {
basePath: 'basePath',
headPath: 'headPath',
baseBlobPath: 'baseBlobPath', baseBlobPath: 'baseBlobPath',
headBlobPath: 'headBlobPath', headBlobPath: 'headBlobPath',
reportsPath: 'reportsPath', reportsPath: 'reportsPath',
...@@ -49,7 +48,6 @@ describe('Codequality Reports actions', () => { ...@@ -49,7 +48,6 @@ describe('Codequality Reports actions', () => {
beforeEach(() => { beforeEach(() => {
localState.reportsPath = endpoint; localState.reportsPath = endpoint;
localState.basePath = '/base/path';
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
}); });
...@@ -92,16 +90,17 @@ describe('Codequality Reports actions', () => { ...@@ -92,16 +90,17 @@ describe('Codequality Reports actions', () => {
}); });
}); });
describe('with no base path', () => { describe('when base report is not found', () => {
it('commits REQUEST_REPORTS and dispatches receiveReportsError', (done) => { it('commits REQUEST_REPORTS and dispatches receiveReportsError', (done) => {
localState.basePath = null; const data = { status: STATUS_NOT_FOUND };
mock.onGet(`${TEST_HOST}/codequality_reports.json`).reply(200, data);
testAction( testAction(
actions.fetchReports, actions.fetchReports,
null, null,
localState, localState,
[{ type: types.REQUEST_REPORTS }], [{ type: types.REQUEST_REPORTS }],
[{ type: 'receiveReportsError' }], [{ type: 'receiveReportsError', payload: data }],
done, done,
); );
}); });
......
import createStore from '~/reports/codequality_report/store'; import createStore from '~/reports/codequality_report/store';
import * as getters from '~/reports/codequality_report/store/getters'; import * as getters from '~/reports/codequality_report/store/getters';
import { LOADING, ERROR, SUCCESS } from '~/reports/constants'; import { LOADING, ERROR, SUCCESS, STATUS_NOT_FOUND } from '~/reports/constants';
describe('Codequality reports store getters', () => { describe('Codequality reports store getters', () => {
let localState; let localState;
...@@ -76,10 +76,9 @@ describe('Codequality reports store getters', () => { ...@@ -76,10 +76,9 @@ describe('Codequality reports store getters', () => {
}); });
describe('codequalityPopover', () => { describe('codequalityPopover', () => {
describe('when head report is available but base report is not', () => { describe('when base report is not available', () => {
it('returns a popover with a documentation link', () => { it('returns a popover with a documentation link', () => {
localState.headPath = 'head.json'; localState.status = STATUS_NOT_FOUND;
localState.basePath = undefined;
localState.helpPath = 'codequality_help.html'; localState.helpPath = 'codequality_help.html';
expect(getters.codequalityPopover(localState).title).toEqual( expect(getters.codequalityPopover(localState).title).toEqual(
......
import createStore from '~/reports/codequality_report/store'; import createStore from '~/reports/codequality_report/store';
import mutations from '~/reports/codequality_report/store/mutations'; import mutations from '~/reports/codequality_report/store/mutations';
import { STATUS_NOT_FOUND } from '~/reports/constants';
describe('Codequality Reports mutations', () => { describe('Codequality Reports mutations', () => {
let localState; let localState;
...@@ -12,24 +13,18 @@ describe('Codequality Reports mutations', () => { ...@@ -12,24 +13,18 @@ describe('Codequality Reports mutations', () => {
describe('SET_PATHS', () => { describe('SET_PATHS', () => {
it('sets paths to given values', () => { it('sets paths to given values', () => {
const basePath = 'base.json';
const headPath = 'head.json';
const baseBlobPath = 'base/blob/path/'; const baseBlobPath = 'base/blob/path/';
const headBlobPath = 'head/blob/path/'; const headBlobPath = 'head/blob/path/';
const reportsPath = 'reports.json'; const reportsPath = 'reports.json';
const helpPath = 'help.html'; const helpPath = 'help.html';
mutations.SET_PATHS(localState, { mutations.SET_PATHS(localState, {
basePath,
headPath,
baseBlobPath, baseBlobPath,
headBlobPath, headBlobPath,
reportsPath, reportsPath,
helpPath, helpPath,
}); });
expect(localState.basePath).toEqual(basePath);
expect(localState.headPath).toEqual(headPath);
expect(localState.baseBlobPath).toEqual(baseBlobPath); expect(localState.baseBlobPath).toEqual(baseBlobPath);
expect(localState.headBlobPath).toEqual(headBlobPath); expect(localState.headBlobPath).toEqual(headBlobPath);
expect(localState.reportsPath).toEqual(reportsPath); expect(localState.reportsPath).toEqual(reportsPath);
...@@ -58,9 +53,10 @@ describe('Codequality Reports mutations', () => { ...@@ -58,9 +53,10 @@ describe('Codequality Reports mutations', () => {
expect(localState.hasError).toEqual(false); expect(localState.hasError).toEqual(false);
}); });
it('clears statusReason', () => { it('clears status and statusReason', () => {
mutations.RECEIVE_REPORTS_SUCCESS(localState, {}); mutations.RECEIVE_REPORTS_SUCCESS(localState, {});
expect(localState.status).toEqual('');
expect(localState.statusReason).toEqual(''); expect(localState.statusReason).toEqual('');
}); });
...@@ -86,6 +82,13 @@ describe('Codequality Reports mutations', () => { ...@@ -86,6 +82,13 @@ describe('Codequality Reports mutations', () => {
expect(localState.hasError).toEqual(true); expect(localState.hasError).toEqual(true);
}); });
it('sets status based on error object', () => {
const error = { status: STATUS_NOT_FOUND };
mutations.RECEIVE_REPORTS_ERROR(localState, error);
expect(localState.status).toEqual(error.status);
});
it('sets statusReason to string from error response data', () => { it('sets statusReason to string from error response data', () => {
const data = { status_reason: 'This merge request does not have codequality reports' }; const data = { status_reason: 'This merge request does not have codequality reports' };
const error = { response: { data } }; const error = { response: { data } };
......
...@@ -234,14 +234,11 @@ export default { ...@@ -234,14 +234,11 @@ export default {
can_revert_on_current_merge_request: true, can_revert_on_current_merge_request: true,
can_cherry_pick_on_current_merge_request: true, can_cherry_pick_on_current_merge_request: true,
}, },
codeclimate: {
head_path: 'head.json',
base_path: 'base.json',
},
blob_path: { blob_path: {
base_path: 'blob_path', base_path: 'blob_path',
head_path: 'blob_path', head_path: 'blob_path',
}, },
codequality_reports_path: 'codequality_reports.json',
codequality_help_path: 'code_quality.html', codequality_help_path: 'code_quality.html',
target_branch_path: '/root/acets-app/branches/main', target_branch_path: '/root/acets-app/branches/main',
source_branch_path: '/root/acets-app/branches/daaaa', source_branch_path: '/root/acets-app/branches/daaaa',
......
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