From ba44d5ef19b9af27c312af6af3011755f5df72b8 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Sun, 24 Jan 2021 18:09:28 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../edit/components/jira_issues_fields.vue | 41 +++++++++- .../javascripts/integrations/edit/index.js | 6 ++ .../integrations/edit/store/actions.js | 15 ++++ .../integrations/edit/store/mutation_types.js | 4 + .../integrations/edit/store/mutations.js | 9 +++ .../integrations/edit/store/state.js | 3 + .../integrations/integration_settings_form.js | 53 ++++++++++++- locale/gitlab.pot | 27 +++++++ .../components/jira_issues_fields_spec.js | 69 ++++++++++++---- .../integrations/edit/store/actions_spec.js | 33 ++++++++ .../integrations/edit/store/mutations_spec.js | 26 ++++++ .../integrations/edit/store/state_spec.js | 3 + .../integration_settings_form_spec.js | 79 +++++++++++++++++++ 13 files changed, 349 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue index 1baa2b440b0..f8a44f0c2a2 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue @@ -8,6 +8,7 @@ import { GlButton, GlCard, } from '@gitlab/ui'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../event_hub'; export default { @@ -20,18 +21,36 @@ export default { GlLink, GlButton, GlCard, + JiraIssueCreationVulnerabilities: () => + import('ee_component/integrations/edit/components/jira_issue_creation_vulnerabilities.vue'), }, + mixins: [glFeatureFlagsMixin()], props: { showJiraIssuesIntegration: { type: Boolean, required: false, default: false, }, + showJiraVulnerabilitiesIntegration: { + type: Boolean, + required: false, + default: false, + }, initialEnableJiraIssues: { type: Boolean, required: false, default: null, }, + initialEnableJiraVulnerabilities: { + type: Boolean, + required: false, + default: false, + }, + initialVulnerabilitiesIssuetype: { + type: String, + required: false, + default: '', + }, initialProjectKey: { type: String, required: false, @@ -45,12 +64,12 @@ export default { upgradePlanPath: { type: String, required: false, - default: null, + default: '', }, editProjectPath: { type: String, required: false, - default: null, + default: '', }, }, data() { @@ -64,6 +83,13 @@ export default { validProjectKey() { return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated; }, + showJiraVulnerabilitiesOptions() { + return ( + this.enableJiraIssues && + this.showJiraVulnerabilitiesIntegration && + this.glFeatures.jiraForVulnerabilities + ); + }, }, created() { eventHub.$on('validateForm', this.validateForm); @@ -75,6 +101,9 @@ export default { validateForm() { this.validated = true; }, + getJiraIssueTypes() { + eventHub.$emit('getJiraIssueTypes'); + }, }, }; @@ -105,6 +134,14 @@ export default { }} + {{ __('This is a Premium feature') }} diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js index 4d63bd5ed3a..7a4e8b11bb0 100644 --- a/app/assets/javascripts/integrations/edit/index.js +++ b/app/assets/javascripts/integrations/edit/index.js @@ -27,6 +27,7 @@ function parseDatasetToProps(data) { cancelPath, testPath, resetPath, + vulnerabilitiesIssuetype, ...booleanAttributes } = data; const { @@ -38,7 +39,9 @@ function parseDatasetToProps(data) { mergeRequestEvents, enableComments, showJiraIssuesIntegration, + showJiraVulnerabilitiesIntegration, enableJiraIssues, + enableJiraVulnerabilities, gitlabIssuesEnabled, } = parseBooleanInData(booleanAttributes); @@ -59,7 +62,10 @@ function parseDatasetToProps(data) { }, jiraIssuesProps: { showJiraIssuesIntegration, + showJiraVulnerabilitiesIntegration, initialEnableJiraIssues: enableJiraIssues, + initialEnableJiraVulnerabilities: enableJiraVulnerabilities, + initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype, initialProjectKey: projectKey, gitlabIssuesEnabled, upgradePlanPath, diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js index 421917b720a..400397c050c 100644 --- a/app/assets/javascripts/integrations/edit/store/actions.js +++ b/app/assets/javascripts/integrations/edit/store/actions.js @@ -26,3 +26,18 @@ export const fetchResetIntegration = ({ dispatch, getters }) => { .then(() => dispatch('receiveResetIntegrationSuccess')) .catch(() => dispatch('receiveResetIntegrationError')); }; + +export const requestJiraIssueTypes = ({ commit }) => { + commit(types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, ''); + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, true); +}; +export const receiveJiraIssueTypesSuccess = ({ commit }, issueTypes = []) => { + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, false); + commit(types.SET_JIRA_ISSUE_TYPES, issueTypes); +}; + +export const receiveJiraIssueTypesError = ({ commit }, errorMessage) => { + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, false); + commit(types.SET_JIRA_ISSUE_TYPES, []); + commit(types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, errorMessage); +}; diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js index 54928148b22..c681056a515 100644 --- a/app/assets/javascripts/integrations/edit/store/mutation_types.js +++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js @@ -3,5 +3,9 @@ export const SET_IS_SAVING = 'SET_IS_SAVING'; export const SET_IS_TESTING = 'SET_IS_TESTING'; export const SET_IS_RESETTING = 'SET_IS_RESETTING'; +export const SET_IS_LOADING_JIRA_ISSUE_TYPES = 'SET_IS_LOADING_JIRA_ISSUE_TYPES'; +export const SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE = 'SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE'; +export const SET_JIRA_ISSUE_TYPES = 'SET_JIRA_ISSUE_TYPES'; + export const REQUEST_RESET_INTEGRATION = 'REQUEST_RESET_INTEGRATION'; export const RECEIVE_RESET_INTEGRATION_ERROR = 'RECEIVE_RESET_INTEGRATION_ERROR'; diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js index 826757e665b..279df1b9266 100644 --- a/app/assets/javascripts/integrations/edit/store/mutations.js +++ b/app/assets/javascripts/integrations/edit/store/mutations.js @@ -19,4 +19,13 @@ export default { [types.RECEIVE_RESET_INTEGRATION_ERROR](state) { state.isResetting = false; }, + [types.SET_JIRA_ISSUE_TYPES](state, jiraIssueTypes) { + state.jiraIssueTypes = jiraIssueTypes; + }, + [types.SET_IS_LOADING_JIRA_ISSUE_TYPES](state, isLoadingJiraIssueTypes) { + state.isLoadingJiraIssueTypes = isLoadingJiraIssueTypes; + }, + [types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE](state, errorMessage) { + state.loadingJiraIssueTypesErrorMessage = errorMessage; + }, }; diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js index aae3db1583f..1c0b274e4ef 100644 --- a/app/assets/javascripts/integrations/edit/store/state.js +++ b/app/assets/javascripts/integrations/edit/store/state.js @@ -8,5 +8,8 @@ export default ({ defaultState = null, customState = {} } = {}) => { isSaving: false, isTesting: false, isResetting: false, + isLoadingJiraIssueTypes: false, + loadingJiraIssueTypesErrorMessage: '', + jiraIssueTypes: [], }; }; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 861655a6a64..486dc50d873 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -33,6 +33,12 @@ export default class IntegrationSettingsForm { eventHub.$on('saveIntegration', () => { this.saveIntegration(); }); + eventHub.$on('getJiraIssueTypes', () => { + // eslint-disable-next-line no-jquery/no-serialize + this.getJiraIssueTypes(this.$form.serialize()); + }); + + eventHub.$emit('formInitialized'); } saveIntegration() { @@ -79,16 +85,59 @@ export default class IntegrationSettingsForm { } } + /** + * Get a list of Jira issue types for the currently configured project + * + * @param {string} formData - URL encoded string containing the form data + * + * @return {Promise} + */ + getJiraIssueTypes(formData) { + const { + $store: { dispatch }, + } = this.vue; + + dispatch('requestJiraIssueTypes'); + + return this.fetchTestSettings(formData) + .then( + ({ + data: { + issuetypes, + error, + message = s__('Integrations|Connection failed. Please check your settings.'), + }, + }) => { + if (error || !issuetypes?.length) { + eventHub.$emit('validateForm'); + throw new Error(message); + } + + dispatch('receiveJiraIssueTypesSuccess', issuetypes); + }, + ) + .catch(({ message = __('Something went wrong on our end.') }) => { + dispatch('receiveJiraIssueTypesError', message); + }); + } + + /** + * Send request to the test endpoint which checks if the current config is valid + */ + fetchTestSettings(formData) { + return axios.put(this.testEndPoint, formData); + } + /** * Test Integration config */ testSettings(formData) { - return axios - .put(this.testEndPoint, formData) + return this.fetchTestSettings(formData) .then(({ data }) => { if (data.error) { toast(`${data.message} ${data.service_response}`); } else { + this.vue.$store.dispatch('receiveJiraIssueTypesSuccess', data.issuetypes); toast(s__('Integrations|Connection successful.')); } }) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 264272aadb9..40accd34767 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16091,15 +16091,27 @@ msgstr "" msgid "JiraService|%{user_link} mentioned this issue in %{entity_link} of %{project_link}%{branch}:{quote}%{entity_message}{quote}" msgstr "" +msgid "JiraService|An error occured while fetching issue list" +msgstr "" + +msgid "JiraService|Define the type of Jira issue to create from a vulnerability." +msgstr "" + msgid "JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they won’t otherwise be used." msgstr "" msgid "JiraService|Enable Jira issues" msgstr "" +msgid "JiraService|Enable Jira issues creation from vulnerabilities" +msgstr "" + msgid "JiraService|Events for %{noteable_model_name} are disabled." msgstr "" +msgid "JiraService|Fetch issue types for this Jira project" +msgstr "" + msgid "JiraService|For example, 12, 24" msgstr "" @@ -16109,6 +16121,9 @@ msgstr "" msgid "JiraService|Issue List" msgstr "" +msgid "JiraService|Issues created from vulnerabilities in this project will be Jira issues, even if GitLab issues are enabled." +msgstr "" + msgid "JiraService|Jira API URL" msgstr "" @@ -16124,6 +16139,9 @@ msgstr "" msgid "JiraService|Jira issue tracker" msgstr "" +msgid "JiraService|Jira issue type" +msgstr "" + msgid "JiraService|Jira project key" msgstr "" @@ -16136,6 +16154,15 @@ msgstr "" msgid "JiraService|Password or API token" msgstr "" +msgid "JiraService|Project key changed, refresh list" +msgstr "" + +msgid "JiraService|Project key is required to generate issue types" +msgstr "" + +msgid "JiraService|Select issue type" +msgstr "" + msgid "JiraService|Set transition IDs for Jira workflow transitions. %{link_start}Learn more%{link_end}" msgstr "" diff --git a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js index eaeed2703d1..b866cf8bc03 100644 --- a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js @@ -3,18 +3,22 @@ import { mount } from '@vue/test-utils'; import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; +import eventHub from '~/integrations/edit/event_hub'; describe('JiraIssuesFields', () => { let wrapper; const defaultProps = { - showJiraIssuesIntegration: true, editProjectPath: '/edit', + showJiraIssuesIntegration: true, + showJiraVulnerabilitiesIntegration: true, }; - const createComponent = (props) => { + const createComponent = ({ props, ...options } = {}) => { wrapper = mount(JiraIssuesFields, { propsData: { ...defaultProps, ...props }, + stubs: ['jira-issue-creation-vulnerabilities'], + ...options, }); }; @@ -28,11 +32,14 @@ describe('JiraIssuesFields', () => { const findEnableCheckbox = () => wrapper.find(GlFormCheckbox); const findProjectKey = () => wrapper.find(GlFormInput); const expectedBannerText = 'This is a Premium feature'; + const findJiraForVulnerabilities = () => wrapper.find('[data-testid="jira-for-vulnerabilities"]'); + const setEnableCheckbox = async (isEnabled = true) => + findEnableCheckbox().vm.$emit('input', isEnabled); describe('template', () => { describe('upgrade banner for non-Premium user', () => { beforeEach(() => { - createComponent({ initialProjectKey: '', showJiraIssuesIntegration: false }); + createComponent({ props: { initialProjectKey: '', showJiraIssuesIntegration: false } }); }); it('shows upgrade banner', () => { @@ -47,7 +54,7 @@ describe('JiraIssuesFields', () => { describe('Enable Jira issues checkbox', () => { beforeEach(() => { - createComponent({ initialProjectKey: '' }); + createComponent({ props: { initialProjectKey: '' } }); }); it('does not show upgrade banner', () => { @@ -69,20 +76,16 @@ describe('JiraIssuesFields', () => { }); describe('on enable issues', () => { - it('enables project_key input', () => { - findEnableCheckbox().vm.$emit('input', true); + it('enables project_key input', async () => { + await setEnableCheckbox(true); - return wrapper.vm.$nextTick().then(() => { - expect(findProjectKey().attributes('disabled')).toBeUndefined(); - }); + expect(findProjectKey().attributes('disabled')).toBeUndefined(); }); - it('requires project_key input', () => { - findEnableCheckbox().vm.$emit('input', true); + it('requires project_key input', async () => { + await setEnableCheckbox(true); - return wrapper.vm.$nextTick().then(() => { - expect(findProjectKey().attributes('required')).toBe('required'); - }); + expect(findProjectKey().attributes('required')).toBe('required'); }); }); }); @@ -103,10 +106,46 @@ describe('JiraIssuesFields', () => { }); it('does not contain warning when GitLab issues is disabled', () => { - createComponent({ gitlabIssuesEnabled: false }); + createComponent({ props: { gitlabIssuesEnabled: false } }); expect(wrapper.text()).not.toContain(expectedText); }); }); + + describe('Vulnerabilities creation', () => { + beforeEach(() => { + createComponent({ provide: { glFeatures: { jiraForVulnerabilities: true } } }); + }); + + it.each([true, false])( + 'shows the jira-vulnerabilities component correctly when jira issues enables is set to "%s"', + async (hasJiraIssuesEnabled) => { + await setEnableCheckbox(hasJiraIssuesEnabled); + + expect(findJiraForVulnerabilities().exists()).toBe(hasJiraIssuesEnabled); + }, + ); + + it('emits "getJiraIssueTypes" to the eventHub when the jira-vulnerabilities component requests to fetch issue types', async () => { + const eventHubEmitSpy = jest.spyOn(eventHub, '$emit'); + + await setEnableCheckbox(true); + await findJiraForVulnerabilities().vm.$emit('request-get-issue-types'); + + expect(eventHubEmitSpy).toHaveBeenCalledWith('getJiraIssueTypes'); + }); + + describe('with "jiraForVulnerabilities" feature flag disabled', () => { + beforeEach(async () => { + createComponent({ + provide: { glFeatures: { jiraForVulnerabilities: false } }, + }); + }); + + it('does not show section', () => { + expect(findJiraForVulnerabilities().exists()).toBe(false); + }); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/store/actions_spec.js b/spec/frontend/integrations/edit/store/actions_spec.js index 1ff881c265d..4b7060fae55 100644 --- a/spec/frontend/integrations/edit/store/actions_spec.js +++ b/spec/frontend/integrations/edit/store/actions_spec.js @@ -9,6 +9,9 @@ import { requestResetIntegration, receiveResetIntegrationSuccess, receiveResetIntegrationError, + requestJiraIssueTypes, + receiveJiraIssueTypesSuccess, + receiveJiraIssueTypesError, } from '~/integrations/edit/store/actions'; import * as types from '~/integrations/edit/store/mutation_types'; @@ -70,4 +73,34 @@ describe('Integration form store actions', () => { ]); }); }); + + describe('requestJiraIssueTypes', () => { + it('should commit SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE and SET_IS_LOADING_JIRA_ISSUE_TYPES mutations', () => { + return testAction(requestJiraIssueTypes, null, state, [ + { type: types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, payload: '' }, + { type: types.SET_IS_LOADING_JIRA_ISSUE_TYPES, payload: true }, + ]); + }); + }); + + describe('receiveJiraIssueTypesSuccess', () => { + it('should commit SET_IS_LOADING_JIRA_ISSUE_TYPES and SET_JIRA_ISSUE_TYPES mutations', () => { + const issueTypes = ['issue', 'epic']; + return testAction(receiveJiraIssueTypesSuccess, issueTypes, state, [ + { type: types.SET_IS_LOADING_JIRA_ISSUE_TYPES, payload: false }, + { type: types.SET_JIRA_ISSUE_TYPES, payload: issueTypes }, + ]); + }); + }); + + describe('receiveJiraIssueTypesError', () => { + it('should commit SET_IS_LOADING_JIRA_ISSUE_TYPES, SET_JIRA_ISSUE_TYPES and SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE mutations', () => { + const errorMessage = 'something went wrong'; + return testAction(receiveJiraIssueTypesError, errorMessage, state, [ + { type: types.SET_IS_LOADING_JIRA_ISSUE_TYPES, payload: false }, + { type: types.SET_JIRA_ISSUE_TYPES, payload: [] }, + { type: types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, payload: errorMessage }, + ]); + }); + }); }); diff --git a/spec/frontend/integrations/edit/store/mutations_spec.js b/spec/frontend/integrations/edit/store/mutations_spec.js index 81f39adb87f..bf2a51d4dad 100644 --- a/spec/frontend/integrations/edit/store/mutations_spec.js +++ b/spec/frontend/integrations/edit/store/mutations_spec.js @@ -56,4 +56,30 @@ describe('Integration form store mutations', () => { expect(state.isResetting).toBe(false); }); }); + + describe(`${types.SET_JIRA_ISSUE_TYPES}`, () => { + it('sets jiraIssueTypes', () => { + const jiraIssueTypes = ['issue', 'epic']; + mutations[types.SET_JIRA_ISSUE_TYPES](state, jiraIssueTypes); + + expect(state.jiraIssueTypes).toBe(jiraIssueTypes); + }); + }); + + describe(`${types.SET_IS_LOADING_JIRA_ISSUE_TYPES}`, () => { + it.each([true, false])('sets isLoadingJiraIssueTypes to "%s"', (isLoading) => { + mutations[types.SET_IS_LOADING_JIRA_ISSUE_TYPES](state, isLoading); + + expect(state.isLoadingJiraIssueTypes).toBe(isLoading); + }); + }); + + describe(`${types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE}`, () => { + it('sets loadingJiraIssueTypesErrorMessage', () => { + const errorMessage = 'something went wrong'; + mutations[types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE](state, errorMessage); + + expect(state.loadingJiraIssueTypesErrorMessage).toBe(errorMessage); + }); + }); }); diff --git a/spec/frontend/integrations/edit/store/state_spec.js b/spec/frontend/integrations/edit/store/state_spec.js index 4d0f4a1da71..6cd84836395 100644 --- a/spec/frontend/integrations/edit/store/state_spec.js +++ b/spec/frontend/integrations/edit/store/state_spec.js @@ -9,6 +9,9 @@ describe('Integration form state factory', () => { isTesting: false, isResetting: false, override: false, + isLoadingJiraIssueTypes: false, + jiraIssueTypes: [], + loadingJiraIssueTypesErrorMessage: '', }); }); diff --git a/spec/frontend/integrations/integration_settings_form_spec.js b/spec/frontend/integrations/integration_settings_form_spec.js index bba851ad796..373f8f196f9 100644 --- a/spec/frontend/integrations/integration_settings_form_spec.js +++ b/spec/frontend/integrations/integration_settings_form_spec.js @@ -132,4 +132,83 @@ describe('IntegrationSettingsForm', () => { expect(dispatchSpy).toHaveBeenCalledWith('setIsTesting', false); }); }); + + describe('getJiraIssueTypes', () => { + let integrationSettingsForm; + let formData; + let mock; + + beforeEach(() => { + mock = new MockAdaptor(axios); + + jest.spyOn(axios, 'put'); + + integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); + integrationSettingsForm.init(); + + // eslint-disable-next-line no-jquery/no-serialize + formData = integrationSettingsForm.$form.serialize(); + }); + + afterEach(() => { + mock.restore(); + }); + + it('should always dispatch `requestJiraIssueTypes`', async () => { + const dispatchSpy = jest.fn(); + + mock.onPut(integrationSettingsForm.testEndPoint).networkError(); + + integrationSettingsForm.vue.$store = { dispatch: dispatchSpy }; + + await integrationSettingsForm.getJiraIssueTypes(); + + expect(dispatchSpy).toHaveBeenCalledWith('requestJiraIssueTypes'); + }); + + it('should make an ajax request with provided `formData`', async () => { + await integrationSettingsForm.getJiraIssueTypes(formData); + + expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData); + }); + + it('should dispatch `receiveJiraIssueTypesSuccess` with the correct payload if ajax request is successful', async () => { + const mockData = ['ISSUE', 'EPIC']; + const dispatchSpy = jest.fn(); + + mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { + error: false, + issuetypes: mockData, + }); + + integrationSettingsForm.vue.$store = { dispatch: dispatchSpy }; + + await integrationSettingsForm.getJiraIssueTypes(formData); + + expect(dispatchSpy).toHaveBeenCalledWith('receiveJiraIssueTypesSuccess', mockData); + }); + + it.each(['something went wrong', undefined])( + 'should dispatch "receiveJiraIssueTypesError" with a message if the backend responds with error', + async (responseErrorMessage) => { + const defaultErrorMessage = 'Connection failed. Please check your settings.'; + const expectedErrorMessage = responseErrorMessage || defaultErrorMessage; + const dispatchSpy = jest.fn(); + + mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { + error: true, + message: responseErrorMessage, + }); + + integrationSettingsForm.vue.$store = { dispatch: dispatchSpy }; + + await integrationSettingsForm.getJiraIssueTypes(formData); + + expect(dispatchSpy).toHaveBeenCalledWith( + 'receiveJiraIssueTypesError', + expectedErrorMessage, + ); + }, + ); + }); });