Commit f7f9847b authored by Eric Eastwood's avatar Eric Eastwood

Port fast-forward MR widget states from EE

See https://gitlab.com/gitlab-org/gitlab-ce/issues/20076
parent ccdf687b
...@@ -26,6 +26,7 @@ export default { ...@@ -26,6 +26,7 @@ export default {
<ci-icon v-else :status="statusObj" /> <ci-icon v-else :status="statusObj" />
<button <button
v-if="showDisabledButton" v-if="showDisabledButton"
ref="mergeButton"
type="button" type="button"
class="btn btn-success btn-sm" class="btn btn-success btn-sm"
disabled="true"> disabled="true">
......
...@@ -10,8 +10,18 @@ export default { ...@@ -10,8 +10,18 @@ export default {
}, },
template: ` template: `
<div class="mr-widget-body media"> <div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton /> <status-icon
ref="statusIcon"
status="failed"
showDisabledButton />
<div class="media-body space-children"> <div class="media-body space-children">
<template v-if="mr.ffOnlyEnabled">
<span class="bold">
Fast-forward merge is not possible.
To merge this request, first rebase locally
</span>
</template>
<template v-else>
<span class="bold"> <span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span> There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge"> <span v-if="!mr.canMerge">
...@@ -20,17 +30,20 @@ export default { ...@@ -20,17 +30,20 @@ export default {
</span> </span>
<a <a
v-if="mr.canMerge && mr.conflictResolutionPath" v-if="mr.canMerge && mr.conflictResolutionPath"
ref="resolveConflictsButton"
:href="mr.conflictResolutionPath" :href="mr.conflictResolutionPath"
class="btn btn-default btn-xs js-resolve-conflicts-button"> class="btn btn-default btn-xs">
Resolve conflicts Resolve conflicts
</a> </a>
<a <a
v-if="mr.canMerge" v-if="mr.canMerge"
class="btn btn-default btn-xs js-merge-locally-button" ref="mergeLocallyButton"
class="btn btn-default btn-xs"
data-toggle="modal" data-toggle="modal"
href="#modal_merge_info"> href="#modal_merge_info">
Merge locally Merge locally
</a> </a>
</template>
</div> </div>
</div> </div>
`, `,
......
...@@ -284,7 +284,14 @@ export default { ...@@ -284,7 +284,14 @@ export default {
:mr="mr" :mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" /> :is-merge-button-disabled="isMergeButtonDisabled" />
<span
v-if="mr.ffOnlyEnabled"
ref="fastForwardMessage">
Fast-forward merge without a merge commit
</span>
<button <button
v-else
ref="modifyCommitMessageButton"
@click="toggleCommitMessageEditor" @click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled" :disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs" class="btn btn-default btn-xs"
......
...@@ -57,6 +57,7 @@ export default class MergeRequestStore { ...@@ -57,6 +57,7 @@ export default class MergeRequestStore {
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false; this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false; this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false;
this.mergePath = data.merge_path; this.mergePath = data.merge_path;
this.ffOnlyEnabled = data.ff_only_enabled;
this.statusPath = data.status_path; this.statusPath = data.status_path;
this.emailPatchesPath = data.email_patches_path; this.emailPatchesPath = data.email_patches_path;
this.plainDiffPath = data.plain_diff_path; this.plainDiffPath = data.plain_diff_path;
......
...@@ -17,7 +17,6 @@ class MergeRequestEntity < IssuableEntity ...@@ -17,7 +17,6 @@ class MergeRequestEntity < IssuableEntity
merge_request.project.merge_requests_ff_only_enabled merge_request.project.merge_requests_ff_only_enabled
end end
# Events # Events
expose :merge_event, using: EventEntity expose :merge_event, using: EventEntity
expose :closed_event, using: EventEntity expose :closed_event, using: EventEntity
......
...@@ -2,17 +2,16 @@ import Vue from 'vue'; ...@@ -2,17 +2,16 @@ import Vue from 'vue';
import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts'; import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts';
const path = '/conflicts'; const path = '/conflicts';
const createComponent = () => { const createComponent = (customConfig = {}) => {
const Component = Vue.extend(conflictsComponent); const Component = Vue.extend(conflictsComponent);
const config = Object.assign({
mr: {},
}, customConfig);
return new Component({ return new Component({
el: document.createElement('div'), el: document.createElement('div'),
propsData: { propsData: config,
mr: {
canMerge: true,
conflictResolutionPath: path,
},
},
}); });
}; };
...@@ -27,44 +26,78 @@ describe('MRWidgetConflicts', () => { ...@@ -27,44 +26,78 @@ describe('MRWidgetConflicts', () => {
}); });
describe('template', () => { describe('template', () => {
it('should have correct elements', () => { describe('when allowed to merge', () => {
const el = createComponent().$el; let vm;
const resolveButton = el.querySelector('.js-resolve-conflicts-button');
const mergeButton = el.querySelector('.mr-widget-body .btn'); beforeEach(() => {
const mergeLocallyButton = el.querySelector('.js-merge-locally-button'); vm = createComponent({
mr: {
expect(el.textContent).toContain('There are merge conflicts'); canMerge: true,
expect(el.textContent).not.toContain('ask someone with write access'); conflictResolutionPath: path,
expect(el.querySelector('.btn-success').disabled).toBeTruthy(); },
});
});
it('should tell you about conflicts without bothering other people', () => {
expect(vm.$el.textContent).toContain('There are merge conflicts');
expect(vm.$el.textContent).not.toContain('ask someone with write access');
});
it('should allow you to resolve the conflicts', () => {
const resolveButton = vm.$refs.resolveConflictsButton;
expect(resolveButton.textContent).toContain('Resolve conflicts'); expect(resolveButton.textContent).toContain('Resolve conflicts');
expect(resolveButton.getAttribute('href')).toEqual(path); expect(resolveButton.getAttribute('href')).toEqual(path);
});
it('should have merge buttons', () => {
const mergeButton = vm.$refs.statusIcon.$refs.mergeButton;
const mergeLocallyButton = vm.$refs.mergeLocallyButton;
expect(mergeButton.textContent).toContain('Merge'); expect(mergeButton.textContent).toContain('Merge');
expect(mergeButton.disabled).toBeTruthy();
expect(mergeButton.classList.contains('btn-success')).toBeTruthy();
expect(mergeLocallyButton.textContent).toContain('Merge locally'); expect(mergeLocallyButton.textContent).toContain('Merge locally');
}); });
});
describe('when user does not have permission to merge', () => { describe('when user does not have permission to merge', () => {
let vm; let vm;
beforeEach(() => { beforeEach(() => {
vm = createComponent(); vm = createComponent({
vm.mr.canMerge = false; mr: {
canMerge: false,
},
});
}); });
it('should show proper message', (done) => { it('should show proper message', () => {
Vue.nextTick(() => {
expect(vm.$el.textContent).toContain('ask someone with write access'); expect(vm.$el.textContent).toContain('ask someone with write access');
done();
}); });
it('should not have action buttons', () => {
expect(vm.$refs.statusIcon.$refs.mergeButton).toBeDefined();
expect(vm.$refs.resolveConflictsButton).toBeUndefined();
expect(vm.$refs.mergeLocallyButton).toBeUndefined();
}); });
});
describe('when fast-forward merge enabled', () => {
let vm;
it('should not have action buttons', (done) => { beforeEach(() => {
Vue.nextTick(() => { vm = createComponent({
expect(vm.$el.querySelectorAll('.btn').length).toBe(1); mr: {
expect(vm.$el.querySelector('.js-resolve-conflicts-button')).toEqual(null); ffOnlyEnabled: true,
expect(vm.$el.querySelector('.js-merge-locally-button')).toEqual(null); },
done();
}); });
}); });
it('should tell you to rebase locally', () => {
expect(vm.$el.textContent).toContain('Fast-forward merge is not possible.');
expect(vm.$el.textContent).toContain('To merge this request, first rebase locally');
});
}); });
}); });
}); });
...@@ -182,36 +182,6 @@ describe('MRWidgetReadyToMerge', () => { ...@@ -182,36 +182,6 @@ describe('MRWidgetReadyToMerge', () => {
expect(vm.isMergeButtonDisabled).toBeTruthy(); expect(vm.isMergeButtonDisabled).toBeTruthy();
}); });
}); });
describe('Remove source branch checkbox', () => {
describe('when user can merge but cannot delete branch', () => {
it('isRemoveSourceBranchButtonDisabled should be true', () => {
expect(vm.isRemoveSourceBranchButtonDisabled).toBe(true);
});
it('should be disabled in the rendered output', () => {
const checkboxElement = vm.$el.querySelector('#remove-source-branch-input');
expect(checkboxElement.getAttribute('disabled')).toBe('disabled');
});
});
describe('when user can merge and can delete branch', () => {
beforeEach(() => {
this.customVm = createComponent({
mr: { canRemoveSourceBranch: true },
});
});
it('isRemoveSourceBranchButtonDisabled should be false', () => {
expect(this.customVm.isRemoveSourceBranchButtonDisabled).toBe(false);
});
it('should be enabled in rendered output', () => {
const checkboxElement = this.customVm.$el.querySelector('#remove-source-branch-input');
expect(checkboxElement.getAttribute('disabled')).toBeNull();
});
});
});
}); });
describe('methods', () => { describe('methods', () => {
...@@ -467,4 +437,54 @@ describe('MRWidgetReadyToMerge', () => { ...@@ -467,4 +437,54 @@ describe('MRWidgetReadyToMerge', () => {
}); });
}); });
}); });
describe('Remove source branch checkbox', () => {
describe('when user can merge but cannot delete branch', () => {
it('isRemoveSourceBranchButtonDisabled should be true', () => {
expect(vm.isRemoveSourceBranchButtonDisabled).toBe(true);
});
it('should be disabled in the rendered output', () => {
const checkboxElement = vm.$el.querySelector('#remove-source-branch-input');
expect(checkboxElement.getAttribute('disabled')).toBe('disabled');
});
});
describe('when user can merge and can delete branch', () => {
beforeEach(() => {
this.customVm = createComponent({
mr: { canRemoveSourceBranch: true },
});
});
it('isRemoveSourceBranchButtonDisabled should be false', () => {
expect(this.customVm.isRemoveSourceBranchButtonDisabled).toBe(false);
});
it('should be enabled in rendered output', () => {
const checkboxElement = this.customVm.$el.querySelector('#remove-source-branch-input');
expect(checkboxElement.getAttribute('disabled')).toBeNull();
});
});
});
describe('Commit message area', () => {
it('when using merge commits, should show "Modify commit message" button', () => {
const customVm = createComponent({
mr: { ffOnlyEnabled: false },
});
expect(customVm.$refs.fastForwardMessage).toBeUndefined();
expect(customVm.$refs.modifyCommitMessageButton).toBeDefined();
});
it('when fast-forward merge is enabled, only show fast-forward message', () => {
const customVm = createComponent({
mr: { ffOnlyEnabled: true },
});
expect(customVm.$refs.fastForwardMessage).toBeDefined();
expect(customVm.$refs.modifyCommitMessageButton).toBeUndefined();
});
});
}); });
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