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.
import { s__ } from '~/locale';
export default {
name: 'ModalFooter',
components: {
DismissButton,
GlButton,
......@@ -58,15 +57,26 @@ export default {
type: Boolean,
required: true,
},
vulnerability: {
type: Object,
required: true,
},
},
computed: {
createIssueButtonText() {
return this.vulnerability.create_jira_issue_url
? s__('ciReport|Create Jira issue')
: s__('ciReport|Create issue');
},
actionButtons() {
const buttons = [];
const issueButton = {
name: s__('ciReport|Create issue'),
name: this.createIssueButtonText,
tagline: s__('ciReport|Investigate this vulnerability by creating an issue'),
isLoading: this.isCreatingIssue,
action: 'createNewIssue',
action: this.vulnerability.create_jira_issue_url ? undefined : 'createNewIssue',
href: this.vulnerability.create_jira_issue_url,
};
const MRButton = {
name: s__('ciReport|Resolve with merge request'),
......@@ -128,11 +138,13 @@ export default {
<gl-button
v-else-if="actionButtons.length > 0"
:loading="actionButtons[0].isLoading"
:disabled="actionButtons[0].isLoading || disabled"
:disabled="disabled"
variant="success"
category="secondary"
class="js-action-button"
data-testid="create-issue-button"
data-qa-selector="create_issue_button"
:target="actionButtons[0].href ? '_blank' : undefined"
:href="actionButtons[0].href"
@click="$emit(actionButtons[0].action)"
>
{{ __(actionButtons[0].name) }}
......
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
export default {
components: {
......@@ -28,7 +29,11 @@ export default {
this.selectedButton = button;
},
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 {
:disabled="disabled"
variant="success"
:text="selectedButton.name"
:href="selectedButton.href"
split
@click="handleClick"
>
......
......@@ -13,11 +13,14 @@ describe('Security Reports modal footer', () => {
isCreatingIssue: false,
isDismissingVulnerability: false,
isCreatingMergeRequest: false,
vulnerability: {},
...propsData,
},
});
};
const findActionButton = () => wrapper.find('[data-testid=create-issue-button]');
describe('can only create issue', () => {
beforeEach(() => {
const propsData = {
......@@ -33,11 +36,11 @@ describe('Security Reports modal footer', () => {
it('only renders the create issue button', () => {
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', () => {
wrapper.find('.js-action-button').trigger('click');
findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().createNewIssue).toBeTruthy();
......@@ -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', () => {
beforeEach(() => {
const propsData = {
......@@ -56,11 +87,11 @@ describe('Security Reports modal footer', () => {
it('only renders the create merge request button', () => {
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', () => {
wrapper.find('.js-action-button').trigger('click');
findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().createMergeRequest).toBeTruthy();
......@@ -79,11 +110,11 @@ describe('Security Reports modal footer', () => {
it('renders the download patch button', () => {
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', () => {
wrapper.find('.js-action-button').trigger('click');
findActionButton().trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().downloadPatch).toBeTruthy();
......
......@@ -108,6 +108,8 @@ describe('Security Reports modal', () => {
});
describe('with merge request created', () => {
const findActionButton = () => wrapper.find('[data-testid=create-issue-button]');
it('renders the issue button as a single button', (done) => {
const propsData = {
modal: createState().modal,
......@@ -122,11 +124,9 @@ describe('Security Reports modal', () => {
Vue.nextTick()
.then(() => {
expect(wrapper.find('.js-split-button').exists()).toBe(false);
expect(wrapper.find('.js-action-button').exists()).toBe(true);
expect(wrapper.find('.js-action-button').text()).not.toContain(
'Resolve with merge request',
);
expect(wrapper.find('.js-action-button').text()).toContain('Create issue');
expect(findActionButton().exists()).toBe(true);
expect(findActionButton().text()).not.toContain('Resolve with merge request');
expect(findActionButton().text()).toContain('Create issue');
done();
})
.catch(done.fail);
......
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SplitButton from 'ee/vue_shared/security_reports/components/split_button.vue';
import * as urlUtility from '~/lib/utils/url_utility';
const buttons = [
{
......@@ -64,6 +65,20 @@ describe('Split Button', () => {
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', () => {
createComponent({
buttons,
......
......@@ -34137,6 +34137,9 @@ msgstr ""
msgid "ciReport|Coverage fuzzing"
msgstr ""
msgid "ciReport|Create Jira issue"
msgstr ""
msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually."
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