diff options
Diffstat (limited to 'spec/frontend/sidebar/components/move/move_issue_button_spec.js')
-rw-r--r-- | spec/frontend/sidebar/components/move/move_issue_button_spec.js | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/spec/frontend/sidebar/components/move/move_issue_button_spec.js b/spec/frontend/sidebar/components/move/move_issue_button_spec.js new file mode 100644 index 00000000000..acd6b23c1f5 --- /dev/null +++ b/spec/frontend/sidebar/components/move/move_issue_button_spec.js @@ -0,0 +1,157 @@ +import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { visitUrl } from '~/lib/utils/url_utility'; +import { createAlert } from '~/flash'; +import ProjectSelect from '~/sidebar/components/move/issuable_move_dropdown.vue'; +import MoveIssueButton from '~/sidebar/components/move/move_issue_button.vue'; +import moveIssueMutation from '~/sidebar/queries/move_issue.mutation.graphql'; + +Vue.use(VueApollo); + +jest.mock('~/flash'); +jest.mock('~/lib/utils/url_utility', () => ({ + visitUrl: jest.fn(), +})); + +const projectFullPath = 'flight/FlightJS'; +const projectsAutocompleteEndpoint = '/-/autocomplete/projects?project_id=1'; +const issueIid = '15'; + +const mockDestinationProject = { + full_path: 'gitlab-org/GitLabTest', +}; + +const mockWebUrl = `${mockDestinationProject.full_path}/issues/${issueIid}`; + +const mockMutationErrorMessage = 'Example error message'; + +const resolvedMutationWithoutErrorsMock = jest.fn().mockResolvedValue({ + data: { + issueMove: { + issue: { + id: issueIid, + webUrl: mockWebUrl, + }, + errors: [], + }, + }, +}); + +const resolvedMutationWithErrorsMock = jest.fn().mockResolvedValue({ + data: { + issueMove: { + errors: [{ message: mockMutationErrorMessage }], + }, + }, +}); + +const rejectedMutationMock = jest.fn().mockRejectedValue({}); + +describe('MoveIssueButton', () => { + let wrapper; + let fakeApollo; + + const findProjectSelect = () => wrapper.findComponent(ProjectSelect); + const emitProjectSelectEvent = () => { + findProjectSelect().vm.$emit('move-issuable', mockDestinationProject); + }; + const createComponent = (mutationResolverMock = rejectedMutationMock) => { + fakeApollo = createMockApollo([[moveIssueMutation, mutationResolverMock]]); + + wrapper = shallowMount(MoveIssueButton, { + provide: { + projectFullPath, + projectsAutocompleteEndpoint, + issueIid, + }, + apolloProvider: fakeApollo, + }); + }; + + afterEach(() => { + fakeApollo = null; + }); + + it('renders the project select dropdown', () => { + createComponent(); + + expect(findProjectSelect().props()).toMatchObject({ + projectsFetchPath: projectsAutocompleteEndpoint, + dropdownButtonTitle: MoveIssueButton.i18n.title, + dropdownHeaderTitle: MoveIssueButton.i18n.title, + moveInProgress: false, + }); + }); + + describe('when the project is selected', () => { + it('sets loading state and dropdown button text when issue is moving', async () => { + createComponent(); + expect(findProjectSelect().props()).toMatchObject({ + dropdownButtonTitle: MoveIssueButton.i18n.title, + moveInProgress: false, + }); + + emitProjectSelectEvent(); + await nextTick(); + + expect(findProjectSelect().props()).toMatchObject({ + dropdownButtonTitle: MoveIssueButton.i18n.titleInProgress, + moveInProgress: true, + }); + }); + + it.each` + condition | mutation + ${'a mutation returns errors'} | ${resolvedMutationWithErrorsMock} + ${'a mutation is rejected'} | ${rejectedMutationMock} + `('sets loading state to false when $condition', async ({ mutation }) => { + createComponent(mutation); + emitProjectSelectEvent(); + + await nextTick(); + expect(findProjectSelect().props('moveInProgress')).toBe(true); + + await waitForPromises(); + expect(findProjectSelect().props('moveInProgress')).toBe(false); + }); + + it('creates a flash and logs errors when a mutation returns errors', async () => { + createComponent(resolvedMutationWithErrorsMock); + emitProjectSelectEvent(); + + await waitForPromises(); + + expect(createAlert).toHaveBeenCalledWith({ + message: MoveIssueButton.i18n.moveErrorMessage, + captureError: true, + error: expect.any(Object), + }); + }); + + it('calls a mutation for the selected issue', async () => { + createComponent(resolvedMutationWithoutErrorsMock); + emitProjectSelectEvent(); + + await waitForPromises(); + + expect(resolvedMutationWithoutErrorsMock).toHaveBeenCalledWith({ + moveIssueInput: { + projectPath: projectFullPath, + iid: issueIid, + targetProjectPath: mockDestinationProject.full_path, + }, + }); + }); + + it('redirects to the correct page when the mutation succeeds', async () => { + createComponent(resolvedMutationWithoutErrorsMock); + emitProjectSelectEvent(); + await waitForPromises(); + + expect(visitUrl).toHaveBeenCalledWith(mockWebUrl); + }); + }); +}); |