Commit 3eeef9cf authored by Savas Vedova's avatar Savas Vedova

Display create jira issue button when available

This MR is part of our Jira integration. Whenever the jira issues
are enabled in a repository, it will be now possible to create a
vulnerability issue directly in jira.
parent 7c03e080
...@@ -5,7 +5,6 @@ import SplitButton from 'ee/vue_shared/security_reports/components/split_button. ...@@ -5,7 +5,6 @@ import SplitButton from 'ee/vue_shared/security_reports/components/split_button.
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export default { export default {
name: 'ModalFooter',
components: { components: {
DismissButton, DismissButton,
GlButton, GlButton,
...@@ -58,15 +57,26 @@ export default { ...@@ -58,15 +57,26 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
vulnerability: {
type: Object,
required: true,
},
}, },
computed: { computed: {
createIssueButtonText() {
return this.vulnerability.create_jira_issue_url
? s__('ciReport|Create Jira issue')
: s__('ciReport|Create issue');
},
actionButtons() { actionButtons() {
const buttons = []; const buttons = [];
const issueButton = { const issueButton = {
name: s__('ciReport|Create issue'), name: this.createIssueButtonText,
tagline: s__('ciReport|Investigate this vulnerability by creating an issue'), tagline: s__('ciReport|Investigate this vulnerability by creating an issue'),
isLoading: this.isCreatingIssue, isLoading: this.isCreatingIssue,
action: 'createNewIssue', action: this.vulnerability.create_jira_issue_url ? undefined : 'createNewIssue',
href: this.vulnerability.create_jira_issue_url,
}; };
const MRButton = { const MRButton = {
name: s__('ciReport|Resolve with merge request'), name: s__('ciReport|Resolve with merge request'),
...@@ -128,11 +138,13 @@ export default { ...@@ -128,11 +138,13 @@ export default {
<gl-button <gl-button
v-else-if="actionButtons.length > 0" v-else-if="actionButtons.length > 0"
:loading="actionButtons[0].isLoading" :loading="actionButtons[0].isLoading"
:disabled="actionButtons[0].isLoading || disabled" :disabled="disabled"
variant="success" variant="success"
category="secondary" category="secondary"
class="js-action-button" data-testid="create-issue-button"
data-qa-selector="create_issue_button" data-qa-selector="create_issue_button"
:target="actionButtons[0].href ? '_blank' : undefined"
:href="actionButtons[0].href"
@click="$emit(actionButtons[0].action)" @click="$emit(actionButtons[0].action)"
> >
{{ __(actionButtons[0].name) }} {{ __(actionButtons[0].name) }}
......
<script> <script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
export default { export default {
components: { components: {
...@@ -28,7 +29,11 @@ export default { ...@@ -28,7 +29,11 @@ export default {
this.selectedButton = button; this.selectedButton = button;
}, },
handleClick() { handleClick() {
this.$emit(this.selectedButton.action); if (this.selectedButton.href) {
visitUrl(this.selectedButton.href, true);
} else {
this.$emit(this.selectedButton.action);
}
}, },
}, },
}; };
...@@ -40,6 +45,7 @@ export default { ...@@ -40,6 +45,7 @@ export default {
:disabled="disabled" :disabled="disabled"
variant="success" variant="success"
:text="selectedButton.name" :text="selectedButton.name"
:href="selectedButton.href"
split split
@click="handleClick" @click="handleClick"
> >
......
...@@ -13,11 +13,14 @@ describe('Security Reports modal footer', () => { ...@@ -13,11 +13,14 @@ describe('Security Reports modal footer', () => {
isCreatingIssue: false, isCreatingIssue: false,
isDismissingVulnerability: false, isDismissingVulnerability: false,
isCreatingMergeRequest: false, isCreatingMergeRequest: false,
vulnerability: {},
...propsData, ...propsData,
}, },
}); });
}; };
const findActionButton = () => wrapper.find('[data-testid=create-issue-button]');
describe('can only create issue', () => { describe('can only create issue', () => {
beforeEach(() => { beforeEach(() => {
const propsData = { const propsData = {
...@@ -33,11 +36,11 @@ describe('Security Reports modal footer', () => { ...@@ -33,11 +36,11 @@ describe('Security Reports modal footer', () => {
it('only renders the create issue button', () => { it('only renders the create issue button', () => {
expect(wrapper.vm.actionButtons[0].name).toBe('Create issue'); expect(wrapper.vm.actionButtons[0].name).toBe('Create issue');
expect(wrapper.find('.js-action-button').text()).toBe('Create issue'); expect(findActionButton().text()).toBe('Create issue');
}); });
it('emits createIssue when create issue button is clicked', () => { it('emits createIssue when create issue button is clicked', () => {
wrapper.find('.js-action-button').trigger('click'); findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().createNewIssue).toBeTruthy(); expect(wrapper.emitted().createNewIssue).toBeTruthy();
...@@ -45,6 +48,34 @@ describe('Security Reports modal footer', () => { ...@@ -45,6 +48,34 @@ describe('Security Reports modal footer', () => {
}); });
}); });
describe('can only create jira issue', () => {
const url = 'https://gitlab.atlassian.net/create-issue';
beforeEach(() => {
const propsData = {
modal: createState().modal,
canCreateIssue: true,
vulnerability: {
create_jira_issue_url: url,
},
};
mountComponent(propsData);
});
it('has target property properly set', () => {
expect(findActionButton().props('target')).toBe('_blank');
expect(findActionButton().props('action')).toBeUndefined();
});
it('has href attribute properly set', () => {
expect(findActionButton().attributes('href')).toBe(url);
});
it('has the correct text', () => {
expect(findActionButton().text()).toBe('Create Jira issue');
});
});
describe('can only create merge request', () => { describe('can only create merge request', () => {
beforeEach(() => { beforeEach(() => {
const propsData = { const propsData = {
...@@ -56,11 +87,11 @@ describe('Security Reports modal footer', () => { ...@@ -56,11 +87,11 @@ describe('Security Reports modal footer', () => {
it('only renders the create merge request button', () => { it('only renders the create merge request button', () => {
expect(wrapper.vm.actionButtons[0].name).toBe('Resolve with merge request'); expect(wrapper.vm.actionButtons[0].name).toBe('Resolve with merge request');
expect(wrapper.find('.js-action-button').text()).toBe('Resolve with merge request'); expect(findActionButton().text()).toBe('Resolve with merge request');
}); });
it('emits createMergeRequest when create merge request button is clicked', () => { it('emits createMergeRequest when create merge request button is clicked', () => {
wrapper.find('.js-action-button').trigger('click'); findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().createMergeRequest).toBeTruthy(); expect(wrapper.emitted().createMergeRequest).toBeTruthy();
...@@ -79,11 +110,11 @@ describe('Security Reports modal footer', () => { ...@@ -79,11 +110,11 @@ describe('Security Reports modal footer', () => {
it('renders the download patch button', () => { it('renders the download patch button', () => {
expect(wrapper.vm.actionButtons[0].name).toBe('Download patch to resolve'); expect(wrapper.vm.actionButtons[0].name).toBe('Download patch to resolve');
expect(wrapper.find('.js-action-button').text()).toBe('Download patch to resolve'); expect(findActionButton().text()).toBe('Download patch to resolve');
}); });
it('emits downloadPatch when download patch button is clicked', () => { it('emits downloadPatch when download patch button is clicked', () => {
wrapper.find('.js-action-button').trigger('click'); findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().downloadPatch).toBeTruthy(); expect(wrapper.emitted().downloadPatch).toBeTruthy();
......
...@@ -108,6 +108,8 @@ describe('Security Reports modal', () => { ...@@ -108,6 +108,8 @@ describe('Security Reports modal', () => {
}); });
describe('with merge request created', () => { describe('with merge request created', () => {
const findActionButton = () => wrapper.find('[data-testid=create-issue-button]');
it('renders the issue button as a single button', (done) => { it('renders the issue button as a single button', (done) => {
const propsData = { const propsData = {
modal: createState().modal, modal: createState().modal,
...@@ -122,11 +124,9 @@ describe('Security Reports modal', () => { ...@@ -122,11 +124,9 @@ describe('Security Reports modal', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(wrapper.find('.js-split-button').exists()).toBe(false); expect(wrapper.find('.js-split-button').exists()).toBe(false);
expect(wrapper.find('.js-action-button').exists()).toBe(true); expect(findActionButton().exists()).toBe(true);
expect(wrapper.find('.js-action-button').text()).not.toContain( expect(findActionButton().text()).not.toContain('Resolve with merge request');
'Resolve with merge request', expect(findActionButton().text()).toContain('Create issue');
);
expect(wrapper.find('.js-action-button').text()).toContain('Create issue');
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
......
import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import SplitButton from 'ee/vue_shared/security_reports/components/split_button.vue'; import SplitButton from 'ee/vue_shared/security_reports/components/split_button.vue';
import * as urlUtility from '~/lib/utils/url_utility';
const buttons = [ const buttons = [
{ {
...@@ -64,6 +65,20 @@ describe('Split Button', () => { ...@@ -64,6 +65,20 @@ describe('Split Button', () => {
expect(wrapper.emitted('button1Action')).toHaveLength(1); expect(wrapper.emitted('button1Action')).toHaveLength(1);
}); });
it('visits url if href property is specified', () => {
const spy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({});
const href = 'https://gitlab.com';
createComponent({
buttons: [{ ...buttons.slice(0), action: undefined, href }],
});
findDropdown().vm.$emit('click');
expect(wrapper.emitted('button1Action')).toBeUndefined();
expect(spy).toHaveBeenCalledWith(href, true);
});
it('renders a correct amount of dropdown items', () => { it('renders a correct amount of dropdown items', () => {
createComponent({ createComponent({
buttons, buttons,
......
...@@ -34137,6 +34137,9 @@ msgstr "" ...@@ -34137,6 +34137,9 @@ msgstr ""
msgid "ciReport|Coverage fuzzing" msgid "ciReport|Coverage fuzzing"
msgstr "" msgstr ""
msgid "ciReport|Create Jira issue"
msgstr ""
msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually." msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually."
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