diff options
Diffstat (limited to 'spec/frontend/jira_connect/branches/components')
3 files changed, 148 insertions, 88 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 index 7326b84ad54..b9fed5f34f1 100644 --- a/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js +++ b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js @@ -1,5 +1,6 @@ -import { GlAlert, GlForm, GlFormInput, GlButton } from '@gitlab/ui'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlAlert, GlForm, GlFormInput, GlButton, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; @@ -9,17 +10,12 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran import { CREATE_BRANCH_ERROR_GENERIC, CREATE_BRANCH_ERROR_WITH_CONTEXT, + I18N_NEW_BRANCH_PERMISSION_ALERT, } from '~/jira_connect/branches/constants'; import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql'; +import { mockProjects } from '../mock_data'; -const mockProject = { - id: 'test', - fullPath: 'test-path', - repository: { - branchNames: ['main', 'f-test', 'release'], - rootRef: 'main', - }, -}; +const mockProject = mockProjects[0]; const mockCreateBranchMutationResponse = { data: { createBranch: { @@ -45,28 +41,27 @@ const mockCreateBranchMutationWithErrors = jest 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 findAlertSprintf = () => findAlert().findComponent(GlSprintf); 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'); + await findInput().vm.$emit('input', 'cool-branch-name'); }; function createMockApolloProvider({ mockCreateBranchMutation = mockCreateBranchMutationSuccess, } = {}) { - localVue.use(VueApollo); + Vue.use(VueApollo); const mockApollo = createMockApollo([[createBranchMutation, mockCreateBranchMutation]]); @@ -75,7 +70,6 @@ describe('NewBranchForm', () => { function createComponent({ mockApollo, provide } = {}) { wrapper = shallowMount(NewBranchForm, { - localVue, apolloProvider: mockApollo || createMockApolloProvider(), provide: { initialBranchName: '', @@ -89,27 +83,107 @@ describe('NewBranchForm', () => { }); describe('when selecting items from dropdowns', () => { - describe('when a project is selected', () => { - it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', async () => { + describe('when no project selected', () => { + beforeEach(() => { createComponent(); + }); - const projectDropdown = findProjectDropdown(); - await projectDropdown.vm.$emit('change', mockProject); + it('hides source branch selection and branch name input', () => { + expect(findSourceBranchDropdown().exists()).toBe(false); + expect(findInput().exists()).toBe(false); + }); - expect(projectDropdown.props('selectedProject')).toEqual(mockProject); - expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject); + it('disables the submit button', () => { + expect(findButton().props('disabled')).toBe(true); }); }); - describe('when a source branch is selected', () => { - it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => { + describe('when a valid project is selected', () => { + describe("when a source branch isn't selected", () => { + beforeEach(async () => { + createComponent(); + await findProjectDropdown().vm.$emit('change', mockProject); + }); + + it('sets the `selectedProject` prop for ProjectDropdown and SourceBranchDropdown', () => { + expect(findProjectDropdown().props('selectedProject')).toEqual(mockProject); + expect(findSourceBranchDropdown().exists()).toBe(true); + expect(findSourceBranchDropdown().props('selectedProject')).toEqual(mockProject); + }); + + it('disables the submit button', () => { + expect(findButton().props('disabled')).toBe(true); + }); + + it('renders branch input field', () => { + expect(findInput().exists()).toBe(true); + }); + }); + + describe('when `initialBranchName` is provided', () => { + it('sets value of branch name input to `initialBranchName` by default', async () => { + const mockInitialBranchName = 'ap1-test-branch-name'; + + createComponent({ provide: { initialBranchName: mockInitialBranchName } }); + await findProjectDropdown().vm.$emit('change', mockProject); + + expect(findInput().attributes('value')).toBe(mockInitialBranchName); + }); + }); + + describe('when a source branch is selected', () => { + it('sets the `selectedBranchName` prop for SourceBranchDropdown', async () => { + createComponent(); + await completeForm(); + + const mockBranchName = 'main'; + const sourceBranchDropdown = findSourceBranchDropdown(); + await sourceBranchDropdown.vm.$emit('change', mockBranchName); + + expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName); + }); + + describe.each` + branchName | submitButtonDisabled + ${undefined} | ${true} + ${''} | ${true} + ${' '} | ${true} + ${'test-branch'} | ${false} + `('when branch name is $branchName', ({ branchName, submitButtonDisabled }) => { + it(`sets submit button 'disabled' prop to ${submitButtonDisabled}`, async () => { + createComponent(); + await completeForm(); + await findInput().vm.$emit('input', branchName); + + expect(findButton().props('disabled')).toBe(submitButtonDisabled); + }); + }); + }); + }); + + describe("when user doesn't have push permissions for the selected project", () => { + beforeEach(async () => { createComponent(); - const mockBranchName = 'main'; - const sourceBranchDropdown = findSourceBranchDropdown(); - await sourceBranchDropdown.vm.$emit('change', mockBranchName); + const projectDropdown = findProjectDropdown(); + await projectDropdown.vm.$emit('change', { + ...mockProject, + userPermissions: { pushCode: false }, + }); + }); + + it('displays an alert', () => { + const alert = findAlert(); + + expect(alert.exists()).toBe(true); + expect(findAlertSprintf().attributes('message')).toBe(I18N_NEW_BRANCH_PERMISSION_ALERT); + expect(alert.props('variant')).toBe('warning'); + expect(alert.props('dismissible')).toBe(false); + }); - expect(sourceBranchDropdown.props('selectedBranchName')).toBe(mockBranchName); + it('hides source branch selection and branch name input', () => { + expect(findSourceBranchDropdown().exists()).toBe(false); + expect(findInput().exists()).toBe(false); }); }); }); @@ -181,7 +255,7 @@ describe('NewBranchForm', () => { it('displays an alert', () => { const alert = findAlert(); expect(alert.exists()).toBe(true); - expect(alert.text()).toBe(alertText); + expect(findAlertSprintf().attributes('message')).toBe(alertText); expect(alert.props()).toMatchObject({ title: alertTitle, variant: 'danger' }); }); @@ -192,15 +266,6 @@ describe('NewBranchForm', () => { }); }); - 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 @@ -211,13 +276,15 @@ describe('NewBranchForm', () => { beforeEach(async () => { createComponent(); + await completeForm(); 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(findAlertSprintf().attributes('message')).toBe(mockErrorMessage); expect(alert.props('variant')).toBe('danger'); }); diff --git a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js index ec4cb2739f8..136a5967ee4 100644 --- a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js +++ b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js @@ -1,5 +1,12 @@ -import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui'; -import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; +import { + GlAvatarLabeled, + GlDropdown, + GlDropdownItem, + GlLoadingIcon, + GlSearchBoxByType, +} from '@gitlab/ui'; +import { mount, 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'; @@ -7,32 +14,7 @@ import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown import { PROJECTS_PER_PAGE } from '~/jira_connect/branches/constants'; import getProjectsQuery from '~/jira_connect/branches/graphql/queries/get_projects.query.graphql'; -const localVue = createLocalVue(); - -const mockProjects = [ - { - id: 'test', - name: 'test', - nameWithNamespace: 'test', - avatarUrl: 'https://gitlab.com', - path: 'test-path', - fullPath: 'test-path', - repository: { - empty: false, - }, - }, - { - id: 'gitlab', - name: 'GitLab', - nameWithNamespace: 'gitlab-org/gitlab', - avatarUrl: 'https://gitlab.com', - path: 'gitlab', - fullPath: 'gitlab-org/gitlab', - repository: { - empty: false, - }, - }, -]; +import { mockProjects } from '../mock_data'; const mockProjectsQueryResponse = { data: { @@ -57,12 +39,12 @@ describe('ProjectDropdown', () => { const findDropdown = () => wrapper.findComponent(GlDropdown); const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findDropdownItemByText = (text) => - findAllDropdownItems().wrappers.find((item) => item.text() === text); + const findDropdownItemByProjectId = (projectId) => + wrapper.find(`[data-testid="test-project-${projectId}"]`); const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); function createMockApolloProvider({ mockGetProjectsQuery = mockGetProjectsQuerySuccess } = {}) { - localVue.use(VueApollo); + Vue.use(VueApollo); const mockApollo = createMockApollo([[getProjectsQuery, mockGetProjectsQuery]]); @@ -71,7 +53,6 @@ describe('ProjectDropdown', () => { function createComponent({ mockApollo, props, mountFn = shallowMount } = {}) { wrapper = mountFn(ProjectDropdown, { - localVue, apolloProvider: mockApollo || createMockApolloProvider(), propsData: props, }); @@ -101,25 +82,38 @@ describe('ProjectDropdown', () => { beforeEach(async () => { createComponent(); await waitForPromises(); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('sets dropdown `loading` prop to `false`', () => { expect(findDropdown().props('loading')).toBe(false); }); - it('renders dropdown items', () => { + it('renders dropdown items with correct props', () => { const dropdownItems = findAllDropdownItems(); + const avatars = dropdownItems.wrappers.map((item) => item.findComponent(GlAvatarLabeled)); + const avatarAttributes = avatars.map((avatar) => avatar.attributes()); + const avatarProps = avatars.map((avatar) => avatar.props()); + expect(dropdownItems.wrappers).toHaveLength(mockProjects.length); - expect(dropdownItems.wrappers.map((item) => item.text())).toEqual( - mockProjects.map((project) => project.nameWithNamespace), + expect(avatarProps).toMatchObject( + mockProjects.map((project) => ({ + label: project.name, + subLabel: project.nameWithNamespace, + })), + ); + expect(avatarAttributes).toMatchObject( + mockProjects.map((project) => ({ + src: project.avatarUrl, + 'entity-name': project.name, + })), ); }); describe('when selecting a dropdown item', () => { - it('emits `change` event with the selected project name', async () => { + it('emits `change` event with the selected project', async () => { const mockProject = mockProjects[0]; - const itemToSelect = findDropdownItemByText(mockProject.nameWithNamespace); + const itemToSelect = findDropdownItemByProjectId(mockProject.id); await itemToSelect.vm.$emit('click'); expect(wrapper.emitted('change')[0]).toEqual([mockProject]); @@ -129,14 +123,14 @@ describe('ProjectDropdown', () => { describe('when `selectedProject` prop is specified', () => { const mockProject = mockProjects[0]; - beforeEach(async () => { + beforeEach(() => { wrapper.setProps({ selectedProject: mockProject, }); }); it('sets `isChecked` prop of the corresponding dropdown item to `true`', () => { - expect(findDropdownItemByText(mockProject.nameWithNamespace).props('isChecked')).toBe(true); + expect(findDropdownItemByProjectId(mockProject.id).props('isChecked')).toBe(true); }); it('sets dropdown text to `selectedBranchName` value', () => { diff --git a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js index 9dd11dd6345..56eb6d75def 100644 --- a/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js +++ b/spec/frontend/jira_connect/branches/components/source_branch_dropdown_spec.js @@ -1,22 +1,22 @@ import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui'; -import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; +import { mount, shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import SourceBranchDropdown from '~/jira_connect/branches/components/source_branch_dropdown.vue'; import { BRANCHES_PER_PAGE } from '~/jira_connect/branches/constants'; import getProjectQuery from '~/jira_connect/branches/graphql/queries/get_project.query.graphql'; - -const localVue = createLocalVue(); +import { mockProjects } from '../mock_data'; const mockProject = { id: 'test', - fullPath: 'test-path', repository: { branchNames: ['main', 'f-test', 'release'], rootRef: 'main', }, }; +const mockSelectedProject = mockProjects[0]; const mockProjectQueryResponse = { data: { @@ -45,7 +45,7 @@ describe('SourceBranchDropdown', () => { }; function createMockApolloProvider({ getProjectQueryLoading = false } = {}) { - localVue.use(VueApollo); + Vue.use(VueApollo); const mockApollo = createMockApollo([ [getProjectQuery, getProjectQueryLoading ? mockQueryLoading : mockGetProjectQuery], @@ -56,7 +56,6 @@ describe('SourceBranchDropdown', () => { function createComponent({ mockApollo, props, mountFn = shallowMount } = {}) { wrapper = mountFn(SourceBranchDropdown, { - localVue, apolloProvider: mockApollo || createMockApolloProvider(), propsData: props, }); @@ -78,7 +77,7 @@ describe('SourceBranchDropdown', () => { describe('when `selectedProject` becomes specified', () => { beforeEach(async () => { wrapper.setProps({ - selectedProject: mockProject, + selectedProject: mockSelectedProject, }); await waitForPromises(); @@ -103,7 +102,7 @@ describe('SourceBranchDropdown', () => { it('renders loading icon in dropdown', () => { createComponent({ mockApollo: createMockApolloProvider({ getProjectQueryLoading: true }), - props: { selectedProject: mockProject }, + props: { selectedProject: mockSelectedProject }, }); expect(findLoadingIcon().isVisible()).toBe(true); @@ -113,7 +112,7 @@ describe('SourceBranchDropdown', () => { describe('when branches have loaded', () => { describe('when searching branches', () => { it('triggers a refetch', async () => { - createComponent({ mountFn: mount, props: { selectedProject: mockProject } }); + createComponent({ mountFn: mount, props: { selectedProject: mockSelectedProject } }); await waitForPromises(); jest.clearAllMocks(); @@ -131,7 +130,7 @@ describe('SourceBranchDropdown', () => { describe('template', () => { beforeEach(async () => { - createComponent({ props: { selectedProject: mockProject } }); + createComponent({ props: { selectedProject: mockSelectedProject } }); await waitForPromises(); }); |