Commit b07b1d99 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch...

Merge branch '273778-integrations-should-have-a-way-to-be-reset-unlinked-from-inheriting-projects' into 'master'

Add reset button and modal to mass integrations

See merge request gitlab-org/gitlab!47188
parents c221a8e4 026baf50
......@@ -8,14 +8,14 @@ export default {
GlModal,
},
computed: {
...mapGetters(['isSavingOrTesting']),
...mapGetters(['isDisabled']),
primaryProps() {
return {
text: __('Save'),
attributes: [
{ variant: 'success' },
{ category: 'primary' },
{ disabled: this.isSavingOrTesting },
{ disabled: this.isDisabled },
],
};
},
......
......@@ -12,6 +12,7 @@ import JiraIssuesFields from './jira_issues_fields.vue';
import TriggerFields from './trigger_fields.vue';
import DynamicField from './dynamic_field.vue';
import ConfirmationModal from './confirmation_modal.vue';
import ResetConfirmationModal from './reset_confirmation_modal.vue';
export default {
name: 'IntegrationForm',
......@@ -23,6 +24,7 @@ export default {
TriggerFields,
DynamicField,
ConfirmationModal,
ResetConfirmationModal,
GlButton,
},
directives: {
......@@ -30,8 +32,8 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapGetters(['currentKey', 'propsSource', 'isSavingOrTesting']),
...mapState(['defaultState', 'override', 'isSaving', 'isTesting']),
...mapGetters(['currentKey', 'propsSource', 'isDisabled']),
...mapState(['defaultState', 'override', 'isSaving', 'isTesting', 'isResetting']),
isEditable() {
return this.propsSource.editable;
},
......@@ -47,9 +49,12 @@ export default {
showJiraIssuesFields() {
return this.isJira && this.glFeatures.jiraIssuesIntegration;
},
showReset() {
return this.isInstanceOrGroupLevel && this.propsSource.resetPath;
},
},
methods: {
...mapActions(['setOverride', 'setIsSaving', 'setIsTesting']),
...mapActions(['setOverride', 'setIsSaving', 'setIsTesting', 'setIsResetting']),
onSaveClick() {
this.setIsSaving(true);
eventHub.$emit('saveIntegration');
......@@ -58,6 +63,7 @@ export default {
this.setIsTesting(true);
eventHub.$emit('testIntegration');
},
onResetClick() {},
},
};
</script>
......@@ -100,7 +106,7 @@ export default {
category="primary"
variant="success"
:loading="isSaving"
:disabled="isSavingOrTesting"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
>
{{ __('Save changes') }}
......@@ -113,7 +119,7 @@ export default {
variant="success"
type="submit"
:loading="isSaving"
:disabled="isSavingOrTesting"
:disabled="isDisabled"
data-qa-selector="save_changes_button"
@click.prevent="onSaveClick"
>
......@@ -123,13 +129,27 @@ export default {
<gl-button
v-if="propsSource.canTest"
:loading="isTesting"
:disabled="isSavingOrTesting"
:disabled="isDisabled"
:href="propsSource.testPath"
@click.prevent="onTestClick"
>
{{ __('Test settings') }}
</gl-button>
<template v-if="showReset">
<gl-button
v-gl-modal.confirmResetIntegration
category="secondary"
variant="default"
:loading="isResetting"
:disabled="isDisabled"
data-testid="reset-button"
>
{{ __('Reset') }}
</gl-button>
<reset-confirmation-modal @reset="onResetClick" />
</template>
<gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button>
</div>
</div>
......
<script>
import { mapGetters } from 'vuex';
import { GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlModal,
},
computed: {
...mapGetters(['isDisabled']),
primaryProps() {
return {
text: __('Reset'),
attributes: [
{ variant: 'warning' },
{ category: 'primary' },
{ disabled: this.isDisabled },
],
};
},
cancelProps() {
return {
text: __('Cancel'),
};
},
},
methods: {
onReset() {
this.$emit('reset');
},
},
};
</script>
<template>
<gl-modal
modal-id="confirmResetIntegration"
size="sm"
:title="s__('Integrations|Reset integration?')"
:action-primary="primaryProps"
:action-cancel="cancelProps"
@primary="onReset"
>
<p>
{{
s__(
'Integrations|Resetting this integration will clear the settings and deactivate this integration.',
)
}}
</p>
<p>
{{ s__('Integrations|All projects inheriting these settings will also be reset.') }}
</p>
<p class="gl-mb-0">
{{ s__('Integrations|Projects using custom settings will not be affected.') }}
</p>
</gl-modal>
</template>
......@@ -26,6 +26,7 @@ function parseDatasetToProps(data) {
integrationLevel,
cancelPath,
testPath,
resetPath,
...booleanAttributes
} = data;
const {
......@@ -49,6 +50,7 @@ function parseDatasetToProps(data) {
editable,
canTest,
testPath,
resetPath,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
......
......@@ -3,3 +3,5 @@ import * as types from './mutation_types';
export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override);
export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING, isSaving);
export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting);
export const setIsResetting = ({ commit }, isResetting) =>
commit(types.SET_IS_RESETTING, isResetting);
export const isInheriting = state => (state.defaultState === null ? false : !state.override);
export const isSavingOrTesting = state => state.isSaving || state.isTesting;
export const isDisabled = state => state.isSaving || state.isTesting || state.isResetting;
export const propsSource = (state, getters) =>
getters.isInheriting ? state.defaultState : state.customState;
......
export const SET_OVERRIDE = 'SET_OVERRIDE';
export const SET_IS_SAVING = 'SET_IS_SAVING';
export const SET_IS_TESTING = 'SET_IS_TESTING';
export const SET_IS_RESETTING = 'SET_IS_RESETTING';
......@@ -10,4 +10,7 @@ export default {
[types.SET_IS_TESTING](state, isTesting) {
state.isTesting = isTesting;
},
[types.SET_IS_RESETTING](state, isResetting) {
state.isResetting = isResetting;
},
};
......@@ -7,5 +7,6 @@ export default ({ defaultState = null, customState = {} } = {}) => {
customState,
isSaving: false,
isTesting: false,
isResetting: false,
};
};
......@@ -93,7 +93,8 @@ module ServicesHelper
editable: integration.editable?.to_s,
cancel_path: scoped_integrations_path,
can_test: integration.can_test?.to_s,
test_path: scoped_test_integration_path(integration)
test_path: scoped_test_integration_path(integration),
reset_path: ''
}
end
......
......@@ -14621,6 +14621,9 @@ msgstr ""
msgid "Integrations|All details"
msgstr ""
msgid "Integrations|All projects inheriting these settings will also be reset."
msgstr ""
msgid "Integrations|Comment detail:"
msgstr ""
......@@ -14654,9 +14657,18 @@ msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
msgid "Integrations|Projects using custom settings will not be affected."
msgstr ""
msgid "Integrations|Projects using custom settings will not be impacted unless the project owner chooses to use parent level defaults."
msgstr ""
msgid "Integrations|Reset integration?"
msgstr ""
msgid "Integrations|Resetting this integration will clear the settings and deactivate this integration."
msgstr ""
msgid "Integrations|Return to GitLab for Jira"
msgstr ""
......@@ -23062,6 +23074,9 @@ msgstr ""
msgid "Resend it"
msgstr ""
msgid "Reset"
msgstr ""
msgid "Reset authorization key"
msgstr ""
......
......@@ -5,6 +5,7 @@ import IntegrationForm from '~/integrations/edit/components/integration_form.vue
import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue';
import ActiveCheckbox from '~/integrations/edit/components/active_checkbox.vue';
import ConfirmationModal from '~/integrations/edit/components/confirmation_modal.vue';
import ResetConfirmationModal from '~/integrations/edit/components/reset_confirmation_modal.vue';
import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue';
import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue';
import TriggerFields from '~/integrations/edit/components/trigger_fields.vue';
......@@ -44,6 +45,8 @@ describe('IntegrationForm', () => {
const findOverrideDropdown = () => wrapper.find(OverrideDropdown);
const findActiveCheckbox = () => wrapper.find(ActiveCheckbox);
const findConfirmationModal = () => wrapper.find(ConfirmationModal);
const findResetConfirmationModal = () => wrapper.find(ResetConfirmationModal);
const findResetButton = () => wrapper.find('[data-testid="reset-button"]');
const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields);
const findJiraIssuesFields = () => wrapper.find(JiraIssuesFields);
const findTriggerFields = () => wrapper.find(TriggerFields);
......@@ -75,6 +78,29 @@ describe('IntegrationForm', () => {
expect(findConfirmationModal().exists()).toBe(true);
});
describe('resetPath is empty', () => {
it('does not render ResetConfirmationModal and button', () => {
createComponent({
integrationLevel: integrationLevels.INSTANCE,
});
expect(findResetButton().exists()).toBe(false);
expect(findResetConfirmationModal().exists()).toBe(false);
});
});
describe('resetPath is present', () => {
it('renders ResetConfirmationModal and button', () => {
createComponent({
integrationLevel: integrationLevels.INSTANCE,
resetPath: 'resetPath',
});
expect(findResetButton().exists()).toBe(true);
expect(findResetConfirmationModal().exists()).toBe(true);
});
});
});
describe('integrationLevel is group', () => {
......@@ -85,6 +111,29 @@ describe('IntegrationForm', () => {
expect(findConfirmationModal().exists()).toBe(true);
});
describe('resetPath is empty', () => {
it('does not render ResetConfirmationModal and button', () => {
createComponent({
integrationLevel: integrationLevels.GROUP,
});
expect(findResetButton().exists()).toBe(false);
expect(findResetConfirmationModal().exists()).toBe(false);
});
});
describe('resetPath is present', () => {
it('renders ResetConfirmationModal and button', () => {
createComponent({
integrationLevel: integrationLevels.GROUP,
resetPath: 'resetPath',
});
expect(findResetButton().exists()).toBe(true);
expect(findResetConfirmationModal().exists()).toBe(true);
});
});
});
describe('integrationLevel is project', () => {
......@@ -95,6 +144,16 @@ describe('IntegrationForm', () => {
expect(findConfirmationModal().exists()).toBe(false);
});
it('does not render ResetConfirmationModal and button', () => {
createComponent({
integrationLevel: 'project',
resetPath: 'resetPath',
});
expect(findResetButton().exists()).toBe(false);
expect(findResetConfirmationModal().exists()).toBe(false);
});
});
describe('type is "slack"', () => {
......
import testAction from 'helpers/vuex_action_helper';
import createState from '~/integrations/edit/store/state';
import { setOverride } from '~/integrations/edit/store/actions';
import {
setOverride,
setIsSaving,
setIsTesting,
setIsResetting,
} from '~/integrations/edit/store/actions';
import * as types from '~/integrations/edit/store/mutation_types';
describe('Integration form store actions', () => {
......@@ -15,4 +20,24 @@ describe('Integration form store actions', () => {
return testAction(setOverride, true, state, [{ type: types.SET_OVERRIDE, payload: true }]);
});
});
describe('setIsSaving', () => {
it('should commit isSaving mutation', () => {
return testAction(setIsSaving, true, state, [{ type: types.SET_IS_SAVING, payload: true }]);
});
});
describe('setIsTesting', () => {
it('should commit isTesting mutation', () => {
return testAction(setIsTesting, true, state, [{ type: types.SET_IS_TESTING, payload: true }]);
});
});
describe('setIsResetting', () => {
it('should commit isResetting mutation', () => {
return testAction(setIsResetting, true, state, [
{ type: types.SET_IS_RESETTING, payload: true },
]);
});
});
});
import { currentKey, isInheriting, propsSource } from '~/integrations/edit/store/getters';
import {
currentKey,
isInheriting,
isDisabled,
propsSource,
} from '~/integrations/edit/store/getters';
import createState from '~/integrations/edit/store/state';
import mutations from '~/integrations/edit/store/mutations';
import * as types from '~/integrations/edit/store/mutation_types';
import { mockIntegrationProps } from '../mock_data';
describe('Integration form store getters', () => {
......@@ -45,6 +52,29 @@ describe('Integration form store getters', () => {
});
});
describe('isDisabled', () => {
it.each`
isSaving | isTesting | isResetting | expected
${false} | ${false} | ${false} | ${false}
${true} | ${false} | ${false} | ${true}
${false} | ${true} | ${false} | ${true}
${false} | ${false} | ${true} | ${true}
${false} | ${true} | ${true} | ${true}
${true} | ${false} | ${true} | ${true}
${true} | ${true} | ${false} | ${true}
${true} | ${true} | ${true} | ${true}
`(
'when isSaving = $isSaving, isTesting = $isTesting, isResetting = $isResetting then isDisabled = $expected',
({ isSaving, isTesting, isResetting, expected }) => {
mutations[types.SET_IS_SAVING](state, isSaving);
mutations[types.SET_IS_TESTING](state, isTesting);
mutations[types.SET_IS_RESETTING](state, isResetting);
expect(isDisabled(state)).toBe(expected);
},
);
});
describe('propsSource', () => {
beforeEach(() => {
state.defaultState = defaultState;
......
......@@ -16,4 +16,28 @@ describe('Integration form store mutations', () => {
expect(state.override).toBe(true);
});
});
describe(`${types.SET_IS_SAVING}`, () => {
it('sets isSaving', () => {
mutations[types.SET_IS_SAVING](state, true);
expect(state.isSaving).toBe(true);
});
});
describe(`${types.SET_IS_TESTING}`, () => {
it('sets isTesting', () => {
mutations[types.SET_IS_TESTING](state, true);
expect(state.isTesting).toBe(true);
});
});
describe(`${types.SET_IS_RESETTING}`, () => {
it('sets isResetting', () => {
mutations[types.SET_IS_RESETTING](state, true);
expect(state.isResetting).toBe(true);
});
});
});
......@@ -7,6 +7,7 @@ describe('Integration form state factory', () => {
customState: {},
isSaving: false,
isTesting: false,
isResetting: false,
override: false,
});
});
......
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