diff options
Diffstat (limited to 'spec/frontend/vue_merge_request_widget/components')
26 files changed, 400 insertions, 468 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, + }, }); }; |