Skip to content
代码片段 群组 项目
提交 92c176f1 编辑于 作者: Vitaly Slobodin's avatar Vitaly Slobodin
浏览文件

Merge branch '285458-group-request-approval-update-action' into 'master'

Allow users to save group MR approval settings

See merge request gitlab-org/gitlab!53975
No related branches found
No related tags found
无相关合并请求
显示
161 个添加36 个删除
...@@ -21,7 +21,7 @@ export default { ...@@ -21,7 +21,7 @@ export default {
}, },
computed: { computed: {
...mapState({ ...mapState({
preventAuthorApproval: (state) => state.approvals.preventAuthorApproval, settings: (state) => state.approvals.settings,
isLoading: (state) => state.approvals.isLoading, isLoading: (state) => state.approvals.isLoading,
}), }),
}, },
...@@ -29,7 +29,10 @@ export default { ...@@ -29,7 +29,10 @@ export default {
this.fetchSettings(this.approvalSettingsPath); this.fetchSettings(this.approvalSettingsPath);
}, },
methods: { methods: {
...mapActions(['fetchSettings', 'updatePreventAuthorApproval']), ...mapActions(['fetchSettings', 'updateSettings']),
onSubmit() {
this.updateSettings(this.approvalSettingsPath);
},
}, },
links: { links: {
preventAuthorApprovalDocsPath: helpPagePath( preventAuthorApprovalDocsPath: helpPagePath(
...@@ -48,12 +51,11 @@ export default { ...@@ -48,12 +51,11 @@ export default {
</script> </script>
<template> <template>
<gl-form> <gl-form @submit.prevent="onSubmit">
<gl-form-group> <gl-form-group>
<gl-form-checkbox <gl-form-checkbox
:checked="preventAuthorApproval" v-model="settings.preventAuthorApproval"
data-testid="prevent-author-approval" data-testid="prevent-author-approval"
@input="updatePreventAuthorApproval"
> >
{{ $options.i18n.authorApprovalLabel }} {{ $options.i18n.authorApprovalLabel }}
<gl-link :href="$options.links.preventAuthorApprovalDocsPath" target="_blank"> <gl-link :href="$options.links.preventAuthorApprovalDocsPath" target="_blank">
......
...@@ -34,7 +34,7 @@ export default { ...@@ -34,7 +34,7 @@ export default {
</script> </script>
<template> <template>
<settings-block :default-expanded="defaultExpanded"> <settings-block :default-expanded="defaultExpanded" data-testid="merge-request-approval-settings">
<template #title> {{ $options.i18n.groupSettingsHeader }}</template> <template #title> {{ $options.i18n.groupSettingsHeader }}</template>
<template #description> <template #description>
<gl-sprintf :message="$options.i18n.groupSettingsDescription"> <gl-sprintf :message="$options.i18n.groupSettingsDescription">
......
...@@ -23,6 +23,26 @@ export const fetchSettings = ({ commit }, endpoint) => { ...@@ -23,6 +23,26 @@ export const fetchSettings = ({ commit }, endpoint) => {
}); });
}; };
export const updatePreventAuthorApproval = ({ commit }, preventAuthorApproval) => { export const updateSettings = ({ commit, state }, endpoint) => {
commit(types.UPDATE_PREVENT_AUTHOR_APPROVAL, preventAuthorApproval); const payload = {
allow_author_approval: !state.settings.preventAuthorApproval,
};
commit(types.REQUEST_UPDATE_SETTINGS);
return axios
.put(endpoint, payload)
.then(({ data }) => {
commit(types.UPDATE_SETTINGS_SUCCESS, data);
})
.catch(({ response }) => {
const error = response?.data?.message;
commit(types.UPDATE_SETTINGS_ERROR, error);
createFlash({
message: __('There was an error updating merge request approval settings.'),
captureError: true,
error,
});
});
}; };
...@@ -3,4 +3,6 @@ export * from '../base/mutation_types'; ...@@ -3,4 +3,6 @@ export * from '../base/mutation_types';
export const REQUEST_SETTINGS = 'REQUEST_SETTINGS'; export const REQUEST_SETTINGS = 'REQUEST_SETTINGS';
export const RECEIVE_SETTINGS_SUCCESS = 'RECEIVE_SETTINGS_SUCCESS'; export const RECEIVE_SETTINGS_SUCCESS = 'RECEIVE_SETTINGS_SUCCESS';
export const RECEIVE_SETTINGS_ERROR = 'RECEIVE_SETTINGS_ERROR'; export const RECEIVE_SETTINGS_ERROR = 'RECEIVE_SETTINGS_ERROR';
export const UPDATE_PREVENT_AUTHOR_APPROVAL = 'UPDATE_PREVENT_AUTHOR_APPROVAL'; export const REQUEST_UPDATE_SETTINGS = 'REQUEST_UPDATE_SETTINGS';
export const UPDATE_SETTINGS_SUCCESS = 'UPDATE_SETTINGS_SUCCESS';
export const UPDATE_SETTINGS_ERROR = 'UPDATE_SETTINGS_ERROR';
...@@ -5,13 +5,20 @@ export default { ...@@ -5,13 +5,20 @@ export default {
state.isLoading = true; state.isLoading = true;
}, },
[types.RECEIVE_SETTINGS_SUCCESS](state, data) { [types.RECEIVE_SETTINGS_SUCCESS](state, data) {
state.preventAuthorApproval = !data.allow_author_approval; state.settings.preventAuthorApproval = !data.allow_author_approval;
state.isLoading = false; state.isLoading = false;
}, },
[types.RECEIVE_SETTINGS_ERROR](state) { [types.RECEIVE_SETTINGS_ERROR](state) {
state.isLoading = false; state.isLoading = false;
}, },
[types.UPDATE_PREVENT_AUTHOR_APPROVAL](state, value) { [types.REQUEST_UPDATE_SETTINGS](state) {
state.preventAuthorApproval = value; state.isLoading = true;
},
[types.UPDATE_SETTINGS_SUCCESS](state, data) {
state.settings.preventAuthorApproval = !data.allow_author_approval;
state.isLoading = false;
},
[types.UPDATE_SETTINGS_ERROR](state) {
state.isLoading = false;
}, },
}; };
export default () => ({ export default () => ({
preventAuthorApproval: true, settings: {},
isLoading: false, isLoading: false,
}); });
...@@ -5,13 +5,16 @@ ...@@ -5,13 +5,16 @@
RSpec.describe 'Edit group settings' do RSpec.describe 'Edit group settings' do
include Select2Helper include Select2Helper
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:developer) { create(:user) } let_it_be(:developer) { create(:user) }
let(:group) { create(:group, path: 'foo') } let_it_be(:group) { create(:group, path: 'foo') }
before do before_all do
group.add_owner(user) group.add_owner(user)
group.add_developer(developer) group.add_developer(developer)
end
before do
sign_in(user) sign_in(user)
end end
...@@ -275,16 +278,38 @@ ...@@ -275,16 +278,38 @@
end end
describe 'merge request approval settings', :js do describe 'merge request approval settings', :js do
let_it_be(:approval_settings) do
create(:group_merge_request_approval_setting, group: group, allow_author_approval: false)
end
context 'when feature flag is enabled and group is licensed' do context 'when feature flag is enabled and group is licensed' do
before do before do
stub_feature_flags(group_merge_request_approval_settings_feature_flag: true) stub_feature_flags(group_merge_request_approval_settings_feature_flag: true)
stub_licensed_features(group_merge_request_approval_settings: true) stub_licensed_features(group_merge_request_approval_settings: true)
end end
it 'is visible' do it 'allows to save settings' do
visit edit_group_path(group) visit edit_group_path(group)
wait_for_all_requests
expect(page).to have_content('Merge request approvals') expect(page).to have_content('Merge request approvals')
within('[data-testid="merge-request-approval-settings"]') do
click_button 'Expand'
expect(find('[data-testid="prevent-author-approval"]')).to be_checked
find('[data-testid="prevent-author-approval"]').set(false)
click_button 'Save changes'
wait_for_all_requests
end
visit edit_group_path(group)
wait_for_all_requests
within('[data-testid="merge-request-approval-settings"]') do
click_button 'Expand'
expect(find('[data-testid="prevent-author-approval"]')).not_to be_checked
end
end end
end end
......
import { GlButton } from '@gitlab/ui'; import { GlButton, GlForm } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
...@@ -24,6 +24,7 @@ describe('ApprovalSettings', () => { ...@@ -24,6 +24,7 @@ describe('ApprovalSettings', () => {
}); });
}; };
const findForm = () => wrapper.findComponent(GlForm);
const findPreventAuthorApproval = () => wrapper.find('[data-testid="prevent-author-approval"]'); const findPreventAuthorApproval = () => wrapper.find('[data-testid="prevent-author-approval"]');
const findSaveButton = () => wrapper.findComponent(GlButton); const findSaveButton = () => wrapper.findComponent(GlButton);
...@@ -31,6 +32,7 @@ describe('ApprovalSettings', () => { ...@@ -31,6 +32,7 @@ describe('ApprovalSettings', () => {
store = createStoreOptions(groupSettingsModule()); store = createStoreOptions(groupSettingsModule());
jest.spyOn(store.modules.approvals.actions, 'fetchSettings').mockImplementation(); jest.spyOn(store.modules.approvals.actions, 'fetchSettings').mockImplementation();
jest.spyOn(store.modules.approvals.actions, 'updateSettings').mockImplementation();
({ actions } = store.modules.approvals); ({ actions } = store.modules.approvals);
}); });
...@@ -52,8 +54,7 @@ describe('ApprovalSettings', () => { ...@@ -52,8 +54,7 @@ describe('ApprovalSettings', () => {
const input = findPreventAuthorApproval(); const input = findPreventAuthorApproval();
await input.vm.$emit('input', false); await input.vm.$emit('input', false);
expect(input.vm.$attrs.checked).toBe(false); expect(store.modules.approvals.state.settings.preventAuthorApproval).toBe(false);
expect(store.modules.approvals.state.preventAuthorApproval).toBe(false);
}); });
}); });
...@@ -74,4 +75,14 @@ describe('ApprovalSettings', () => { ...@@ -74,4 +75,14 @@ describe('ApprovalSettings', () => {
expect(findSaveButton().props('disabled')).toBe(true); expect(findSaveButton().props('disabled')).toBe(true);
}); });
}); });
describe('form submission', () => {
it('update settings via API', async () => {
createWrapper();
await findForm().vm.$emit('submit', { preventDefault: () => {} });
expect(actions.updateSettings).toHaveBeenCalledWith(expect.any(Object), approvalSettingsPath);
});
});
}); });
...@@ -69,17 +69,55 @@ describe('EE approvals group settings module actions', () => { ...@@ -69,17 +69,55 @@ describe('EE approvals group settings module actions', () => {
}); });
}); });
describe('updatePreventAuthorApproval', () => { describe('updateSettings', () => {
it('updates payload', () => { beforeEach(() => {
const value = false; state = {
settings: {
preventAuthorApproval: false,
},
};
});
describe('on success', () => {
it('dispatches the request and updates payload', () => {
const data = { allow_author_approval: true };
mock.onPut(approvalSettingsPath).replyOnce(httpStatus.OK, data);
return testAction(
actions.updateSettings,
approvalSettingsPath,
state,
[
{ type: types.REQUEST_UPDATE_SETTINGS },
{ type: types.UPDATE_SETTINGS_SUCCESS, payload: data },
],
[],
);
});
});
describe('on error', () => {
it('dispatches the request, updates payload and sets error message', () => {
const data = { message: 'Internal Server Error' };
mock.onPut(approvalSettingsPath).replyOnce(httpStatus.INTERNAL_SERVER_ERROR, data);
return testAction( return testAction(
actions.updatePreventAuthorApproval, actions.updateSettings,
value, approvalSettingsPath,
state, state,
[{ type: types.UPDATE_PREVENT_AUTHOR_APPROVAL, payload: value }], [
[], { type: types.REQUEST_UPDATE_SETTINGS },
); { type: types.UPDATE_SETTINGS_ERROR, payload: data.message },
],
[],
).then(() => {
expect(createFlash).toHaveBeenCalledWith({
message: 'There was an error updating merge request approval settings.',
captureError: true,
error: 'Internal Server Error',
});
});
});
}); });
}); });
}); });
...@@ -20,7 +20,7 @@ describe('Group settings store mutations', () => { ...@@ -20,7 +20,7 @@ describe('Group settings store mutations', () => {
it('updates settings', () => { it('updates settings', () => {
mutations.RECEIVE_SETTINGS_SUCCESS(state, { allow_author_approval: true }); mutations.RECEIVE_SETTINGS_SUCCESS(state, { allow_author_approval: true });
expect(state.preventAuthorApproval).toBe(false); expect(state.settings.preventAuthorApproval).toBe(false);
expect(state.isLoading).toBe(false); expect(state.isLoading).toBe(false);
}); });
}); });
...@@ -33,11 +33,28 @@ describe('Group settings store mutations', () => { ...@@ -33,11 +33,28 @@ describe('Group settings store mutations', () => {
}); });
}); });
describe('UPDATE_PREVENT_AUTHOR_APPROVAL', () => { describe('REQUEST_UPDATE_SETTINGS', () => {
it('updates setting', () => { it('sets loading state', () => {
mutations.UPDATE_PREVENT_AUTHOR_APPROVAL(state, false); mutations.REQUEST_UPDATE_SETTINGS(state);
expect(state.isLoading).toBe(true);
});
});
describe('UPDATE_SETTINGS_SUCCESS', () => {
it('updates settings', () => {
mutations.UPDATE_SETTINGS_SUCCESS(state, { allow_author_approval: true });
expect(state.preventAuthorApproval).toBe(false); expect(state.settings.preventAuthorApproval).toBe(false);
expect(state.isLoading).toBe(false);
});
});
describe('UPDATE_SETTINGS_ERROR', () => {
it('sets loading state', () => {
mutations.UPDATE_SETTINGS_ERROR(state);
expect(state.isLoading).toBe(false);
}); });
}); });
}); });
...@@ -29701,6 +29701,9 @@ msgstr "" ...@@ -29701,6 +29701,9 @@ msgstr ""
msgid "There was an error trying to validate your query" msgid "There was an error trying to validate your query"
msgstr "" msgstr ""
   
msgid "There was an error updating merge request approval settings."
msgstr ""
msgid "There was an error updating the Geo Settings" msgid "There was an error updating the Geo Settings"
msgstr "" msgstr ""
   
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册