diff options
Diffstat (limited to 'spec/frontend/projects')
18 files changed, 800 insertions, 13 deletions
diff --git a/spec/frontend/projects/commit/components/form_modal_spec.js b/spec/frontend/projects/commit/components/form_modal_spec.js index 1569f5b4bbe..708644cb7ee 100644 --- a/spec/frontend/projects/commit/components/form_modal_spec.js +++ b/spec/frontend/projects/commit/components/form_modal_spec.js @@ -7,6 +7,7 @@ import axios from '~/lib/utils/axios_utils'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import BranchesDropdown from '~/projects/commit/components/branches_dropdown.vue'; import CommitFormModal from '~/projects/commit/components/form_modal.vue'; +import ProjectsDropdown from '~/projects/commit/components/projects_dropdown.vue'; import eventHub from '~/projects/commit/event_hub'; import createStore from '~/projects/commit/store'; import mockData from '../mock_data'; @@ -20,7 +21,10 @@ describe('CommitFormModal', () => { store = createStore({ ...mockData.mockModal, ...state }); wrapper = extendedWrapper( method(CommitFormModal, { - provide, + provide: { + ...provide, + glFeatures: { pickIntoProject: true }, + }, propsData: { ...mockData.modalPropsData }, store, attrs: { @@ -33,7 +37,9 @@ describe('CommitFormModal', () => { const findModal = () => wrapper.findComponent(GlModal); const findStartBranch = () => wrapper.find('#start_branch'); - const findDropdown = () => wrapper.findComponent(BranchesDropdown); + const findTargetProject = () => wrapper.find('#target_project_id'); + const findBranchesDropdown = () => wrapper.findComponent(BranchesDropdown); + const findProjectsDropdown = () => wrapper.findComponent(ProjectsDropdown); const findForm = () => findModal().findComponent(GlForm); const findCheckBox = () => findForm().findComponent(GlFormCheckbox); const findPrependedText = () => wrapper.findByTestId('prepended-text'); @@ -146,11 +152,19 @@ describe('CommitFormModal', () => { }); it('Changes the start_branch input value', async () => { - findDropdown().vm.$emit('selectBranch', '_changed_branch_value_'); + findBranchesDropdown().vm.$emit('selectBranch', '_changed_branch_value_'); await wrapper.vm.$nextTick(); expect(findStartBranch().attributes('value')).toBe('_changed_branch_value_'); }); + + it('Changes the target_project_id input value', async () => { + findProjectsDropdown().vm.$emit('selectProject', '_changed_project_value_'); + + await wrapper.vm.$nextTick(); + + expect(findTargetProject().attributes('value')).toBe('_changed_project_value_'); + }); }); }); diff --git a/spec/frontend/projects/commit/components/projects_dropdown_spec.js b/spec/frontend/projects/commit/components/projects_dropdown_spec.js new file mode 100644 index 00000000000..bb20918e0cd --- /dev/null +++ b/spec/frontend/projects/commit/components/projects_dropdown_spec.js @@ -0,0 +1,124 @@ +import { GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import ProjectsDropdown from '~/projects/commit/components/projects_dropdown.vue'; + +Vue.use(Vuex); + +describe('ProjectsDropdown', () => { + let wrapper; + let store; + const spyFetchProjects = jest.fn(); + const projectsMockData = [ + { id: '1', name: '_project_1_', refsUrl: '_project_1_/refs' }, + { id: '2', name: '_project_2_', refsUrl: '_project_2_/refs' }, + { id: '3', name: '_project_3_', refsUrl: '_project_3_/refs' }, + ]; + + const createComponent = (term, state = {}) => { + store = new Vuex.Store({ + getters: { + sortedProjects: () => projectsMockData, + }, + state, + }); + + wrapper = extendedWrapper( + shallowMount(ProjectsDropdown, { + store, + propsData: { + value: term, + }, + }), + ); + }; + + const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType); + const findDropdownItemByIndex = (index) => wrapper.findAllComponents(GlDropdownItem).at(index); + const findNoResults = () => wrapper.findByTestId('empty-result-message'); + + afterEach(() => { + wrapper.destroy(); + spyFetchProjects.mockReset(); + }); + + describe('No projects found', () => { + beforeEach(() => { + createComponent('_non_existent_project_'); + }); + + it('renders empty results message', () => { + expect(findNoResults().text()).toBe('No matching results'); + }); + + it('shows GlSearchBoxByType with default attributes', () => { + expect(findSearchBoxByType().exists()).toBe(true); + expect(findSearchBoxByType().vm.$attrs).toMatchObject({ + placeholder: 'Search projects', + }); + }); + }); + + describe('Search term is empty', () => { + beforeEach(() => { + createComponent(''); + }); + + it('renders all projects when search term is empty', () => { + expect(findAllDropdownItems()).toHaveLength(3); + expect(findDropdownItemByIndex(0).text()).toBe('_project_1_'); + expect(findDropdownItemByIndex(1).text()).toBe('_project_2_'); + expect(findDropdownItemByIndex(2).text()).toBe('_project_3_'); + }); + + it('should not be selected on the inactive project', () => { + expect(wrapper.vm.isSelected('_project_1_')).toBe(false); + }); + }); + + describe('Projects found', () => { + beforeEach(() => { + createComponent('_project_1_', { targetProjectId: '1' }); + }); + + it('renders only the project searched for', () => { + expect(findAllDropdownItems()).toHaveLength(1); + expect(findDropdownItemByIndex(0).text()).toBe('_project_1_'); + }); + + it('should not display empty results message', () => { + expect(findNoResults().exists()).toBe(false); + }); + + it('should signify this project is selected', () => { + expect(findDropdownItemByIndex(0).props('isChecked')).toBe(true); + }); + + it('should signify the project is not selected', () => { + expect(wrapper.vm.isSelected('_not_selected_project_')).toBe(false); + }); + + describe('Custom events', () => { + it('should emit selectProject if a project is clicked', () => { + findDropdownItemByIndex(0).vm.$emit('click'); + + expect(wrapper.emitted('selectProject')).toEqual([['1']]); + expect(wrapper.vm.filterTerm).toBe('_project_1_'); + }); + }); + }); + + describe('Case insensitive for search term', () => { + beforeEach(() => { + createComponent('_PrOjEcT_1_'); + }); + + it('renders only the project searched for', () => { + expect(findAllDropdownItems()).toHaveLength(1); + expect(findDropdownItemByIndex(0).text()).toBe('_project_1_'); + }); + }); +}); diff --git a/spec/frontend/projects/commit/mock_data.js b/spec/frontend/projects/commit/mock_data.js index 2b3b5a14c98..e4dcb24c4c0 100644 --- a/spec/frontend/projects/commit/mock_data.js +++ b/spec/frontend/projects/commit/mock_data.js @@ -24,4 +24,5 @@ export default { openModal: '_open_modal_', }, mockBranches: ['_branch_1', '_abc_', '_master_'], + mockProjects: ['_project_1', '_abc_', '_project_'], }; diff --git a/spec/frontend/projects/commit/store/actions_spec.js b/spec/frontend/projects/commit/store/actions_spec.js index 458372229cf..305257c9ca5 100644 --- a/spec/frontend/projects/commit/store/actions_spec.js +++ b/spec/frontend/projects/commit/store/actions_spec.js @@ -47,7 +47,7 @@ describe('Commit form modal store actions', () => { it('dispatch correct actions on fetchBranches', (done) => { jest .spyOn(axios, 'get') - .mockImplementation(() => Promise.resolve({ data: mockData.mockBranches })); + .mockImplementation(() => Promise.resolve({ data: { Branches: mockData.mockBranches } })); testAction( actions.fetchBranches, @@ -108,4 +108,43 @@ describe('Commit form modal store actions', () => { ]); }); }); + + describe('setBranchesEndpoint', () => { + it('commits SET_BRANCHES_ENDPOINT mutation', () => { + const endpoint = 'some/endpoint'; + + testAction(actions.setBranchesEndpoint, endpoint, {}, [ + { + type: types.SET_BRANCHES_ENDPOINT, + payload: endpoint, + }, + ]); + }); + }); + + describe('setSelectedProject', () => { + const id = 1; + + it('commits SET_SELECTED_PROJECT mutation', () => { + testAction( + actions.setSelectedProject, + id, + {}, + [ + { + type: types.SET_SELECTED_PROJECT, + payload: id, + }, + ], + [ + { + type: 'setBranchesEndpoint', + }, + { + type: 'fetchBranches', + }, + ], + ); + }); + }); }); diff --git a/spec/frontend/projects/commit/store/getters_spec.js b/spec/frontend/projects/commit/store/getters_spec.js index bd0cb356854..38c45af7aa0 100644 --- a/spec/frontend/projects/commit/store/getters_spec.js +++ b/spec/frontend/projects/commit/store/getters_spec.js @@ -18,4 +18,21 @@ describe('Commit form modal getters', () => { expect(getters.joinedBranches(state)).toEqual(branches.slice(1)); }); }); + + describe('sortedProjects', () => { + it('should sort projects with variable branches', () => { + const state = { + projects: mockData.mockProjects, + }; + + expect(getters.sortedProjects(state)).toEqual(mockData.mockProjects.sort()); + }); + + it('should provide a uniq list of projects', () => { + const projects = ['_project_', '_project_', '_some_other_project']; + const state = { projects }; + + expect(getters.sortedProjects(state)).toEqual(projects.slice(1)); + }); + }); }); diff --git a/spec/frontend/projects/commit/store/mutations_spec.js b/spec/frontend/projects/commit/store/mutations_spec.js index 2ea50e71772..8989e769772 100644 --- a/spec/frontend/projects/commit/store/mutations_spec.js +++ b/spec/frontend/projects/commit/store/mutations_spec.js @@ -35,6 +35,16 @@ describe('Commit form modal mutations', () => { }); }); + describe('SET_BRANCHES_ENDPOINT', () => { + it('should set branchesEndpoint', () => { + stateCopy = { branchesEndpoint: 'endpoint/1' }; + + mutations[types.SET_BRANCHES_ENDPOINT](stateCopy, 'endpoint/2'); + + expect(stateCopy.branchesEndpoint).toBe('endpoint/2'); + }); + }); + describe('SET_BRANCH', () => { it('should set branch', () => { stateCopy = { branch: '_master_' }; @@ -54,4 +64,14 @@ describe('Commit form modal mutations', () => { expect(stateCopy.selectedBranch).toBe('_changed_branch_'); }); }); + + describe('SET_SELECTED_PROJECT', () => { + it('should set targetProjectId', () => { + stateCopy = { targetProjectId: '_project_1_' }; + + mutations[types.SET_SELECTED_PROJECT](stateCopy, '_project_2_'); + + expect(stateCopy.targetProjectId).toBe('_project_2_'); + }); + }); }); diff --git a/spec/frontend/projects/compare/components/app_legacy_spec.js b/spec/frontend/projects/compare/components/app_legacy_spec.js new file mode 100644 index 00000000000..4c7f0d5cccc --- /dev/null +++ b/spec/frontend/projects/compare/components/app_legacy_spec.js @@ -0,0 +1,116 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import CompareApp from '~/projects/compare/components/app_legacy.vue'; +import RevisionDropdown from '~/projects/compare/components/revision_dropdown_legacy.vue'; + +jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); + +const projectCompareIndexPath = 'some/path'; +const refsProjectPath = 'some/refs/path'; +const paramsFrom = 'master'; +const paramsTo = 'master'; + +describe('CompareApp component', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(CompareApp, { + propsData: { + projectCompareIndexPath, + refsProjectPath, + paramsFrom, + paramsTo, + projectMergeRequestPath: '', + createMrPath: '', + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + beforeEach(() => { + createComponent(); + }); + + it('renders component with prop', () => { + expect(wrapper.props()).toEqual( + expect.objectContaining({ + projectCompareIndexPath, + refsProjectPath, + paramsFrom, + paramsTo, + }), + ); + }); + + it('contains the correct form attributes', () => { + expect(wrapper.attributes('action')).toBe(projectCompareIndexPath); + expect(wrapper.attributes('method')).toBe('POST'); + }); + + it('has input with csrf token', () => { + expect(wrapper.find('input[name="authenticity_token"]').attributes('value')).toBe( + 'mock-csrf-token', + ); + }); + + it('has ellipsis', () => { + expect(wrapper.find('[data-testid="ellipsis"]').exists()).toBe(true); + }); + + it('render Source and Target BranchDropdown components', () => { + const branchDropdowns = wrapper.findAll(RevisionDropdown); + + expect(branchDropdowns.length).toBe(2); + expect(branchDropdowns.at(0).props('revisionText')).toBe('Source'); + expect(branchDropdowns.at(1).props('revisionText')).toBe('Target'); + }); + + describe('compare button', () => { + const findCompareButton = () => wrapper.find(GlButton); + + it('renders button', () => { + expect(findCompareButton().exists()).toBe(true); + }); + + it('submits form', () => { + findCompareButton().vm.$emit('click'); + expect(wrapper.find('form').element.submit).toHaveBeenCalled(); + }); + + it('has compare text', () => { + expect(findCompareButton().text()).toBe('Compare'); + }); + }); + + describe('merge request buttons', () => { + const findProjectMrButton = () => wrapper.find('[data-testid="projectMrButton"]'); + const findCreateMrButton = () => wrapper.find('[data-testid="createMrButton"]'); + + it('does not have merge request buttons', () => { + createComponent(); + expect(findProjectMrButton().exists()).toBe(false); + expect(findCreateMrButton().exists()).toBe(false); + }); + + it('has "View open merge request" button', () => { + createComponent({ + projectMergeRequestPath: 'some/project/merge/request/path', + }); + expect(findProjectMrButton().exists()).toBe(true); + expect(findCreateMrButton().exists()).toBe(false); + }); + + it('has "Create merge request" button', () => { + createComponent({ + createMrPath: 'some/create/create/mr/path', + }); + expect(findProjectMrButton().exists()).toBe(false); + expect(findCreateMrButton().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/projects/compare/components/app_spec.js b/spec/frontend/projects/compare/components/app_spec.js index d28a30e93b1..6de06e4373c 100644 --- a/spec/frontend/projects/compare/components/app_spec.js +++ b/spec/frontend/projects/compare/components/app_spec.js @@ -1,7 +1,7 @@ import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import CompareApp from '~/projects/compare/components/app.vue'; -import RevisionDropdown from '~/projects/compare/components/revision_dropdown.vue'; +import RevisionCard from '~/projects/compare/components/revision_card.vue'; jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); @@ -63,11 +63,11 @@ describe('CompareApp component', () => { }); it('render Source and Target BranchDropdown components', () => { - const branchDropdowns = wrapper.findAll(RevisionDropdown); + const revisionCards = wrapper.findAll(RevisionCard); - expect(branchDropdowns.length).toBe(2); - expect(branchDropdowns.at(0).props('revisionText')).toBe('Source'); - expect(branchDropdowns.at(1).props('revisionText')).toBe('Target'); + expect(revisionCards.length).toBe(2); + expect(revisionCards.at(0).props('revisionText')).toBe('Source'); + expect(revisionCards.at(1).props('revisionText')).toBe('Target'); }); describe('compare button', () => { diff --git a/spec/frontend/projects/compare/components/repo_dropdown_spec.js b/spec/frontend/projects/compare/components/repo_dropdown_spec.js new file mode 100644 index 00000000000..af76632515c --- /dev/null +++ b/spec/frontend/projects/compare/components/repo_dropdown_spec.js @@ -0,0 +1,98 @@ +import { GlDropdown } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import RepoDropdown from '~/projects/compare/components/repo_dropdown.vue'; + +const defaultProps = { + paramsName: 'to', +}; + +const projectToId = '1'; +const projectToName = 'some-to-name'; +const projectFromId = '2'; +const projectFromName = 'some-from-name'; + +const defaultProvide = { + projectTo: { id: projectToId, name: projectToName }, + projectsFrom: [ + { id: projectFromId, name: projectFromName }, + { id: 3, name: 'some-from-another-name' }, + ], +}; + +describe('RepoDropdown component', () => { + let wrapper; + + const createComponent = (props = {}, provide = {}) => { + wrapper = shallowMount(RepoDropdown, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + ...defaultProvide, + ...provide, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findGlDropdown = () => wrapper.find(GlDropdown); + const findHiddenInput = () => wrapper.find('input[type="hidden"]'); + + describe('Source Revision', () => { + beforeEach(() => { + createComponent(); + }); + + it('set hidden input', () => { + expect(findHiddenInput().attributes('value')).toBe(projectToId); + }); + + it('displays the project name in the disabled dropdown', () => { + expect(findGlDropdown().props('text')).toBe(projectToName); + expect(findGlDropdown().props('disabled')).toBe(true); + }); + + it('does not emit `changeTargetProject` event', async () => { + wrapper.vm.emitTargetProject('foo'); + await wrapper.vm.$nextTick(); + expect(wrapper.emitted('changeTargetProject')).toBeUndefined(); + }); + }); + + describe('Target Revision', () => { + beforeEach(() => { + createComponent({ paramsName: 'from' }); + }); + + it('set hidden input of the first project', () => { + expect(findHiddenInput().attributes('value')).toBe(projectFromId); + }); + + it('displays the first project name initially in the dropdown', () => { + expect(findGlDropdown().props('text')).toBe(projectFromName); + }); + + it('updates the hiddin input value when onClick method is triggered', async () => { + const repoId = '100'; + wrapper.vm.onClick({ id: repoId }); + await wrapper.vm.$nextTick(); + expect(findHiddenInput().attributes('value')).toBe(repoId); + }); + + it('emits initial `changeTargetProject` event with target project', () => { + expect(wrapper.emitted('changeTargetProject')).toEqual([[projectFromName]]); + }); + + it('emits `changeTargetProject` event when another target project is selected', async () => { + const newTargetProject = 'new-from-name'; + wrapper.vm.$emit('changeTargetProject', newTargetProject); + await wrapper.vm.$nextTick(); + expect(wrapper.emitted('changeTargetProject')[1]).toEqual([newTargetProject]); + }); + }); +}); diff --git a/spec/frontend/projects/compare/components/revision_card_spec.js b/spec/frontend/projects/compare/components/revision_card_spec.js new file mode 100644 index 00000000000..83f858f4454 --- /dev/null +++ b/spec/frontend/projects/compare/components/revision_card_spec.js @@ -0,0 +1,49 @@ +import { GlCard } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import RepoDropdown from '~/projects/compare/components/repo_dropdown.vue'; +import RevisionCard from '~/projects/compare/components/revision_card.vue'; +import RevisionDropdown from '~/projects/compare/components/revision_dropdown.vue'; + +const defaultProps = { + refsProjectPath: 'some/refs/path', + revisionText: 'Source', + paramsName: 'to', + paramsBranch: 'master', +}; + +describe('RepoDropdown component', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(RevisionCard, { + propsData: { + ...defaultProps, + ...props, + }, + stubs: { + GlCard, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + beforeEach(() => { + createComponent(); + }); + + it('displays revision text', () => { + expect(wrapper.find(GlCard).text()).toContain(defaultProps.revisionText); + }); + + it('renders RepoDropdown component', () => { + expect(wrapper.findAll(RepoDropdown).exists()).toBe(true); + }); + + it('renders RevisionDropdown component', () => { + expect(wrapper.findAll(RevisionDropdown).exists()).toBe(true); + }); +}); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js new file mode 100644 index 00000000000..270c89e674c --- /dev/null +++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js @@ -0,0 +1,106 @@ +import { GlDropdown } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import AxiosMockAdapter from 'axios-mock-adapter'; +import createFlash from '~/flash'; +import axios from '~/lib/utils/axios_utils'; +import RevisionDropdown from '~/projects/compare/components/revision_dropdown_legacy.vue'; + +const defaultProps = { + refsProjectPath: 'some/refs/path', + revisionText: 'Target', + paramsName: 'from', + paramsBranch: 'master', +}; + +jest.mock('~/flash'); + +describe('RevisionDropdown component', () => { + let wrapper; + let axiosMock; + + const createComponent = (props = {}) => { + wrapper = shallowMount(RevisionDropdown, { + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + beforeEach(() => { + axiosMock = new AxiosMockAdapter(axios); + }); + + afterEach(() => { + wrapper.destroy(); + axiosMock.restore(); + }); + + const findGlDropdown = () => wrapper.find(GlDropdown); + + it('sets hidden input', () => { + createComponent(); + expect(wrapper.find('input[type="hidden"]').attributes('value')).toBe( + defaultProps.paramsBranch, + ); + }); + + it('update the branches on success', async () => { + const Branches = ['branch-1', 'branch-2']; + const Tags = ['tag-1', 'tag-2', 'tag-3']; + + axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, { + Branches, + Tags, + }); + + createComponent(); + + await axios.waitForAll(); + + expect(wrapper.vm.branches).toEqual(Branches); + expect(wrapper.vm.tags).toEqual(Tags); + }); + + it('sets branches and tags to be an empty array when no tags or branches are given', async () => { + axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, { + Branches: undefined, + Tags: undefined, + }); + + createComponent(); + + await axios.waitForAll(); + + expect(wrapper.vm.branches).toEqual([]); + expect(wrapper.vm.tags).toEqual([]); + }); + + it('shows flash message on error', async () => { + axiosMock.onGet('some/invalid/path').replyOnce(404); + + createComponent(); + + await wrapper.vm.fetchBranchesAndTags(); + expect(createFlash).toHaveBeenCalled(); + }); + + describe('GlDropdown component', () => { + it('renders props', () => { + createComponent(); + expect(wrapper.props()).toEqual(expect.objectContaining(defaultProps)); + }); + + it('display default text', () => { + createComponent({ + paramsBranch: null, + }); + expect(findGlDropdown().props('text')).toBe('Select branch/tag'); + }); + + it('display params branch text', () => { + createComponent(); + expect(findGlDropdown().props('text')).toBe(defaultProps.paramsBranch); + }); + }); +}); diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js index f3ff5e26d2b..69d3167c99c 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js @@ -7,7 +7,6 @@ import RevisionDropdown from '~/projects/compare/components/revision_dropdown.vu const defaultProps = { refsProjectPath: 'some/refs/path', - revisionText: 'Target', paramsName: 'from', paramsBranch: 'master', }; @@ -57,7 +56,6 @@ describe('RevisionDropdown component', () => { createComponent(); await axios.waitForAll(); - expect(wrapper.vm.branches).toEqual(Branches); expect(wrapper.vm.tags).toEqual(Tags); }); @@ -71,6 +69,22 @@ describe('RevisionDropdown component', () => { expect(createFlash).toHaveBeenCalled(); }); + it('makes a new request when refsProjectPath is changed', async () => { + jest.spyOn(axios, 'get'); + + const newRefsProjectPath = 'new-selected-project-path'; + + createComponent(); + + wrapper.setProps({ + ...defaultProps, + refsProjectPath: newRefsProjectPath, + }); + + await axios.waitForAll(); + expect(axios.get).toHaveBeenLastCalledWith(newRefsProjectPath); + }); + describe('GlDropdown component', () => { it('renders props', () => { createComponent(); diff --git a/spec/frontend/projects/details/upload_button_spec.js b/spec/frontend/projects/details/upload_button_spec.js new file mode 100644 index 00000000000..ebb2b499ead --- /dev/null +++ b/spec/frontend/projects/details/upload_button_spec.js @@ -0,0 +1,61 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import UploadButton from '~/projects/details/upload_button.vue'; +import { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking'; +import UploadBlobModal from '~/repository/components/upload_blob_modal.vue'; + +jest.mock('~/projects/upload_file_experiment_tracking'); + +const MODAL_ID = 'details-modal-upload-blob'; + +describe('UploadButton', () => { + let wrapper; + let glModalDirective; + + const createComponent = () => { + glModalDirective = jest.fn(); + + return shallowMount(UploadButton, { + directives: { + glModal: { + bind(_, { value }) { + glModalDirective(value); + }, + }, + }, + }); + }; + + beforeEach(() => { + wrapper = createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays an upload button', () => { + expect(wrapper.find(GlButton).exists()).toBe(true); + }); + + it('contains a modal', () => { + const modal = wrapper.find(UploadBlobModal); + + expect(modal.exists()).toBe(true); + expect(modal.props('modalId')).toBe(MODAL_ID); + }); + + describe('when clickinig the upload file button', () => { + beforeEach(() => { + wrapper.find(GlButton).vm.$emit('click'); + }); + + it('tracks the click_upload_modal_trigger event', () => { + expect(trackFileUploadEvent).toHaveBeenCalledWith('click_upload_modal_trigger'); + }); + + it('opens the modal', () => { + expect(glModalDirective).toHaveBeenCalledWith(MODAL_ID); + }); + }); +}); diff --git a/spec/frontend/projects/experiment_new_project_creation/components/new_project_push_tip_popover_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/new_project_push_tip_popover_spec.js new file mode 100644 index 00000000000..1ce16640d4a --- /dev/null +++ b/spec/frontend/projects/experiment_new_project_creation/components/new_project_push_tip_popover_spec.js @@ -0,0 +1,75 @@ +import { GlPopover, GlFormInputGroup } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import NewProjectPushTipPopover from '~/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; + +describe('New project push tip popover', () => { + let wrapper; + const targetId = 'target'; + const pushToCreateProjectCommand = 'command'; + const workingWithProjectsHelpPath = 'path'; + + const findPopover = () => wrapper.findComponent(GlPopover); + const findClipboardButton = () => wrapper.findComponent(ClipboardButton); + const findFormInput = () => wrapper.findComponent(GlFormInputGroup); + const findHelpLink = () => wrapper.find('a'); + const findTarget = () => document.getElementById(targetId); + + const buildWrapper = () => { + wrapper = shallowMount(NewProjectPushTipPopover, { + propsData: { + target: findTarget(), + }, + stubs: { + GlFormInputGroup, + }, + provide: { + pushToCreateProjectCommand, + workingWithProjectsHelpPath, + }, + }); + }; + + beforeEach(() => { + setFixtures(`<a id="${targetId}"></a>`); + buildWrapper(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders popover that targets the specified target', () => { + expect(findPopover().props()).toMatchObject({ + target: findTarget(), + triggers: 'click blur', + placement: 'top', + title: 'Push to create a project', + }); + }); + + it('renders a readonly form input with the push to create command', () => { + expect(findFormInput().props()).toMatchObject({ + value: pushToCreateProjectCommand, + selectOnClick: true, + }); + expect(findFormInput().attributes()).toMatchObject({ + 'aria-label': 'Push project from command line', + readonly: 'readonly', + }); + }); + + it('allows copying the push command using the clipboard button', () => { + expect(findClipboardButton().props()).toMatchObject({ + text: pushToCreateProjectCommand, + tooltipPlacement: 'right', + title: 'Copy command', + }); + }); + + it('displays a link to open the push command help page reference', () => { + expect(findHelpLink().attributes().href).toBe( + `${workingWithProjectsHelpPath}#push-to-create-a-new-project`, + ); + }); +}); diff --git a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js index d6764f75262..f26d1a6d2a3 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js @@ -1,5 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import { mockTracking } from 'helpers/tracking_helper'; +import NewProjectPushTipPopover from '~/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue'; import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; describe('Welcome page', () => { @@ -28,4 +29,13 @@ describe('Welcome page', () => { expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', { label: 'test' }); }); }); + + it('renders new project push tip popover', () => { + createComponent({ panels: [{ name: 'test', href: '#' }] }); + + const popover = wrapper.findComponent(NewProjectPushTipPopover); + + expect(popover.exists()).toBe(true); + expect(popover.props().target()).toBe(wrapper.find({ ref: 'clipTip' }).element); + }); }); diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js index f9fbb1b3016..8acf2376860 100644 --- a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js +++ b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js @@ -154,7 +154,7 @@ describe('ServiceDeskRoot', () => { }); it('shows an error message', () => { - expect(getAlertText()).toContain('An error occured while saving changes:'); + expect(getAlertText()).toContain('An error occurred while saving changes:'); }); }); }); diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js index f6744f4971e..5323c1afbb5 100644 --- a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js +++ b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js @@ -40,7 +40,7 @@ describe('ServiceDeskSetting', () => { }); it('should see activation checkbox', () => { - expect(findToggle().exists()).toBe(true); + expect(findToggle().props('label')).toBe(ServiceDeskSetting.i18n.toggleLabel); }); it('should see main panel with the email info', () => { diff --git a/spec/frontend/projects/upload_file_experiment_tracking_spec.js b/spec/frontend/projects/upload_file_experiment_tracking_spec.js new file mode 100644 index 00000000000..6817529e07e --- /dev/null +++ b/spec/frontend/projects/upload_file_experiment_tracking_spec.js @@ -0,0 +1,43 @@ +import ExperimentTracking from '~/experimentation/experiment_tracking'; +import { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking'; + +jest.mock('~/experimentation/experiment_tracking'); + +const eventName = 'click_upload_modal_form_submit'; +const fixture = `<a class='js-upload-file-experiment-trigger'></a><div class='project-home-panel empty-project'></div>`; + +beforeEach(() => { + document.body.innerHTML = fixture; +}); + +afterEach(() => { + document.body.innerHTML = ''; +}); + +describe('trackFileUploadEvent', () => { + it('initializes ExperimentTracking with the correct tracking event', () => { + trackFileUploadEvent(eventName); + + expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(eventName); + }); + + it('calls ExperimentTracking with the correct arguments', () => { + trackFileUploadEvent(eventName); + + expect(ExperimentTracking).toHaveBeenCalledWith('empty_repo_upload', { + label: 'blob-upload-modal', + property: 'empty', + }); + }); + + it('calls ExperimentTracking with the correct arguments when the project is not empty', () => { + document.querySelector('.empty-project').remove(); + + trackFileUploadEvent(eventName); + + expect(ExperimentTracking).toHaveBeenCalledWith('empty_repo_upload', { + label: 'blob-upload-modal', + property: 'nonempty', + }); + }); +}); |