diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue index 247877a82359363e766fecab328e705b595c9dc9..e0c4679b9834f5424c2ba41277eb16370e234432 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue @@ -1,7 +1,14 @@ <script> -import { MERGE_ACTIVE_STATUS_PHRASES } from '../../constants'; +import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; +import simplePoll from '~/lib/utils/simple_poll'; +import MergeRequest from '../../../merge_request'; +import eventHub from '../../event_hub'; +import { MERGE_ACTIVE_STATUS_PHRASES, STATE_MACHINE } from '../../constants'; import statusIcon from '../mr_widget_status_icon.vue'; +const { transitions } = STATE_MACHINE; +const { MERGE_FAILURE } = transitions; + export default { name: 'MRWidgetMerging', components: { @@ -12,6 +19,10 @@ export default { type: Object, required: true, }, + service: { + type: Object, + required: true, + }, }, data() { const statusCount = MERGE_ACTIVE_STATUS_PHRASES.length; @@ -20,6 +31,53 @@ export default { mergeStatus: MERGE_ACTIVE_STATUS_PHRASES[Math.floor(Math.random() * statusCount)], }; }, + mounted() { + this.initiateMergePolling(); + }, + methods: { + initiateMergePolling() { + simplePoll( + (continuePolling, stopPolling) => { + this.handleMergePolling(continuePolling, stopPolling); + }, + { timeout: 0 }, + ); + }, + handleMergePolling(continuePolling, stopPolling) { + this.service + .poll() + .then((res) => res.data) + .then((data) => { + if (data.state === 'merged') { + // If state is merged we should update the widget and stop the polling + eventHub.$emit('MRWidgetUpdateRequested'); + eventHub.$emit('FetchActionsContent'); + MergeRequest.hideCloseButton(); + MergeRequest.decreaseCounter(); + stopPolling(); + + refreshUserMergeRequestCounts(); + + // If user checked remove source branch and we didn't remove the branch yet + // we should start another polling for source branch remove process + if (this.removeSourceBranch && data.source_branch_exists) { + this.initiateRemoveSourceBranchPolling(); + } + } else if (data.merge_error) { + eventHub.$emit('FailedToMerge', data.merge_error); + this.mr.transitionStateMachine({ transition: MERGE_FAILURE }); + stopPolling(); + } else { + // MR is not merged yet, continue polling until the state becomes 'merged' + continuePolling(); + } + }) + .catch(() => { + this.mr.transitionStateMachine({ transition: MERGE_FAILURE }); + stopPolling(); + }); + }, + }, }; </script> <template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index bdad1c4baec06b2e4c5b5744fdc3ba6d82d13964..bc094501e8939c30809311ffeeda3ae5249796c0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -14,7 +14,6 @@ import { import { isEmpty } from 'lodash'; import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge'; import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql'; -import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import createFlash from '~/flash'; import { secondsToMilliseconds } from '~/lib/utils/datetime_utility'; import simplePoll from '~/lib/utils/simple_poll'; @@ -22,7 +21,6 @@ import { __, s__ } from '~/locale'; import SmartInterval from '~/smart_interval'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { helpPagePath } from '~/helpers/help_page_helper'; -import MergeRequest from '../../../merge_request'; import { AUTO_MERGE_STRATEGIES, WARNING, @@ -51,7 +49,7 @@ const MERGE_SUCCESS_STATUS = 'success'; const MERGE_HOOK_VALIDATION_ERROR_STATUS = 'hook_validation_error'; const { transitions } = STATE_MACHINE; -const { MERGE, MERGED, MERGE_FAILURE, AUTO_MERGE } = transitions; +const { MERGE, MERGE_FAILURE, AUTO_MERGE, MERGING } = transitions; export default { name: 'ReadyToMerge', @@ -412,7 +410,7 @@ export default { eventHub.$emit('MRWidgetUpdateRequested'); this.mr.transitionStateMachine({ transition: AUTO_MERGE }); } else if (data.status === MERGE_SUCCESS_STATUS) { - this.initiateMergePolling(); + this.mr.transitionStateMachine({ transition: MERGING }); } else if (hasError) { eventHub.$emit('FailedToMerge', data.merge_error); this.mr.transitionStateMachine({ transition: MERGE_FAILURE }); @@ -443,52 +441,6 @@ export default { onMergeWithFailedPipelineConfirmation() { this.handleMergeButtonClick(false, true, true); }, - initiateMergePolling() { - simplePoll( - (continuePolling, stopPolling) => { - this.handleMergePolling(continuePolling, stopPolling); - }, - { timeout: 0 }, - ); - }, - handleMergePolling(continuePolling, stopPolling) { - this.service - .poll() - .then((res) => res.data) - .then((data) => { - if (data.state === 'merged') { - // If state is merged we should update the widget and stop the polling - eventHub.$emit('MRWidgetUpdateRequested'); - eventHub.$emit('FetchActionsContent'); - MergeRequest.hideCloseButton(); - MergeRequest.decreaseCounter(); - this.mr.transitionStateMachine({ transition: MERGED }); - stopPolling(); - - refreshUserMergeRequestCounts(); - - // If user checked remove source branch and we didn't remove the branch yet - // we should start another polling for source branch remove process - if (this.removeSourceBranch && data.source_branch_exists) { - this.initiateRemoveSourceBranchPolling(); - } - } else if (data.merge_error) { - eventHub.$emit('FailedToMerge', data.merge_error); - this.mr.transitionStateMachine({ transition: MERGE_FAILURE }); - stopPolling(); - } else { - // MR is not merged yet, continue polling until the state becomes 'merged' - continuePolling(); - } - }) - .catch(() => { - createFlash({ - message: __('Something went wrong while merging this merge request. Please try again.'), - }); - this.mr.transitionStateMachine({ transition: MERGE_FAILURE }); - stopPolling(); - }); - }, initiateRemoveSourceBranchPolling() { // We need to show source branch is being removed spinner in another component eventHub.$emit('SetBranchRemoveFlag', [true]); diff --git a/app/assets/javascripts/vue_merge_request_widget/constants.js b/app/assets/javascripts/vue_merge_request_widget/constants.js index 32effb91043184043115672b73dc0eec89b7c6d7..d337a55466377e62110285e32a6e2edfe0fe8c2f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/constants.js +++ b/app/assets/javascripts/vue_merge_request_widget/constants.js @@ -68,6 +68,7 @@ const STATE_MACHINE = { states: { IDLE: 'IDLE', MERGING: 'MERGING', + MERGED: 'MERGED', AUTO_MERGE: 'AUTO_MERGE', }, transitions: { @@ -75,6 +76,7 @@ const STATE_MACHINE = { AUTO_MERGE: 'start-auto-merge', MERGE_FAILURE: 'merge-failed', MERGED: 'merge-done', + MERGING: 'merging', }, }; const { states, transitions } = STATE_MACHINE; @@ -86,11 +88,12 @@ STATE_MACHINE.definition = { on: { [transitions.MERGE]: states.MERGING, [transitions.AUTO_MERGE]: states.AUTO_MERGE, + [transitions.MERGING]: states.MERGING, }, }, [states.MERGING]: { on: { - [transitions.MERGED]: states.IDLE, + [transitions.MERGED]: states.MERGED, [transitions.MERGE_FAILURE]: states.IDLE, }, }, @@ -110,6 +113,7 @@ export const stateToTransitionMap = { }; export const stateToComponentMap = { [states.MERGING]: classStateMap[stateKey.merging], + [states.MERGED]: classStateMap[stateKey.merged], [states.AUTO_MERGE]: classStateMap[stateKey.autoMergeEnabled], }; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2fc2e9228bc121947a4a85921bcee9b04b698d72..84d1210488d646c5b7d33cab5755a32ad461a19f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -33905,9 +33905,6 @@ msgstr "" msgid "Something went wrong while inserting your image. Please try again." msgstr "" -msgid "Something went wrong while merging this merge request. Please try again." -msgstr "" - msgid "Something went wrong while obtaining the Let's Encrypt certificate." msgstr "" diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js index e6b2e9fa176d9d4ea92878c7bcfaab7865f5b8de..e16c897a49b69bf6cadc8118a9e682053f99f6e1 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_merging_spec.js @@ -1,6 +1,11 @@ import { shallowMount } from '@vue/test-utils'; +import simplePoll from '~/lib/utils/simple_poll'; import MrWidgetMerging from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue'; +jest.mock('~/lib/utils/simple_poll', () => + jest.fn().mockImplementation(jest.requireActual('~/lib/utils/simple_poll').default), +); + describe('MRWidgetMerging', () => { let wrapper; @@ -11,6 +16,10 @@ describe('MRWidgetMerging', () => { mr: { targetBranchPath: '/branch-path', targetBranch: 'branch', + transitionStateMachine() {}, + }, + service: { + poll: jest.fn().mockResolvedValue(), }, }, stubs: { @@ -46,4 +55,20 @@ describe('MRWidgetMerging', () => { expect(wrapper.find('a').attributes('href')).toBe('/branch-path'); }); + + describe('initiateMergePolling', () => { + it('should call simplePoll', () => { + wrapper.vm.initiateMergePolling(); + + expect(simplePoll).toHaveBeenCalledWith(expect.any(Function), { timeout: 0 }); + }); + + it('should call handleMergePolling', () => { + jest.spyOn(wrapper.vm, 'handleMergePolling').mockImplementation(() => {}); + + wrapper.vm.initiateMergePolling(); + + expect(wrapper.vm.handleMergePolling).toHaveBeenCalled(); + }); + }); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index d1ac1608fd54a4f45b03d11c9516d3c046a58dd0..0b5f5d5ff093f6ab72557d1400d6c8bcdb2f7d16 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -328,7 +328,7 @@ describe('ReadyToMerge', () => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue(returnPromise('success')); - jest.spyOn(wrapper.vm, 'initiateMergePolling').mockImplementation(() => {}); + jest.spyOn(wrapper.vm.mr, 'transitionStateMachine'); wrapper.vm.handleMergeButtonClick(); expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', { @@ -337,7 +337,9 @@ describe('ReadyToMerge', () => { setImmediate(() => { expect(wrapper.vm.isMakingRequest).toBeTruthy(); - expect(wrapper.vm.initiateMergePolling).toHaveBeenCalled(); + expect(wrapper.vm.mr.transitionStateMachine).toHaveBeenCalledWith({ + transition: 'start-merge', + }); const params = wrapper.vm.service.merge.mock.calls[0][0]; @@ -348,26 +350,6 @@ describe('ReadyToMerge', () => { }); }); - describe('initiateMergePolling', () => { - it('should call simplePoll', () => { - createComponent(); - - wrapper.vm.initiateMergePolling(); - - expect(simplePoll).toHaveBeenCalledWith(expect.any(Function), { timeout: 0 }); - }); - - it('should call handleMergePolling', () => { - createComponent(); - - jest.spyOn(wrapper.vm, 'handleMergePolling').mockImplementation(() => {}); - - wrapper.vm.initiateMergePolling(); - - expect(wrapper.vm.handleMergePolling).toHaveBeenCalled(); - }); - }); - describe('initiateRemoveSourceBranchPolling', () => { it('should emit event and call simplePoll', () => { createComponent();