Commit bf144ed7 authored by Alexander Turinske's avatar Alexander Turinske

Refactor button to allow for multiple actions

- abstract out button actions to accept a generic button
  configuration
- allows for button to be adjusted easier if it is not
  being used for create issue
parent ee8c2f61
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
data() { data() {
return { return {
isLoadingVulnerability: false, isLoadingVulnerability: false,
isCreatingIssue: false, isProcessing: false,
isLoadingUser: false, isLoadingUser: false,
vulnerability: this.initialVulnerability, vulnerability: this.initialVulnerability,
user: undefined, user: undefined,
...@@ -56,6 +56,20 @@ export default { ...@@ -56,6 +56,20 @@ export default {
}, },
computed: { computed: {
actionButtons() {
const buttons = [];
const issueButton = {
name: s__('ciReport|Create issue'),
isLoading: this.isProcessing,
action: 'createIssue',
};
if (!this.hasIssue) {
buttons.push(issueButton);
}
return buttons;
},
hasIssue() { hasIssue() {
return Boolean(this.finding.issue_feedback?.issue_iid); return Boolean(this.finding.issue_feedback?.issue_iid);
}, },
...@@ -95,6 +109,9 @@ export default { ...@@ -95,6 +109,9 @@ export default {
}, },
methods: { methods: {
triggerClick(action) {
this[action]();
},
changeVulnerabilityState(newState) { changeVulnerabilityState(newState) {
this.isLoadingVulnerability = true; this.isLoadingVulnerability = true;
...@@ -115,7 +132,7 @@ export default { ...@@ -115,7 +132,7 @@ export default {
}); });
}, },
createIssue() { createIssue() {
this.isCreatingIssue = true; this.isProcessing = true;
axios axios
.post(this.createIssueUrl, { .post(this.createIssueUrl, {
vulnerability_feedback: { vulnerability_feedback: {
...@@ -134,7 +151,7 @@ export default { ...@@ -134,7 +151,7 @@ export default {
redirectTo(issue_url); redirectTo(issue_url);
}) })
.catch(() => { .catch(() => {
this.isCreatingIssue = false; this.isProcessing = false;
createFlash( createFlash(
s__('VulnerabilityManagement|Something went wrong, could not create an issue.'), s__('VulnerabilityManagement|Something went wrong, could not create an issue.'),
); );
...@@ -182,15 +199,14 @@ export default { ...@@ -182,15 +199,14 @@ export default {
@change="changeVulnerabilityState" @change="changeVulnerabilityState"
/> />
<gl-deprecated-button <gl-deprecated-button
v-if="!hasIssue" v-if="actionButtons.length > 0"
ref="create-issue-btn"
class="ml-2" class="ml-2"
variant="success" variant="success"
category="secondary" category="secondary"
:loading="isCreatingIssue" :loading="isProcessing"
@click="createIssue" @click="triggerClick(actionButtons[0].action)"
> >
{{ s__('VulnerabilityManagement|Create issue') }} {{ actionButtons[0].name }}
</gl-deprecated-button> </gl-deprecated-button>
</div> </div>
</div> </div>
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlDeprecatedButton } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import UsersMockHelper from 'helpers/user_mock_data_helper'; import UsersMockHelper from 'helpers/user_mock_data_helper';
...@@ -64,7 +65,7 @@ describe('Vulnerability Header', () => { ...@@ -64,7 +65,7 @@ describe('Vulnerability Header', () => {
return user; return user;
}; };
const findCreateIssueButton = () => wrapper.find({ ref: 'create-issue-btn' }); const findGlDeprecatedButton = () => wrapper.find(GlDeprecatedButton);
const findBadge = () => wrapper.find({ ref: 'badge' }); const findBadge = () => wrapper.find({ ref: 'badge' });
const findResolutionAlert = () => wrapper.find(ResolutionAlert); const findResolutionAlert = () => wrapper.find(ResolutionAlert);
const findStatusDescription = () => wrapper.find(StatusDescription); const findStatusDescription = () => wrapper.find(StatusDescription);
...@@ -147,54 +148,57 @@ describe('Vulnerability Header', () => { ...@@ -147,54 +148,57 @@ describe('Vulnerability Header', () => {
}); });
}); });
describe('create issue button', () => { describe('single action button', () => {
beforeEach(createWrapper); it('does not display if there are no actions', () => {
it('does display if there is not an issue already created', () => {
expect(findCreateIssueButton().exists()).toBe(true);
});
it('does not display if there is an issue already created', () => {
createWrapper({}, findingWithIssue); createWrapper({}, findingWithIssue);
expect(findCreateIssueButton().exists()).toBe(false); expect(findGlDeprecatedButton().exists()).toBe(false);
}); });
it('calls create issue endpoint on click and redirects to new issue', () => { describe('create issue', () => {
const issueUrl = '/group/project/issues/123'; beforeEach(createWrapper);
const spy = jest.spyOn(urlUtility, 'redirectTo');
mockAxios.onPost(dataset.createIssueUrl).reply(200, { it('does display if there is only one action and not an issue already created', () => {
issue_url: issueUrl, expect(findGlDeprecatedButton().exists()).toBe(true);
expect(findGlDeprecatedButton().text()).toBe('Create issue');
}); });
findCreateIssueButton().vm.$emit('click');
return waitForPromises().then(() => { it('calls create issue endpoint on click and redirects to new issue', () => {
expect(mockAxios.history.post).toHaveLength(1); const issueUrl = '/group/project/issues/123';
const [postRequest] = mockAxios.history.post; const spy = jest.spyOn(urlUtility, 'redirectTo');
expect(postRequest.url).toBe(dataset.createIssueUrl); mockAxios.onPost(dataset.createIssueUrl).reply(200, {
expect(JSON.parse(postRequest.data)).toMatchObject({ issue_url: issueUrl,
vulnerability_feedback: { });
feedback_type: 'issue', findGlDeprecatedButton().vm.$emit('click');
category: defaultVulnerability.report_type, return waitForPromises().then(() => {
project_fingerprint: dataset.projectFingerprint, expect(mockAxios.history.post).toHaveLength(1);
vulnerability_data: { const [postRequest] = mockAxios.history.post;
...defaultVulnerability, expect(postRequest.url).toBe(dataset.createIssueUrl);
...findingWithoutIssue, expect(JSON.parse(postRequest.data)).toMatchObject({
vulnerability_feedback: {
feedback_type: 'issue',
category: defaultVulnerability.report_type, category: defaultVulnerability.report_type,
vulnerability_id: defaultVulnerability.id, project_fingerprint: dataset.projectFingerprint,
vulnerability_data: {
...defaultVulnerability,
...findingWithoutIssue,
category: defaultVulnerability.report_type,
vulnerability_id: defaultVulnerability.id,
},
}, },
}, });
expect(spy).toHaveBeenCalledWith(issueUrl);
}); });
expect(spy).toHaveBeenCalledWith(issueUrl);
}); });
});
it('shows an error message when issue creation fails', () => { it('shows an error message when issue creation fails', () => {
mockAxios.onPost(dataset.createIssueUrl).reply(500); mockAxios.onPost(dataset.createIssueUrl).reply(500);
findCreateIssueButton().vm.$emit('click'); findGlDeprecatedButton().vm.$emit('click');
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(mockAxios.history.post).toHaveLength(1); expect(mockAxios.history.post).toHaveLength(1);
expect(createFlash).toHaveBeenCalledWith( expect(createFlash).toHaveBeenCalledWith(
'Something went wrong, could not create an issue.', 'Something went wrong, could not create an issue.',
); );
});
}); });
}); });
}); });
......
...@@ -23697,9 +23697,6 @@ msgstr "" ...@@ -23697,9 +23697,6 @@ msgstr ""
msgid "VulnerabilityManagement|Confirmed %{timeago} by %{user}" msgid "VulnerabilityManagement|Confirmed %{timeago} by %{user}"
msgstr "" msgstr ""
msgid "VulnerabilityManagement|Create issue"
msgstr ""
msgid "VulnerabilityManagement|Detected %{timeago} in pipeline %{pipelineLink}" msgid "VulnerabilityManagement|Detected %{timeago} in pipeline %{pipelineLink}"
msgstr "" msgstr ""
......
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