summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_merge_request_widget/components/states
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_merge_request_widget/components/states')
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js7
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js23
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js18
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js11
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js7
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js14
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js576
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js9
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js42
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js182
13 files changed, 490 insertions, 411 deletions
diff --git a/spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js
index 8eeba4d6274..e4448346685 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import MergeChecksFailed from '~/vue_merge_request_widget/components/states/merge_checks_failed.vue';
import { DETAILED_MERGE_STATUS } from '~/vue_merge_request_widget/constants';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
let wrapper;
@@ -23,6 +24,7 @@ describe('Merge request widget merge checks failed state component', () => {
`('display $displayText text for $mrState', ({ mrState, displayText }) => {
factory({ mr: mrState });
- expect(wrapper.text()).toContain(MergeChecksFailed.i18n[displayText]);
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain(MergeChecksFailed.i18n[displayText]);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
index 5c07f4ce143..08700e834d7 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived.vue';
import StateContainer from '~/vue_merge_request_widget/components/state_container.vue';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
describe('MRWidgetArchived', () => {
let wrapper;
@@ -20,8 +21,8 @@ describe('MRWidgetArchived', () => {
});
it('renders information about merging', () => {
- expect(wrapper.text()).toContain(
- 'Merge unavailable: merge requests are read-only on archived projects.',
- );
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain('Merge unavailable:');
+ expect(message).toContain('merge requests are read-only on archived projects.');
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js
index 774e2bafed3..a6d3a6286a7 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js
@@ -58,13 +58,8 @@ describe('Commits header component', () => {
expect(findCommitToggle().attributes('aria-label')).toBe('Expand');
});
- it('has a chevron-right icon', async () => {
+ it('has a chevron-right icon', () => {
createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ expanded: false });
-
- await nextTick();
expect(findCommitToggle().props('icon')).toBe('chevron-right');
});
@@ -110,25 +105,21 @@ describe('Commits header component', () => {
});
describe('when expanded', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createComponent();
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ expanded: true });
+ findCommitToggle().trigger('click');
+ await nextTick();
});
- it('toggle has aria-label equal to collapse', async () => {
- await nextTick();
+ it('toggle has aria-label equal to collapse', () => {
expect(findCommitToggle().attributes('aria-label')).toBe('Collapse');
});
- it('has a chevron-down icon', async () => {
- await nextTick();
+ it('has a chevron-down icon', () => {
expect(findCommitToggle().props('icon')).toBe('chevron-down');
});
- it('has a collapse text', async () => {
- await nextTick();
+ it('has a collapse text', () => {
expect(findHeaderWrapper().text()).toBe('Collapse');
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js
index a16e4d4a6ea..2ca9dc61745 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js
@@ -12,9 +12,9 @@ describe('MRWidgetConflicts', () => {
const findResolveButton = () => wrapper.findByTestId('resolve-conflicts-button');
const findMergeLocalButton = () => wrapper.findByTestId('merge-locally-button');
- const mergeConflictsText = 'Merge blocked: merge conflicts must be resolved.';
+ const mergeConflictsText = 'merge conflicts must be resolved.';
const fastForwardMergeText =
- 'Merge blocked: fast-forward merge is not possible. To merge this request, first rebase locally.';
+ 'fast-forward merge is not possible. To merge this request, first rebase locally.';
const userCannotMergeText =
'Users who can write to the source or target branches can resolve the conflicts.';
const resolveConflictsBtnText = 'Resolve conflicts';
@@ -76,8 +76,9 @@ describe('MRWidgetConflicts', () => {
});
it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain(mergeConflictsText);
+ expect(text).not.toContain(userCannotMergeText);
});
it('should not allow you to resolve the conflicts', () => {
@@ -102,8 +103,8 @@ describe('MRWidgetConflicts', () => {
});
it('should tell you about conflicts', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).toContain(userCannotMergeText);
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain(userCannotMergeText);
});
it('should allow you to resolve the conflicts', () => {
@@ -129,8 +130,9 @@ describe('MRWidgetConflicts', () => {
});
it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain(mergeConflictsText);
+ expect(text).not.toContain(userCannotMergeText);
});
it('should allow you to resolve the conflicts', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js
index 49bd3739fdb..5408f731b34 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js
@@ -1,6 +1,7 @@
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';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
jest.mock('~/lib/utils/simple_poll', () =>
jest.fn().mockImplementation(jest.requireActual('~/lib/utils/simple_poll').default),
@@ -33,14 +34,8 @@ describe('MRWidgetMerging', () => {
});
it('renders information about merge request being merged', () => {
- expect(
- wrapper
- .find('.media-body')
- .text()
- .trim()
- .replace(/\s\s+/g, ' ')
- .replace(/[\r\n]+/g, ' '),
- ).toContain('Merging!');
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain('Merging!');
});
describe('initiateMergePolling', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
index c6e7198c678..42515c597c5 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import notAllowedComponent from '~/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
describe('MRWidgetNotAllowed', () => {
let wrapper;
@@ -20,8 +21,9 @@ describe('MRWidgetNotAllowed', () => {
});
it('renders informative text', () => {
- expect(wrapper.text()).toContain('Ready to be merged automatically.');
- expect(wrapper.text()).toContain(
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain('Ready to be merged automatically.');
+ expect(message).toContain(
'Ask someone with write access to this repository to merge this request',
);
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
index 4219ad70b4c..c0197b5e20a 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import PipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
describe('MRWidgetPipelineBlocked', () => {
let wrapper;
@@ -20,8 +21,10 @@ describe('MRWidgetPipelineBlocked', () => {
});
it('renders information text', () => {
- expect(wrapper.text()).toBe(
- "Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.",
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain('Merge blocked:');
+ expect(message).toContain(
+ "pipeline must succeed. It's waiting for a manual action to continue.",
);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
index bd158d59d74..8bae2b62ed1 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
@@ -1,7 +1,9 @@
import { GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { removeBreakLine } from 'helpers/text_helper';
import PipelineFailed from '~/vue_merge_request_widget/components/states/pipeline_failed.vue';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import BoldText from '~/vue_merge_request_widget/components/bold_text.vue';
describe('PipelineFailed', () => {
let wrapper;
@@ -32,16 +34,20 @@ describe('PipelineFailed', () => {
it('should render error message with a disabled merge button', () => {
createComponent();
- expect(wrapper.text()).toContain('Merge blocked: pipeline must succeed.');
- expect(wrapper.text()).toContain('Push a commit that fixes the failure');
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain('Merge blocked:');
+ expect(text).toContain('pipeline must succeed');
+ expect(text).toContain('Push a commit that fixes the failure');
expect(wrapper.findComponent(GlLink).text()).toContain('learn about other solutions');
});
it('should render pipeline blocked message', () => {
createComponent({ isPipelineBlocked: true });
- expect(wrapper.text()).toContain(
- "Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.",
+ const message = wrapper.findComponent(BoldText).props('message');
+ expect(message).toContain('Merge blocked:');
+ expect(message).toContain(
+ "pipeline must succeed. It's waiting for a manual action to continue.",
);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
index d34fc0c1e61..1e4e089e7c1 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -79,13 +79,10 @@ const createTestService = () => ({
Vue.use(VueApollo);
+let service;
let wrapper;
let readyToMergeResponseSpy;
-const findMergeButton = () => wrapper.find('[data-testid="merge-button"]');
-const findPipelineFailedConfirmModal = () =>
- wrapper.findComponent(MergeFailedPipelineConfirmationDialog);
-
const createReadyToMergeResponse = (customMr) => {
return produce(readyToMergeResponse, (draft) => {
Object.assign(draft.data.project.mergeRequest, customMr);
@@ -96,7 +93,7 @@ const createComponent = (customConfig = {}, createState = true) => {
wrapper = shallowMount(ReadyToMerge, {
propsData: {
mr: createTestMr(customConfig),
- service: createTestService(),
+ service,
},
data() {
if (createState) {
@@ -119,6 +116,13 @@ const createComponent = (customConfig = {}, createState = true) => {
});
};
+const findMergeButton = () => wrapper.find('[data-testid="merge-button"]');
+const findMergeImmediatelyDropdown = () =>
+ wrapper.find('[data-testid="merge-immediately-dropdown"');
+const findSourceBranchDeletedText = () =>
+ wrapper.find('[data-testid="source-branch-deleted-text"]');
+const findPipelineFailedConfirmModal = () =>
+ wrapper.findComponent(MergeFailedPipelineConfirmationDialog);
const findCheckboxElement = () => wrapper.findComponent(SquashBeforeMerge);
const findCommitEditElements = () => wrapper.findAllComponents(CommitEdit);
const findCommitDropdownElement = () => wrapper.findComponent(CommitMessageDropdown);
@@ -129,33 +133,20 @@ const findCommitEditWithInputId = (inputId) =>
const findMergeCommitMessage = () => findCommitEditWithInputId('merge-message-edit').props('value');
const findSquashCommitMessage = () =>
findCommitEditWithInputId('squash-message-edit').props('value');
+const findDeleteSourceBranchCheckbox = () =>
+ wrapper.find('[data-testid="delete-source-branch-checkbox"]');
const triggerApprovalUpdated = () => eventHub.$emit('ApprovalUpdated');
+const triggerEditCommitInput = () =>
+ wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
describe('ReadyToMerge', () => {
beforeEach(() => {
+ service = createTestService();
readyToMergeResponseSpy = jest.fn().mockResolvedValueOnce(readyToMergeResponse);
});
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('computed', () => {
- describe('isAutoMergeAvailable', () => {
- it('should return true when at least one merge strategy is available', () => {
- createComponent({});
-
- expect(wrapper.vm.isAutoMergeAvailable).toBe(true);
- });
-
- it('should return false when no merge strategies are available', () => {
- createComponent({ mr: { availableAutoMergeStrategies: [] } });
-
- expect(wrapper.vm.isAutoMergeAvailable).toBe(false);
- });
- });
-
describe('status', () => {
it('defaults to success', () => {
createComponent({ mr: { pipeline: true, availableAutoMergeStrategies: [] } });
@@ -190,16 +181,6 @@ describe('ReadyToMerge', () => {
});
});
- describe('Merge Button Variant', () => {
- it('defaults to confirm class', () => {
- createComponent({
- mr: { availableAutoMergeStrategies: [], mergeable: true },
- });
-
- expect(findMergeButton().attributes('variant')).toBe('confirm');
- });
- });
-
describe('status icon', () => {
it('defaults to tick icon', () => {
createComponent({ mr: { mergeable: true } });
@@ -219,334 +200,313 @@ describe('ReadyToMerge', () => {
expect(wrapper.vm.iconClass).toEqual('success');
});
});
+ });
- describe('mergeButtonText', () => {
- it('should return "Merge" when no auto merge strategies are available', () => {
- createComponent({ mr: { availableAutoMergeStrategies: [] } });
+ describe('merge button text', () => {
+ it('should return "Merge" when no auto merge strategies are available', () => {
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
+
+ expect(findMergeButton().text()).toBe('Merge');
+ });
- expect(wrapper.vm.mergeButtonText).toEqual('Merge');
+ it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
+ createComponent({
+ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY },
});
- it('should return "Merge in progress"', async () => {
- createComponent();
+ expect(findMergeButton().text()).toBe('Merge when pipeline succeeds');
+ });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ isMergingImmediately: true });
+ it('should return Merge when pipeline succeeds', () => {
+ createComponent({ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY } });
- await nextTick();
+ expect(findMergeButton().text()).toBe('Merge when pipeline succeeds');
+ });
+ });
- expect(wrapper.vm.mergeButtonText).toEqual('Merge in progress');
+ describe('merge immediately dropdown', () => {
+ it('dropdown should be hidden if no pipeline is active', () => {
+ createComponent({
+ mr: { isPipelineActive: false, onlyAllowMergeIfPipelineSucceeds: false },
});
- it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
- createComponent({
- mr: { isMergingImmediately: false, preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY },
- });
+ expect(findMergeImmediatelyDropdown().exists()).toBe(false);
+ });
- expect(wrapper.vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
- });
+ it('dropdown should be hidden if "Pipelines must succeed" is enabled', () => {
+ createComponent({ mr: { isPipelineActive: true, onlyAllowMergeIfPipelineSucceeds: true } });
+
+ expect(findMergeImmediatelyDropdown().exists()).toBe(false);
});
+ });
- describe('autoMergeText', () => {
- it('should return Merge when pipeline succeeds', () => {
- createComponent({ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY } });
+ describe('merge button disabled state', () => {
+ it('should not be disabled initally', () => {
+ createComponent();
- expect(wrapper.vm.autoMergeText).toEqual('Merge when pipeline succeeds');
- });
+ expect(findMergeButton().props('disabled')).toBe(false);
});
- describe('shouldShowMergeImmediatelyDropdown', () => {
- it('should return false if no pipeline is active', () => {
- createComponent({
- mr: { isPipelineActive: false, onlyAllowMergeIfPipelineSucceeds: false },
- });
+ it('should be disabled when there is no commit message', () => {
+ createComponent({ mr: { commitMessage: '' } });
- expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
- });
+ expect(findMergeButton().props('disabled')).toBe(true);
+ });
- it('should return false if "Pipelines must succeed" is enabled for the current project', () => {
- createComponent({ mr: { isPipelineActive: true, onlyAllowMergeIfPipelineSucceeds: true } });
+ it('should be disabled if merge is not allowed', () => {
+ createComponent({ mr: { preventMerge: true } });
- expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
- });
+ expect(findMergeButton().props('disabled')).toBe(true);
});
- describe('isMergeButtonDisabled', () => {
- it('should return false with initial data', () => {
- createComponent({ mr: { isMergeAllowed: true, mergeable: false } });
+ it('should be disabled when making request', async () => {
+ createComponent({ mr: { isMergeAllowed: true } }, true);
- expect(wrapper.vm.isMergeButtonDisabled).toBe(false);
- });
+ findMergeButton().vm.$emit('click');
- it('should return true when there is no commit message', () => {
- createComponent({ mr: { isMergeAllowed: true, commitMessage: '' } });
+ await nextTick();
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
+ expect(findMergeButton().props('disabled')).toBe(true);
+ });
+ });
- it('should return true if merge is not allowed', () => {
+ describe('sourceBranchDeletedText', () => {
+ const should = 'Source branch will be deleted.';
+ const shouldNot = 'Source branch will not be deleted.';
+ const did = 'Deleted the source branch.';
+ const didNot = 'Did not delete the source branch.';
+ const scenarios = [
+ "the MR hasn't merged yet, and the backend-provided value expects to delete the branch",
+ "the MR hasn't merged yet, and the backend-provided value expects to leave the branch",
+ "the MR hasn't merged yet, and the backend-provided value is a non-boolean falsey value",
+ "the MR hasn't merged yet, and the backend-provided value is a non-boolean truthy value",
+ 'the MR has been merged, and the backend reports that the branch has been removed',
+ 'the MR has been merged, and the backend reports that the branch has not been removed',
+ 'the MR has been merged, and the backend reports a non-boolean falsey value',
+ 'the MR has been merged, and the backend reports a non-boolean truthy value',
+ ];
+
+ it.each`
+ describe | premerge | mrShould | mrRemoved | output
+ ${scenarios[0]} | ${true} | ${true} | ${null} | ${should}
+ ${scenarios[1]} | ${true} | ${false} | ${null} | ${shouldNot}
+ ${scenarios[2]} | ${true} | ${null} | ${null} | ${shouldNot}
+ ${scenarios[3]} | ${true} | ${'yeah'} | ${null} | ${should}
+ ${scenarios[4]} | ${false} | ${null} | ${true} | ${did}
+ ${scenarios[5]} | ${false} | ${null} | ${false} | ${didNot}
+ ${scenarios[6]} | ${false} | ${null} | ${null} | ${didNot}
+ ${scenarios[7]} | ${false} | ${null} | ${'yep'} | ${did}
+ `(
+ 'in the case that $describe, returns "$output"',
+ ({ premerge, mrShould, mrRemoved, output }) => {
createComponent({
mr: {
- isMergeAllowed: false,
- availableAutoMergeStrategies: [],
- onlyAllowMergeIfPipelineSucceeds: true,
- mergeable: false,
+ state: !premerge ? 'merged' : 'literally-anything-else',
+ shouldRemoveSourceBranch: mrShould,
+ sourceBranchRemoved: mrRemoved,
+ autoMergeEnabled: true,
},
});
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
-
- it('should return true when the vm instance is making request', async () => {
- createComponent({ mr: { isMergeAllowed: true } });
-
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ isMakingRequest: true });
-
- await nextTick();
-
- expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
- });
- });
-
- describe('sourceBranchDeletedText', () => {
- const should = 'Source branch will be deleted.';
- const shouldNot = 'Source branch will not be deleted.';
- const did = 'Deleted the source branch.';
- const didNot = 'Did not delete the source branch.';
- const scenarios = [
- "the MR hasn't merged yet, and the backend-provided value expects to delete the branch",
- "the MR hasn't merged yet, and the backend-provided value expects to leave the branch",
- "the MR hasn't merged yet, and the backend-provided value is a non-boolean falsey value",
- "the MR hasn't merged yet, and the backend-provided value is a non-boolean truthy value",
- 'the MR has been merged, and the backend reports that the branch has been removed',
- 'the MR has been merged, and the backend reports that the branch has not been removed',
- 'the MR has been merged, and the backend reports a non-boolean falsey value',
- 'the MR has been merged, and the backend reports a non-boolean truthy value',
- ];
-
- it.each`
- describe | premerge | mrShould | mrRemoved | output
- ${scenarios[0]} | ${true} | ${true} | ${null} | ${should}
- ${scenarios[1]} | ${true} | ${false} | ${null} | ${shouldNot}
- ${scenarios[2]} | ${true} | ${null} | ${null} | ${shouldNot}
- ${scenarios[3]} | ${true} | ${'yeah'} | ${null} | ${should}
- ${scenarios[4]} | ${false} | ${null} | ${true} | ${did}
- ${scenarios[5]} | ${false} | ${null} | ${false} | ${didNot}
- ${scenarios[6]} | ${false} | ${null} | ${null} | ${didNot}
- ${scenarios[7]} | ${false} | ${null} | ${'yep'} | ${did}
- `(
- 'in the case that $describe, returns "$output"',
- ({ premerge, mrShould, mrRemoved, output }) => {
- createComponent({
- mr: {
- state: !premerge ? 'merged' : 'literally-anything-else',
- shouldRemoveSourceBranch: mrShould,
- sourceBranchRemoved: mrRemoved,
- },
- });
-
- expect(wrapper.vm.sourceBranchDeletedText).toBe(output);
- },
- );
- });
+ expect(findSourceBranchDeletedText().text()).toBe(output);
+ },
+ );
});
- describe('methods', () => {
- describe('handleMergeButtonClick', () => {
- const response = (status) => ({
- data: {
- status,
- },
+ describe('Merge Button Variant', () => {
+ it('defaults to confirm class', () => {
+ createComponent({
+ mr: { availableAutoMergeStrategies: [], mergeable: true },
});
- beforeEach(() => {
- readyToMergeResponseSpy = jest
- .fn()
- .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true }))
- .mockResolvedValue(
- createReadyToMergeResponse({
- squash: true,
- squashOnMerge: true,
- defaultMergeCommitMessage: '',
- defaultSquashCommitMessage: '',
- }),
- );
- });
+ expect(findMergeButton().attributes('variant')).toBe('confirm');
+ });
+ });
- it('should handle merge when pipeline succeeds', async () => {
- createComponent();
+ describe('Merge button click', () => {
+ const response = (status) => ({
+ data: {
+ status,
+ },
+ });
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest
- .spyOn(wrapper.vm.service, 'merge')
- .mockResolvedValue(response('merge_when_pipeline_succeeds'));
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ removeSourceBranch: false });
+ beforeEach(() => {
+ readyToMergeResponseSpy = jest
+ .fn()
+ .mockResolvedValueOnce(createReadyToMergeResponse({ squash: true, squashOnMerge: true }))
+ .mockResolvedValue(
+ createReadyToMergeResponse({
+ squash: true,
+ squashOnMerge: true,
+ defaultMergeCommitMessage: '',
+ defaultSquashCommitMessage: '',
+ }),
+ );
+ });
- wrapper.vm.handleMergeButtonClick(true);
+ it('should handle merge when pipeline succeeds', async () => {
+ createComponent({ mr: { shouldRemoveSourceBranch: false } }, true);
- await waitForPromises();
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(service, 'merge').mockResolvedValue(response('merge_when_pipeline_succeeds'));
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
- transition: 'start-auto-merge',
- });
+ findMergeButton().vm.$emit('click');
- const params = wrapper.vm.service.merge.mock.calls[0][0];
+ await waitForPromises();
- expect(params).toEqual(
- expect.objectContaining({
- sha: wrapper.vm.mr.sha,
- commit_message: wrapper.vm.mr.commitMessage,
- should_remove_source_branch: false,
- auto_merge_strategy: 'merge_when_pipeline_succeeds',
- }),
- );
+ expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
+ expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
+ transition: 'start-auto-merge',
});
- it('should handle merge failed', async () => {
- createComponent();
+ const params = service.merge.mock.calls[0][0];
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('failed'));
- wrapper.vm.handleMergeButtonClick(false, true);
+ expect(params).toEqual(
+ expect.objectContaining({
+ sha: '12345678',
+ commit_message: commitMessage,
+ should_remove_source_branch: false,
+ auto_merge_strategy: 'merge_when_pipeline_succeeds',
+ }),
+ );
+ });
- await waitForPromises();
+ it('should handle merge failed', async () => {
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
- expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(service, 'merge').mockResolvedValue(response('failed'));
- const params = wrapper.vm.service.merge.mock.calls[0][0];
+ findMergeButton().vm.$emit('click');
- expect(params.should_remove_source_branch).toBe(true);
- expect(params.auto_merge_strategy).toBeUndefined();
- });
+ await waitForPromises();
- it('should handle merge action accepted case', async () => {
- createComponent();
+ expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('success'));
- jest.spyOn(wrapper.vm.mr, 'transitionStateMachine');
- wrapper.vm.handleMergeButtonClick();
+ const params = service.merge.mock.calls[0][0];
- expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
- transition: 'start-merge',
- });
+ expect(params.should_remove_source_branch).toBe(true);
+ expect(params.auto_merge_strategy).toBeUndefined();
+ });
- await waitForPromises();
+ it('should handle merge action accepted case', async () => {
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
- expect(wrapper.vm.mr.transitionStateMachine).toHaveBeenCalledWith({
- transition: 'start-merge',
- });
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(service, 'merge').mockResolvedValue(response('success'));
+ jest.spyOn(wrapper.vm.mr, 'transitionStateMachine');
- const params = wrapper.vm.service.merge.mock.calls[0][0];
+ findMergeButton().vm.$emit('click');
- expect(params.should_remove_source_branch).toBe(true);
- expect(params.auto_merge_strategy).toBeUndefined();
+ expect(eventHub.$emit).toHaveBeenCalledWith('StateMachineValueChanged', {
+ transition: 'start-merge',
});
- it('hides edit commit message', async () => {
- createComponent({}, true, true);
+ await waitForPromises();
- await waitForPromises();
+ expect(wrapper.vm.mr.transitionStateMachine).toHaveBeenCalledWith({
+ transition: 'start-merge',
+ });
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'merge').mockResolvedValue(response('success'));
+ const params = service.merge.mock.calls[0][0];
- await wrapper
- .findComponent('[data-testid="widget_edit_commit_message"]')
- .vm.$emit('input', true);
+ expect(params.should_remove_source_branch).toBe(true);
+ expect(params.auto_merge_strategy).toBeUndefined();
+ });
- expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(true);
+ it('hides edit commit message', async () => {
+ createComponent();
- wrapper.vm.handleMergeButtonClick();
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(service, 'merge').mockResolvedValue(response('success'));
- await waitForPromises();
+ await triggerEditCommitInput();
- expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(false);
- });
+ expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(true);
+
+ findMergeButton().vm.$emit('click');
+
+ await waitForPromises();
+
+ expect(wrapper.findComponent('[data-testid="edit_commit_message"]').exists()).toBe(false);
});
+ });
- describe('initiateRemoveSourceBranchPolling', () => {
- it('should emit event and call simplePoll', () => {
- createComponent();
+ describe('initiateRemoveSourceBranchPolling', () => {
+ it('should emit event and call simplePoll', () => {
+ createComponent();
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- wrapper.vm.initiateRemoveSourceBranchPolling();
+ wrapper.vm.initiateRemoveSourceBranchPolling();
- expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
- expect(simplePoll).toHaveBeenCalled();
- });
+ expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
+ expect(simplePoll).toHaveBeenCalled();
});
+ });
- describe('handleRemoveBranchPolling', () => {
- const response = (state) => ({
- data: {
- source_branch_exists: state,
- },
- });
+ describe('handleRemoveBranchPolling', () => {
+ const response = (state) => ({
+ data: {
+ source_branch_exists: state,
+ },
+ });
- it('should call start and stop polling when MR merged', async () => {
- createComponent();
+ it('should call start and stop polling when MR merged', async () => {
+ createComponent();
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(wrapper.vm.service, 'poll').mockResolvedValue(response(false));
+ jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
+ jest.spyOn(service, 'poll').mockResolvedValue(response(false));
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
+ let cpc = false; // continuePollingCalled
+ let spc = false; // stopPollingCalled
- wrapper.vm.handleRemoveBranchPolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
+ wrapper.vm.handleRemoveBranchPolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
- await waitForPromises();
+ await waitForPromises();
- expect(wrapper.vm.service.poll).toHaveBeenCalled();
+ expect(service.poll).toHaveBeenCalled();
- const args = eventHub.$emit.mock.calls[0];
+ const args = eventHub.$emit.mock.calls[0];
- expect(args[0]).toEqual('MRWidgetUpdateRequested');
- expect(args[1]).toBeDefined();
- args[1]();
+ expect(args[0]).toEqual('MRWidgetUpdateRequested');
+ expect(args[1]).toBeDefined();
+ args[1]();
- expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [false]);
+ expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [false]);
- expect(cpc).toBe(false);
- expect(spc).toBe(true);
- });
+ expect(cpc).toBe(false);
+ expect(spc).toBe(true);
+ });
- it('should continue polling until MR is merged', async () => {
- createComponent();
+ it('should continue polling until MR is merged', async () => {
+ createComponent();
- jest.spyOn(wrapper.vm.service, 'poll').mockResolvedValue(response(true));
+ jest.spyOn(service, 'poll').mockResolvedValue(response(true));
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
+ let cpc = false; // continuePollingCalled
+ let spc = false; // stopPollingCalled
- wrapper.vm.handleRemoveBranchPolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
+ wrapper.vm.handleRemoveBranchPolling(
+ () => {
+ cpc = true;
+ },
+ () => {
+ spc = true;
+ },
+ );
- await waitForPromises();
+ await waitForPromises();
- expect(cpc).toBe(true);
- expect(spc).toBe(false);
- });
+ expect(cpc).toBe(true);
+ expect(spc).toBe(false);
});
});
@@ -563,7 +523,7 @@ describe('ReadyToMerge', () => {
},
});
- expect(wrapper.find('#remove-source-branch-input').exists()).toBe(false);
+ expect(findDeleteSourceBranchCheckbox().exists()).toBe(false);
});
});
@@ -575,7 +535,7 @@ describe('ReadyToMerge', () => {
});
it('isRemoveSourceBranchButtonDisabled should be false', () => {
- expect(wrapper.find('#remove-source-branch-input').props('disabled')).toBe(undefined);
+ expect(findDeleteSourceBranchCheckbox().props('disabled')).toBe(undefined);
});
});
});
@@ -646,7 +606,7 @@ describe('ReadyToMerge', () => {
},
});
- expect(findCommitEditElements().length).toBe(0);
+ expect(findCommitEditElements()).toHaveLength(0);
});
it('should not be rendered if squash before merge is disabled', () => {
@@ -659,7 +619,7 @@ describe('ReadyToMerge', () => {
},
});
- expect(findCommitEditElements().length).toBe(0);
+ expect(findCommitEditElements()).toHaveLength(0);
});
it('should not be rendered if there is only one commit', () => {
@@ -672,7 +632,7 @@ describe('ReadyToMerge', () => {
},
});
- expect(findCommitEditElements().length).toBe(0);
+ expect(findCommitEditElements()).toHaveLength(0);
});
it('should have one edit component if squash is enabled and there is more than 1 commit', async () => {
@@ -686,9 +646,9 @@ describe('ReadyToMerge', () => {
},
});
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
- expect(findCommitEditElements().length).toBe(1);
+ expect(findCommitEditElements()).toHaveLength(1);
expect(findFirstCommitEditLabel()).toBe('Squash commit message');
});
});
@@ -702,16 +662,15 @@ describe('ReadyToMerge', () => {
},
});
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
- expect(findCommitEditElements().length).toBe(2);
+ expect(findCommitEditElements()).toHaveLength(2);
});
- it('should have two edit components when squash is enabled and there is more than 1 commit and mergeRequestWidgetGraphql is enabled', async () => {
+ it('should have two edit components when squash is enabled', async () => {
createComponent(
{
mr: {
- commitsCount: 2,
squashIsSelected: true,
enableSquashBeforeMerge: true,
},
@@ -719,37 +678,9 @@ describe('ReadyToMerge', () => {
true,
);
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- loading: false,
- state: {
- ...createTestMr({}),
- userPermissions: {},
- squash: true,
- mergeable: true,
- commitCount: 2,
- commitsWithoutMergeCommits: {},
- },
- });
- await nextTick();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
-
- expect(findCommitEditElements().length).toBe(2);
- });
-
- it('should have one edit components when squash is enabled and there is 1 commit only', async () => {
- createComponent({
- mr: {
- commitsCount: 1,
- squash: true,
- enableSquashBeforeMerge: true,
- },
- });
+ await triggerEditCommitInput();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
-
- expect(findCommitEditElements().length).toBe(1);
+ expect(findCommitEditElements()).toHaveLength(2);
});
it('should have correct edit squash commit label', async () => {
@@ -761,7 +692,7 @@ describe('ReadyToMerge', () => {
},
});
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
expect(findFirstCommitEditLabel()).toBe('Squash commit message');
});
@@ -779,7 +710,7 @@ describe('ReadyToMerge', () => {
mr: { enableSquashBeforeMerge: true, squashIsSelected: true, commitsCount: 2 },
});
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
expect(findCommitDropdownElement().exists()).toBe(true);
});
@@ -788,7 +719,7 @@ describe('ReadyToMerge', () => {
it('renders a tip including a link to docs on templates', async () => {
createComponent();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
expect(findTipLink().exists()).toBe(true);
});
@@ -891,7 +822,8 @@ describe('ReadyToMerge', () => {
createDefaultGqlComponent();
await waitForPromises();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+
+ await triggerEditCommitInput();
expect(finderFn()).toBe(initialValue);
});
@@ -899,7 +831,7 @@ describe('ReadyToMerge', () => {
it('should have updated value after graphql refetch', async () => {
createDefaultGqlComponent();
await waitForPromises();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
triggerApprovalUpdated();
await waitForPromises();
@@ -910,7 +842,7 @@ describe('ReadyToMerge', () => {
it('should not update if user has touched', async () => {
createDefaultGqlComponent();
await waitForPromises();
- await wrapper.find('[data-testid="widget_edit_commit_message"]').vm.$emit('input', true);
+ await triggerEditCommitInput();
const input = wrapper.find(inputId);
input.element.value = USER_COMMIT_MESSAGE;
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js
index 2a343997cf5..aaa4591d67d 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js
@@ -25,7 +25,7 @@ describe('ShaMismatch', () => {
});
it('should render warning message', () => {
- expect(wrapper.element.innerText).toContain(I18N_SHA_MISMATCH.warningMessage);
+ expect(wrapper.text()).toContain('Merge blocked: new changes were just added.');
});
it('action button should have correct label', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js
index e2d79c61b9b..c97b42f61ac 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js
@@ -1,5 +1,6 @@
import { mount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
+import { removeBreakLine } from 'helpers/text_helper';
import notesEventHub from '~/notes/event_hub';
import UnresolvedDiscussions from '~/vue_merge_request_widget/components/states/unresolved_discussions.vue';
@@ -42,7 +43,9 @@ describe('UnresolvedDiscussions', () => {
});
it('should have correct elements', () => {
- expect(wrapper.element.innerText).toContain(`Merge blocked: all threads must be resolved.`);
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain('Merge blocked:');
+ expect(text).toContain('all threads must be resolved.');
expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
expect(wrapper.element.innerText).toContain('Create issue to resolve all threads');
@@ -54,7 +57,9 @@ describe('UnresolvedDiscussions', () => {
describe('without threads path', () => {
it('should not show create issue link if user cannot create issue', () => {
- expect(wrapper.element.innerText).toContain(`Merge blocked: all threads must be resolved.`);
+ const text = removeBreakLine(wrapper.text()).trim();
+ expect(text).toContain('Merge blocked:');
+ expect(text).toContain('all threads must be resolved.');
expect(wrapper.element.innerText).toContain('Jump to first unresolved thread');
expect(wrapper.element.innerText).not.toContain('Create issue to resolve all threads');
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js
deleted file mode 100644
index 82aeac1a47d..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { mount } from '@vue/test-utils';
-import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
-
-let wrapper;
-
-const createComponent = (updateMergeRequest = true) => {
- wrapper = mount(WorkInProgress, {
- propsData: {
- mr: {},
- },
- data() {
- return {
- userPermissions: {
- updateMergeRequest,
- },
- };
- },
- });
-};
-
-describe('Merge request widget draft state component', () => {
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('template', () => {
- it('should have correct elements', () => {
- createComponent(true);
-
- expect(wrapper.text()).toContain(
- "Merge blocked: merge request must be marked as ready. It's still marked as draft.",
- );
- expect(wrapper.find('[data-testid="removeWipButton"]').text()).toContain('Mark as ready');
- });
-
- it('should not show removeWIP button is user cannot update MR', () => {
- createComponent(false);
-
- expect(wrapper.find('[data-testid="removeWipButton"]').exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js b/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js
new file mode 100644
index 00000000000..e610ceb2122
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js
@@ -0,0 +1,182 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import getStateQueryResponse from 'test_fixtures/graphql/merge_requests/get_state.query.graphql.json';
+import { createAlert } from '~/flash';
+import WorkInProgress, {
+ MSG_SOMETHING_WENT_WRONG,
+ MSG_MARK_READY,
+} from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
+import draftQuery from '~/vue_merge_request_widget/queries/states/draft.query.graphql';
+import getStateQuery from '~/vue_merge_request_widget/queries/get_state.query.graphql';
+import removeDraftMutation from '~/vue_merge_request_widget/queries/toggle_draft.mutation.graphql';
+import MergeRequest from '~/merge_request';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+
+Vue.use(VueApollo);
+
+const TEST_PROJECT_ID = getStateQueryResponse.data.project.id;
+const TEST_MR_ID = getStateQueryResponse.data.project.mergeRequest.id;
+const TEST_MR_IID = '23';
+const TEST_MR_TITLE = 'Test MR Title';
+const TEST_PROJECT_PATH = 'lorem/ipsum';
+
+jest.mock('~/flash');
+jest.mock('~/merge_request');
+
+describe('~/vue_merge_request_widget/components/states/work_in_progress.vue', () => {
+ let wrapper;
+ let apolloProvider;
+
+ let draftQuerySpy;
+ let removeDraftMutationSpy;
+
+ const findWIPButton = () => wrapper.findByTestId('removeWipButton');
+
+ const createDraftQueryResponse = (canUpdateMergeRequest) => ({
+ data: {
+ project: {
+ __typename: 'Project',
+ id: TEST_PROJECT_ID,
+ mergeRequest: {
+ __typename: 'MergeRequest',
+ id: TEST_MR_ID,
+ userPermissions: {
+ updateMergeRequest: canUpdateMergeRequest,
+ },
+ },
+ },
+ },
+ });
+ const createRemoveDraftMutationResponse = () => ({
+ data: {
+ mergeRequestSetDraft: {
+ __typename: 'MergeRequestSetWipPayload',
+ errors: [],
+ mergeRequest: {
+ __typename: 'MergeRequest',
+ id: TEST_MR_ID,
+ title: TEST_MR_TITLE,
+ draft: false,
+ mergeableDiscussionsState: true,
+ },
+ },
+ },
+ });
+
+ const createComponent = async () => {
+ wrapper = mountExtended(WorkInProgress, {
+ apolloProvider,
+ propsData: {
+ mr: {
+ issuableId: TEST_MR_ID,
+ title: TEST_MR_TITLE,
+ iid: TEST_MR_IID,
+ targetProjectFullPath: TEST_PROJECT_PATH,
+ },
+ },
+ });
+
+ await waitForPromises();
+
+ // why: work_in_progress.vue has some coupling that this query has been read before
+ // for some reason this has to happen **after** the component has mounted
+ // or apollo throws errors.
+ apolloProvider.defaultClient.cache.writeQuery({
+ query: getStateQuery,
+ variables: {
+ projectPath: TEST_PROJECT_PATH,
+ iid: TEST_MR_IID,
+ },
+ data: getStateQueryResponse.data,
+ });
+ };
+
+ beforeEach(() => {
+ draftQuerySpy = jest.fn().mockResolvedValue(createDraftQueryResponse(true));
+ removeDraftMutationSpy = jest.fn().mockResolvedValue(createRemoveDraftMutationResponse());
+
+ apolloProvider = createMockApollo([
+ [draftQuery, draftQuerySpy],
+ [removeDraftMutation, removeDraftMutationSpy],
+ ]);
+ });
+
+ describe('when user can update MR', () => {
+ beforeEach(async () => {
+ await createComponent();
+ });
+
+ it('renders text', () => {
+ const message = wrapper.text();
+ expect(message).toContain('Merge blocked:');
+ expect(message).toContain('Select Mark as ready to remove it from Draft status.');
+ });
+
+ it('renders mark ready button', () => {
+ expect(findWIPButton().text()).toBe(MSG_MARK_READY);
+ });
+
+ it('does not call remove draft mutation', () => {
+ expect(removeDraftMutationSpy).not.toHaveBeenCalled();
+ });
+
+ describe('when mark ready button is clicked', () => {
+ beforeEach(async () => {
+ findWIPButton().vm.$emit('click');
+
+ await waitForPromises();
+ });
+
+ it('calls mutation spy', () => {
+ expect(removeDraftMutationSpy).toHaveBeenCalledWith({
+ draft: false,
+ iid: TEST_MR_IID,
+ projectPath: TEST_PROJECT_PATH,
+ });
+ });
+
+ it('does not create alert', () => {
+ expect(createAlert).not.toHaveBeenCalled();
+ });
+
+ it('calls toggleDraftStatus', () => {
+ expect(MergeRequest.toggleDraftStatus).toHaveBeenCalledWith(TEST_MR_TITLE, true);
+ });
+ });
+
+ describe('when mutation fails and ready button is clicked', () => {
+ beforeEach(async () => {
+ removeDraftMutationSpy.mockRejectedValue(new Error('TEST FAIL'));
+ findWIPButton().vm.$emit('click');
+
+ await waitForPromises();
+ });
+
+ it('creates alert', () => {
+ expect(createAlert).toHaveBeenCalledWith({
+ message: MSG_SOMETHING_WENT_WRONG,
+ });
+ });
+
+ it('does not call toggleDraftStatus', () => {
+ expect(MergeRequest.toggleDraftStatus).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('when user cannot update MR', () => {
+ beforeEach(async () => {
+ draftQuerySpy.mockResolvedValue(createDraftQueryResponse(false));
+
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('does not render mark ready button', () => {
+ expect(findWIPButton().exists()).toBe(false);
+ });
+ });
+});