Commit ddefc08d authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '214595-refresh-needed-for-state-changes' into 'master'

Fetch discussions on vulnerability state change

See merge request gitlab-org/gitlab!30244
parents 6fc49556 288af839
......@@ -33,7 +33,7 @@ function createFooterApp() {
return false;
}
const { vulnerabilityFeedbackHelpPath, hasMr } = el.dataset;
const { vulnerabilityFeedbackHelpPath, hasMr, discussionsUrl } = el.dataset;
const vulnerability = JSON.parse(el.dataset.vulnerabilityJson);
const finding = JSON.parse(el.dataset.findingJson);
const { issue_feedback: feedback, remediation, solution } = finding;
......@@ -42,6 +42,7 @@ function createFooterApp() {
);
const props = {
discussionsUrl,
solutionInfo: {
solution,
remediation,
......
......@@ -2,15 +2,19 @@
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import { joinPaths } from '~/lib/utils/url_utility';
import IssueNote from 'ee/vue_shared/security_reports/components/issue_note.vue';
import SolutionCard from 'ee/vue_shared/security_reports/components/solution_card.vue';
import HistoryEntry from './history_entry.vue';
import VulnerabilitiesEventBus from './vulnerabilities_event_bus';
export default {
name: 'VulnerabilityFooter',
components: { IssueNote, SolutionCard, HistoryEntry },
props: {
discussionsUrl: {
type: String,
required: true,
},
feedback: {
type: Object,
required: false,
......@@ -40,20 +44,26 @@ export default {
},
created() {
// window.location.pathname is the URL without the protocol or hash/querystring
// i.e. http://server/url?query=string#note_123 -> /server/url
axios
.get(joinPaths(window.location.pathname, 'discussions'))
.then(({ data }) => {
this.discussions = data;
})
.catch(() => {
createFlash(
s__(
'VulnerabilityManagement|Something went wrong while trying to retrieve the vulnerability history. Please try again later.',
),
);
});
this.fetchDiscussions();
VulnerabilitiesEventBus.$on('VULNERABILITY_STATE_CHANGE', this.fetchDiscussions);
},
methods: {
fetchDiscussions() {
axios
.get(this.discussionsUrl)
.then(({ data }) => {
this.discussions = data;
})
.catch(() => {
createFlash(
s__(
'VulnerabilityManagement|Something went wrong while trying to retrieve the vulnerability history. Please try again later.',
),
);
});
},
},
};
</script>
......
......@@ -10,6 +10,7 @@ import ResolutionAlert from './resolution_alert.vue';
import VulnerabilityStateDropdown from './vulnerability_state_dropdown.vue';
import StatusDescription from './status_description.vue';
import { VULNERABILITY_STATE_OBJECTS } from '../constants';
import VulnerabilitiesEventBus from './vulnerabilities_event_bus';
export default {
name: 'VulnerabilityHeader',
......@@ -110,6 +111,7 @@ export default {
})
.finally(() => {
this.isLoadingVulnerability = false;
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGE');
});
},
createIssue() {
......
// TODO: Replace with mitt implementation for Vue3 once it has been verified
// (https://gitlab.com/gitlab-org/gitlab/-/issues/215672)
import Vue from 'vue';
export default new Vue();
import { shallowMount } from '@vue/test-utils';
import VulnerabilityFooter from 'ee/vulnerabilities/components/footer.vue';
import HistoryEntry from 'ee/vulnerabilities/components/history_entry.vue';
import VulnerabilitiesEventBus from 'ee/vulnerabilities/components/vulnerabilities_event_bus';
import SolutionCard from 'ee/vue_shared/security_reports/components/solution_card.vue';
import IssueNote from 'ee/vue_shared/security_reports/components/issue_note.vue';
import { TEST_HOST } from 'helpers/test_constants';
......@@ -15,6 +16,7 @@ describe('Vulnerability Footer', () => {
let wrapper;
const minimumProps = {
discussionsUrl: `/discussions`,
solutionInfo: {
hasDownload: false,
hasMr: false,
......@@ -63,9 +65,19 @@ describe('Vulnerability Footer', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockAxios.reset();
});
describe('vulnerabilities event bus listener', () => {
it('calls the discussion url on vulnerabilities event bus emit of VULNERABILITY_STATE_CHANGE', () => {
createWrapper();
jest.spyOn(axios, 'get');
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGE');
expect(axios.get).toHaveBeenCalledTimes(1);
});
});
describe('solution card', () => {
it('does show solution card when there is one', () => {
createWrapper({ ...minimumProps, solutionInfo: solutionInfoProp });
......
......@@ -10,6 +10,7 @@ import Header from 'ee/vulnerabilities/components/header.vue';
import StatusDescription from 'ee/vulnerabilities/components/status_description.vue';
import ResolutionAlert from 'ee/vulnerabilities/components/resolution_alert.vue';
import VulnerabilityStateDropdown from 'ee/vulnerabilities/components/vulnerability_state_dropdown.vue';
import VulnerabilitiesEventBus from 'ee/vulnerabilities/components/vulnerabilities_event_bus';
import { VULNERABILITY_STATE_OBJECTS } from 'ee/vulnerabilities/constants';
const vulnerabilityStateEntries = Object.entries(VULNERABILITY_STATE_OBJECTS);
......@@ -80,6 +81,7 @@ describe('Vulnerability Header', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
mockAxios.reset();
createFlash.mockReset();
});
......@@ -102,6 +104,36 @@ describe('Vulnerability Header', () => {
});
});
it('when the vulnerability state dropdown emits a change event, the state badge updates', () => {
const newState = 'dismiss';
mockAxios.onPost().reply(201, { state: newState });
expect(findBadge().text()).not.toBe(newState);
const dropdown = wrapper.find(VulnerabilityStateDropdown);
dropdown.vm.$emit('change');
return waitForPromises().then(() => {
expect(findBadge().text()).toBe(newState);
});
});
it('when the vulnerability state dropdown emits a change event, the vulnerabilities event bus event is emitted with the proper event', () => {
const newState = 'dismiss';
jest.spyOn(VulnerabilitiesEventBus, '$emit');
mockAxios.onPost().reply(201, { state: newState });
expect(findBadge().text()).not.toBe(newState);
const dropdown = wrapper.find(VulnerabilityStateDropdown);
dropdown.vm.$emit('change');
return waitForPromises().then(() => {
expect(VulnerabilitiesEventBus.$emit).toHaveBeenCalledTimes(1);
expect(VulnerabilitiesEventBus.$emit).toHaveBeenCalledWith('VULNERABILITY_STATE_CHANGE');
});
});
it('when the vulnerability state changes but the API call fails, an error message is displayed', () => {
const dropdown = wrapper.find(VulnerabilityStateDropdown);
mockAxios.onPost().reply(400);
......
import Vue from 'vue';
import vulnerabilitiesEventBus from 'ee/vulnerabilities/components/vulnerabilities_event_bus';
describe('Vulnerabilities event bus', () => {
it('default exports a vue instance', () => {
expect(vulnerabilitiesEventBus instanceof Vue).toBe(true);
});
});
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