diff options
Diffstat (limited to 'spec/frontend/vue_merge_request_widget')
58 files changed, 288 insertions, 431 deletions
diff --git a/spec/frontend/vue_merge_request_widget/components/action_buttons.js b/spec/frontend/vue_merge_request_widget/components/action_buttons.js index 6d714aeaf18..7334f061dc9 100644 --- a/spec/frontend/vue_merge_request_widget/components/action_buttons.js +++ b/spec/frontend/vue_merge_request_widget/components/action_buttons.js @@ -11,10 +11,6 @@ function factory(propsData = {}) { } describe('MR widget extension actions', () => { - afterEach(() => { - wrapper.destroy(); - }); - describe('tertiaryButtons', () => { it('renders buttons', () => { factory({ diff --git a/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js b/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js index 063425454d7..4164a7df482 100644 --- a/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js @@ -14,10 +14,6 @@ function factory(propsData) { } describe('Widget added commit message', () => { - afterEach(() => { - wrapper.destroy(); - }); - it('displays changes where not merged when state is closed', () => { factory({ state: 'closed' }); 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 bf208f16d18..e78e1be7882 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,26 +1,32 @@ -import { nextTick } from 'vue'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; import { GlButton, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { createAlert } from '~/flash'; +import approvedByCurrentUser from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql.json'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { createAlert } from '~/alert'; 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'; import { - FETCH_LOADING, - FETCH_ERROR, APPROVE_ERROR, UNAPPROVE_ERROR, } from '~/vue_merge_request_widget/components/approvals/messages'; import eventHub from '~/vue_merge_request_widget/event_hub'; +import approvedByQuery from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approvals.query.graphql'; +import { createCanApproveResponse } from 'jest/approvals/mock_data'; + +Vue.use(VueApollo); const mockAlertDismiss = jest.fn(); -jest.mock('~/flash', () => ({ +jest.mock('~/alert', () => ({ createAlert: jest.fn().mockImplementation(() => ({ dismiss: mockAlertDismiss, })), })); -const RULE_NAME = 'first_rule'; const TEST_HELP_PATH = 'help/path'; const testApprovedBy = () => [1, 7, 10].map((id) => ({ id })); const testApprovals = () => ({ @@ -34,15 +40,18 @@ const testApprovals = () => ({ require_password_to_approve: false, invalid_approvers_rules: [], }); -const testApprovalRulesResponse = () => ({ rules: [{ id: 2 }] }); describe('MRWidget approvals', () => { let wrapper; let service; let mr; - const createComponent = (props = {}) => { + const createComponent = (props = {}, response = approvedByCurrentUser) => { + const requestHandlers = [[approvedByQuery, jest.fn().mockResolvedValue(response)]]; + const apolloProvider = createMockApollo(requestHandlers); + wrapper = shallowMount(Approvals, { + apolloProvider, propsData: { mr, service, @@ -68,15 +77,10 @@ describe('MRWidget approvals', () => { }; const findSummary = () => wrapper.findComponent(ApprovalsSummary); const findOptionalSummary = () => wrapper.findComponent(ApprovalsSummaryOptional); - const findInvalidRules = () => wrapper.find('[data-testid="invalid-rules"]'); beforeEach(() => { service = { ...{ - fetchApprovals: jest.fn().mockReturnValue(Promise.resolve(testApprovals())), - fetchApprovalSettings: jest - .fn() - .mockReturnValue(Promise.resolve(testApprovalRulesResponse())), approveMergeRequest: jest.fn().mockReturnValue(Promise.resolve(testApprovals())), unapproveMergeRequest: jest.fn().mockReturnValue(Promise.resolve(testApprovals())), approveMergeRequestWithAuth: jest.fn().mockReturnValue(Promise.resolve(testApprovals())), @@ -97,55 +101,21 @@ describe('MRWidget approvals', () => { }; jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - describe('when created', () => { - it('shows loading message', async () => { - service = { - fetchApprovals: jest.fn().mockReturnValue(new Promise(() => {})), - }; - - createComponent(); - await nextTick(); - expect(wrapper.text()).toContain(FETCH_LOADING); - }); - - it('fetches approvals', () => { - createComponent(); - expect(service.fetchApprovals).toHaveBeenCalled(); - }); - }); - - describe('when fetch approvals error', () => { - beforeEach(() => { - jest.spyOn(service, 'fetchApprovals').mockReturnValue(Promise.reject()); - createComponent(); - return nextTick(); - }); - - it('still shows loading message', () => { - expect(wrapper.text()).toContain(FETCH_LOADING); - }); - - it('flashes error', () => { - expect(createAlert).toHaveBeenCalledWith({ message: FETCH_ERROR }); - }); + gon.current_user_id = getIdFromGraphQLId( + approvedByCurrentUser.data.project.mergeRequest.approvedBy.nodes[0].id, + ); }); describe('action button', () => { describe('when mr is closed', () => { - beforeEach(() => { + beforeEach(async () => { + const response = createCanApproveResponse(); + mr.isOpen = false; - mr.approvals.user_has_approved = false; - mr.approvals.user_can_approve = true; - createComponent(); - return nextTick(); + createComponent({}, response); + await waitForPromises(); }); it('action is not rendered', () => { @@ -154,12 +124,12 @@ describe('MRWidget approvals', () => { }); describe('when user cannot approve', () => { - beforeEach(() => { - mr.approvals.user_has_approved = false; - mr.approvals.user_can_approve = false; + beforeEach(async () => { + const response = JSON.parse(JSON.stringify(approvedByCurrentUser)); + response.data.project.mergeRequest.approvedBy.nodes = []; - createComponent(); - return nextTick(); + createComponent({}, response); + await waitForPromises(); }); it('action is not rendered', () => { @@ -168,15 +138,16 @@ describe('MRWidget approvals', () => { }); describe('when user can approve', () => { + let canApproveResponse; + beforeEach(() => { - mr.approvals.user_has_approved = false; - mr.approvals.user_can_approve = true; + canApproveResponse = createCanApproveResponse(); }); describe('and MR is unapproved', () => { - beforeEach(() => { - createComponent(); - return nextTick(); + beforeEach(async () => { + createComponent({}, canApproveResponse); + await waitForPromises(); }); it('approve action is rendered', () => { @@ -190,30 +161,33 @@ describe('MRWidget approvals', () => { describe('and MR is approved', () => { beforeEach(() => { - mr.approvals.approved = true; + canApproveResponse.data.project.mergeRequest.approved = true; }); describe('with no approvers', () => { - beforeEach(() => { - mr.approvals.approved_by = []; - createComponent(); - return nextTick(); + beforeEach(async () => { + canApproveResponse.data.project.mergeRequest.approvedBy.nodes = []; + createComponent({}, canApproveResponse); + await nextTick(); }); - it('approve action (with inverted style) is rendered', () => { - expect(findActionData()).toEqual({ + it('approve action is rendered', () => { + expect(findActionData()).toMatchObject({ variant: 'confirm', text: 'Approve', - category: 'secondary', }); }); }); describe('with approvers', () => { - beforeEach(() => { - mr.approvals.approved_by = [{ user: { id: 7 } }]; - createComponent(); - return nextTick(); + beforeEach(async () => { + canApproveResponse.data.project.mergeRequest.approvedBy.nodes = + approvedByCurrentUser.data.project.mergeRequest.approvedBy.nodes; + + canApproveResponse.data.project.mergeRequest.approvedBy.nodes[0].id = 2; + + createComponent({}, canApproveResponse); + await waitForPromises(); }); it('approve additionally action is rendered', () => { @@ -227,9 +201,9 @@ describe('MRWidget approvals', () => { }); describe('when approve action is clicked', () => { - beforeEach(() => { - createComponent(); - return nextTick(); + beforeEach(async () => { + createComponent({}, canApproveResponse); + await waitForPromises(); }); it('shows loading icon', () => { @@ -258,10 +232,6 @@ describe('MRWidget approvals', () => { it('emits to eventHub', () => { expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); }); - - it('calls store setApprovals', () => { - expect(mr.setApprovals).toHaveBeenCalledWith(testApprovals()); - }); }); describe('and error', () => { @@ -286,12 +256,12 @@ describe('MRWidget approvals', () => { }); describe('when user has approved', () => { - beforeEach(() => { - mr.approvals.user_has_approved = true; - mr.approvals.user_can_approve = false; + beforeEach(async () => { + const response = JSON.parse(JSON.stringify(approvedByCurrentUser)); - createComponent(); - return nextTick(); + createComponent({}, response); + + await waitForPromises(); }); it('revoke action is rendered', () => { @@ -316,10 +286,6 @@ describe('MRWidget approvals', () => { it('emits to eventHub', () => { expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); }); - - it('calls store setApprovals', () => { - expect(mr.setApprovals).toHaveBeenCalledWith(testApprovals()); - }); }); describe('and error', () => { @@ -329,7 +295,7 @@ describe('MRWidget approvals', () => { return nextTick(); }); - it('flashes error message', () => { + it('alerts error message', () => { expect(createAlert).toHaveBeenCalledWith({ message: UNAPPROVE_ERROR }); }); }); @@ -338,19 +304,24 @@ describe('MRWidget approvals', () => { }); describe('approvals optional summary', () => { + let optionalApprovalsResponse; + + beforeEach(() => { + optionalApprovalsResponse = JSON.parse(JSON.stringify(approvedByCurrentUser)); + }); + describe('when no approvals required and no approvers', () => { beforeEach(() => { - mr.approvals.approved_by = []; - mr.approvals.approvals_required = 0; - mr.approvals.user_has_approved = false; + optionalApprovalsResponse.data.project.mergeRequest.approvedBy.nodes = []; + optionalApprovalsResponse.data.project.mergeRequest.approvalsRequired = 0; }); describe('and can approve', () => { - beforeEach(() => { - mr.approvals.user_can_approve = true; + beforeEach(async () => { + optionalApprovalsResponse.data.project.mergeRequest.userPermissions.canApprove = true; - createComponent(); - return nextTick(); + createComponent({}, optionalApprovalsResponse); + await waitForPromises(); }); it('is shown', () => { @@ -363,11 +334,9 @@ describe('MRWidget approvals', () => { }); describe('and cannot approve', () => { - beforeEach(() => { - mr.approvals.user_can_approve = false; - - createComponent(); - return nextTick(); + beforeEach(async () => { + createComponent({}, optionalApprovalsResponse); + await nextTick(); }); it('is shown', () => { @@ -382,9 +351,9 @@ describe('MRWidget approvals', () => { }); describe('approvals summary', () => { - beforeEach(() => { + beforeEach(async () => { createComponent(); - return nextTick(); + await nextTick(); }); it('is rendered with props', () => { @@ -393,41 +362,7 @@ describe('MRWidget approvals', () => { expect(findOptionalSummary().exists()).toBe(false); expect(summary.exists()).toBe(true); expect(summary.props()).toMatchObject({ - projectPath: 'gitlab-org/gitlab', - iid: '1', - updatedCount: 0, - }); - }); - }); - - describe('invalid rules', () => { - beforeEach(() => { - mr.approvals.merge_request_approvers_available = true; - createComponent(); - }); - - it('does not render related components', () => { - expect(findInvalidRules().exists()).toBe(false); - }); - - describe('when invalid rules are present', () => { - beforeEach(() => { - mr.approvals.invalid_approvers_rules = [{ name: RULE_NAME }]; - createComponent(); - }); - - it('renders related components', () => { - const invalidRules = findInvalidRules(); - - expect(invalidRules.exists()).toBe(true); - - const invalidRulesText = invalidRules.text(); - - expect(invalidRulesText).toContain(RULE_NAME); - expect(invalidRulesText).toContain( - 'GitLab has approved this rule automatically to unblock the merge request.', - ); - expect(invalidRulesText).toContain('Learn more.'); + approvalState: approvedByCurrentUser.data.project.mergeRequest, }); }); }); 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 e6fb0495947..bf3df70d423 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 @@ -13,11 +13,6 @@ describe('MRWidget approvals summary optional', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - const findHelpLink = () => wrapper.findComponent(GlLink); describe('when can approve', () => { 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 e75ce7c60c9..8c6b3cc464c 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 @@ -1,11 +1,10 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { mount } from '@vue/test-utils'; -import approvedByMultipleUsers from 'test_fixtures/graphql/merge_requests/approvals/approved_by.query.graphql_multiple_users.json'; -import noApprovalsResponse from 'test_fixtures/graphql/merge_requests/approvals/approved_by.query.graphql_no_approvals.json'; -import approvedByCurrentUser from 'test_fixtures/graphql/merge_requests/approvals/approved_by.query.graphql.json'; +import approvedByMultipleUsers from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql_multiple_users.json'; +import noApprovalsResponse from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql_no_approvals.json'; +import approvedByCurrentUser from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql.json'; import waitForPromises from 'helpers/wait_for_promises'; -import createMockApollo from 'helpers/mock_apollo_helper'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import ApprovalsSummary from '~/vue_merge_request_widget/components/approvals/approvals_summary.vue'; import { @@ -14,32 +13,22 @@ import { APPROVED_BY_YOU_AND_OTHERS, } from '~/vue_merge_request_widget/components/approvals/messages'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; -import approvedByQuery from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql'; Vue.use(VueApollo); describe('MRWidget approvals summary', () => { - const originalUserId = gon.current_user_id; let wrapper; - const createComponent = (response = approvedByCurrentUser) => { + const createComponent = (data = approvedByCurrentUser) => { wrapper = mount(ApprovalsSummary, { propsData: { - projectPath: 'gitlab-org/gitlab', - iid: '1', + approvalState: data.data.project.mergeRequest, }, - apolloProvider: createMockApollo([[approvedByQuery, jest.fn().mockResolvedValue(response)]]), }); }; const findAvatars = () => wrapper.findComponent(UserAvatarList); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - gon.current_user_id = originalUserId; - }); - describe('when approved', () => { beforeEach(async () => { createComponent(); 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 52e2393bf05..332f14a1721 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 @@ -26,7 +26,6 @@ describe('Merge Requests Artifacts list app', () => { }); afterEach(() => { - wrapper.destroy(); mock.restore(); }); 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 b7bf72cd215..bb049a5d52f 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 @@ -18,10 +18,6 @@ describe('Artifacts List', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - beforeEach(() => { mountComponent(data); }); diff --git a/spec/frontend/vue_merge_request_widget/components/extensions/child_content_spec.js b/spec/frontend/vue_merge_request_widget/components/extensions/child_content_spec.js index 198a4c2823a..3a621db7b44 100644 --- a/spec/frontend/vue_merge_request_widget/components/extensions/child_content_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/extensions/child_content_spec.js @@ -20,11 +20,6 @@ function factory(propsData) { } describe('MR widget extension child content', () => { - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('renders child components', () => { factory({ data: { diff --git a/spec/frontend/vue_merge_request_widget/components/extensions/status_icon_spec.js b/spec/frontend/vue_merge_request_widget/components/extensions/status_icon_spec.js index f3aa5bb774f..ffa6b5538d3 100644 --- a/spec/frontend/vue_merge_request_widget/components/extensions/status_icon_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/extensions/status_icon_spec.js @@ -11,10 +11,6 @@ function factory(propsData = {}) { } describe('MR widget extensions status icon', () => { - afterEach(() => { - wrapper.destroy(); - }); - it('renders loading icon', () => { factory({ name: 'test', isLoading: true, iconName: 'failed' }); 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 81f266d8070..6b22c2e26ac 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 @@ -25,10 +25,6 @@ describe('Merge Request Collapsible Extension', () => { const findErrorMessage = () => wrapper.find('.js-error-state'); const findIcon = () => wrapper.findComponent(GlIcon); - afterEach(() => { - wrapper.destroy(); - }); - describe('while collapsed', () => { beforeEach(() => { mountComponent(data); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_alert_message_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_alert_message_spec.js index 5d923d0383f..01178dab9bb 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_alert_message_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_alert_message_spec.js @@ -11,10 +11,6 @@ function createComponent(propsData = {}) { } describe('MrWidgetAlertMessage', () => { - afterEach(() => { - wrapper.destroy(); - }); - it('should render a GlAert', () => { createComponent({ type: 'danger' }); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_author_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_author_spec.js index 8a42e2e2ce7..7eafccae083 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_author_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_author_spec.js @@ -29,7 +29,6 @@ describe('MrWidgetAuthor', () => { }); afterEach(() => { - wrapper.destroy(); window.gl = oldWindowGl; }); 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 90a29d15488..534b745aed2 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 @@ -23,10 +23,6 @@ describe('MrWidgetAuthorTime', () => { }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('renders provided action text', () => { expect(wrapper.text()).toContain('Merged by'); }); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_container_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_container_spec.js index 8dadb0c65d0..25de76ba33c 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_container_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_container_spec.js @@ -13,10 +13,6 @@ describe('MrWidgetContainer', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - it('has layout', () => { factory(); 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 6a9b019fb4f..090a96d576c 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 @@ -15,10 +15,6 @@ describe('MrWidgetIcon', () => { }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('renders icon and container', () => { expect(wrapper.element.className).toContain('circle-icon-container'); 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 13beb43e10b..18842e996de 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 @@ -29,10 +29,6 @@ describe('MrWidgetPipelineContainer', () => { mock.onGet().reply(HTTP_STATUS_OK, {}); }); - afterEach(() => { - wrapper.destroy(); - }); - const findDeploymentList = () => wrapper.findComponent(DeploymentList); const findCIErrorMessage = () => wrapper.findByTestId('ci-error-message'); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js index ec047fe0714..f284ec98a73 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js @@ -1,3 +1,4 @@ +import { GlModal } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { nextTick } from 'vue'; import WidgetRebase from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue'; @@ -8,8 +9,11 @@ jest.mock('~/vue_shared/plugins/global_toast'); let wrapper; -function createWrapper(propsData) { +function createWrapper(propsData, provideData) { wrapper = mount(WidgetRebase, { + provide: { + ...provideData, + }, propsData, data() { return { @@ -19,6 +23,7 @@ function createWrapper(propsData) { userPermissions: { pushToSourceBranch: propsData.mr.canPushToSourceBranch, }, + pipelines: propsData.mr.pipelines, }, }; }, @@ -37,11 +42,8 @@ describe('Merge request widget rebase component', () => { const findRebaseMessageText = () => findRebaseMessage().text(); const findStandardRebaseButton = () => wrapper.find('[data-testid="standard-rebase-button"]'); const findRebaseWithoutCiButton = () => wrapper.find('[data-testid="rebase-without-ci-button"]'); + const findModal = () => wrapper.findComponent(GlModal); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); describe('while rebasing', () => { it('should show progress message', () => { createWrapper({ @@ -199,6 +201,72 @@ describe('Merge request widget rebase component', () => { expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true }); }); }); + + describe('security modal', () => { + it('displays modal and rebases after confirming', () => { + createWrapper( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + sourceProjectFullPath: 'user/forked', + targetProjectFullPath: 'root/original', + pipelines: { + nodes: [ + { + id: '1', + project: { + id: '2', + fullPath: 'user/forked', + }, + }, + ], + }, + }, + service: { + rebase: rebaseMock, + poll: pollMock, + }, + }, + { canCreatePipelineInTargetProject: true }, + ); + + findModal().vm.show = jest.fn(); + + findStandardRebaseButton().vm.$emit('click'); + + expect(findModal().vm.show).toHaveBeenCalled(); + + findModal().vm.$emit('primary'); + + expect(rebaseMock).toHaveBeenCalled(); + }); + + it('does not display modal', () => { + createWrapper( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + sourceProjectFullPath: 'user/forked', + targetProjectFullPath: 'root/original', + }, + service: { + rebase: rebaseMock, + poll: pollMock, + }, + }, + { canCreatePipelineInTargetProject: false }, + ); + + findModal().vm.show = jest.fn(); + + findStandardRebaseButton().vm.$emit('click'); + + expect(findModal().vm.show).not.toHaveBeenCalled(); + expect(rebaseMock).toHaveBeenCalled(); + }); + }); }); describe('without permissions', () => { diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_related_links_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_related_links_spec.js index 15522f7ac1d..42a16090510 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_related_links_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_related_links_spec.js @@ -9,10 +9,6 @@ describe('MRWidgetRelatedLinks', () => { wrapper = shallowMount(RelatedLinks, { propsData }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('computed', () => { describe('closesText', () => { it('returns Closes text for open merge request', () => { diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js index 530549b7b9c..b210327aa31 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js @@ -17,11 +17,6 @@ describe('MR widget status icon component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - describe('while loading', () => { it('renders loading icon', () => { createWrapper({ status: 'loading' }); 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 73358edee78..70c76687a79 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 @@ -18,10 +18,6 @@ describe('MRWidgetSuggestPipeline', () => { describe('template', () => { let wrapper; - afterEach(() => { - wrapper.destroy(); - }); - describe('core functionality', () => { const findOkBtn = () => wrapper.find('[data-testid="ok"]'); let trackingSpy; diff --git a/spec/frontend/vue_merge_request_widget/components/review_app_link_spec.js b/spec/frontend/vue_merge_request_widget/components/review_app_link_spec.js index e393b56034d..48484551d59 100644 --- a/spec/frontend/vue_merge_request_widget/components/review_app_link_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/review_app_link_spec.js @@ -17,10 +17,6 @@ describe('review app link', () => { wrapper = shallowMount(ReviewAppLink, { propsData: props }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('renders provided link as href attribute', () => { expect(wrapper.attributes('href')).toBe(props.link); }); diff --git a/spec/frontend/vue_merge_request_widget/components/states/commit_edit_spec.js b/spec/frontend/vue_merge_request_widget/components/states/commit_edit_spec.js index c0add94e6ed..f520c6a4f78 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/commit_edit_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/commit_edit_spec.js @@ -26,10 +26,6 @@ describe('Commits edit component', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - const findTextarea = () => wrapper.find('.form-control'); it('has a correct label', () => { 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 e4448346685..c2ab0e384e8 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 @@ -12,10 +12,6 @@ function factory(propsData = {}) { } describe('Merge request widget merge checks failed state component', () => { - afterEach(() => { - wrapper.destroy(); - }); - it.each` mrState | displayText ${{ approvals: true, isApproved: false }} | ${'approvalNeeded'} diff --git a/spec/frontend/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js b/spec/frontend/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js index c9aca01083d..7d471b91c37 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog_spec.js @@ -37,10 +37,6 @@ describe('MergeFailedPipelineConfirmationDialog', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('should render informational text explaining why merging immediately can be dangerous', () => { expect(trimText(wrapper.text())).toContain( 'The latest pipeline for this merge request did not succeed. The latest changes are unverified. Are you sure you want to attempt to merge?', diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js index 08700e834d7..3e18ee75125 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js @@ -10,11 +10,6 @@ describe('MRWidgetArchived', () => { wrapper = shallowMount(archivedComponent, { propsData: { mr: {} } }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('renders error icon', () => { expect(wrapper.findComponent(StateContainer).exists()).toBe(true); expect(wrapper.findComponent(StateContainer).props().status).toBe('failed'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js index fef5fee5f19..65d170cae8b 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js @@ -83,8 +83,6 @@ describe('MRWidgetAutoMergeEnabled', () => { afterEach(() => { window.gl = oldWindowGl; - wrapper.destroy(); - wrapper = null; }); describe('computed', () => { 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 826f708069c..9b043bda72d 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 @@ -18,10 +18,6 @@ describe('MRWidgetAutoMergeFailed', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - beforeEach(() => { createComponent({ mr: { mergeError }, diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js index ac18ccf9e26..6c3b7f76fe6 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js @@ -9,11 +9,6 @@ describe('MRWidgetChecking', () => { wrapper = shallowMount(CheckingComponent, { propsData: { mr: {} } }); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('renders loading icon', () => { expect(wrapper.findComponent(StateContainer).exists()).toBe(true); expect(wrapper.findComponent(StateContainer).props().status).toBe('loading'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js index 5d2d1fdd6f1..e4febda1daa 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js @@ -36,10 +36,6 @@ describe('Commits message dropdown component', () => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - const findDropdownElements = () => wrapper.findAllComponents(GlDropdownItem); const findFirstDropdownElement = () => findDropdownElements().at(0); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js index a6d3a6286a7..b3843b066df 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commits_header_spec.js @@ -21,10 +21,6 @@ describe('Commits header component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - const findHeaderWrapper = () => wrapper.find('.js-mr-widget-commits-count'); const findCommitToggle = () => wrapper.find('.commit-edit-toggle'); const findTargetBranchMessage = () => wrapper.find('.label-branch'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js index 2ca9dc61745..7f0a171d712 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_conflicts_spec.js @@ -50,10 +50,6 @@ describe('MRWidgetConflicts', () => { await nextTick(); } - afterEach(() => { - wrapper.destroy(); - }); - // There are two permissions we need to consider: // // 1. Is the user allowed to merge to the target branch? diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js index 833fa27d453..38e5422325a 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js @@ -28,10 +28,6 @@ describe('MRWidgetFailedToMerge', () => { jest.spyOn(window, 'clearInterval').mockImplementation(); }); - afterEach(() => { - wrapper.destroy(); - }); - describe('interval', () => { it('sets interval to refresh', () => { createComponent(); 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 a3aa563b516..e44e2834a0e 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 @@ -62,10 +62,6 @@ describe('MRWidgetMerged', () => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); }); - afterEach(() => { - wrapper.destroy(); - }); - const findButtonByText = (text) => wrapper.findAll('button').wrappers.find((w) => w.text() === text); const findRemoveSourceBranchButton = () => findButtonByText('Delete source branch'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js index 5408f731b34..ca75ca11e5b 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_merging_spec.js @@ -29,10 +29,6 @@ describe('MRWidgetMerging', () => { }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('renders information about merge request being merged', () => { const message = wrapper.findComponent(BoldText).props('message'); expect(message).toContain('Merging!'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js index f29cf55f7ce..fca25b8bb94 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_missing_branch_spec.js @@ -15,10 +15,6 @@ function factory(sourceBranchRemoved) { } describe('MRWidgetMissingBranch', () => { - afterEach(() => { - wrapper.destroy(); - }); - it.each` sourceBranchRemoved | branchName ${true} | ${'source'} diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js index 42515c597c5..40b053282de 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js @@ -10,11 +10,6 @@ describe('MRWidgetNotAllowed', () => { wrapper = shallowMount(notAllowedComponent); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('renders success icon', () => { expect(wrapper.findComponent(StatusIcon).exists()).toBe(true); expect(wrapper.findComponent(StatusIcon).props().status).toBe('success'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js index 6de0c06c33d..c8fa1399dcb 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_nothing_to_merge_spec.js @@ -1,28 +1,58 @@ -import Vue, { nextTick } from 'vue'; +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import NothingToMerge from '~/vue_merge_request_widget/components/states/nothing_to_merge.vue'; describe('NothingToMerge', () => { - describe('template', () => { - const Component = Vue.extend(NothingToMerge); - const newBlobPath = '/foo'; - const vm = new Component({ - el: document.createElement('div'), + let wrapper; + const newBlobPath = '/foo'; + + const defaultProps = { + mr: { + newBlobPath, + }, + }; + + const createComponent = (props = defaultProps) => { + wrapper = shallowMountExtended(NothingToMerge, { propsData: { - mr: { newBlobPath }, + ...props, + }, + stubs: { + GlSprintf, }, }); + }; + + const findCreateButton = () => wrapper.findByTestId('createFileButton'); + const findNothingToMergeTextBody = () => wrapper.findByTestId('nothing-to-merge-body'); + + describe('With Blob link', () => { + beforeEach(() => { + createComponent(); + }); + + it('shows the component with the correct text and highlights', () => { + expect(wrapper.text()).toContain('This merge request contains no changes.'); + expect(findNothingToMergeTextBody().text()).toContain( + 'Use merge requests to propose changes to your project and discuss them with your team. To make changes, push a commit or edit this merge request to use a different branch.', + ); + }); + + it('shows the Create file button with the correct attributes', () => { + const createButton = findCreateButton(); + + expect(createButton.exists()).toBe(true); + expect(createButton.attributes('href')).toBe(newBlobPath); + }); + }); - it('should have correct elements', () => { - expect(vm.$el.classList.contains('mr-widget-body')).toBe(true); - expect(vm.$el.querySelector('[data-testid="createFileButton"]').href).toContain(newBlobPath); - expect(vm.$el.innerText).toContain('Use merge requests to propose changes to your project'); + describe('Without Blob link', () => { + beforeEach(() => { + createComponent({ mr: { newBlobPath: '' } }); }); - it('should not show new blob link if there is no link available', () => { - vm.mr.newBlobPath = null; - nextTick(() => { - expect(vm.$el.querySelector('[data-testid="createFileButton"]')).toEqual(null); - }); + it('does not show the Create file button', () => { + expect(findCreateButton().exists()).toBe(false); }); }); }); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js index c0197b5e20a..d99106df0a2 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js @@ -10,11 +10,6 @@ describe('MRWidgetPipelineBlocked', () => { wrapper = shallowMount(PipelineBlockedComponent); }); - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('renders error icon', () => { expect(wrapper.findComponent(StatusIcon).exists()).toBe(true); expect(wrapper.findComponent(StatusIcon).props().status).toBe('failed'); 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 8bae2b62ed1..ea93463f3ab 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 @@ -19,11 +19,6 @@ describe('PipelineFailed', () => { }); }; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - it('should render error status icon', () => { createComponent(); diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js index aaa4591d67d..02b71ebf183 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_sha_mismatch_spec.js @@ -20,10 +20,6 @@ describe('ShaMismatch', () => { wrapper = createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('should render warning message', () => { expect(wrapper.text()).toContain('Merge blocked: new changes were just added.'); }); 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 c839fa17fe5..97f8e695df9 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 @@ -14,10 +14,6 @@ describe('Squash before merge component', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - const findCheckbox = () => wrapper.findComponent(GlFormCheckbox); describe('checkbox', () => { diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js index c97b42f61ac..58b9f162815 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions_spec.js @@ -21,10 +21,6 @@ describe('UnresolvedDiscussions', () => { wrapper = createComponent(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('triggers the correct notes event when the jump to first unresolved discussion button is clicked', () => { jest.spyOn(notesEventHub, '$emit'); @@ -38,10 +34,6 @@ describe('UnresolvedDiscussions', () => { wrapper = createComponent({ path: TEST_HOST }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('should have correct elements', () => { const text = removeBreakLine(wrapper.text()).trim(); expect(text).toContain('Merge blocked:'); diff --git a/spec/frontend/vue_merge_request_widget/components/states/new_ready_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/new_ready_to_merge_spec.js index 5ec9654a4af..20d06a7aaee 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/new_ready_to_merge_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/new_ready_to_merge_spec.js @@ -15,10 +15,6 @@ function factory({ canMerge }) { } describe('New ready to merge state component', () => { - afterEach(() => { - wrapper.destroy(); - }); - it.each` canMerge ${true} diff --git a/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js b/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js index e610ceb2122..43ce1769ff3 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/work_in_progress_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import getStateQueryResponse from 'test_fixtures/graphql/merge_requests/get_state.query.graphql.json'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import WorkInProgress, { MSG_SOMETHING_WENT_WRONG, MSG_MARK_READY, @@ -22,7 +22,7 @@ const TEST_MR_IID = '23'; const TEST_MR_TITLE = 'Test MR Title'; const TEST_PROJECT_PATH = 'lorem/ipsum'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/merge_request'); describe('~/vue_merge_request_widget/components/states/work_in_progress.vue', () => { diff --git a/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js index 366ea113162..adefce9060c 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/widget/action_buttons_spec.js @@ -11,10 +11,6 @@ function factory(propsData = {}) { } describe('~/vue_merge_request_widget/components/widget/action_buttons.vue', () => { - afterEach(() => { - wrapper.destroy(); - }); - describe('tertiaryButtons', () => { it('renders buttons', () => { factory({ 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 973866176c2..5887670a58d 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 @@ -50,10 +50,6 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('on mount', () => { it('fetches collapsed', async () => { const fetchCollapsedData = jest 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 1bad5dacefa..785515ae846 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 @@ -25,10 +25,6 @@ describe('Deployment action button', () => { }); }; - afterEach(() => { - wrapper.destroy(); - }); - describe('when passed only icon via props', () => { beforeEach(() => { factory({ 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 41df485b0de..1fdbbadf8b0 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 { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; import { visitUrl } from '~/lib/utils/url_utility'; import { @@ -21,7 +21,7 @@ import { retryDetails, } from './deployment_mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/lib/utils/url_utility'); jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'); @@ -54,7 +54,6 @@ describe('DeploymentAction component', () => { }); afterEach(() => { - wrapper.destroy(); confirmAction.mockReset(); }); diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_list_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_list_spec.js index 948d7ebab5e..77dac4204db 100644 --- a/spec/frontend/vue_merge_request_widget/deployment/deployment_list_spec.js +++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_list_spec.js @@ -28,7 +28,6 @@ describe('~/vue_merge_request_widget/components/deployment/deployment_list.vue', afterEach(() => { wrapper?.destroy?.(); - wrapper = null; }); describe('with few deployments', () => { 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 f310f7669a9..74122f47ad3 100644 --- a/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js +++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_spec.js @@ -32,10 +32,6 @@ describe('Deployment component', () => { }); }); - afterEach(() => { - wrapper.destroy(); - }); - it('always renders DeploymentInfo', () => { expect(wrapper.findComponent(DeploymentInfo).exists()).toBe(true); }); 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 8994fa522d0..7a151c26934 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 @@ -28,10 +28,6 @@ describe('Deployment View App button', () => { }); }); - afterEach(() => { - wrapper.destroy(); - }); - const findReviewAppLink = () => wrapper.findComponent(ReviewAppLink); const findMrWigdetDeploymentDropdown = () => wrapper.findComponent(GlDropdown); const findMrWigdetDeploymentDropdownIcon = () => 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 548b68bc103..d2d622d0534 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 @@ -73,7 +73,6 @@ describe('Test report extension', () => { }); afterEach(() => { - wrapper.destroy(); mock.restore(); }); diff --git a/spec/frontend/vue_merge_request_widget/extentions/accessibility/index_spec.js b/spec/frontend/vue_merge_request_widget/extentions/accessibility/index_spec.js index 01049e54a7f..40158917f52 100644 --- a/spec/frontend/vue_merge_request_widget/extentions/accessibility/index_spec.js +++ b/spec/frontend/vue_merge_request_widget/extentions/accessibility/index_spec.js @@ -39,7 +39,6 @@ describe('Accessibility extension', () => { }); afterEach(() => { - wrapper.destroy(); mock.restore(); }); diff --git a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js index 67b327217ef..4b7870842bd 100644 --- a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js +++ b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js @@ -61,7 +61,6 @@ describe('Code Quality extension', () => { }); afterEach(() => { - wrapper.destroy(); mock.restore(); }); diff --git a/spec/frontend/vue_merge_request_widget/extentions/terraform/index_spec.js b/spec/frontend/vue_merge_request_widget/extentions/terraform/index_spec.js index 13384e1efca..52a244107bd 100644 --- a/spec/frontend/vue_merge_request_widget/extentions/terraform/index_spec.js +++ b/spec/frontend/vue_merge_request_widget/extentions/terraform/index_spec.js @@ -48,7 +48,6 @@ describe('Terraform extension', () => { }); afterEach(() => { - wrapper.destroy(); mock.restore(); }); 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 015d394312a..20f1796008a 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 @@ -15,11 +15,6 @@ describe('MRWidgetHowToMerge', () => { }); } - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - beforeEach(() => { mountComponent(); }); 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 f37276ad594..fad501ee7f5 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 @@ -1,9 +1,10 @@ import { GlBadge, GlLink, GlIcon, GlButton, GlDropdown } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { mount, shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import * as Sentry from '@sentry/browser'; +import approvedByCurrentUser from 'test_fixtures/graphql/merge_requests/approvals/approvals.query.graphql.json'; import getStateQueryResponse from 'test_fixtures/graphql/merge_requests/get_state.query.graphql.json'; import readyToMergeResponse from 'test_fixtures/graphql/merge_requests/states/ready_to_merge.query.graphql.json'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -20,6 +21,7 @@ import { registerExtension, registeredExtensions, } from '~/vue_merge_request_widget/components/extensions'; +import { STATE_QUERY_POLLING_INTERVAL_BACKOFF } from '~/vue_merge_request_widget/constants'; import { SUCCESS } from '~/vue_merge_request_widget/components/deployment/constants'; import eventHub from '~/vue_merge_request_widget/event_hub'; import MrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; @@ -28,6 +30,7 @@ import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_ import securityReportMergeRequestDownloadPathsQuery from '~/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql'; import getStateQuery from '~/vue_merge_request_widget/queries/get_state.query.graphql'; import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql'; +import approvalsQuery from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approvals.query.graphql'; import userPermissionsQuery from '~/vue_merge_request_widget/queries/permissions.query.graphql'; import conflictsStateQuery from '~/vue_merge_request_widget/queries/states/conflicts.query.graphql'; import { faviconDataUrl, overlayDataUrl } from '../lib/utils/mock_data'; @@ -60,6 +63,8 @@ jest.mock('@sentry/browser', () => ({ Vue.use(VueApollo); describe('MrWidgetOptions', () => { + let stateQueryHandler; + let queryResponse; let wrapper; let mock; @@ -83,37 +88,41 @@ describe('MrWidgetOptions', () => { afterEach(() => { mock.restore(); + // eslint-disable-next-line @gitlab/vtu-no-explicit-wrapper-destroy wrapper.destroy(); - gl.mrWidgetData = {}; - gon.features = {}; }); - const createComponent = (mrData = mockData, options = {}) => { - wrapper = mount(MrWidgetOptions, { + const createComponent = (mrData = mockData, options = {}, data = {}, fullMount = true) => { + const mounting = fullMount ? mount : shallowMount; + + queryResponse = { + data: { + project: { + ...getStateQueryResponse.data.project, + mergeRequest: { + ...getStateQueryResponse.data.project.mergeRequest, + mergeError: mrData.mergeError || null, + }, + }, + }, + }; + stateQueryHandler = jest.fn().mockResolvedValue(queryResponse); + wrapper = mounting(MrWidgetOptions, { propsData: { mrData: { ...mrData }, }, data() { - return { loading: false }; + return { + loading: false, + ...data, + }; }, ...options, apolloProvider: createMockApollo([ - [ - getStateQuery, - jest.fn().mockResolvedValue({ - data: { - project: { - ...getStateQueryResponse.data.project, - mergeRequest: { - ...getStateQueryResponse.data.project.mergeRequest, - mergeError: mrData.mergeError || null, - }, - }, - }, - }), - ], + [approvalsQuery, jest.fn().mockResolvedValue(approvedByCurrentUser)], + [getStateQuery, stateQueryHandler], [readyToMergeQuery, jest.fn().mockResolvedValue(readyToMergeResponse)], [ userPermissionsQuery, @@ -351,18 +360,6 @@ describe('MrWidgetOptions', () => { }); }); - describe('initPolling', () => { - it('should call SmartInterval', () => { - wrapper.vm.initPolling(); - - expect(SmartInterval).toHaveBeenCalledWith( - expect.objectContaining({ - callback: wrapper.vm.checkStatus, - }), - ); - }); - }); - describe('initDeploymentsPolling', () => { it('should call SmartInterval', () => { wrapper.vm.initDeploymentsPolling(); @@ -529,23 +526,64 @@ describe('MrWidgetOptions', () => { }); }); - describe('resumePolling', () => { - it('should call stopTimer on pollingInterval', () => { - jest.spyOn(wrapper.vm.pollingInterval, 'resume').mockImplementation(() => {}); + describe('Apollo query', () => { + const interval = 5; + const data = 'foo'; + const mockCheckStatus = jest.fn().mockResolvedValue({ data }); + const mockSetGraphqlData = jest.fn(); + const mockSetData = jest.fn(); - wrapper.vm.resumePolling(); + beforeEach(() => { + wrapper.destroy(); + + return createComponent( + mockData, + {}, + { + pollInterval: interval, + startingPollInterval: interval, + mr: { + setData: mockSetData, + setGraphqlData: mockSetGraphqlData, + }, + service: { + checkStatus: mockCheckStatus, + }, + }, + false, + ); + }); - expect(wrapper.vm.pollingInterval.resume).toHaveBeenCalled(); + describe('normal polling behavior', () => { + it('responds to the GraphQL query finishing', () => { + expect(mockSetGraphqlData).toHaveBeenCalledWith(queryResponse.data.project); + expect(mockCheckStatus).toHaveBeenCalled(); + expect(mockSetData).toHaveBeenCalledWith(data, undefined); + expect(stateQueryHandler).toHaveBeenCalledTimes(1); + }); }); - }); - describe('stopPolling', () => { - it('should call stopTimer on pollingInterval', () => { - jest.spyOn(wrapper.vm.pollingInterval, 'stopTimer').mockImplementation(() => {}); + describe('external event control', () => { + describe('enablePolling', () => { + it('enables the Apollo query polling using the event hub', () => { + eventHub.$emit('EnablePolling'); + + expect(stateQueryHandler).toHaveBeenCalled(); + jest.advanceTimersByTime(interval * STATE_QUERY_POLLING_INTERVAL_BACKOFF); + expect(stateQueryHandler).toHaveBeenCalledTimes(2); + }); + }); + + describe('disablePolling', () => { + it('disables the Apollo query polling using the event hub', () => { + expect(stateQueryHandler).toHaveBeenCalledTimes(1); - wrapper.vm.stopPolling(); + eventHub.$emit('DisablePolling'); + jest.advanceTimersByTime(interval * STATE_QUERY_POLLING_INTERVAL_BACKOFF); - expect(wrapper.vm.pollingInterval.stopTimer).toHaveBeenCalled(); + expect(stateQueryHandler).toHaveBeenCalledTimes(1); // no additional polling after a real interval timeout + }); + }); }); }); }); @@ -890,11 +928,7 @@ describe('MrWidgetOptions', () => { }); describe('mock extension', () => { - let pollRequest; - beforeEach(() => { - pollRequest = jest.spyOn(Poll.prototype, 'makeRequest'); - registerExtension(workingExtension()); createComponent(); @@ -945,10 +979,6 @@ describe('MrWidgetOptions', () => { 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', () => { - expect(pollRequest).toHaveBeenCalledTimes(0); - }); }); describe('expansion', () => { @@ -1235,10 +1265,6 @@ describe('MrWidgetOptions', () => { }); describe('widget container', () => { - afterEach(() => { - delete window.gon.features.refactorSecurityExtension; - }); - it('should not be displayed when the refactor_security_extension feature flag is turned off', () => { createComponent(); expect(findWidgetContainer().exists()).toBe(false); 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 88d9d0b4cff..a6288b9c725 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 @@ -20,7 +20,7 @@ describe('getStateKey', () => { }; const bound = getStateKey.bind(context); - expect(bound()).toEqual(null); + expect(bound()).toEqual('checking'); context.detailedMergeStatus = 'MERGEABLE'; |