Add merge train support to MR merge button (CE)
This commit updates the merge request widget's "Merge" button to support merge trains.
This commit is contained in:
parent
fc27c93e4b
commit
35ae9d8a74
14 changed files with 273 additions and 104 deletions
|
@ -1,15 +1,19 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merge';
|
||||
import Flash from '../../../flash';
|
||||
import statusIcon from '../mr_widget_status_icon.vue';
|
||||
import MrWidgetAuthor from '../../components/mr_widget_author.vue';
|
||||
import eventHub from '../../event_hub';
|
||||
import { AUTO_MERGE_STRATEGIES } from '../../constants';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetMergeWhenPipelineSucceeds',
|
||||
name: 'MRWidgetAutoMergeEnabled',
|
||||
components: {
|
||||
MrWidgetAuthor,
|
||||
statusIcon,
|
||||
},
|
||||
mixins: [autoMergeMixin],
|
||||
props: {
|
||||
mr: {
|
||||
type: Object,
|
||||
|
@ -57,7 +61,7 @@ export default {
|
|||
removeSourceBranch() {
|
||||
const options = {
|
||||
sha: this.mr.sha,
|
||||
auto_merge_strategy: 'merge_when_pipeline_succeeds',
|
||||
auto_merge_strategy: this.mr.autoMergeStrategy,
|
||||
should_remove_source_branch: true,
|
||||
};
|
||||
|
||||
|
@ -66,7 +70,7 @@ export default {
|
|||
.merge(options)
|
||||
.then(res => res.data)
|
||||
.then(data => {
|
||||
if (data.status === 'merge_when_pipeline_succeeds') {
|
||||
if (_.includes(AUTO_MERGE_STRATEGIES, data.status)) {
|
||||
eventHub.$emit('MRWidgetUpdateRequested');
|
||||
}
|
||||
})
|
||||
|
@ -84,9 +88,9 @@ export default {
|
|||
<div class="media-body">
|
||||
<h4 class="d-flex align-items-start">
|
||||
<span class="append-right-10">
|
||||
{{ s__('mrWidget|Set by') }}
|
||||
<span class="js-status-text-before-author">{{ statusTextBeforeAuthor }}</span>
|
||||
<mr-widget-author :author="mr.setToAutoMergeBy" />
|
||||
{{ s__('mrWidget|to be merged automatically when the pipeline succeeds') }}
|
||||
<span class="js-status-text-after-author">{{ statusTextAfterAuthor }}</span>
|
||||
</span>
|
||||
<a
|
||||
v-if="mr.canCancelAutomaticMerge"
|
||||
|
@ -97,7 +101,7 @@ export default {
|
|||
@click.prevent="cancelAutomaticMerge"
|
||||
>
|
||||
<i v-if="isCancellingAutoMerge" class="fa fa-spinner fa-spin" aria-hidden="true"> </i>
|
||||
{{ s__('mrWidget|Cancel automatic merge') }}
|
||||
{{ cancelButtonText }}
|
||||
</a>
|
||||
</h4>
|
||||
<section class="mr-info-list">
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import successSvg from 'icons/_icon_status_success.svg';
|
||||
import warningSvg from 'icons/_icon_status_warning.svg';
|
||||
import simplePoll from '~/lib/utils/simple_poll';
|
||||
|
@ -12,6 +13,7 @@ import SquashBeforeMerge from './squash_before_merge.vue';
|
|||
import CommitsHeader from './commits_header.vue';
|
||||
import CommitEdit from './commit_edit.vue';
|
||||
import CommitMessageDropdown from './commit_message_dropdown.vue';
|
||||
import { AUTO_MERGE_STRATEGIES } from '../../constants';
|
||||
|
||||
export default {
|
||||
name: 'ReadyToMerge',
|
||||
|
@ -30,8 +32,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
removeSourceBranch: this.mr.shouldRemoveSourceBranch,
|
||||
mergeWhenBuildSucceeds: false,
|
||||
autoMergeStrategy: undefined,
|
||||
isMakingRequest: false,
|
||||
isMergingImmediately: false,
|
||||
commitMessage: this.mr.commitMessage,
|
||||
|
@ -42,18 +42,18 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
shouldShowAutoMergeText() {
|
||||
return this.mr.isPipelineActive;
|
||||
isAutoMergeAvailable() {
|
||||
return !_.isEmpty(this.mr.availableAutoMergeStrategies);
|
||||
},
|
||||
status() {
|
||||
const { pipeline, isPipelineActive, isPipelineFailed, hasCI, ciStatus } = this.mr;
|
||||
const { pipeline, isPipelineFailed, hasCI, ciStatus } = this.mr;
|
||||
|
||||
if (hasCI && !ciStatus) {
|
||||
return 'failed';
|
||||
} else if (this.isAutoMergeAvailable) {
|
||||
return 'pending';
|
||||
} else if (!pipeline) {
|
||||
return 'success';
|
||||
} else if (isPipelineActive) {
|
||||
return 'pending';
|
||||
} else if (isPipelineFailed) {
|
||||
return 'failed';
|
||||
}
|
||||
|
@ -87,14 +87,14 @@ export default {
|
|||
mergeButtonText() {
|
||||
if (this.isMergingImmediately) {
|
||||
return __('Merge in progress');
|
||||
} else if (this.shouldShowAutoMergeText) {
|
||||
return __('Merge when pipeline succeeds');
|
||||
} else if (this.isAutoMergeAvailable) {
|
||||
return this.autoMergeText;
|
||||
}
|
||||
|
||||
return 'Merge';
|
||||
return __('Merge');
|
||||
},
|
||||
shouldShowMergeOptionsDropdown() {
|
||||
return this.mr.isPipelineActive && !this.mr.onlyAllowMergeIfPipelineSucceeds;
|
||||
return this.isAutoMergeAvailable && !this.mr.onlyAllowMergeIfPipelineSucceeds;
|
||||
},
|
||||
isRemoveSourceBranchButtonDisabled() {
|
||||
return this.isMergeButtonDisabled;
|
||||
|
@ -104,7 +104,7 @@ export default {
|
|||
return enableSquashBeforeMerge && commitsCount > 1;
|
||||
},
|
||||
shouldShowMergeControls() {
|
||||
return this.mr.isMergeAllowed || this.shouldShowAutoMergeText;
|
||||
return this.mr.isMergeAllowed || this.isAutoMergeAvailable;
|
||||
},
|
||||
shouldShowSquashEdit() {
|
||||
return this.squashBeforeMerge && this.shouldShowSquashBeforeMerge;
|
||||
|
@ -118,20 +118,15 @@ export default {
|
|||
const { commitMessageWithDescription, commitMessage } = this.mr;
|
||||
this.commitMessage = includeDescription ? commitMessageWithDescription : commitMessage;
|
||||
},
|
||||
handleMergeButtonClick(mergeWhenBuildSucceeds, mergeImmediately) {
|
||||
// TODO: Remove no-param-reassign
|
||||
if (mergeWhenBuildSucceeds === undefined) {
|
||||
mergeWhenBuildSucceeds = this.mr.isPipelineActive; // eslint-disable-line no-param-reassign
|
||||
} else if (mergeImmediately) {
|
||||
handleMergeButtonClick(useAutoMerge, mergeImmediately = false) {
|
||||
if (mergeImmediately) {
|
||||
this.isMergingImmediately = true;
|
||||
}
|
||||
|
||||
this.autoMergeStrategy = mergeWhenBuildSucceeds ? 'merge_when_pipeline_succeeds' : undefined;
|
||||
|
||||
const options = {
|
||||
sha: this.mr.sha,
|
||||
commit_message: this.commitMessage,
|
||||
auto_merge_strategy: this.autoMergeStrategy,
|
||||
auto_merge_strategy: useAutoMerge ? this.mr.preferredAutoMergeStrategy : undefined,
|
||||
should_remove_source_branch: this.removeSourceBranch === true,
|
||||
squash: this.squashBeforeMerge,
|
||||
squash_commit_message: this.squashCommitMessage,
|
||||
|
@ -144,7 +139,7 @@ export default {
|
|||
.then(data => {
|
||||
const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
|
||||
|
||||
if (data.status === 'merge_when_pipeline_succeeds') {
|
||||
if (_.includes(AUTO_MERGE_STRATEGIES, data.status)) {
|
||||
eventHub.$emit('MRWidgetUpdateRequested');
|
||||
} else if (data.status === 'success') {
|
||||
this.initiateMergePolling();
|
||||
|
@ -242,13 +237,13 @@ export default {
|
|||
:class="mergeButtonClass"
|
||||
type="button"
|
||||
class="qa-merge-button"
|
||||
@click="handleMergeButtonClick()"
|
||||
@click="handleMergeButtonClick(isAutoMergeAvailable)"
|
||||
>
|
||||
<i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
|
||||
{{ mergeButtonText }}
|
||||
</button>
|
||||
<button
|
||||
v-if="shouldShowMergeOptionsDropdown"
|
||||
v-if="isAutoMergeAvailable"
|
||||
:disabled="isMergeButtonDisabled"
|
||||
type="button"
|
||||
class="btn btn-sm btn-info dropdown-toggle js-merge-moment"
|
||||
|
@ -264,15 +259,13 @@ export default {
|
|||
>
|
||||
<li>
|
||||
<a
|
||||
class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option"
|
||||
class="auto_merge_enabled qa-merge-when-pipeline-succeeds-option"
|
||||
href="#"
|
||||
@click.prevent="handleMergeButtonClick(true)"
|
||||
>
|
||||
<span class="media">
|
||||
<span class="merge-opt-icon" aria-hidden="true" v-html="successSvg"></span>
|
||||
<span class="media-body merge-opt-title">{{
|
||||
__('Merge when pipeline succeeds')
|
||||
}}</span>
|
||||
<span class="media-body merge-opt-title">{{ autoMergeText }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -3,3 +3,13 @@ export const DANGER = 'danger';
|
|||
|
||||
export const WARNING_MESSAGE_CLASS = 'warning_message';
|
||||
export const DANGER_MESSAGE_CLASS = 'danger_message';
|
||||
|
||||
export const MWPS_MERGE_STRATEGY = 'merge_when_pipeline_succeeds';
|
||||
export const ATMTWPS_MERGE_STRATEGY = 'add_to_merge_train_when_pipeline_succeeds';
|
||||
export const MT_MERGE_STRATEGY = 'merge_train';
|
||||
|
||||
export const AUTO_MERGE_STRATEGIES = [
|
||||
MWPS_MERGE_STRATEGY,
|
||||
ATMTWPS_MERGE_STRATEGY,
|
||||
MT_MERGE_STRATEGY,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
statusTextBeforeAuthor() {
|
||||
return s__('mrWidget|Set by');
|
||||
},
|
||||
statusTextAfterAuthor() {
|
||||
return s__('mrWidget|to be merged automatically when the pipeline succeeds');
|
||||
},
|
||||
cancelButtonText() {
|
||||
return s__('mrWidget|Cancel automatic merge');
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
isMergeButtonDisabled() {
|
||||
|
@ -9,5 +11,9 @@ export default {
|
|||
this.mr.preventMerge,
|
||||
);
|
||||
},
|
||||
autoMergeText() {
|
||||
// MWPS is currently the only auto merge strategy available in CE
|
||||
return __('Merge when pipeline succeeds');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ import UnresolvedDiscussionsState from './components/states/unresolved_discussio
|
|||
import PipelineBlockedState from './components/states/mr_widget_pipeline_blocked.vue';
|
||||
import PipelineFailedState from './components/states/pipeline_failed.vue';
|
||||
import FailedToMerge from './components/states/mr_widget_failed_to_merge.vue';
|
||||
import MergeWhenPipelineSucceedsState from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
|
||||
import MrWidgetAutoMergeEnabled from './components/states/mr_widget_auto_merge_enabled.vue';
|
||||
import AutoMergeFailed from './components/states/mr_widget_auto_merge_failed.vue';
|
||||
import CheckingState from './components/states/mr_widget_checking.vue';
|
||||
import eventHub from './event_hub';
|
||||
|
@ -64,7 +64,7 @@ export default {
|
|||
'mr-widget-unresolved-discussions': UnresolvedDiscussionsState,
|
||||
'mr-widget-pipeline-blocked': PipelineBlockedState,
|
||||
'mr-widget-pipeline-failed': PipelineFailedState,
|
||||
'mr-widget-merge-when-pipeline-succeeds': MergeWhenPipelineSucceedsState,
|
||||
MrWidgetAutoMergeEnabled,
|
||||
'mr-widget-auto-merge-failed': AutoMergeFailed,
|
||||
'mr-widget-rebase': RebaseState,
|
||||
SourceBranchRemovalStatus,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import Timeago from 'timeago.js';
|
||||
import _ from 'underscore';
|
||||
import getStateKey from 'ee_else_ce/vue_merge_request_widget/stores/get_state_key';
|
||||
import { stateKey } from './state_maps';
|
||||
import { formatDate } from '../../lib/utils/datetime_utility';
|
||||
import { ATMTWPS_MERGE_STRATEGY, MT_MERGE_STRATEGY, MWPS_MERGE_STRATEGY } from '../constants';
|
||||
|
||||
export default class MergeRequestStore {
|
||||
constructor(data) {
|
||||
|
@ -77,6 +79,10 @@ export default class MergeRequestStore {
|
|||
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
|
||||
this.autoMergeEnabled = Boolean(data.auto_merge_enabled);
|
||||
this.autoMergeStrategy = data.auto_merge_strategy;
|
||||
this.availableAutoMergeStrategies = data.available_auto_merge_strategies;
|
||||
this.preferredAutoMergeStrategy = MergeRequestStore.getPreferredAutoMergeStrategy(
|
||||
this.availableAutoMergeStrategies,
|
||||
);
|
||||
this.mergePath = data.merge_path;
|
||||
this.ffOnlyEnabled = data.ff_only_enabled;
|
||||
this.shouldBeRebased = Boolean(data.should_be_rebased);
|
||||
|
@ -104,7 +110,9 @@ export default class MergeRequestStore {
|
|||
this.sourceProjectFullPath = data.source_project_full_path;
|
||||
this.sourceProjectId = data.source_project_id;
|
||||
this.targetProjectId = data.target_project_id;
|
||||
this.mergePipelinesEnabled = data.merge_pipelines_enabled;
|
||||
this.mergePipelinesEnabled = Boolean(data.merge_pipelines_enabled);
|
||||
this.mergeTrainsCount = data.merge_trains_count || 0;
|
||||
this.mergeTrainIndex = data.merge_train_index;
|
||||
|
||||
// Cherry-pick and Revert actions related
|
||||
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
|
||||
|
@ -204,4 +212,16 @@ export default class MergeRequestStore {
|
|||
|
||||
return timeagoInstance.format(date);
|
||||
}
|
||||
|
||||
static getPreferredAutoMergeStrategy(availableAutoMergeStrategies) {
|
||||
if (_.includes(availableAutoMergeStrategies, ATMTWPS_MERGE_STRATEGY)) {
|
||||
return ATMTWPS_MERGE_STRATEGY;
|
||||
} else if (_.includes(availableAutoMergeStrategies, MT_MERGE_STRATEGY)) {
|
||||
return MT_MERGE_STRATEGY;
|
||||
} else if (_.includes(availableAutoMergeStrategies, MWPS_MERGE_STRATEGY)) {
|
||||
return MWPS_MERGE_STRATEGY;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ const stateToComponentMap = {
|
|||
unresolvedDiscussions: 'mr-widget-unresolved-discussions',
|
||||
pipelineBlocked: 'mr-widget-pipeline-blocked',
|
||||
pipelineFailed: 'mr-widget-pipeline-failed',
|
||||
autoMergeEnabled: 'mr-widget-merge-when-pipeline-succeeds',
|
||||
autoMergeEnabled: 'mr-widget-auto-merge-enabled',
|
||||
failedToMerge: 'mr-widget-failed-to-merge',
|
||||
autoMergeFailed: 'mr-widget-auto-merge-failed',
|
||||
shaMismatch: 'sha-mismatch',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update the merge request widget's "Merge" button to support merge trains
|
||||
merge_request: 27594
|
||||
author:
|
||||
type: added
|
|
@ -6045,6 +6045,9 @@ msgstr ""
|
|||
msgid "Members of <strong>%{project_name}</strong>"
|
||||
msgstr ""
|
||||
|
||||
msgid "Merge"
|
||||
msgstr ""
|
||||
|
||||
msgid "Merge Request"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import Vue from 'vue';
|
||||
import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
|
||||
import autoMergeEnabledComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue';
|
||||
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
|
||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
import { trimText } from 'spec/helpers/text_helper';
|
||||
import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
|
||||
|
||||
describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
||||
describe('MRWidgetAutoMergeEnabled', () => {
|
||||
let vm;
|
||||
const targetBranchPath = '/foo/bar';
|
||||
const targetBranch = 'foo';
|
||||
const sha = '1EA2EZ34';
|
||||
|
||||
beforeEach(() => {
|
||||
const Component = Vue.extend(mwpsComponent);
|
||||
const Component = Vue.extend(autoMergeEnabledComponent);
|
||||
spyOn(eventHub, '$emit');
|
||||
|
||||
vm = mountComponent(Component, {
|
||||
|
@ -25,6 +27,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
sha,
|
||||
targetBranchPath,
|
||||
targetBranch,
|
||||
autoMergeStrategy: MWPS_MERGE_STRATEGY,
|
||||
},
|
||||
service: new MRWidgetService({}),
|
||||
});
|
||||
|
@ -66,6 +69,32 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
expect(vm.canRemoveSourceBranch).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('statusTextBeforeAuthor', () => {
|
||||
it('should return "Set by" if the MWPS is selected', () => {
|
||||
Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
expect(vm.statusTextBeforeAuthor).toBe('Set by');
|
||||
});
|
||||
});
|
||||
|
||||
describe('statusTextAfterAuthor', () => {
|
||||
it('should return "to be merged automatically..." if MWPS is selected', () => {
|
||||
Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
expect(vm.statusTextAfterAuthor).toBe(
|
||||
'to be merged automatically when the pipeline succeeds',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancelButtonText', () => {
|
||||
it('should return "Cancel automatic merge" if MWPS is selected', () => {
|
||||
Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
expect(vm.cancelButtonText).toBe('Cancel automatic merge');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
|
@ -96,7 +125,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
spyOn(vm.service, 'merge').and.returnValue(
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 'merge_when_pipeline_succeeds',
|
||||
status: MWPS_MERGE_STRATEGY,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -106,7 +135,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
|
||||
expect(vm.service.merge).toHaveBeenCalledWith({
|
||||
sha,
|
||||
auto_merge_strategy: 'merge_when_pipeline_succeeds',
|
||||
auto_merge_strategy: MWPS_MERGE_STRATEGY,
|
||||
should_remove_source_branch: true,
|
||||
});
|
||||
done();
|
||||
|
@ -119,6 +148,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
it('should have correct elements', () => {
|
||||
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
|
||||
expect(vm.$el.innerText).toContain('to be merged automatically when the pipeline succeeds');
|
||||
|
||||
expect(vm.$el.innerText).toContain('The changes will be merged into');
|
||||
expect(vm.$el.innerText).toContain(targetBranch);
|
||||
expect(vm.$el.innerText).toContain('The source branch will not be deleted');
|
||||
|
@ -174,5 +204,27 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the status text as "...to merged automatically" if MWPS is selected', done => {
|
||||
Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const statusText = trimText(vm.$el.querySelector('.js-status-text-after-author').innerText);
|
||||
|
||||
expect(statusText).toBe('to be merged automatically when the pipeline succeeds');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render the cancel button as "Cancel automatic merge" if MWPS is selected', done => {
|
||||
Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const cancelButtonText = trimText(vm.$el.querySelector('.js-cancel-auto-merge').innerText);
|
||||
|
||||
expect(cancelButtonText).toBe('Cancel automatic merge');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,6 +6,7 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit
|
|||
import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
|
||||
import eventHub from '~/vue_merge_request_widget/event_hub';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import { MWPS_MERGE_STRATEGY, ATMTWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
|
||||
|
||||
const commitMessage = 'This is the commit message';
|
||||
const squashCommitMessage = 'This is the squash commit message';
|
||||
|
@ -29,6 +30,8 @@ const createTestMr = customConfig => {
|
|||
shouldRemoveSourceBranch: true,
|
||||
canRemoveSourceBranch: false,
|
||||
targetBranch: 'master',
|
||||
preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY,
|
||||
availableAutoMergeStrategies: [MWPS_MERGE_STRATEGY],
|
||||
};
|
||||
|
||||
Object.assign(mr, customConfig.mr);
|
||||
|
@ -80,7 +83,6 @@ describe('ReadyToMerge', () => {
|
|||
it('should have default data', () => {
|
||||
expect(vm.mergeWhenBuildSucceeds).toBeFalsy();
|
||||
expect(vm.useCommitMessageWithDescription).toBeFalsy();
|
||||
expect(vm.autoMergeStrategy).toBeUndefined();
|
||||
expect(vm.showCommitMessageEditor).toBeFalsy();
|
||||
expect(vm.isMakingRequest).toBeFalsy();
|
||||
expect(vm.isMergingImmediately).toBeFalsy();
|
||||
|
@ -91,47 +93,51 @@ describe('ReadyToMerge', () => {
|
|||
});
|
||||
|
||||
describe('computed', () => {
|
||||
describe('shouldShowAutoMergeText', () => {
|
||||
it('should return true with active pipeline', () => {
|
||||
vm.mr.isPipelineActive = true;
|
||||
describe('isAutoMergeAvailable', () => {
|
||||
it('should return true when at least one merge strategy is available', () => {
|
||||
vm.mr.availableAutoMergeStrategies = [MWPS_MERGE_STRATEGY];
|
||||
|
||||
expect(vm.shouldShowAutoMergeText).toBeTruthy();
|
||||
expect(vm.isAutoMergeAvailable).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false with inactive pipeline', () => {
|
||||
vm.mr.isPipelineActive = false;
|
||||
it('should return false when no merge strategies are available', () => {
|
||||
vm.mr.availableAutoMergeStrategies = [];
|
||||
|
||||
expect(vm.shouldShowAutoMergeText).toBeFalsy();
|
||||
expect(vm.isAutoMergeAvailable).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('status', () => {
|
||||
it('defaults to success', () => {
|
||||
vm.mr.pipeline = true;
|
||||
Vue.set(vm.mr, 'pipeline', true);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.status).toEqual('success');
|
||||
});
|
||||
|
||||
it('returns failed when MR has CI but also has an unknown status', () => {
|
||||
vm.mr.hasCI = true;
|
||||
Vue.set(vm.mr, 'hasCI', true);
|
||||
|
||||
expect(vm.status).toEqual('failed');
|
||||
});
|
||||
|
||||
it('returns default when MR has no pipeline', () => {
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.status).toEqual('success');
|
||||
});
|
||||
|
||||
it('returns pending when pipeline is active', () => {
|
||||
vm.mr.pipeline = {};
|
||||
vm.mr.isPipelineActive = true;
|
||||
Vue.set(vm.mr, 'pipeline', {});
|
||||
Vue.set(vm.mr, 'isPipelineActive', true);
|
||||
|
||||
expect(vm.status).toEqual('pending');
|
||||
});
|
||||
|
||||
it('returns failed when pipeline is failed', () => {
|
||||
vm.mr.pipeline = {};
|
||||
vm.mr.isPipelineFailed = true;
|
||||
Vue.set(vm.mr, 'pipeline', {});
|
||||
Vue.set(vm.mr, 'isPipelineFailed', true);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.status).toEqual('failed');
|
||||
});
|
||||
|
@ -143,18 +149,20 @@ describe('ReadyToMerge', () => {
|
|||
const inActionClass = `${defaultClass} btn-info`;
|
||||
|
||||
it('defaults to success class', () => {
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.mergeButtonClass).toEqual(defaultClass);
|
||||
});
|
||||
|
||||
it('returns success class for success status', () => {
|
||||
vm.mr.pipeline = true;
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
Vue.set(vm.mr, 'pipeline', true);
|
||||
|
||||
expect(vm.mergeButtonClass).toEqual(defaultClass);
|
||||
});
|
||||
|
||||
it('returns info class for pending status', () => {
|
||||
vm.mr.pipeline = {};
|
||||
vm.mr.isPipelineActive = true;
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
|
||||
|
||||
expect(vm.mergeButtonClass).toEqual(inActionClass);
|
||||
});
|
||||
|
@ -198,69 +206,82 @@ describe('ReadyToMerge', () => {
|
|||
});
|
||||
|
||||
describe('mergeButtonText', () => {
|
||||
it('should return Merge', () => {
|
||||
it('should return "Merge" when no auto merge strategies are available', () => {
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.mergeButtonText).toEqual('Merge');
|
||||
});
|
||||
|
||||
it('should return Merge in progress', () => {
|
||||
vm.isMergingImmediately = true;
|
||||
it('should return "Merge in progress"', () => {
|
||||
Vue.set(vm, 'isMergingImmediately', true);
|
||||
|
||||
expect(vm.mergeButtonText).toEqual('Merge in progress');
|
||||
});
|
||||
|
||||
it('should return Merge when pipeline succeeds', () => {
|
||||
vm.isMergingImmediately = false;
|
||||
vm.mr.isPipelineActive = true;
|
||||
it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
|
||||
Vue.set(vm, 'isMergingImmediately', false);
|
||||
Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
expect(vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
|
||||
});
|
||||
});
|
||||
|
||||
describe('autoMergeText', () => {
|
||||
it('should return Merge when pipeline succeeds', () => {
|
||||
Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
|
||||
|
||||
expect(vm.autoMergeText).toEqual('Merge when pipeline succeeds');
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowMergeOptionsDropdown', () => {
|
||||
it('should return false with initial data', () => {
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy();
|
||||
it('should return false when no auto merge strategies are available', () => {
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when pipeline active', () => {
|
||||
vm.mr.isPipelineActive = true;
|
||||
it('should return true when at least one auto merge strategy is available', () => {
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
|
||||
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBeTruthy();
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when pipeline active but only merge when pipeline succeeds set in project options', () => {
|
||||
vm.mr.isPipelineActive = true;
|
||||
vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
|
||||
Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
|
||||
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy();
|
||||
expect(vm.shouldShowMergeOptionsDropdown).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isMergeButtonDisabled', () => {
|
||||
it('should return false with initial data', () => {
|
||||
vm.mr.isMergeAllowed = true;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', true);
|
||||
|
||||
expect(vm.isMergeButtonDisabled).toBeFalsy();
|
||||
expect(vm.isMergeButtonDisabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when there is no commit message', () => {
|
||||
vm.mr.isMergeAllowed = true;
|
||||
vm.commitMessage = '';
|
||||
Vue.set(vm.mr, 'isMergeAllowed', true);
|
||||
Vue.set(vm, 'commitMessage', '');
|
||||
|
||||
expect(vm.isMergeButtonDisabled).toBeTruthy();
|
||||
expect(vm.isMergeButtonDisabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if merge is not allowed', () => {
|
||||
vm.mr.isMergeAllowed = false;
|
||||
vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', false);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
|
||||
|
||||
expect(vm.isMergeButtonDisabled).toBeTruthy();
|
||||
expect(vm.isMergeButtonDisabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when the vm instance is making request', () => {
|
||||
vm.mr.isMergeAllowed = true;
|
||||
vm.isMakingRequest = true;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', true);
|
||||
Vue.set(vm, 'isMakingRequest', true);
|
||||
|
||||
expect(vm.isMergeButtonDisabled).toBeTruthy();
|
||||
expect(vm.isMergeButtonDisabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -268,31 +289,31 @@ describe('ReadyToMerge', () => {
|
|||
describe('methods', () => {
|
||||
describe('shouldShowMergeControls', () => {
|
||||
it('should return false when an external pipeline is running and required to succeed', () => {
|
||||
vm.mr.isMergeAllowed = false;
|
||||
vm.mr.isPipelineActive = false;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', false);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.shouldShowMergeControls).toBeFalsy();
|
||||
expect(vm.shouldShowMergeControls).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when the build succeeded or build not required to succeed', () => {
|
||||
vm.mr.isMergeAllowed = true;
|
||||
vm.mr.isPipelineActive = false;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', true);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
|
||||
|
||||
expect(vm.shouldShowMergeControls).toBeTruthy();
|
||||
expect(vm.shouldShowMergeControls).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when showing the MWPS button and a pipeline is running that needs to be successful', () => {
|
||||
vm.mr.isMergeAllowed = false;
|
||||
vm.mr.isPipelineActive = true;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', false);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
|
||||
|
||||
expect(vm.shouldShowMergeControls).toBeTruthy();
|
||||
expect(vm.shouldShowMergeControls).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when showing the MWPS button but not required for the pipeline to succeed', () => {
|
||||
vm.mr.isMergeAllowed = true;
|
||||
vm.mr.isPipelineActive = true;
|
||||
Vue.set(vm.mr, 'isMergeAllowed', true);
|
||||
Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
|
||||
|
||||
expect(vm.shouldShowMergeControls).toBeTruthy();
|
||||
expect(vm.shouldShowMergeControls).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -325,7 +346,6 @@ describe('ReadyToMerge', () => {
|
|||
vm.handleMergeButtonClick(true);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.autoMergeStrategy).toBe('merge_when_pipeline_succeeds');
|
||||
expect(vm.isMakingRequest).toBeTruthy();
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
|
||||
|
||||
|
@ -349,14 +369,13 @@ describe('ReadyToMerge', () => {
|
|||
vm.handleMergeButtonClick(false, true);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.autoMergeStrategy).toBeUndefined();
|
||||
expect(vm.isMakingRequest).toBeTruthy();
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
|
||||
|
||||
const params = vm.service.merge.calls.argsFor(0)[0];
|
||||
|
||||
expect(params.should_remove_source_branch).toBeTruthy();
|
||||
expect(params.merge_when_pipeline_succeeds).toBeFalsy();
|
||||
expect(params.auto_merge_strategy).toBeUndefined();
|
||||
done();
|
||||
}, 333);
|
||||
});
|
||||
|
@ -367,14 +386,13 @@ describe('ReadyToMerge', () => {
|
|||
vm.handleMergeButtonClick();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.autoMergeStrategy).toBeUndefined();
|
||||
expect(vm.isMakingRequest).toBeTruthy();
|
||||
expect(vm.initiateMergePolling).toHaveBeenCalled();
|
||||
|
||||
const params = vm.service.merge.calls.argsFor(0)[0];
|
||||
|
||||
expect(params.should_remove_source_branch).toBeTruthy();
|
||||
expect(params.merge_when_pipeline_succeeds).toBeFalsy();
|
||||
expect(params.auto_merge_strategy).toBeUndefined();
|
||||
done();
|
||||
}, 333);
|
||||
});
|
||||
|
|
|
@ -25,7 +25,6 @@ export default {
|
|||
},
|
||||
merge_status: 'can_be_merged',
|
||||
merge_user_id: null,
|
||||
merge_when_pipeline_succeeds: false,
|
||||
source_branch: 'daaaa',
|
||||
source_branch_link: 'daaaa',
|
||||
source_project_id: 19,
|
||||
|
@ -210,8 +209,7 @@ export default {
|
|||
source_branch_path: '/root/acets-app/branches/daaaa',
|
||||
conflict_resolution_ui_path: '/root/acets-app/merge_requests/22/conflicts',
|
||||
remove_wip_path: '/root/acets-app/merge_requests/22/remove_wip',
|
||||
cancel_merge_when_pipeline_succeeds_path:
|
||||
'/root/acets-app/merge_requests/22/cancel_merge_when_pipeline_succeeds',
|
||||
cancel_auto_merge_path: '/root/acets-app/merge_requests/22/cancel_auto_merge',
|
||||
create_issue_to_resolve_discussions_path:
|
||||
'/root/acets-app/issues/new?merge_request_to_resolve_discussions_of=22',
|
||||
merge_path: '/root/acets-app/merge_requests/22/merge',
|
||||
|
@ -237,6 +235,9 @@ export default {
|
|||
merge_request_pipelines_docs_path: '/help/ci/merge_request_pipelines/index.md',
|
||||
squash: true,
|
||||
visual_review_app_available: true,
|
||||
merge_trains_enabled: true,
|
||||
merge_trains_count: 3,
|
||||
merge_train_index: 1,
|
||||
};
|
||||
|
||||
export const mockStore = {
|
||||
|
|
|
@ -82,5 +82,47 @@ describe('MergeRequestStore', () => {
|
|||
expect(store.isNothingToMergeState).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergePipelinesEnabled', () => {
|
||||
it('should set mergePipelinesEnabled = true when merge_pipelines_enabled is true', () => {
|
||||
store.setData({ ...mockData, merge_pipelines_enabled: true });
|
||||
|
||||
expect(store.mergePipelinesEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should set mergePipelinesEnabled = false when merge_pipelines_enabled is not provided', () => {
|
||||
store.setData({ ...mockData, merge_pipelines_enabled: undefined });
|
||||
|
||||
expect(store.mergePipelinesEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeTrainsCount', () => {
|
||||
it('should set mergeTrainsCount when merge_trains_count is provided', () => {
|
||||
store.setData({ ...mockData, merge_trains_count: 3 });
|
||||
|
||||
expect(store.mergeTrainsCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should set mergeTrainsCount = 0 when merge_trains_count is not provided', () => {
|
||||
store.setData({ ...mockData, merge_trains_count: undefined });
|
||||
|
||||
expect(store.mergeTrainsCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeTrainIndex', () => {
|
||||
it('should set mergeTrainIndex when merge_train_index is provided', () => {
|
||||
store.setData({ ...mockData, merge_train_index: 3 });
|
||||
|
||||
expect(store.mergeTrainIndex).toBe(3);
|
||||
});
|
||||
|
||||
it('should not set mergeTrainIndex when merge_train_index is not provided', () => {
|
||||
store.setData({ ...mockData, merge_train_index: undefined });
|
||||
|
||||
expect(store.mergeTrainIndex).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue