diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-20 12:40:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-20 12:40:42 +0300 |
commit | ee664acb356f8123f4f6b00b73c1e1cf0866c7fb (patch) | |
tree | f8479f94a28f66654c6a4f6fb99bad6b4e86a40e /spec/frontend/vue_merge_request_widget | |
parent | 62f7d5c5b69180e82ae8196b7b429eeffc8e7b4f (diff) |
Add latest changes from gitlab-org/gitlab@15-5-stable-eev15.5.0-rc42
Diffstat (limited to 'spec/frontend/vue_merge_request_widget')
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'); |