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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-10-20 12:40:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-10-20 12:40:42 +0300
commitee664acb356f8123f4f6b00b73c1e1cf0866c7fb (patch)
treef8479f94a28f66654c6a4f6fb99bad6b4e86a40e /spec/frontend/vue_merge_request_widget
parent62f7d5c5b69180e82ae8196b7b429eeffc8e7b4f (diff)
Add latest changes from gitlab-org/gitlab@15-5-stable-eev15.5.0-rc42
Diffstat (limited to 'spec/frontend/vue_merge_request_widget')
-rw-r--r--spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js14
-rw-r--r--spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_optional_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/artifacts_list_app_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_collapsible_extension_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_author_time_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_expandable_section_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_icon_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js12
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap10
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/merge_checks_failed_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_merged_spec.js222
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js21
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js77
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_squash_before_merge_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js175
-rw-r--r--spec/frontend/vue_merge_request_widget/components/terraform/terraform_plan_spec.js93
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap35
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/dynamic_content_spec.js52
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js65
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js39
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js7
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_action_button_spec.js20
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js20
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js16
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_view_button_spec.js94
-rw-r--r--spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/mr_widget_how_to_merge_modal_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js96
-rw-r--r--spec/frontend/vue_merge_request_widget/stores/get_state_key_spec.js17
34 files changed, 570 insertions, 565 deletions
diff --git a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
index 05cd1bb5b3d..1f3b6dce620 100644
--- a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_spec.js
@@ -1,7 +1,7 @@
import { nextTick } from 'vue';
import { GlButton, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import Approvals from '~/vue_merge_request_widget/components/approvals/approvals.vue';
import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/approvals_summary.vue';
import ApprovalsSummaryOptional from '~/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
@@ -49,7 +49,7 @@ describe('MRWidget approvals', () => {
});
};
- const findAction = () => wrapper.find(GlButton);
+ const findAction = () => wrapper.findComponent(GlButton);
const findActionData = () => {
const action = findAction();
@@ -61,8 +61,8 @@ describe('MRWidget approvals', () => {
text: action.text(),
};
};
- const findSummary = () => wrapper.find(ApprovalsSummary);
- const findOptionalSummary = () => wrapper.find(ApprovalsSummaryOptional);
+ const findSummary = () => wrapper.findComponent(ApprovalsSummary);
+ const findOptionalSummary = () => wrapper.findComponent(ApprovalsSummaryOptional);
const findInvalidRules = () => wrapper.find('[data-testid="invalid-rules"]');
beforeEach(() => {
@@ -129,7 +129,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: FETCH_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: FETCH_ERROR });
});
});
@@ -268,7 +268,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: APPROVE_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: APPROVE_ERROR });
});
});
});
@@ -319,7 +319,7 @@ describe('MRWidget approvals', () => {
});
it('flashes error message', () => {
- expect(createFlash).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR });
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_optional_spec.js b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_optional_spec.js
index 65cafc647e0..e6fb0495947 100644
--- a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_optional_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_optional_spec.js
@@ -18,7 +18,7 @@ describe('MRWidget approvals summary optional', () => {
wrapper = null;
});
- const findHelpLink = () => wrapper.find(GlLink);
+ const findHelpLink = () => wrapper.findComponent(GlLink);
describe('when can approve', () => {
beforeEach(() => {
diff --git a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_spec.js b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_spec.js
index c2606346292..f4234083346 100644
--- a/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/approvals/approvals_summary_spec.js
@@ -29,7 +29,7 @@ describe('MRWidget approvals summary', () => {
});
};
- const findAvatars = () => wrapper.find(UserAvatarList);
+ const findAvatars = () => wrapper.findComponent(UserAvatarList);
afterEach(() => {
wrapper.destroy();
@@ -136,7 +136,7 @@ describe('MRWidget approvals summary', () => {
});
it('does not render avatar list', () => {
- expect(wrapper.find(UserAvatarList).exists()).toBe(false);
+ expect(wrapper.findComponent(UserAvatarList).exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/artifacts_list_app_spec.js b/spec/frontend/vue_merge_request_widget/components/artifacts_list_app_spec.js
index e2386bc7f2b..73fa4b7b08f 100644
--- a/spec/frontend/vue_merge_request_widget/components/artifacts_list_app_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/artifacts_list_app_spec.js
@@ -60,7 +60,7 @@ describe('Merge Requests Artifacts list app', () => {
});
it('renders a loading icon', () => {
- const loadingIcon = wrapper.find(GlLoadingIcon);
+ const loadingIcon = wrapper.findComponent(GlLoadingIcon);
expect(loadingIcon.exists()).toBe(true);
});
diff --git a/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js b/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
index d519ad2cdb0..b7bf72cd215 100644
--- a/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
@@ -31,11 +31,11 @@ describe('Artifacts List', () => {
});
it('renders link for the artifact', () => {
- expect(wrapper.find(GlLink).attributes('href')).toEqual(data.artifacts[0].url);
+ expect(wrapper.findComponent(GlLink).attributes('href')).toEqual(data.artifacts[0].url);
});
it('renders artifact name', () => {
- expect(wrapper.find(GlLink).text()).toEqual(data.artifacts[0].text);
+ expect(wrapper.findComponent(GlLink).text()).toEqual(data.artifacts[0].text);
});
it('renders job url', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_collapsible_extension_spec.js
index 01fbcb2154f..c253dc63f23 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_collapsible_extension_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_collapsible_extension_spec.js
@@ -23,7 +23,7 @@ describe('Merge Request Collapsible Extension', () => {
const findTitle = () => wrapper.find('[data-testid="mr-collapsible-title"]');
const findErrorMessage = () => wrapper.find('.js-error-state');
- const findIcon = () => wrapper.find(GlIcon);
+ const findIcon = () => wrapper.findComponent(GlIcon);
afterEach(() => {
wrapper.destroy();
@@ -77,7 +77,7 @@ describe('Merge Request Collapsible Extension', () => {
});
it('renders loading spinner', () => {
- expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).isVisible()).toBe(true);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_author_time_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_author_time_spec.js
index 8fd93809e01..90a29d15488 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_author_time_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_author_time_spec.js
@@ -32,7 +32,9 @@ describe('MrWidgetAuthorTime', () => {
});
it('renders author', () => {
- expect(wrapper.find(MrWidgetAuthor).props('author')).toStrictEqual(defaultProps.author);
+ expect(wrapper.findComponent(MrWidgetAuthor).props('author')).toStrictEqual(
+ defaultProps.author,
+ );
});
it('renders provided time', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_expandable_section_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_expandable_section_spec.js
index 631aef412a6..8eaed998eb5 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_expandable_section_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_expandable_section_spec.js
@@ -6,8 +6,8 @@ import MrCollapsibleSection from '~/vue_merge_request_widget/components/mr_widge
describe('MrWidgetExpanableSection', () => {
let wrapper;
- const findButton = () => wrapper.find(GlButton);
- const findCollapse = () => wrapper.find(GlCollapse);
+ const findButton = () => wrapper.findComponent(GlButton);
+ const findCollapse = () => wrapper.findComponent(GlCollapse);
beforeEach(() => {
wrapper = shallowMount(MrCollapsibleSection, {
@@ -19,7 +19,7 @@ describe('MrWidgetExpanableSection', () => {
});
it('renders Icon', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
});
it('renders header slot', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_icon_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_icon_spec.js
index ebd10f31fa7..6a9b019fb4f 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_icon_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_icon_spec.js
@@ -21,6 +21,6 @@ describe('MrWidgetIcon', () => {
it('renders icon and container', () => {
expect(wrapper.element.className).toContain('circle-icon-container');
- expect(wrapper.find(GlIcon).props('name')).toEqual(TEST_ICON);
+ expect(wrapper.findComponent(GlIcon).props('name')).toEqual(TEST_ICON);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
index efe2bf75c3f..c3f6331e560 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
@@ -41,8 +41,8 @@ describe('MrWidgetPipelineContainer', () => {
});
it('renders pipeline', () => {
- expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
- expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({
+ expect(wrapper.findComponent(MrWidgetPipeline).exists()).toBe(true);
+ expect(wrapper.findComponent(MrWidgetPipeline).props()).toMatchObject({
pipeline: mockStore.pipeline,
pipelineCoverageDelta: mockStore.pipelineCoverageDelta,
ciStatus: mockStore.ciStatus,
@@ -82,9 +82,9 @@ describe('MrWidgetPipelineContainer', () => {
});
it('renders pipeline', () => {
- expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
+ expect(wrapper.findComponent(MrWidgetPipeline).exists()).toBe(true);
expect(findCIErrorMessage().exists()).toBe(false);
- expect(wrapper.find(MrWidgetPipeline).props()).toMatchObject({
+ expect(wrapper.findComponent(MrWidgetPipeline).props()).toMatchObject({
pipeline: mockStore.mergePipeline,
pipelineCoverageDelta: mockStore.pipelineCoverageDelta,
ciStatus: mockStore.mergePipeline.details.status.text,
@@ -102,7 +102,7 @@ describe('MrWidgetPipelineContainer', () => {
targetBranch: 'Foo<script>alert("XSS")</script>',
},
});
- expect(wrapper.find(MrWidgetPipeline).props().sourceBranchLink).toBe('Foo');
+ expect(wrapper.findComponent(MrWidgetPipeline).props().sourceBranchLink).toBe('Foo');
});
it('renders deployments', () => {
@@ -125,7 +125,7 @@ describe('MrWidgetPipelineContainer', () => {
it('renders the artifacts app', () => {
factory();
- expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true);
+ expect(wrapper.findComponent(ArtifactsApp).isVisible()).toBe(true);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
index d6c67dab381..73358edee78 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
@@ -57,7 +57,7 @@ describe('MRWidgetSuggestPipeline', () => {
});
it('renders widget icon', () => {
- const icon = wrapper.find(MrWidgetIcon);
+ const icon = wrapper.findComponent(MrWidgetIcon);
expect(icon.exists()).toBe(true);
expect(icon.props()).toEqual(
@@ -115,7 +115,7 @@ describe('MRWidgetSuggestPipeline', () => {
});
describe('dismissible', () => {
- const findDismissContainer = () => wrapper.find(dismissibleContainer);
+ const findDismissContainer = () => wrapper.findComponent(dismissibleContainer);
beforeEach(() => {
wrapper = shallowMount(suggestPipelineComponent, { propsData: suggestProps });
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 635ef0f6b0d..5f383c468d8 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
@@ -72,11 +72,14 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have
<div
class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
>
- <div>
+ <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
@@ -246,11 +249,14 @@ exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have c
<div
class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
>
- <div>
+ <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
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 1900b53ac11..d85574262fe 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
@@ -15,9 +15,9 @@ describe('Merge request widget merge checks failed state component', () => {
});
it.each`
- mrState | displayText
- ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'}
- ${{ blockingMergeRequests: { total_count: 1 } }} | ${'blockingMergeRequests'}
+ mrState | displayText
+ ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'}
+ ${{ detailedMergeStatus: 'BLOCKED_STATUS' }} | ${'blockingMergeRequests'}
`('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_failed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed_spec.js
index 9320e733636..398a3912882 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
@@ -7,7 +7,7 @@ import eventHub from '~/vue_merge_request_widget/event_hub';
describe('MRWidgetAutoMergeFailed', () => {
let wrapper;
const mergeError = 'This is the merge error';
- const findButton = () => wrapper.find(GlButton);
+ const findButton = () => wrapper.findComponent(GlButton);
const createComponent = (props = {}, mergeRequestWidgetGraphql = false) => {
wrapper = mount(AutoMergeFailedComponent, {
@@ -61,7 +61,7 @@ describe('MRWidgetAutoMergeFailed', () => {
await nextTick();
expect(findButton().attributes('disabled')).toBe('disabled');
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merged_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merged_spec.js
index 2606933450e..a3aa563b516 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merged_spec.js
@@ -1,180 +1,172 @@
import { getByRole } from '@testing-library/dom';
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
+import { nextTick } from 'vue';
+import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants';
import modalEventHub from '~/projects/commit/event_hub';
-import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
+import MergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
describe('MRWidgetMerged', () => {
- let vm;
+ let wrapper;
const targetBranch = 'foo';
-
- beforeEach(() => {
- jest.spyOn(document, 'dispatchEvent');
- const Component = Vue.extend(mergedComponent);
- const mr = {
- isRemovingSourceBranch: false,
- cherryPickInForkPath: false,
- canCherryPickInCurrentMR: true,
- revertInForkPath: false,
- canRevertInCurrentMR: true,
- canRemoveSourceBranch: true,
- sourceBranchRemoved: true,
- metrics: {
- mergedBy: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl:
- 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ const mr = {
+ isRemovingSourceBranch: false,
+ cherryPickInForkPath: false,
+ canCherryPickInCurrentMR: true,
+ revertInForkPath: false,
+ canRevertInCurrentMR: true,
+ canRemoveSourceBranch: true,
+ sourceBranchRemoved: true,
+ metrics: {
+ mergedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl:
+ 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm UTC',
+ readableMergedAt: '',
+ closedBy: {},
+ closedAt: 'Jan 24, 2018 1:02pm UTC',
+ readableClosedAt: '',
+ },
+ updatedAt: 'mergedUpdatedAt',
+ shortMergeCommitSha: '958c0475',
+ mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed',
+ mergeCommitPath:
+ 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
+ sourceBranch: 'bar',
+ targetBranch,
+ };
+
+ const service = {
+ removeSourceBranch: () => nextTick(),
+ };
+
+ const createComponent = (customMrFields = {}) => {
+ wrapper = mount(MergedComponent, {
+ propsData: {
+ mr: {
+ ...mr,
+ ...customMrFields,
},
- mergedAt: 'Jan 24, 2018 1:02pm UTC',
- readableMergedAt: '',
- closedBy: {},
- closedAt: 'Jan 24, 2018 1:02pm UTC',
- readableClosedAt: '',
+ service,
},
- updatedAt: 'mergedUpdatedAt',
- shortMergeCommitSha: '958c0475',
- mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed',
- mergeCommitPath:
- 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
- sourceBranch: 'bar',
- targetBranch,
- };
-
- const service = {
- removeSourceBranch() {},
- };
+ });
+ };
+ beforeEach(() => {
+ jest.spyOn(document, 'dispatchEvent');
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
-
- vm = mountComponent(Component, { mr, service });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- describe('computed', () => {
- describe('shouldShowRemoveSourceBranch', () => {
- it('returns true when sourceBranchRemoved is false', () => {
- vm.mr.sourceBranchRemoved = false;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
- });
-
- it('returns false when sourceBranchRemoved is true', () => {
- vm.mr.sourceBranchRemoved = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
-
- it('returns false when canRemoveSourceBranch is false', () => {
- vm.mr.sourceBranchRemoved = false;
- vm.mr.canRemoveSourceBranch = false;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
-
- it('returns false when is making request', () => {
- vm.mr.canRemoveSourceBranch = true;
- vm.isMakingRequest = true;
-
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
+ const findButtonByText = (text) =>
+ wrapper.findAll('button').wrappers.find((w) => w.text() === text);
+ const findRemoveSourceBranchButton = () => findButtonByText('Delete source branch');
- it('returns true when all are true', () => {
- vm.mr.isRemovingSourceBranch = true;
- vm.mr.canRemoveSourceBranch = true;
- vm.isMakingRequest = true;
+ describe('remove source branch button', () => {
+ it('is displayed when sourceBranchRemoved is false', () => {
+ createComponent({ sourceBranchRemoved: false });
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
- });
+ expect(findRemoveSourceBranchButton().exists()).toBe(true);
});
- describe('shouldShowSourceBranchRemoving', () => {
- it('should correct value when fields changed', () => {
- vm.mr.sourceBranchRemoved = false;
+ it('is not displayed when sourceBranchRemoved is true', () => {
+ createComponent({ sourceBranchRemoved: true });
- expect(vm.shouldShowSourceBranchRemoving).toEqual(false);
+ expect(findRemoveSourceBranchButton()).toBe(undefined);
+ });
- vm.mr.sourceBranchRemoved = true;
+ it('is not displayed when canRemoveSourceBranch is true', () => {
+ createComponent({ sourceBranchRemoved: false, canRemoveSourceBranch: false });
- expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ expect(findRemoveSourceBranchButton()).toBe(undefined);
+ });
- vm.mr.sourceBranchRemoved = false;
- vm.isMakingRequest = true;
+ it('is not displayed when is making request', async () => {
+ createComponent({ sourceBranchRemoved: false, canRemoveSourceBranch: true });
- expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
+ await findRemoveSourceBranchButton().trigger('click');
- vm.isMakingRequest = false;
- vm.mr.isRemovingSourceBranch = true;
+ expect(findRemoveSourceBranchButton()).toBe(undefined);
+ });
- expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
+ it('is not displayed when all are true', () => {
+ createComponent({
+ isRemovingSourceBranch: true,
+ sourceBranchRemoved: false,
+ canRemoveSourceBranch: true,
});
+
+ expect(findRemoveSourceBranchButton()).toBe(undefined);
});
});
- describe('methods', () => {
- describe('removeSourceBranch', () => {
- it('should set flag and call service then request main component to update the widget', async () => {
- jest.spyOn(vm.service, 'removeSourceBranch').mockReturnValue(
- new Promise((resolve) => {
- resolve({
- data: {
- message: 'Branch was deleted',
- },
- });
- }),
- );
+ it('should set flag and call service then request main component to update the widget when branch is removed', async () => {
+ createComponent({ sourceBranchRemoved: false });
+ jest.spyOn(service, 'removeSourceBranch').mockResolvedValue({
+ data: {
+ message: 'Branch was deleted',
+ },
+ });
- vm.removeSourceBranch();
+ await findRemoveSourceBranchButton().trigger('click');
- await waitForPromises();
+ await waitForPromises();
- const args = eventHub.$emit.mock.calls[0];
+ const args = eventHub.$emit.mock.calls[0];
- expect(vm.isMakingRequest).toEqual(true);
- expect(args[0]).toEqual('MRWidgetUpdateRequested');
- expect(args[1]).not.toThrow();
- });
- });
+ expect(args[0]).toEqual('MRWidgetUpdateRequested');
+ expect(args[1]).not.toThrow();
});
it('calls dispatchDocumentEvent to load in the modal component', () => {
+ createComponent();
+
expect(document.dispatchEvent).toHaveBeenCalledWith(new CustomEvent('merged:UpdateActions'));
});
it('emits event to open the revert modal on revert button click', () => {
+ createComponent();
const eventHubSpy = jest.spyOn(modalEventHub, '$emit');
- getByRole(vm.$el, 'button', { name: /Revert/i }).click();
+ getByRole(wrapper.element, 'button', { name: /Revert/i }).click();
expect(eventHubSpy).toHaveBeenCalledWith(OPEN_REVERT_MODAL);
});
it('emits event to open the cherry-pick modal on cherry-pick button click', () => {
+ createComponent();
const eventHubSpy = jest.spyOn(modalEventHub, '$emit');
- getByRole(vm.$el, 'button', { name: /Cherry-pick/i }).click();
+ getByRole(wrapper.element, 'button', { name: /Cherry-pick/i }).click();
expect(eventHubSpy).toHaveBeenCalledWith(OPEN_CHERRY_PICK_MODAL);
});
it('has merged by information', () => {
- expect(vm.$el.textContent).toContain('Merged by');
- expect(vm.$el.textContent).toContain('Administrator');
+ createComponent();
+
+ expect(wrapper.text()).toContain('Merged by');
+ expect(wrapper.text()).toContain('Administrator');
});
it('shows revert and cherry-pick buttons', () => {
- expect(vm.$el.textContent).toContain('Revert');
- expect(vm.$el.textContent).toContain('Cherry-pick');
+ createComponent();
+
+ expect(wrapper.text()).toContain('Revert');
+ expect(wrapper.text()).toContain('Cherry-pick');
});
it('should use mergedEvent mergedAt as tooltip title', () => {
- expect(vm.$el.querySelector('time').getAttribute('title')).toBe('Jan 24, 2018 1:02pm UTC');
+ createComponent();
+
+ expect(wrapper.find('time').attributes('title')).toBe('Jan 24, 2018 1:02pm UTC');
});
});
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 d5619d4996d..bd158d59d74 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
@@ -6,31 +6,42 @@ import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_i
describe('PipelineFailed', () => {
let wrapper;
- const createComponent = () => {
+ const createComponent = (mr = {}) => {
wrapper = shallowMount(PipelineFailed, {
+ propsData: {
+ mr,
+ },
stubs: {
GlSprintf,
},
});
};
- beforeEach(() => {
- createComponent();
- });
-
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should render error status icon', () => {
+ createComponent();
+
expect(wrapper.findComponent(StatusIcon).exists()).toBe(true);
expect(wrapper.findComponent(StatusIcon).props().status).toBe('failed');
});
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');
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.",
+ );
+ });
});
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 9a6bf66909e..48d3f15560b 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
@@ -105,16 +105,17 @@ const createComponent = (
},
stubs: {
CommitEdit,
+ GlSprintf,
},
apolloProvider: createMockApollo([[readyToMergeQuery, readyToMergeResponseSpy]]),
});
};
-const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
+const findCheckboxElement = () => wrapper.findComponent(SquashBeforeMerge);
const findCommitEditElements = () => wrapper.findAllComponents(CommitEdit);
-const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
+const findCommitDropdownElement = () => wrapper.findComponent(CommitMessageDropdown);
const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
-const findTipLink = () => wrapper.find(GlSprintf);
+const findTipLink = () => wrapper.findComponent(GlSprintf);
const findCommitEditWithInputId = (inputId) =>
findCommitEditElements().wrappers.find((x) => x.props('inputId') === inputId);
const findMergeCommitMessage = () => findCommitEditWithInputId('merge-message-edit').props('value');
@@ -300,6 +301,48 @@ describe('ReadyToMerge', () => {
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);
+ },
+ );
+ });
});
describe('methods', () => {
@@ -733,6 +776,34 @@ describe('ReadyToMerge', () => {
});
});
+ describe('source and target branches diverged', () => {
+ describe('when the MR is showing the Merge button', () => {
+ it('does not display the diverged commits message if the source branch is not behind the target', () => {
+ createComponent({ mr: { divergedCommitsCount: 0 } });
+
+ const textBody = wrapper.text();
+
+ expect(textBody).toEqual(
+ expect.not.stringContaining('The source branch is 0 commits behind the target branch'),
+ );
+ expect(textBody).toEqual(
+ expect.not.stringContaining('The source branch is 0 commit behind the target branch'),
+ );
+ expect(textBody).toEqual(
+ expect.not.stringContaining('The source branch is behind the target branch'),
+ );
+ });
+
+ it('shows the diverged commits text when the source branch is behind the target', () => {
+ createComponent({ mr: { divergedCommitsCount: 9001, canMerge: false } });
+
+ expect(wrapper.text()).toEqual(
+ expect.stringContaining('The source branch is 9001 commits behind the target branch'),
+ );
+ });
+ });
+ });
+
describe('Merge button when pipeline has failed', () => {
beforeEach(() => {
createComponent({
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_squash_before_merge_spec.js
index 6ea2e8675d3..c839fa17fe5 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_squash_before_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_squash_before_merge_spec.js
@@ -18,7 +18,7 @@ describe('Squash before merge component', () => {
wrapper.destroy();
});
- const findCheckbox = () => wrapper.find(GlFormCheckbox);
+ const findCheckbox = () => wrapper.findComponent(GlFormCheckbox);
describe('checkbox', () => {
it('is unchecked if passed value prop is false', () => {
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 af52901f508..7259f210b6e 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
@@ -38,7 +38,7 @@ describe('Wip', () => {
it('should have default data', () => {
const vm = createComponent();
- expect(vm.isMakingRequest).toBeFalsy();
+ expect(vm.isMakingRequest).toBe(false);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js b/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js
deleted file mode 100644
index 7a868eb8cc9..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import { GlSkeletonLoader, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import axios from '~/lib/utils/axios_utils';
-import Poll from '~/lib/utils/poll';
-import MrWidgetExpanableSection from '~/vue_merge_request_widget/components/mr_widget_expandable_section.vue';
-import MrWidgetTerraformContainer from '~/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue';
-import TerraformPlan from '~/vue_merge_request_widget/components/terraform/terraform_plan.vue';
-import { invalidPlanWithName, plans, validPlanWithName } from './mock_data';
-
-describe('MrWidgetTerraformConainer', () => {
- let mock;
- let wrapper;
-
- const propsData = { endpoint: '/path/to/terraform/report.json' };
-
- const findHeader = () => wrapper.find('[data-testid="terraform-header-text"]');
- const findPlans = () =>
- wrapper.findAllComponents(TerraformPlan).wrappers.map((x) => x.props('plan'));
-
- const mockPollingApi = (response, body, header) => {
- mock.onGet(propsData.endpoint).reply(response, body, header);
- };
-
- const mountWrapper = () => {
- wrapper = shallowMount(MrWidgetTerraformContainer, {
- propsData,
- stubs: { MrWidgetExpanableSection, GlSprintf },
- });
- return axios.waitForAll();
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('when data is loading', () => {
- beforeEach(async () => {
- mockPollingApi(200, plans, {});
-
- await mountWrapper();
- // 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: true });
- await nextTick();
- });
-
- it('diplays loading skeleton', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
- expect(wrapper.find(MrWidgetExpanableSection).exists()).toBe(false);
- });
- });
-
- describe('when data has finished loading', () => {
- beforeEach(() => {
- mockPollingApi(200, plans, {});
- return mountWrapper();
- });
-
- it('displays terraform content', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
- expect(wrapper.find(MrWidgetExpanableSection).exists()).toBe(true);
- expect(findPlans()).toEqual(Object.values(plans));
- });
-
- describe('when data includes one invalid plan', () => {
- beforeEach(() => {
- const invalidPlanGroup = { bad_plan: invalidPlanWithName };
- mockPollingApi(200, invalidPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for one invalid plan', () => {
- expect(findHeader().text()).toBe('1 Terraform report failed to generate');
- });
- });
-
- describe('when data includes multiple invalid plans', () => {
- beforeEach(() => {
- const invalidPlanGroup = {
- bad_plan_one: invalidPlanWithName,
- bad_plan_two: invalidPlanWithName,
- };
-
- mockPollingApi(200, invalidPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for multiple invalid plans', () => {
- expect(findHeader().text()).toBe('2 Terraform reports failed to generate');
- });
- });
-
- describe('when data includes one valid plan', () => {
- beforeEach(() => {
- const validPlanGroup = { valid_plan: validPlanWithName };
- mockPollingApi(200, validPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for one valid plans', () => {
- expect(findHeader().text()).toBe('1 Terraform report was generated in your pipelines');
- });
- });
-
- describe('when data includes multiple valid plans', () => {
- beforeEach(() => {
- const validPlanGroup = {
- valid_plan_one: validPlanWithName,
- valid_plan_two: validPlanWithName,
- };
- mockPollingApi(200, validPlanGroup, {});
- return mountWrapper();
- });
-
- it('displays header text for multiple valid plans', () => {
- expect(findHeader().text()).toBe('2 Terraform reports were generated in your pipelines');
- });
- });
- });
-
- describe('polling', () => {
- let pollRequest;
- let pollStop;
-
- beforeEach(() => {
- pollRequest = jest.spyOn(Poll.prototype, 'makeRequest');
- pollStop = jest.spyOn(Poll.prototype, 'stop');
- });
-
- afterEach(() => {
- pollRequest.mockRestore();
- pollStop.mockRestore();
- });
-
- describe('successful poll', () => {
- beforeEach(() => {
- mockPollingApi(200, plans, {});
-
- return mountWrapper();
- });
-
- it('does not make additional requests after poll is successful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- expect(pollStop).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('polling fails', () => {
- beforeEach(() => {
- mockPollingApi(500, null, {});
- return mountWrapper();
- });
-
- it('stops loading', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
- });
-
- it('generates one broken plan', () => {
- expect(findPlans()).toEqual([{ tf_report_error: 'api_error' }]);
- });
-
- it('does not make additional requests after poll is unsuccessful', () => {
- expect(pollRequest).toHaveBeenCalledTimes(1);
- expect(pollStop).toHaveBeenCalledTimes(1);
- });
- });
- });
-});
diff --git a/spec/frontend/vue_merge_request_widget/components/terraform/terraform_plan_spec.js b/spec/frontend/vue_merge_request_widget/components/terraform/terraform_plan_spec.js
deleted file mode 100644
index 3c9f6c2e165..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/terraform/terraform_plan_spec.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import { GlLink, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import TerraformPlan from '~/vue_merge_request_widget/components/terraform/terraform_plan.vue';
-import {
- invalidPlanWithName,
- invalidPlanWithoutName,
- validPlanWithName,
- validPlanWithoutName,
-} from './mock_data';
-
-describe('TerraformPlan', () => {
- let wrapper;
-
- const findIcon = () => wrapper.find('[data-testid="change-type-icon"]');
- const findLogButton = () => wrapper.find('[data-testid="terraform-report-link"]');
-
- const mountWrapper = (propsData) => {
- wrapper = shallowMount(TerraformPlan, { stubs: { GlLink, GlSprintf }, propsData });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('valid plan with job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: validPlanWithName });
- });
-
- it('displays a document icon', () => {
- expect(findIcon().attributes('name')).toBe('doc-changes');
- });
-
- it('diplays the header text with a name', () => {
- expect(wrapper.text()).toContain(`The job ${validPlanWithName.job_name} generated a report.`);
- });
-
- it('diplays the reported changes', () => {
- expect(wrapper.text()).toContain(
- `Reported Resource Changes: ${validPlanWithName.create} to add, ${validPlanWithName.update} to change, ${validPlanWithName.delete} to delete`,
- );
- });
-
- it('renders button when url is found', () => {
- expect(findLogButton().exists()).toBe(true);
- expect(findLogButton().text()).toEqual('View full log');
- });
- });
-
- describe('valid plan without job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: validPlanWithoutName });
- });
-
- it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A report was generated in your pipelines.');
- });
- });
-
- describe('invalid plan with job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: invalidPlanWithName });
- });
-
- it('displays a warning icon', () => {
- expect(findIcon().attributes('name')).toBe('warning');
- });
-
- it('diplays the header text with a name', () => {
- expect(wrapper.text()).toContain(
- `The job ${invalidPlanWithName.job_name} failed to generate a report.`,
- );
- });
-
- it('diplays generic error since report values are missing', () => {
- expect(wrapper.text()).toContain('Generating the report caused an error.');
- });
- });
-
- describe('invalid plan with out job_name', () => {
- beforeEach(() => {
- mountWrapper({ plan: invalidPlanWithoutName });
- });
-
- it('diplays the header text without a name', () => {
- expect(wrapper.text()).toContain('A report failed to generate.');
- });
-
- it('does not render button because url is missing', () => {
- expect(findLogButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap
new file mode 100644
index 00000000000..08424077269
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap
@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`~/vue_merge_request_widget/components/widget/dynamic_content.vue renders given data 1`] = `
+"<content-row-stub level=\\"2\\" statusiconname=\\"success\\" widgetname=\\"MyWidget\\" header=\\"This is a header,This is a subheader\\">
+ <div class=\\"gl-display-flex gl-flex-direction-column\\">
+ <div>
+ <p class=\\"gl-mb-0\\">Main text for the row</p>
+ <gl-link-stub href=\\"https://gitlab.com\\">Optional link to display after text</gl-link-stub>
+ <!---->
+ <gl-badge-stub size=\\"md\\" variant=\\"info\\">
+ Badge is optional. Text to be displayed inside badge
+ </gl-badge-stub>
+ <actions-stub widget=\\"MyWidget\\" tertiarybuttons=\\"\\" class=\\"gl-ml-auto gl-pl-3\\"></actions-stub>
+ <p class=\\"gl-m-0 gl-font-sm\\">Optional: Smaller sub-text to be displayed below the main text</p>
+ </div>
+ <ul class=\\"gl-m-0 gl-p-0 gl-list-style-none\\">
+ <li>
+ <content-row-stub level=\\"3\\" statusiconname=\\"\\" widgetname=\\"MyWidget\\" header=\\"Child row header\\" data-qa-selector=\\"child_content\\">
+ <div class=\\"gl-display-flex gl-flex-direction-column\\">
+ <div>
+ <p class=\\"gl-mb-0\\">This is recursive. It will be listed in level 3.</p>
+ <!---->
+ <!---->
+ <!---->
+ <actions-stub widget=\\"MyWidget\\" tertiarybuttons=\\"\\" class=\\"gl-ml-auto gl-pl-3\\"></actions-stub>
+ <!---->
+ </div>
+ <!---->
+ </div>
+ </content-row-stub>
+ </li>
+ </ul>
+ </div>
+</content-row-stub>"
+`;
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/dynamic_content_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/dynamic_content_spec.js
new file mode 100644
index 00000000000..b7753a58747
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/widget/dynamic_content_spec.js
@@ -0,0 +1,52 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { EXTENSION_ICONS } from '~/vue_merge_request_widget/constants';
+import DynamicContent from '~/vue_merge_request_widget/components/widget/dynamic_content.vue';
+
+describe('~/vue_merge_request_widget/components/widget/dynamic_content.vue', () => {
+ let wrapper;
+
+ const createComponent = ({ propsData } = {}) => {
+ wrapper = shallowMountExtended(DynamicContent, {
+ propsData: {
+ widgetName: 'MyWidget',
+ ...propsData,
+ },
+ stubs: {
+ DynamicContent,
+ },
+ });
+ };
+
+ it('renders given data', () => {
+ createComponent({
+ propsData: {
+ data: {
+ id: 'row-id',
+ header: ['This is a header', 'This is a subheader'],
+ text: 'Main text for the row',
+ subtext: 'Optional: Smaller sub-text to be displayed below the main text',
+ icon: {
+ name: EXTENSION_ICONS.success,
+ },
+ badge: {
+ text: 'Badge is optional. Text to be displayed inside badge',
+ variant: 'info',
+ },
+ link: {
+ text: 'Optional link to display after text',
+ href: 'https://gitlab.com',
+ },
+ children: [
+ {
+ id: 'row-id-2',
+ header: 'Child row header',
+ text: 'This is recursive. It will be listed in level 3.',
+ },
+ ],
+ },
+ },
+ });
+
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js
new file mode 100644
index 00000000000..9eddd091ad0
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_row_spec.js
@@ -0,0 +1,65 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/widget/status_icon.vue';
+
+describe('~/vue_merge_request_widget/components/widget/widget_content_row.vue', () => {
+ let wrapper;
+
+ const findStatusIcon = () => wrapper.findComponent(StatusIcon);
+
+ const createComponent = ({ propsData, slots } = {}) => {
+ wrapper = shallowMountExtended(WidgetContentRow, {
+ propsData: {
+ widgetName: 'MyWidget',
+ level: 2,
+ ...propsData,
+ },
+ slots,
+ });
+ };
+
+ describe('body', () => {
+ it('renders the status icon when provided', () => {
+ createComponent({ propsData: { statusIconName: 'failed' } });
+ expect(findStatusIcon().exists()).toBe(true);
+ });
+
+ it('does not render the status icon when it is not provided', () => {
+ createComponent();
+ expect(findStatusIcon().exists()).toBe(false);
+ });
+
+ it('renders slots properly', () => {
+ createComponent({
+ propsData: {
+ statusIconName: 'success',
+ },
+ slots: {
+ header: '<span>this is a header</span>',
+ body: '<span>this is a body</span>',
+ },
+ });
+
+ expect(wrapper.findByText('this is a body').exists()).toBe(true);
+ expect(wrapper.findByText('this is a header').exists()).toBe(true);
+ });
+ });
+
+ describe('header', () => {
+ it('renders an array of header and subheader', () => {
+ createComponent({ propsData: { header: ['this is a header', 'this is a subheader'] } });
+ expect(wrapper.findByText('this is a header').exists()).toBe(true);
+ expect(wrapper.findByText('this is a subheader').exists()).toBe(true);
+ });
+
+ it('renders a string', () => {
+ createComponent({ propsData: { header: 'this is a header' } });
+ expect(wrapper.findByText('this is a header').exists()).toBe(true);
+ });
+
+ it('escapes html injection properly', () => {
+ createComponent({ propsData: { header: '<b role="header">this is a header</b>' } });
+ expect(wrapper.findByText('<b role="header">this is a header</b>').exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js
deleted file mode 100644
index c2128d3ff33..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import WidgetContentSection from '~/vue_merge_request_widget/components/widget/widget_content_section.vue';
-import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
-
-describe('~/vue_merge_request_widget/components/widget/widget_content_section.vue', () => {
- let wrapper;
-
- const findStatusIcon = () => wrapper.findComponent(StatusIcon);
-
- const createComponent = ({ propsData, slots } = {}) => {
- wrapper = shallowMountExtended(WidgetContentSection, {
- propsData: {
- widgetName: 'MyWidget',
- ...propsData,
- },
- slots,
- });
- };
-
- it('does not render the status icon when it is not provided', () => {
- createComponent();
- expect(findStatusIcon().exists()).toBe(false);
- });
-
- it('renders the status icon when provided', () => {
- createComponent({ propsData: { statusIconName: 'failed' } });
- expect(findStatusIcon().exists()).toBe(true);
- });
-
- it('renders the default slot', () => {
- createComponent({
- slots: {
- default: 'Hello world',
- },
- });
-
- expect(wrapper.findByText('Hello world').exists()).toBe(true);
- });
-});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
index b67b5703ad5..4826fecf98d 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js
@@ -5,8 +5,9 @@ import waitForPromises from 'helpers/wait_for_promises';
import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
import ActionButtons from '~/vue_merge_request_widget/components/action_buttons.vue';
import Widget from '~/vue_merge_request_widget/components/widget/widget.vue';
+import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue';
-describe('MR Widget', () => {
+describe('~/vue_merge_request_widget/components/widget/widget.vue', () => {
let wrapper;
const findStatusIcon = () => wrapper.findComponent(StatusIcon);
@@ -27,6 +28,10 @@ describe('MR Widget', () => {
...propsData,
},
slots,
+ stubs: {
+ StatusIcon,
+ ContentRow: WidgetContentRow,
+ },
});
};
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_action_button_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_action_button_spec.js
index 7e7438bcc0f..1bad5dacefa 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_action_button_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_action_button_spec.js
@@ -41,7 +41,7 @@ describe('Deployment action button', () => {
});
it('renders prop icon correctly', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
});
});
@@ -59,7 +59,7 @@ describe('Deployment action button', () => {
});
it('renders slot and icon prop correctly', () => {
- expect(wrapper.find(GlIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
expect(wrapper.text()).toContain(actionButtonMocks[DEPLOYING].toString());
});
});
@@ -75,8 +75,8 @@ describe('Deployment action button', () => {
});
it('is disabled and shows the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlButton).props('disabled')).toBe(true);
});
});
@@ -90,8 +90,8 @@ describe('Deployment action button', () => {
});
});
it('is disabled and does not show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlButton).props('disabled')).toBe(true);
});
});
@@ -106,8 +106,8 @@ describe('Deployment action button', () => {
});
});
it('is disabled and does not show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlButton).props('disabled')).toBe(true);
});
});
@@ -118,8 +118,8 @@ describe('Deployment action button', () => {
});
});
it('is not disabled nor does it show the loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlButton).props('disabled')).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlButton).props('disabled')).toBe(false);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
index a8912405fa8..58dadb2c679 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
@@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { visitUrl } from '~/lib/utils/url_utility';
import {
@@ -11,6 +11,7 @@ import {
REDEPLOYING,
STOPPING,
} from '~/vue_merge_request_widget/components/deployment/constants';
+import eventHub from '~/vue_merge_request_widget/event_hub';
import DeploymentActions from '~/vue_merge_request_widget/components/deployment/deployment_actions.vue';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import {
@@ -167,7 +168,7 @@ describe('DeploymentAction component', () => {
});
it('should not throw an error', () => {
- expect(createFlash).not.toHaveBeenCalled();
+ expect(createAlert).not.toHaveBeenCalled();
});
describe('response includes redirect_url', () => {
@@ -192,6 +193,7 @@ describe('DeploymentAction component', () => {
describe('it should call the executeAction method', () => {
beforeEach(async () => {
jest.spyOn(wrapper.vm, 'executeAction').mockImplementation();
+ jest.spyOn(eventHub, '$emit');
await waitForPromises();
@@ -206,11 +208,16 @@ describe('DeploymentAction component', () => {
actionButtonMocks[configConst],
);
});
+
+ it('emits the FetchDeployments event', () => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('FetchDeployments');
+ });
});
describe('when executeInlineAction errors', () => {
beforeEach(async () => {
executeActionSpy.mockRejectedValueOnce();
+ jest.spyOn(eventHub, '$emit');
await waitForPromises();
@@ -218,12 +225,15 @@ describe('DeploymentAction component', () => {
finderFn().trigger('click');
});
- it('should call createFlash with error message', () => {
- expect(createFlash).toHaveBeenCalled();
- expect(createFlash).toHaveBeenCalledWith({
+ it('should call createAlert with error message', () => {
+ expect(createAlert).toHaveBeenCalledWith({
message: actionButtonMocks[configConst].errorMessage,
});
});
+
+ it('emits the FetchDeployments event', () => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('FetchDeployments');
+ });
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js
index c27cbd8b781..f310f7669a9 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js
@@ -37,7 +37,7 @@ describe('Deployment component', () => {
});
it('always renders DeploymentInfo', () => {
- expect(wrapper.find(DeploymentInfo).exists()).toBe(true);
+ expect(wrapper.findComponent(DeploymentInfo).exists()).toBe(true);
});
describe('status message and buttons', () => {
@@ -111,7 +111,7 @@ describe('Deployment component', () => {
});
it(`renders the text: ${text}`, () => {
- expect(wrapper.find(DeploymentInfo).text()).toContain(text);
+ expect(wrapper.findComponent(DeploymentInfo).text()).toContain(text);
});
if (actionButtons.length > 0) {
@@ -137,9 +137,11 @@ describe('Deployment component', () => {
if (actionButtons.includes(DeploymentViewButton)) {
it('renders the View button with expected text', () => {
if (status === SUCCESS) {
- expect(wrapper.find(DeploymentViewButton).text()).toContain('View app');
+ expect(wrapper.findComponent(DeploymentViewButton).text()).toContain('View app');
} else {
- expect(wrapper.find(DeploymentViewButton).text()).toContain('View latest app');
+ expect(wrapper.findComponent(DeploymentViewButton).text()).toContain(
+ 'View latest app',
+ );
}
});
}
@@ -150,7 +152,7 @@ describe('Deployment component', () => {
describe('hasExternalUrls', () => {
describe('when deployment has both external_url_formatted and external_url', () => {
it('should render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(true);
+ expect(wrapper.findComponent(DeploymentViewButton).exists()).toBe(true);
});
});
@@ -165,7 +167,7 @@ describe('Deployment component', () => {
});
it('should not render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
+ expect(wrapper.findComponent(DeploymentViewButton).exists()).toBe(false);
});
});
@@ -180,7 +182,7 @@ describe('Deployment component', () => {
});
it('should not render the View Button', () => {
- expect(wrapper.find(DeploymentViewButton).exists()).toBe(false);
+ expect(wrapper.findComponent(DeploymentViewButton).exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_view_button_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_view_button_spec.js
index eb6e3711e2e..8994fa522d0 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_view_button_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_view_button_spec.js
@@ -2,6 +2,7 @@ import { GlDropdown, GlLink } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import DeploymentViewButton from '~/vue_merge_request_widget/components/deployment/deployment_view_button.vue';
import ReviewAppLink from '~/vue_merge_request_widget/components/review_app_link.vue';
+import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import { deploymentMockData } from './deployment_mock_data';
const appButtonText = {
@@ -36,6 +37,7 @@ describe('Deployment View App button', () => {
const findMrWigdetDeploymentDropdownIcon = () =>
wrapper.findByTestId('mr-wigdet-deployment-dropdown-icon');
const findDeployUrlMenuItems = () => wrapper.findAllComponents(GlLink);
+ const findCopyButton = () => wrapper.findComponent(ModalCopyButton);
describe('text', () => {
it('renders text as passed', () => {
@@ -44,39 +46,93 @@ describe('Deployment View App button', () => {
});
describe('without changes', () => {
+ let deployment;
+
beforeEach(() => {
- createComponent({
- propsData: {
- deployment: { ...deploymentMockData, changes: null },
- appButtonText,
- },
+ deployment = { ...deploymentMockData, changes: null };
+ });
+
+ describe('with safe url', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ deployment,
+ appButtonText,
+ },
+ });
+ });
+
+ it('renders the link to the review app without dropdown', () => {
+ expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
+ expect(findReviewAppLink().attributes('href')).toBe(deployment.external_url);
});
});
- it('renders the link to the review app without dropdown', () => {
- expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
+ describe('without safe URL', () => {
+ beforeEach(() => {
+ deployment = { ...deployment, external_url: 'postgres://example' };
+ createComponent({
+ propsData: {
+ deployment,
+ appButtonText,
+ },
+ });
+ });
+
+ it('renders the link as a copy button', () => {
+ expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
+ expect(findCopyButton().props('text')).toBe(deployment.external_url);
+ });
});
});
describe('with a single change', () => {
+ let deployment;
+ let change;
+
beforeEach(() => {
- createComponent({
- propsData: {
- deployment: { ...deploymentMockData, changes: [deploymentMockData.changes[0]] },
- appButtonText,
- },
- });
+ [change] = deploymentMockData.changes;
+ deployment = { ...deploymentMockData, changes: [change] };
});
- it('renders the link to the review app without dropdown', () => {
- expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
- expect(findMrWigdetDeploymentDropdownIcon().exists()).toBe(false);
+ describe('with safe URL', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ deployment,
+ appButtonText,
+ },
+ });
+ });
+
+ it('renders the link to the review app without dropdown', () => {
+ expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
+ expect(findMrWigdetDeploymentDropdownIcon().exists()).toBe(false);
+ });
+
+ it('renders the link to the review app linked to to the first change', () => {
+ const expectedUrl = deploymentMockData.changes[0].external_url;
+
+ expect(findReviewAppLink().attributes('href')).toBe(expectedUrl);
+ });
});
- it('renders the link to the review app linked to to the first change', () => {
- const expectedUrl = deploymentMockData.changes[0].external_url;
+ describe('with unsafe URL', () => {
+ beforeEach(() => {
+ change = { ...change, external_url: 'postgres://example' };
+ deployment = { ...deployment, changes: [change] };
+ createComponent({
+ propsData: {
+ deployment,
+ appButtonText,
+ },
+ });
+ });
- expect(findReviewAppLink().attributes('href')).toBe(expectedUrl);
+ it('renders the link as a copy button', () => {
+ expect(findMrWigdetDeploymentDropdown().exists()).toBe(false);
+ expect(findCopyButton().props('text')).toBe(change.external_url);
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js b/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
index 82743275739..05df66165dd 100644
--- a/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
+++ b/spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js
@@ -42,7 +42,7 @@ describe('Test report extension', () => {
const findFullReportLink = () => wrapper.findByTestId('full-report-link');
const findCopyFailedSpecsBtn = () => wrapper.findByTestId('copy-failed-specs-btn');
const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item');
- const findModal = () => wrapper.find(TestCaseDetails);
+ const findModal = () => wrapper.findComponent(TestCaseDetails);
const createComponent = () => {
wrapper = mountExtended(extensionsContainer, {
diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_how_to_merge_modal_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_how_to_merge_modal_spec.js
index 295b9df30b9..d038660e6d3 100644
--- a/spec/frontend/vue_merge_request_widget/mr_widget_how_to_merge_modal_spec.js
+++ b/spec/frontend/vue_merge_request_widget/mr_widget_how_to_merge_modal_spec.js
@@ -24,7 +24,7 @@ describe('MRWidgetHowToMerge', () => {
mountComponent();
});
- const findModal = () => wrapper.find(GlModal);
+ const findModal = () => wrapper.findComponent(GlModal);
const findInstructionsFields = () =>
wrapper.findAll('[ data-testid="how-to-merge-instructions"]');
const findTipLink = () => wrapper.find("[data-testid='docs-tip']");
diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
index cc894f94f80..6622749da92 100644
--- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
@@ -368,12 +368,13 @@ describe('MrWidgetOptions', () => {
describe('bindEventHubListeners', () => {
it.each`
- event | method | methodArgs
- ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${(x) => [x]}
- ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${(x) => [x, true]}
- ${'FetchActionsContent'} | ${'fetchActionsContent'} | ${() => []}
- ${'EnablePolling'} | ${'resumePolling'} | ${() => []}
- ${'DisablePolling'} | ${'stopPolling'} | ${() => []}
+ event | method | methodArgs
+ ${'MRWidgetUpdateRequested'} | ${'checkStatus'} | ${(x) => [x]}
+ ${'MRWidgetRebaseSuccess'} | ${'checkStatus'} | ${(x) => [x, true]}
+ ${'FetchActionsContent'} | ${'fetchActionsContent'} | ${() => []}
+ ${'EnablePolling'} | ${'resumePolling'} | ${() => []}
+ ${'DisablePolling'} | ${'stopPolling'} | ${() => []}
+ ${'FetchDeployments'} | ${'fetchPreMergeDeployments'} | ${() => []}
`('should bind to $event', ({ event, method, methodArgs }) => {
jest.spyOn(wrapper.vm, method).mockImplementation();
@@ -771,34 +772,40 @@ describe('MrWidgetOptions', () => {
});
describe('security widget', () => {
- describe.each`
- context | hasPipeline | shouldRender
- ${'there is a pipeline'} | ${true} | ${true}
- ${'no pipeline'} | ${false} | ${false}
- `('given $context', ({ hasPipeline, shouldRender }) => {
- beforeEach(() => {
- const mrData = {
- ...mockData,
- ...(hasPipeline ? {} : { pipeline: null }),
- };
+ const setup = async (hasPipeline) => {
+ const mrData = {
+ ...mockData,
+ ...(hasPipeline ? {} : { pipeline: null }),
+ };
- // Override top-level mocked requests, which always use a fresh copy of
- // mockData, which always includes the full pipeline object.
- mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]);
- mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]);
-
- return createComponent(mrData, {
- apolloProvider: createMockApollo([
- [
- securityReportMergeRequestDownloadPathsQuery,
- async () => ({ data: securityReportMergeRequestDownloadPathsQueryResponse }),
- ],
- ]),
- });
+ // Override top-level mocked requests, which always use a fresh copy of
+ // mockData, which always includes the full pipeline object.
+ mock.onGet(mockData.merge_request_widget_path).reply(() => [200, mrData]);
+ mock.onGet(mockData.merge_request_cached_widget_path).reply(() => [200, mrData]);
+
+ return createComponent(mrData, {
+ apolloProvider: createMockApollo([
+ [
+ securityReportMergeRequestDownloadPathsQuery,
+ async () => ({ data: securityReportMergeRequestDownloadPathsQueryResponse }),
+ ],
+ ]),
});
+ };
+
+ describe('with a pipeline', () => {
+ it('renders the security widget', async () => {
+ await setup(true);
+
+ expect(findSecurityMrWidget().exists()).toBe(true);
+ });
+ });
+
+ describe('with no pipeline', () => {
+ it('does not render the security widget', async () => {
+ await setup(false);
- it(shouldRender ? 'renders' : 'does not render', () => {
- expect(findSecurityMrWidget().exists()).toBe(shouldRender);
+ expect(findSecurityMrWidget().exists()).toBe(false);
});
});
});
@@ -881,7 +888,10 @@ describe('MrWidgetOptions', () => {
await nextTick();
expect(
- wrapper.find('[data-testid="widget-extension-top-level"]').find(GlDropdown).exists(),
+ wrapper
+ .find('[data-testid="widget-extension-top-level"]')
+ .findComponent(GlDropdown)
+ .exists(),
).toBe(false);
await nextTick();
@@ -891,19 +901,19 @@ describe('MrWidgetOptions', () => {
expect(collapsedSection.text()).toContain('Hello world');
// Renders icon in the row
- expect(collapsedSection.find(GlIcon).exists()).toBe(true);
- expect(collapsedSection.find(GlIcon).props('name')).toBe('status-failed');
+ expect(collapsedSection.findComponent(GlIcon).exists()).toBe(true);
+ expect(collapsedSection.findComponent(GlIcon).props('name')).toBe('status-failed');
// Renders badge in the row
- expect(collapsedSection.find(GlBadge).exists()).toBe(true);
- expect(collapsedSection.find(GlBadge).text()).toBe('Closed');
+ expect(collapsedSection.findComponent(GlBadge).exists()).toBe(true);
+ expect(collapsedSection.findComponent(GlBadge).text()).toBe('Closed');
// Renders a link in the row
- expect(collapsedSection.find(GlLink).exists()).toBe(true);
- expect(collapsedSection.find(GlLink).text()).toBe('GitLab.com');
+ expect(collapsedSection.findComponent(GlLink).exists()).toBe(true);
+ expect(collapsedSection.findComponent(GlLink).text()).toBe('GitLab.com');
- expect(collapsedSection.find(GlButton).exists()).toBe(true);
- expect(collapsedSection.find(GlButton).text()).toBe('Full report');
+ expect(collapsedSection.findComponent(GlButton).exists()).toBe(true);
+ expect(collapsedSection.findComponent(GlButton).text()).toBe('Full report');
});
it('extension polling is not called if enablePolling flag is not passed', () => {
@@ -994,7 +1004,7 @@ describe('MrWidgetOptions', () => {
await createComponent();
- expect(pollRequest).toHaveBeenCalledTimes(4);
+ expect(pollRequest).toHaveBeenCalledTimes(2);
});
});
@@ -1032,7 +1042,7 @@ describe('MrWidgetOptions', () => {
registerExtension(pollingErrorExtension);
await createComponent();
- expect(pollRequest).toHaveBeenCalledTimes(4);
+ expect(pollRequest).toHaveBeenCalledTimes(2);
});
it('captures sentry error and displays error when poll has failed', async () => {
@@ -1134,7 +1144,7 @@ describe('MrWidgetOptions', () => {
${'WidgetCodeQuality'} | ${'i_testing_code_quality_widget_total'}
${'WidgetTerraform'} | ${'i_testing_terraform_widget_total'}
${'WidgetIssues'} | ${'i_testing_issues_widget_total'}
- ${'WidgetTestReport'} | ${'i_testing_summary_widget_total'}
+ ${'WidgetTestSummary'} | ${'i_testing_summary_widget_total'}
`(
"sends non-standard events for the '$widgetName' widget",
async ({ widgetName, nonStandardEvent }) => {
diff --git a/spec/frontend/vue_merge_request_widget/stores/get_state_key_spec.js b/spec/frontend/vue_merge_request_widget/stores/get_state_key_spec.js
index 0246a8d4b0f..88d9d0b4cff 100644
--- a/spec/frontend/vue_merge_request_widget/stores/get_state_key_spec.js
+++ b/spec/frontend/vue_merge_request_widget/stores/get_state_key_spec.js
@@ -16,12 +16,13 @@ describe('getStateKey', () => {
commitsCount: 2,
hasConflicts: false,
draft: false,
+ detailedMergeStatus: null,
};
const bound = getStateKey.bind(context);
expect(bound()).toEqual(null);
- context.canBeMerged = true;
+ context.detailedMergeStatus = 'MERGEABLE';
expect(bound()).toEqual('readyToMerge');
@@ -36,21 +37,15 @@ describe('getStateKey', () => {
expect(bound()).toEqual('shaMismatch');
context.canMerge = false;
- context.isPipelineBlocked = true;
-
- expect(bound()).toEqual('pipelineBlocked');
-
- context.hasMergeableDiscussionsState = true;
- context.autoMergeEnabled = false;
+ context.detailedMergeStatus = 'DISCUSSIONS_NOT_RESOLVED';
expect(bound()).toEqual('unresolvedDiscussions');
- context.draft = true;
+ context.detailedMergeStatus = 'DRAFT_STATUS';
expect(bound()).toEqual('draft');
- context.onlyAllowMergeIfPipelineSucceeds = true;
- context.isPipelineFailed = true;
+ context.detailedMergeStatus = 'CI_MUST_PASS';
expect(bound()).toEqual('pipelineFailed');
@@ -62,7 +57,7 @@ describe('getStateKey', () => {
expect(bound()).toEqual('conflicts');
- context.mergeStatus = 'unchecked';
+ context.detailedMergeStatus = 'CHECKING';
expect(bound()).toEqual('checking');