Commit 52a2b0d6 authored by Kev's avatar Kev

Add basic vulnerability component

The events footer <-> header don't work yet, including all related tests
but all three components are now in a root component.
parent 652e84b1
import Vue from 'vue';
import MainApp from 'ee/vulnerabilities/components/main.vue';
import HeaderApp from 'ee/vulnerabilities/components/header.vue';
import DetailsApp from 'ee/vulnerabilities/components/details.vue';
import FooterApp from 'ee/vulnerabilities/components/footer.vue';
......@@ -101,8 +102,32 @@ function createFooterApp() {
});
}
function createMainApp() {
const el = document.getElementById('js-vulnerability-main');
const vulnerability = JSON.parse(el.dataset.vulnerability);
return new Vue({
el,
provide: {
reportType: vulnerability.report_type,
createIssueUrl: vulnerability.create_issue_url,
projectFingerprint: vulnerability.project_fingerprint,
vulnerabilityId: vulnerability.id,
},
render: h =>
h(MainApp, {
props: {
vulnerability,
},
}),
});
}
window.addEventListener('DOMContentLoaded', () => {
createHeaderApp();
createDetailsApp();
createFooterApp();
// createHeaderApp();
// createDetailsApp();
// createFooterApp();
createMainApp();
});
......@@ -10,7 +10,6 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, __ } from '~/locale';
import RelatedIssues from './related_issues.vue';
import HistoryEntry from './history_entry.vue';
import VulnerabilitiesEventBus from './vulnerabilities_event_bus';
import initUserPopovers from '~/user_popovers';
export default {
......@@ -84,8 +83,6 @@ export default {
created() {
this.fetchDiscussions();
VulnerabilitiesEventBus.$on('VULNERABILITY_STATE_CHANGE', this.fetchDiscussions);
},
updated() {
......@@ -187,7 +184,7 @@ export default {
// Emit an event that tells the header to refresh the vulnerability.
if (isVulnerabilityStateChanged) {
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGED');
this.$emit('vulnerability-state-change');
}
},
},
......
......@@ -13,7 +13,6 @@ import ResolutionAlert from './resolution_alert.vue';
import VulnerabilityStateDropdown from './vulnerability_state_dropdown.vue';
import StatusDescription from './status_description.vue';
import { VULNERABILITY_STATE_OBJECTS, FEEDBACK_TYPES, HEADER_ACTION_BUTTONS } from '../constants';
import VulnerabilitiesEventBus from './vulnerabilities_event_bus';
export default {
name: 'VulnerabilityHeader',
......@@ -116,14 +115,6 @@ export default {
},
},
created() {
VulnerabilitiesEventBus.$on('VULNERABILITY_STATE_CHANGED', this.refreshVulnerability);
},
destroyed() {
VulnerabilitiesEventBus.$off('VULNERABILITY_STATE_CHANGED', this.refreshVulnerability);
},
methods: {
triggerClick(action) {
const fn = this[action];
......@@ -145,7 +136,7 @@ export default {
})
.finally(() => {
this.isLoadingVulnerability = false;
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGE');
this.$emit('vulnerability-state-change');
});
},
createMergeRequest() {
......@@ -237,9 +228,7 @@ export default {
:class="
`text-capitalize align-self-center issuable-status-box status-box status-box-${statusBoxStyle}`
"
>
{{ vulnerability.state }}
</span>
>{{ vulnerability.state }}</span>
<status-description
class="issuable-meta"
......@@ -273,9 +262,7 @@ export default {
category="secondary"
:loading="isProcessingAction"
@click="triggerClick(actionButtons[0].action)"
>
{{ actionButtons[0].name }}
</gl-button>
>{{ actionButtons[0].name }}</gl-button>
</div>
</div>
</div>
......
<script>
import Vue from 'vue';
import VulnerabilityHeader from './header.vue';
import VulnerabilityDetails from './details.vue';
import VulnerabilityFooter from './footer.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { VULNERABILITY_STATE_OBJECTS } from 'ee/vulnerabilities/constants';
export default {
name: 'Vulnerability',
components: { VulnerabilityHeader, VulnerabilityDetails, VulnerabilityFooter },
props: {
vulnerability: {
type: Object,
required: true,
},
},
computed: {
footerInfo: function() {
const {
vulnerabilityFeedbackHelpPath,
hasMr,
discussionsUrl,
createIssueUrl,
state,
issueFeedback,
mergeRequestFeedback,
notesUrl,
project,
projectFingerprint,
remediations,
reportType,
solution,
id,
canModifyRelatedIssues,
relatedIssuesHelpPath,
} = convertObjectPropsToCamelCase(this.vulnerability);
const remediation = remediations?.length ? remediations[0] : null;
const hasDownload = Boolean(
state !== VULNERABILITY_STATE_OBJECTS.resolved.state && remediation?.diff?.length && !hasMr,
);
const hasRemediation = Boolean(remediation);
const props = {
vulnerabilityId: id,
discussionsUrl,
notesUrl,
projectFingerprint,
solutionInfo: {
solution,
remediation,
hasDownload,
hasMr,
hasRemediation,
vulnerabilityFeedbackHelpPath,
isStandaloneVulnerability: true,
},
createIssueUrl,
reportType,
issueFeedback,
mergeRequestFeedback,
canModifyRelatedIssues,
project: {
url: this.vulnerability.project.full_path,
value: this.vulnerability.project.full_name,
},
relatedIssuesHelpPath,
};
return props;
},
},
methods: {
handleVulnerabilityStateChange(a) {
console.error('state-change', this.footerInfo);
this.$refs.footer.fetchDiscussions();
},
},
};
</script>
<template>
<div>
<vulnerability-header
:initialVulnerability="vulnerability"
v-on:vulnerability-state-change="handleVulnerabilityStateChange"
></vulnerability-header>
<vulnerability-details :vulnerability="vulnerability"></vulnerability-details>
<vulnerability-footer
v-bind="footerInfo"
v-on:vulnerability-state-change="handleVulnerabilityStateChange"
ref="footer"
></vulnerability-footer>
</div>
</template>
\ No newline at end of file
......@@ -8,3 +8,5 @@
#js-vulnerability-header{ data: vulnerability_init_details }
#js-vulnerability-details{ data: vulnerability_init_details }
#js-vulnerability-footer{ data: vulnerability_init_details }
#js-vulnerability-main{ data: vulnerability_init_details }
......@@ -123,7 +123,6 @@ describe('Vulnerability Header', () => {
it('when the vulnerability state dropdown emits a change event, the vulnerabilities event bus event is emitted with the proper event', () => {
const newState = 'dismiss';
const spy = jest.spyOn(VulnerabilitiesEventBus, '$emit');
mockAxios.onPost().reply(201, { state: newState });
expect(findBadge().text()).not.toBe(newState);
......@@ -132,8 +131,7 @@ describe('Vulnerability Header', () => {
dropdown.vm.$emit('change');
return waitForPromises().then(() => {
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith('VULNERABILITY_STATE_CHANGE');
expect(wrapper.emitted()['vulnerability-state-change']).toBeTruthy();
});
});
......@@ -374,7 +372,8 @@ describe('Vulnerability Header', () => {
const vulnerability = { state: 'dismissed' };
mockAxios.onGet(url).replyOnce(200, vulnerability);
createWrapper();
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGED');
wrapper.vm.$emit('vulnerability-state-change');
await waitForPromises();
expect(findBadge().text()).toBe(vulnerability.state);
......@@ -384,7 +383,7 @@ describe('Vulnerability Header', () => {
it('shows an error message when the vulnerability cannot be loaded', async () => {
mockAxios.onGet().replyOnce(500);
createWrapper();
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGED');
wrapper.vm.$emit('vulnerability-state-change');
await waitForPromises();
expect(createFlash).toHaveBeenCalledTimes(1);
......@@ -394,12 +393,12 @@ describe('Vulnerability Header', () => {
it('cancels a pending refresh request if the vulnerability state has changed', async () => {
mockAxios.onGet().reply(200);
createWrapper();
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGED');
wrapper.vm.$emit('vulnerability-state-change');
const source = wrapper.vm.refreshVulnerabilitySource;
const spy = jest.spyOn(source, 'cancel');
VulnerabilitiesEventBus.$emit('VULNERABILITY_STATE_CHANGED');
wrapper.vm.$emit('vulnerability-state-change');
await waitForPromises();
expect(createFlash).toHaveBeenCalledTimes(0);
......
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