Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
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/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap202
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js8
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js182
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js60
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js342
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js43
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js70
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js109
8 files changed, 345 insertions, 671 deletions
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
index 5f383c468d8..bd40a968392 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
+++ b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have correct elements 1`] = `
+exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
<div
- class="mr-widget-body media"
+ class="mr-widget-body media mr-widget-body-line-height-1 gl-line-height-normal"
>
<div
class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
@@ -44,210 +44,18 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have
class="gl-display-flex gl-w-full"
>
<div
- class="media-body gl-display-flex"
+ class="media-body gl-display-flex gl-align-items-center"
>
<h4
class="gl-mr-3"
data-testid="statusText"
>
- Set by
- <a
- class="author-link inline"
- >
- <img
- class="avatar avatar-inline s16"
- src="no_avatar.png"
- />
-
- <span
- class="author"
- >
-
- </span>
- </a>
- to be merged automatically when the pipeline succeeds
- </h4>
-
- <div
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
- >
- <div
- class="gl-display-flex gl-align-items-flex-start"
- >
- <div
- class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
- lazy=""
- no-caret=""
- title="Options"
- >
- <!---->
- <button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
- type="button"
- >
- <!---->
-
- <svg
- aria-hidden="true"
- class="dropdown-icon gl-icon s16"
- data-testid="ellipsis_v-icon"
- role="img"
- >
- <use
- href="#ellipsis_v"
- />
- </svg>
-
- <span
- class="gl-new-dropdown-button-text gl-sr-only"
- >
-
- </span>
-
- <svg
- aria-hidden="true"
- class="gl-button-icon dropdown-chevron gl-icon s16"
- data-testid="chevron-down-icon"
- role="img"
- >
- <use
- href="#chevron-down"
- />
- </svg>
- </button>
- <ul
- class="dropdown-menu dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
- </div>
-
- <button
- class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Cancel auto-merge
-
- </span>
- </button>
- </div>
- </div>
- </div>
-
- <div
- class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
- >
- <button
- class="btn gl-vertical-align-top btn-default btn-sm gl-button btn-default-tertiary btn-icon"
- title="Collapse merge details"
- type="button"
- >
- <!---->
-
- <svg
- aria-hidden="true"
- class="gl-button-icon gl-icon s16"
- data-testid="chevron-lg-up-icon"
- role="img"
- >
- <use
- href="#chevron-lg-up"
- />
- </svg>
-
- <!---->
- </button>
- </div>
- </div>
-</div>
-`;
-
-exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have correct elements 1`] = `
-<div
- class="mr-widget-body media"
->
- <div
- class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
- >
- <div
- class="gl-display-flex gl-m-auto"
- >
- <div
- class="gl-mr-3 gl-p-2 gl-m-0! gl-text-blue-500 gl-w-6 gl-p-2"
- >
- <div
- class="gl-rounded-full gl-relative gl-display-flex mr-widget-extension-icon"
- >
- <div
- class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto"
- >
- <div
- class="gl-display-flex gl-m-auto gl-translate-y-n50"
- >
- <svg
- aria-label="Scheduled "
- class="gl-display-block gl-icon s12"
- data-qa-selector="status_scheduled_icon"
- data-testid="status-scheduled-icon"
- role="img"
- >
- <use
- href="#status-scheduled"
- />
- </svg>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <div
- class="gl-display-flex gl-w-full"
- >
- <div
- class="media-body gl-display-flex"
- >
-
- <h4
- class="gl-mr-3"
- data-testid="statusText"
- >
- Set by
- <a
- class="author-link inline"
- >
- <img
- class="avatar avatar-inline s16"
- src="no_avatar.png"
- />
-
- <span
- class="author"
- >
-
- </span>
- </a>
- to be merged automatically when the pipeline succeeds
+ Set by to be merged automatically when the pipeline succeeds
</h4>
<div
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
+ class="gl-display-flex gl-font-size-0 gl-ml-auto gl-gap-3"
>
<div
class="gl-display-flex gl-align-items-flex-start"
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 d85574262fe..8eeba4d6274 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,5 +1,6 @@
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';
let wrapper;
@@ -15,9 +16,10 @@ describe('Merge request widget merge checks failed state component', () => {
});
it.each`
- mrState | displayText
- ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'}
- ${{ detailedMergeStatus: 'BLOCKED_STATUS' }} | ${'blockingMergeRequests'}
+ mrState | displayText
+ ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'}
+ ${{ detailedMergeStatus: DETAILED_MERGE_STATUS.BLOCKED_STATUS }} | ${'blockingMergeRequests'}
+ ${{ detailedMergeStatus: DETAILED_MERGE_STATUS.EXTERNAL_STATUS_CHECKS }} | ${'externalStatusChecksFailed'}
`('display $displayText text for $mrState', ({ mrState, displayText }) => {
factory({ mr: mrState });
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
index 28182793683..5b9f30dfb86 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
@@ -9,7 +9,6 @@ import eventHub from '~/vue_merge_request_widget/event_hub';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
let wrapper;
-let mergeRequestWidgetGraphqlEnabled = false;
function convertPropsToGraphqlState(props) {
return {
@@ -30,12 +29,6 @@ function convertPropsToGraphqlState(props) {
}
function factory(propsData, stateOverride = {}) {
- let state = {};
-
- if (mergeRequestWidgetGraphqlEnabled) {
- state = { ...convertPropsToGraphqlState(propsData), ...stateOverride };
- }
-
wrapper = extendedWrapper(
mount(autoMergeEnabledComponent, {
propsData: {
@@ -43,9 +36,8 @@ function factory(propsData, stateOverride = {}) {
service: new MRWidgetService({}),
},
data() {
- return { state };
+ return { ...convertPropsToGraphqlState(propsData), ...stateOverride };
},
- provide: { glFeatures: { mergeRequestWidgetGraphql: mergeRequestWidgetGraphqlEnabled } },
mocks: {
$apollo: {
queries: {
@@ -95,130 +87,88 @@ describe('MRWidgetAutoMergeEnabled', () => {
wrapper = null;
});
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'disabled'}`, () => {
- beforeEach(() => {
- mergeRequestWidgetGraphqlEnabled = mergeRequestWidgetGraphql;
+ describe('computed', () => {
+ describe('cancelButtonText', () => {
+ it('should return "Cancel" if MWPS is selected', () => {
+ factory({
+ ...defaultMrProps(),
+ autoMergeStrategy: MWPS_MERGE_STRATEGY,
+ });
+
+ expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe('Cancel auto-merge');
});
+ });
+ });
- describe('computed', () => {
- describe('cancelButtonText', () => {
- it('should return "Cancel" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
+ describe('methods', () => {
+ describe('cancelAutomaticMerge', () => {
+ it('should set flag and call service then tell main component to update the widget with data', async () => {
+ factory({
+ ...defaultMrProps(),
+ });
+ const mrObj = {
+ is_new_mr_data: true,
+ };
+ jest.spyOn(wrapper.vm.service, 'cancelAutomaticMerge').mockReturnValue(
+ new Promise((resolve) => {
+ resolve({
+ data: mrObj,
});
+ }),
+ );
- expect(wrapper.findByTestId('cancelAutomaticMergeButton').text()).toBe(
- 'Cancel auto-merge',
- );
- });
- });
- });
+ wrapper.vm.cancelAutomaticMerge();
- describe('methods', () => {
- describe('cancelAutomaticMerge', () => {
- it('should set flag and call service then tell main component to update the widget with data', async () => {
- factory({
- ...defaultMrProps(),
- });
- const mrObj = {
- is_new_mr_data: true,
- };
- jest.spyOn(wrapper.vm.service, 'cancelAutomaticMerge').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }),
- );
-
- wrapper.vm.cancelAutomaticMerge();
-
- await waitForPromises();
-
- expect(wrapper.vm.isCancellingAutoMerge).toBe(true);
- if (mergeRequestWidgetGraphql) {
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- } else {
- expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
- }
- });
- });
+ await waitForPromises();
- describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', async () => {
- factory({
- ...defaultMrProps(),
- });
- jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue(
- Promise.resolve({
- data: {
- status: MWPS_MERGE_STRATEGY,
- },
- }),
- );
-
- wrapper.vm.removeSourceBranch();
-
- await waitForPromises();
-
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(wrapper.vm.service.merge).toHaveBeenCalledWith({
- sha,
- auto_merge_strategy: MWPS_MERGE_STRATEGY,
- should_remove_source_branch: true,
- });
- });
- });
+ expect(wrapper.vm.isCancellingAutoMerge).toBe(true);
+ expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
});
+ });
+ });
- describe('template', () => {
- it('should have correct elements', () => {
- factory({
- ...defaultMrProps(),
- });
+ describe('template', () => {
+ it('should have correct elements', () => {
+ factory({
+ ...defaultMrProps(),
+ });
- expect(wrapper.element).toMatchSnapshot();
- });
+ expect(wrapper.element).toMatchSnapshot();
+ });
- it('should disable cancel auto merge button when the action is in progress', async () => {
- factory({
- ...defaultMrProps(),
- });
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- isCancellingAutoMerge: true,
- });
+ it('should disable cancel auto merge button when the action is in progress', async () => {
+ factory({
+ ...defaultMrProps(),
+ });
+ // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
+ // eslint-disable-next-line no-restricted-syntax
+ wrapper.setData({
+ isCancellingAutoMerge: true,
+ });
- await nextTick();
+ await nextTick();
- expect(wrapper.find('.js-cancel-auto-merge').props('loading')).toBe(true);
- });
+ expect(wrapper.find('.js-cancel-auto-merge').props('loading')).toBe(true);
+ });
- it('should render the status text as "...to merged automatically" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
- });
+ it('should render the status text as "...to merged automatically" if MWPS is selected', () => {
+ factory({
+ ...defaultMrProps(),
+ autoMergeStrategy: MWPS_MERGE_STRATEGY,
+ });
- expect(getStatusText()).toContain(
- 'to be merged automatically when the pipeline succeeds',
- );
- });
+ expect(getStatusText()).toContain('to be merged automatically when the pipeline succeeds');
+ });
- it('should render the cancel button as "Cancel" if MWPS is selected', () => {
- factory({
- ...defaultMrProps(),
- autoMergeStrategy: MWPS_MERGE_STRATEGY,
- });
+ it('should render the cancel button as "Cancel" if MWPS is selected', () => {
+ factory({
+ ...defaultMrProps(),
+ autoMergeStrategy: MWPS_MERGE_STRATEGY,
+ });
- const cancelButtonText = trimText(wrapper.find('.js-cancel-auto-merge').text());
+ const cancelButtonText = trimText(wrapper.find('.js-cancel-auto-merge').text());
- expect(cancelButtonText).toBe('Cancel auto-merge');
- });
- });
+ expect(cancelButtonText).toBe('Cancel auto-merge');
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js
index 398a3912882..826f708069c 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -9,18 +9,11 @@ describe('MRWidgetAutoMergeFailed', () => {
const mergeError = 'This is the merge error';
const findButton = () => wrapper.findComponent(GlButton);
- const createComponent = (props = {}, mergeRequestWidgetGraphql = false) => {
+ const createComponent = (props = {}) => {
wrapper = mount(AutoMergeFailedComponent, {
propsData: { ...props },
data() {
- if (mergeRequestWidgetGraphql) {
- return { mergeError: props.mr?.mergeError };
- }
-
- return {};
- },
- provide: {
- glFeatures: { mergeRequestWidgetGraphql },
+ return { mergeError: props.mr?.mergeError };
},
});
};
@@ -29,40 +22,33 @@ describe('MRWidgetAutoMergeFailed', () => {
wrapper.destroy();
});
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`when graphql is ${mergeRequestWidgetGraphql ? 'enabled' : 'dislabed'}`, () => {
- beforeEach(() => {
- createComponent(
- {
- mr: { mergeError },
- },
- mergeRequestWidgetGraphql,
- );
- });
+ beforeEach(() => {
+ createComponent({
+ mr: { mergeError },
+ });
+ });
- it('renders failed message', () => {
- expect(wrapper.text()).toContain('This merge request failed to be merged automatically');
- });
+ it('renders failed message', () => {
+ expect(wrapper.text()).toContain('This merge request failed to be merged automatically');
+ });
- it('renders merge error provided', () => {
- expect(wrapper.text()).toContain(mergeError);
- });
+ it('renders merge error provided', () => {
+ expect(wrapper.text()).toContain(mergeError);
+ });
- it('render refresh button', () => {
- expect(findButton().text()).toBe('Refresh');
- });
+ it('render refresh button', () => {
+ expect(findButton().text()).toBe('Refresh');
+ });
- it('emits event and shows loading icon when button is clicked', async () => {
- jest.spyOn(eventHub, '$emit');
- findButton().vm.$emit('click');
+ it('emits event and shows loading icon when button is clicked', async () => {
+ jest.spyOn(eventHub, '$emit');
+ findButton().vm.$emit('click');
- expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested');
+ expect(eventHub.$emit.mock.calls[0][0]).toBe('MRWidgetUpdateRequested');
- await nextTick();
+ await nextTick();
- expect(findButton().attributes('disabled')).toBe('disabled');
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
- });
+ expect(findButton().attributes('disabled')).toBe('disabled');
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
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 7a9fd5b002d..a16e4d4a6ea 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
@@ -7,7 +7,6 @@ import ConflictsComponent from '~/vue_merge_request_widget/components/states/mr_
describe('MRWidgetConflicts', () => {
let wrapper;
- let mergeRequestWidgetGraphql = null;
const path = '/conflicts';
const findResolveButton = () => wrapper.findByTestId('resolve-conflicts-button');
@@ -25,10 +24,17 @@ describe('MRWidgetConflicts', () => {
wrapper = extendedWrapper(
mount(ConflictsComponent, {
propsData,
- provide: {
- glFeatures: {
- mergeRequestWidgetGraphql,
- },
+ data() {
+ return {
+ userPermissions: {
+ canMerge: propsData.mr.canMerge,
+ pushToSourceBranch: propsData.mr.canPushToSourceBranch,
+ },
+ state: {
+ shouldBeRebased: propsData.mr.shouldBeRebased,
+ sourceBranchProtected: propsData.mr.sourceBranchProtected,
+ },
+ };
},
mocks: {
$apollo: {
@@ -41,212 +47,188 @@ describe('MRWidgetConflicts', () => {
}),
);
- if (mergeRequestWidgetGraphql) {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- userPermissions: {
- canMerge: propsData.mr.canMerge,
- pushToSourceBranch: propsData.mr.canPushToSourceBranch,
- },
- stateData: {
- shouldBeRebased: propsData.mr.shouldBeRebased,
- sourceBranchProtected: propsData.mr.sourceBranchProtected,
- },
- });
- }
-
await nextTick();
}
afterEach(() => {
- mergeRequestWidgetGraphql = null;
wrapper.destroy();
});
- [false, true].forEach((featureEnabled) => {
- describe(`with GraphQL feature flag ${featureEnabled ? 'enabled' : 'disabled'}`, () => {
- beforeEach(() => {
- mergeRequestWidgetGraphql = featureEnabled;
+ // There are two permissions we need to consider:
+ //
+ // 1. Is the user allowed to merge to the target branch?
+ // 2. Is the user allowed to push to the source branch?
+ //
+ // This yields 4 possible permutations that we need to test, and
+ // we test them below. A user who can push to the source
+ // branch should be allowed to resolve conflicts. This is
+ // consistent with what the backend does.
+ describe('when allowed to merge but not allowed to push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: false,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
});
+ });
- // There are two permissions we need to consider:
- //
- // 1. Is the user allowed to merge to the target branch?
- // 2. Is the user allowed to push to the source branch?
- //
- // This yields 4 possible permutations that we need to test, and
- // we test them below. A user who can push to the source
- // branch should be allowed to resolve conflicts. This is
- // consistent with what the backend does.
- describe('when allowed to merge but not allowed to push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: false,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(wrapper.text()).toContain(mergeConflictsText);
+ expect(wrapper.text()).not.toContain(userCannotMergeText);
+ });
- it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
- });
+ it('should not allow you to resolve the conflicts', () => {
+ expect(wrapper.text()).not.toContain(resolveConflictsBtnText);
+ });
- it('should not allow you to resolve the conflicts', () => {
- expect(wrapper.text()).not.toContain(resolveConflictsBtnText);
- });
+ it('should have merge buttons', () => {
+ expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
+ });
+ });
- it('should have merge buttons', () => {
- expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
- });
+ describe('when not allowed to merge but allowed to push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
});
+ });
- describe('when not allowed to merge but allowed to push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).toContain(userCannotMergeText);
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(path);
- });
-
- it('should not have merge buttons', () => {
- expect(wrapper.text()).not.toContain(mergeLocallyBtnText);
- });
+ it('should tell you about conflicts', () => {
+ expect(wrapper.text()).toContain(mergeConflictsText);
+ expect(wrapper.text()).toContain(userCannotMergeText);
+ });
+
+ it('should allow you to resolve the conflicts', () => {
+ expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
+ expect(findResolveButton().attributes('href')).toEqual(path);
+ });
+
+ it('should not have merge buttons', () => {
+ expect(wrapper.text()).not.toContain(mergeLocallyBtnText);
+ });
+ });
+
+ describe('when allowed to merge and push to source branch', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
});
+ });
- describe('when allowed to merge and push to source branch', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: path,
- conflictsDocsPath: '',
- },
- });
- });
-
- it('should tell you about conflicts without bothering other people', () => {
- expect(wrapper.text()).toContain(mergeConflictsText);
- expect(wrapper.text()).not.toContain(userCannotMergeText);
- });
-
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(path);
- });
-
- it('should have merge buttons', () => {
- expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
- });
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(wrapper.text()).toContain(mergeConflictsText);
+ expect(wrapper.text()).not.toContain(userCannotMergeText);
+ });
+
+ it('should allow you to resolve the conflicts', () => {
+ expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
+ expect(findResolveButton().attributes('href')).toEqual(path);
+ });
+
+ it('should have merge buttons', () => {
+ expect(findMergeLocalButton().text()).toContain(mergeLocallyBtnText);
+ });
+ });
+
+ describe('when user does not have permission to push to source branch', () => {
+ it('should show proper message', async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: false,
+ conflictsDocsPath: '',
+ },
});
- describe('when user does not have permission to push to source branch', () => {
- it('should show proper message', async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
- });
+ expect(wrapper.text().trim().replace(/\s\s+/g, ' ')).toContain(userCannotMergeText);
+ });
- expect(wrapper.text().trim().replace(/\s\s+/g, ' ')).toContain(userCannotMergeText);
- });
+ it('should not have action buttons', async () => {
+ await createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: false,
+ conflictsDocsPath: '',
+ },
+ });
- it('should not have action buttons', async () => {
- await createComponent({
- mr: {
- canMerge: false,
- canPushToSourceBranch: false,
- conflictsDocsPath: '',
- },
- });
-
- expect(findResolveButton().exists()).toBe(false);
- expect(findMergeLocalButton().exists()).toBe(false);
- });
-
- it('should not have resolve button when no conflict resolution path', async () => {
- await createComponent({
- mr: {
- canMerge: true,
- conflictResolutionPath: null,
- conflictsDocsPath: '',
- },
- });
+ expect(findResolveButton().exists()).toBe(false);
+ expect(findMergeLocalButton().exists()).toBe(false);
+ });
- expect(findResolveButton().exists()).toBe(false);
- });
+ it('should not have resolve button when no conflict resolution path', async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ conflictResolutionPath: null,
+ conflictsDocsPath: '',
+ },
});
- describe('when fast-forward or semi-linear merge enabled', () => {
- it('should tell you to rebase locally', async () => {
- await createComponent({
- mr: {
- shouldBeRebased: true,
- conflictsDocsPath: '',
- },
- });
+ expect(findResolveButton().exists()).toBe(false);
+ });
+ });
- expect(removeBreakLine(wrapper.text()).trim()).toContain(fastForwardMergeText);
- });
+ describe('when fast-forward or semi-linear merge enabled', () => {
+ it('should tell you to rebase locally', async () => {
+ await createComponent({
+ mr: {
+ shouldBeRebased: true,
+ conflictsDocsPath: '',
+ },
});
- describe('when source branch protected', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: true,
- conflictsDocsPath: '',
- },
- });
- });
+ expect(removeBreakLine(wrapper.text()).trim()).toContain(fastForwardMergeText);
+ });
+ });
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().exists()).toBe(true);
- });
+ describe('when source branch protected', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: TEST_HOST,
+ sourceBranchProtected: true,
+ conflictsDocsPath: '',
+ },
});
+ });
- describe('when source branch not protected', () => {
- beforeEach(async () => {
- await createComponent({
- mr: {
- canMerge: true,
- canPushToSourceBranch: true,
- conflictResolutionPath: TEST_HOST,
- sourceBranchProtected: false,
- conflictsDocsPath: '',
- },
- });
- });
+ it('should not allow you to resolve the conflicts', () => {
+ expect(findResolveButton().exists()).toBe(false);
+ });
+ });
- it('should allow you to resolve the conflicts', () => {
- expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
- expect(findResolveButton().attributes('href')).toEqual(TEST_HOST);
- });
+ describe('when source branch not protected', () => {
+ beforeEach(async () => {
+ await createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: TEST_HOST,
+ sourceBranchProtected: false,
+ conflictsDocsPath: '',
+ },
});
});
+
+ it('should allow you to resolve the conflicts', () => {
+ expect(findResolveButton().text()).toContain(resolveConflictsBtnText);
+ expect(findResolveButton().attributes('href')).toEqual(TEST_HOST);
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js
index ddce07954ab..f29cf55f7ce 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js
@@ -1,26 +1,17 @@
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
import MissingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
let wrapper;
-async function factory(sourceBranchRemoved, mergeRequestWidgetGraphql) {
+function factory(sourceBranchRemoved) {
wrapper = shallowMount(MissingBranchComponent, {
propsData: {
mr: { sourceBranchRemoved },
},
- provide: {
- glFeatures: { mergeRequestWidgetGraphql },
+ data() {
+ return { state: { sourceBranchExists: !sourceBranchRemoved } };
},
});
-
- if (mergeRequestWidgetGraphql) {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ state: { sourceBranchExists: !sourceBranchRemoved } });
- }
-
- await nextTick();
}
describe('MRWidgetMissingBranch', () => {
@@ -28,22 +19,16 @@ describe('MRWidgetMissingBranch', () => {
wrapper.destroy();
});
- [true, false].forEach((mergeRequestWidgetGraphql) => {
- describe(`widget GraphQL feature flag is ${
- mergeRequestWidgetGraphql ? 'enabled' : 'disabled'
- }`, () => {
- it.each`
- sourceBranchRemoved | branchName
- ${true} | ${'source'}
- ${false} | ${'target'}
- `(
- 'should set missing branch name as $branchName when sourceBranchRemoved is $sourceBranchRemoved',
- async ({ sourceBranchRemoved, branchName }) => {
- await factory(sourceBranchRemoved, mergeRequestWidgetGraphql);
+ it.each`
+ sourceBranchRemoved | branchName
+ ${true} | ${'source'}
+ ${false} | ${'target'}
+ `(
+ 'should set missing branch name as $branchName when sourceBranchRemoved is $sourceBranchRemoved',
+ ({ sourceBranchRemoved, branchName }) => {
+ factory(sourceBranchRemoved);
- expect(wrapper.find('[data-testid="widget-content"]').text()).toContain(branchName);
- },
- );
- });
- });
+ expect(wrapper.find('[data-testid="widget-content"]').text()).toContain(branchName);
+ },
+ );
});
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 48d3f15560b..407bd60b2b7 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
@@ -60,6 +60,11 @@ const createTestMr = (customConfig) => {
translateStateToMachine: () => this.transitionStateMachine(),
state: 'open',
canMerge: true,
+ mergeable: true,
+ userPermissions: {
+ removeSourceBranch: true,
+ canMerge: true,
+ },
};
Object.assign(mr, customConfig.mr);
@@ -68,7 +73,7 @@ const createTestMr = (customConfig) => {
};
const createTestService = () => ({
- merge: jest.fn(),
+ merge: jest.fn().mockResolvedValue(),
poll: jest.fn().mockResolvedValue(),
});
@@ -87,21 +92,24 @@ const createReadyToMergeResponse = (customMr) => {
});
};
-const createComponent = (
- customConfig = {},
- mergeRequestWidgetGraphql = false,
- restructuredMrWidget = true,
-) => {
+const createComponent = (customConfig = {}, createState = true) => {
wrapper = shallowMount(ReadyToMerge, {
propsData: {
mr: createTestMr(customConfig),
service: createTestService(),
},
- provide: {
- glFeatures: {
- mergeRequestWidgetGraphql,
- restructuredMrWidget,
- },
+ data() {
+ if (createState) {
+ return {
+ loading: false,
+ state: {
+ ...createTestMr(customConfig),
+ },
+ };
+ }
+ return {
+ loading: true,
+ };
},
stubs: {
CommitEdit,
@@ -136,7 +144,7 @@ describe('ReadyToMerge', () => {
describe('computed', () => {
describe('isAutoMergeAvailable', () => {
it('should return true when at least one merge strategy is available', () => {
- createComponent();
+ createComponent({});
expect(wrapper.vm.isAutoMergeAvailable).toBe(true);
});
@@ -168,14 +176,14 @@ describe('ReadyToMerge', () => {
});
it('returns pending when pipeline is active', () => {
- createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
+ createComponent({ mr: { pipeline: { active: true }, isPipelineActive: true } });
expect(wrapper.vm.status).toEqual('pending');
});
it('returns failed when pipeline is failed', () => {
createComponent({
- mr: { pipeline: {}, isPipelineFailed: true, availableAutoMergeStrategies: [] },
+ mr: { pipeline: { status: 'FAILED' }, availableAutoMergeStrategies: [], hasCI: true },
});
expect(wrapper.vm.status).toEqual('failed');
@@ -185,7 +193,7 @@ describe('ReadyToMerge', () => {
describe('Merge Button Variant', () => {
it('defaults to confirm class', () => {
createComponent({
- mr: { availableAutoMergeStrategies: [] },
+ mr: { availableAutoMergeStrategies: [], mergeable: true },
});
expect(findMergeButton().attributes('variant')).toBe('confirm');
@@ -194,19 +202,19 @@ describe('ReadyToMerge', () => {
describe('status icon', () => {
it('defaults to tick icon', () => {
- createComponent();
+ createComponent({ mr: { mergeable: true } });
expect(wrapper.vm.iconClass).toEqual('success');
});
it('shows tick for success status', () => {
- createComponent({ mr: { pipeline: true } });
+ createComponent({ mr: { pipeline: { status: 'SUCCESS' }, mergeable: true } });
expect(wrapper.vm.iconClass).toEqual('success');
});
it('shows tick for pending status', () => {
- createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
+ createComponent({ mr: { pipeline: { active: true }, mergeable: true } });
expect(wrapper.vm.iconClass).toEqual('success');
});
@@ -266,7 +274,7 @@ describe('ReadyToMerge', () => {
describe('isMergeButtonDisabled', () => {
it('should return false with initial data', () => {
- createComponent({ mr: { isMergeAllowed: true } });
+ createComponent({ mr: { isMergeAllowed: true, mergeable: false } });
expect(wrapper.vm.isMergeButtonDisabled).toBe(false);
});
@@ -283,6 +291,7 @@ describe('ReadyToMerge', () => {
isMergeAllowed: false,
availableAutoMergeStrategies: [],
onlyAllowMergeIfPipelineSucceeds: true,
+ mergeable: false,
},
});
@@ -544,7 +553,15 @@ describe('ReadyToMerge', () => {
describe('Remove source branch checkbox', () => {
describe('when user can merge but cannot delete branch', () => {
it('should be disabled in the rendered output', () => {
- createComponent();
+ createComponent({
+ mr: {
+ mergeable: true,
+ userPermissions: {
+ removeSourceBranch: false,
+ canMerge: true,
+ },
+ },
+ });
expect(wrapper.find('#remove-source-branch-input').exists()).toBe(false);
});
@@ -553,7 +570,7 @@ describe('ReadyToMerge', () => {
describe('when user can merge and can delete branch', () => {
beforeEach(() => {
createComponent({
- mr: { canRemoveSourceBranch: true },
+ mr: { canRemoveSourceBranch: true, mergeable: true },
});
});
@@ -567,7 +584,7 @@ describe('ReadyToMerge', () => {
describe('squash checkbox', () => {
it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
createComponent({
- mr: { commitsCount: 2, enableSquashBeforeMerge: true },
+ mr: { commitsCount: 2, enableSquashBeforeMerge: true, mergeable: true },
});
expect(findCheckboxElement().exists()).toBe(true);
@@ -665,6 +682,7 @@ describe('ReadyToMerge', () => {
squashIsSelected: true,
enableSquashBeforeMerge: true,
commitsCount: 2,
+ mergeRequestsFfOnlyEnabled: true,
},
});
@@ -795,7 +813,9 @@ describe('ReadyToMerge', () => {
});
it('shows the diverged commits text when the source branch is behind the target', () => {
- createComponent({ mr: { divergedCommitsCount: 9001, canMerge: false } });
+ createComponent({
+ mr: { divergedCommitsCount: 9001, userPermissions: { canMerge: false }, canMerge: false },
+ });
expect(wrapper.text()).toEqual(
expect.stringContaining('The source branch is 9001 commits behind the target branch'),
@@ -807,7 +827,7 @@ describe('ReadyToMerge', () => {
describe('Merge button when pipeline has failed', () => {
beforeEach(() => {
createComponent({
- mr: { pipeline: {}, isPipelineFailed: true, availableAutoMergeStrategies: [] },
+ mr: { headPipeline: { status: 'FAILED' }, availableAutoMergeStrategies: [], hasCI: true },
});
});
@@ -830,7 +850,7 @@ describe('ReadyToMerge', () => {
const USER_COMMIT_MESSAGE = 'Merge message provided manually by user';
const createDefaultGqlComponent = () =>
- createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: true } }, true);
+ createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: true } }, false);
beforeEach(() => {
readyToMergeResponseSpy = jest
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
index 7259f210b6e..82aeac1a47d 100644
--- 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
@@ -1,101 +1,42 @@
-import Vue, { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
+import { mount } from '@vue/test-utils';
import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
-import toast from '~/vue_shared/plugins/global_toast';
-import eventHub from '~/vue_merge_request_widget/event_hub';
-jest.mock('~/vue_shared/plugins/global_toast');
-
-const createComponent = () => {
- const Component = Vue.extend(WorkInProgress);
- const mr = {
- title: 'The best MR ever',
- removeWIPPath: '/path/to/remove/wip',
- };
- const service = {
- removeWIP() {},
- };
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
+let wrapper;
+
+const createComponent = (updateMergeRequest = true) => {
+ wrapper = mount(WorkInProgress, {
+ propsData: {
+ mr: {},
+ },
+ data() {
+ return {
+ userPermissions: {
+ updateMergeRequest,
+ },
+ };
+ },
});
};
-describe('Wip', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = WorkInProgress.props;
-
- expect(mr.type instanceof Object).toBe(true);
- expect(mr.required).toBe(true);
-
- expect(service.type instanceof Object).toBe(true);
- expect(service.required).toBe(true);
- });
- });
-
- describe('data', () => {
- it('should have default data', () => {
- const vm = createComponent();
-
- expect(vm.isMakingRequest).toBe(false);
- });
- });
-
- describe('methods', () => {
- const mrObj = {
- is_new_mr_data: true,
- };
-
- describe('handleRemoveDraft', () => {
- it('should make a request to service and handle response', async () => {
- const vm = createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(vm.service, 'removeWIP').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: mrObj,
- });
- }),
- );
-
- vm.handleRemoveDraft();
-
- await waitForPromises();
-
- expect(vm.isMakingRequest).toBe(true);
- expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj);
- expect(toast).toHaveBeenCalledWith('Marked as ready. Merging is now allowed.');
- });
- });
+describe('Merge request widget draft state component', () => {
+ afterEach(() => {
+ wrapper.destroy();
});
describe('template', () => {
- let vm;
- let el;
-
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
-
it('should have correct elements', () => {
- expect(el.classList.contains('mr-widget-body')).toBe(true);
- expect(el.innerText).toContain(
+ createComponent(true);
+
+ expect(wrapper.text()).toContain(
"Merge blocked: merge request must be marked as ready. It's still marked as draft.",
);
- expect(el.querySelector('.js-remove-draft').innerText.replace(/\s\s+/g, ' ')).toContain(
- 'Mark as ready',
- );
+ expect(wrapper.find('[data-testid="removeWipButton"]').text()).toContain('Mark as ready');
});
- it('should not show removeWIP button is user cannot update MR', async () => {
- vm.mr.removeWIPPath = '';
-
- await nextTick();
+ it('should not show removeWIP button is user cannot update MR', () => {
+ createComponent(false);
- expect(el.querySelector('.js-remove-draft')).toBeNull();
+ expect(wrapper.find('[data-testid="removeWipButton"]').exists()).toBe(false);
});
});
});