diff options
Diffstat (limited to 'spec/frontend/jira_connect/branches/components/new_branch_form_spec.js')
-rw-r--r-- | spec/frontend/jira_connect/branches/components/new_branch_form_spec.js | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js new file mode 100644 index 00000000000..7326b84ad54 --- /dev/null +++ b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js @@ -0,0 +1,236 @@ +import { GlAlert, GlForm, GlFormInput, GlButton } from '@gitlab/ui'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import NewBranchForm from '~/jira_connect/branches/components/new_branch_form.vue'; +import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown.vue'; +import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue'; +import { + CREATE_BRANCH_ERROR_GENERIC, + CREATE_BRANCH_ERROR_WITH_CONTEXT, +} from '~/jira_connect/branches/constants'; +import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql'; + +const mockProject = { + id: 'test', + fullPath: 'test-path', + repository: { + branchNames: ['main', 'f-test', 'release'], + rootRef: 'main', + }, +}; +const mockCreateBranchMutationResponse = { + data: { + createBranch: { + clientMutationId: 1, + errors: [], + }, + }, +}; +const mockCreateBranchMutationResponseWithErrors = { + data: { + createBranch: { + clientMutationId: 1, + errors: ['everything is broken, sorry.'], + }, + }, +}; +const mockCreateBranchMutationSuccess = jest + .fn() + .mockResolvedValue(mockCreateBranchMutationResponse); +const mockCreateBranchMutationWithErrors = jest + .fn() + .mockResolvedValue(mockCreateBranchMutationResponseWithErrors); +const mockCreateBranchMutationFailed = jest.fn().mockRejectedValue(new Error('GraphQL error')); +const mockMutationLoading = jest.fn().mockReturnValue(new Promise(() => {})); + +const localVue = createLocalVue(); + +describe('NewBranchForm', () => { + let wrapper; + + const findSourceBranchDropdown = () => wrapper.findComponent(SourceBranchDropdown); + const findProjectDropdown = () => wrapper.findComponent(ProjectDropdown); + const findAlert = () => wrapper.findComponent(GlAlert); + const findForm = () => wrapper.findComponent(GlForm); + const findInput = () => wrapper.findComponent(GlFormInput); + const findButton = () => wrapper.findComponent(GlButton); + + const completeForm = async () => { + await findInput().vm.$emit('input', 'cool-branch-name'); + await findProjectDropdown().vm.$emit('change', mockProject); + await findSourceBranchDropdown().vm.$emit('change', 'source-branch'); + }; + + function createMockApolloProvider({ + mockCreateBranchMutation = mockCreateBranchMutationSuccess, + } = {}) { + localVue.use(VueApollo); + + const mockApollo = createMockApollo([[createBranchMutation, mockCreateBranchMutation]]); + + return mockApollo; + } + + function createComponent({ mockApollo, provide } = {}) { + wrapper = shallowMount(NewBranchForm, { + localVue, + apolloProvider: mockApollo || createMockApolloProvider(), + provide: { + initialBranchName: '', + ...provide, + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when selecting items from dropdowns', () => { + describe('when a project is selected', () => { + it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', async () => { + createComponent(); + + const projectDropdown = findProjectDropdown(); + await projectDropdown.vm.$emit('change', mockProject); + + expect(projectDropdown.props('selectedProject')).toEqual(mockProject); + expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject); + }); + }); + + describe('when a source branch is selected', () => { + it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => { + createComponent(); + + const mockBranchName = 'main'; + const sourceBranchDropdown = findSourceBranchDropdown(); + await sourceBranchDropdown.vm.$emit('change', mockBranchName); + + expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName); + }); + }); + }); + + describe('when submitting form', () => { + describe('when form submission is loading', () => { + it('sets submit button `loading` prop to `true`', async () => { + createComponent({ + mockApollo: createMockApolloProvider({ + mockCreateBranchMutation: mockMutationLoading, + }), + }); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + + expect(findButton().props('loading')).toBe(true); + }); + }); + + describe('when form submission is successful', () => { + beforeEach(async () => { + createComponent(); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + }); + + it('emits `success` event', () => { + expect(wrapper.emitted('success')).toBeTruthy(); + }); + + it('called `createBranch` mutation correctly', () => { + expect(mockCreateBranchMutationSuccess).toHaveBeenCalledWith({ + name: 'cool-branch-name', + projectPath: mockProject.fullPath, + ref: 'source-branch', + }); + }); + + it('sets submit button `loading` prop to `false`', () => { + expect(findButton().props('loading')).toBe(false); + }); + }); + + describe('when form submission fails', () => { + describe.each` + scenario | mutation | alertTitle | alertText + ${'with errors-as-data'} | ${mockCreateBranchMutationWithErrors} | ${CREATE_BRANCH_ERROR_WITH_CONTEXT} | ${mockCreateBranchMutationResponseWithErrors.data.createBranch.errors[0]} + ${'top-level error'} | ${mockCreateBranchMutationFailed} | ${''} | ${CREATE_BRANCH_ERROR_GENERIC} + `('', ({ mutation, alertTitle, alertText }) => { + beforeEach(async () => { + createComponent({ + mockApollo: createMockApolloProvider({ + mockCreateBranchMutation: mutation, + }), + }); + + await completeForm(); + + await findForm().vm.$emit('submit', new Event('submit')); + await waitForPromises(); + }); + + it('displays an alert', () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + expect(alert.text()).toBe(alertText); + expect(alert.props()).toMatchObject({ title: alertTitle, variant: 'danger' }); + }); + + it('sets submit button `loading` prop to `false`', () => { + expect(findButton().props('loading')).toBe(false); + }); + }); + }); + }); + + describe('when `initialBranchName` is specified', () => { + it('sets value of branch name input to `initialBranchName` by default', () => { + const mockInitialBranchName = 'ap1-test-branch-name'; + + createComponent({ provide: { initialBranchName: mockInitialBranchName } }); + expect(findInput().attributes('value')).toBe(mockInitialBranchName); + }); + }); + + describe('error handling', () => { + describe.each` + component | componentName + ${SourceBranchDropdown} | ${'SourceBranchDropdown'} + ${ProjectDropdown} | ${'ProjectDropdown'} + `('when $componentName emits error', ({ component }) => { + const mockErrorMessage = 'oh noes!'; + + beforeEach(async () => { + createComponent(); + await wrapper.findComponent(component).vm.$emit('error', { message: mockErrorMessage }); + }); + + it('displays an alert', () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + expect(alert.text()).toBe(mockErrorMessage); + expect(alert.props('variant')).toBe('danger'); + }); + + describe('when alert is dismissed', () => { + it('hides alert', async () => { + const alert = findAlert(); + expect(alert.exists()).toBe(true); + + await alert.vm.$emit('dismiss'); + + expect(alert.exists()).toBe(false); + }); + }); + }); + }); +}); |