diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /spec/frontend/vue_shared/components/sidebar | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) | |
download | gitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/components/sidebar')
3 files changed, 386 insertions, 4 deletions
diff --git a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js new file mode 100644 index 00000000000..a97e26caf53 --- /dev/null +++ b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js @@ -0,0 +1,375 @@ +import { shallowMount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; +import { + GlIcon, + GlLoadingIcon, + GlDropdown, + GlDropdownForm, + GlDropdownItem, + GlSearchBoxByType, + GlButton, +} from '@gitlab/ui'; + +import axios from '~/lib/utils/axios_utils'; +import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue'; + +const mockProjects = [ + { + id: 2, + name_with_namespace: 'Gitlab Org / Gitlab Shell', + full_path: 'gitlab-org/gitlab-shell', + }, + { + id: 3, + name_with_namespace: 'Gnuwget / Wget2', + full_path: 'gnuwget/wget2', + }, + { + id: 4, + name_with_namespace: 'Commit451 / Lab Coat', + full_path: 'Commit451/lab-coat', + }, +]; + +const mockProps = { + projectsFetchPath: '/-/autocomplete/projects?project_id=1', + dropdownButtonTitle: 'Move issuable', + dropdownHeaderTitle: 'Move issuable', + moveInProgress: false, +}; + +const mockEvent = { + stopPropagation: jest.fn(), + preventDefault: jest.fn(), +}; + +const createComponent = (propsData = mockProps) => + shallowMount(IssuableMoveDropdown, { + propsData, + }); + +describe('IssuableMoveDropdown', () => { + let mock; + let wrapper; + + beforeEach(() => { + mock = new MockAdapter(axios); + wrapper = createComponent(); + wrapper.vm.$refs.dropdown.hide = jest.fn(); + wrapper.vm.$refs.searchInput.focusInput = jest.fn(); + }); + + afterEach(() => { + wrapper.destroy(); + mock.restore(); + }); + + describe('watch', () => { + describe('searchKey', () => { + it('calls `fetchProjects` with value of the prop', async () => { + jest.spyOn(wrapper.vm, 'fetchProjects'); + wrapper.setData({ + searchKey: 'foo', + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.fetchProjects).toHaveBeenCalledWith('foo'); + }); + }); + }); + + describe('methods', () => { + describe('fetchProjects', () => { + it('sets projectsListLoading to true and projectsListLoadFailed to false', () => { + wrapper.vm.fetchProjects(); + + expect(wrapper.vm.projectsListLoading).toBe(true); + expect(wrapper.vm.projectsListLoadFailed).toBe(false); + }); + + it('calls `axios.get` with `projectsFetchPath` and query param `search`', () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + wrapper.vm.fetchProjects('foo'); + + expect(axios.get).toHaveBeenCalledWith( + mockProps.projectsFetchPath, + expect.objectContaining({ + params: { + search: 'foo', + }, + }), + ); + }); + + it('sets response to `projects` and focuses on searchInput when request is successful', async () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projects).toBe(mockProjects); + expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled(); + }); + + it('sets projectsListLoadFailed to true when request fails', async () => { + jest.spyOn(axios, 'get').mockRejectedValue({}); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projectsListLoadFailed).toBe(true); + }); + + it('sets projectsListLoading to false when request completes', async () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + await wrapper.vm.fetchProjects('foo'); + + expect(wrapper.vm.projectsListLoading).toBe(false); + }); + }); + + describe('isSelectedProject', () => { + it.each` + project | selectedProject | title | returnValue + ${mockProjects[0]} | ${mockProjects[0]} | ${'are same projects'} | ${true} + ${mockProjects[0]} | ${mockProjects[1]} | ${'are different projects'} | ${false} + `( + 'returns $returnValue when selectedProject and provided project param $title', + async ({ project, selectedProject, returnValue }) => { + wrapper.setData({ + selectedProject, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isSelectedProject(project)).toBe(returnValue); + }, + ); + + it('returns false when selectedProject is null', async () => { + wrapper.setData({ + selectedProject: null, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.isSelectedProject(mockProjects[0])).toBe(false); + }); + }); + }); + + describe('template', () => { + const findDropdownEl = () => wrapper.find(GlDropdown); + + it('renders collapsed state element with icon', () => { + const collapsedEl = wrapper.find('[data-testid="move-collapsed"]'); + + expect(collapsedEl.exists()).toBe(true); + expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle); + expect(collapsedEl.find(GlIcon).exists()).toBe(true); + expect(collapsedEl.find(GlIcon).props('name')).toBe('arrow-right'); + }); + + describe('gl-dropdown component', () => { + it('renders component container element', () => { + expect(findDropdownEl().exists()).toBe(true); + expect(findDropdownEl().props('block')).toBe(true); + }); + + it('renders gl-dropdown-form component', () => { + expect( + findDropdownEl() + .find(GlDropdownForm) + .exists(), + ).toBe(true); + }); + + it('renders header element', () => { + const headerEl = findDropdownEl().find('[data-testid="header"]'); + + expect(headerEl.exists()).toBe(true); + expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle); + expect(headerEl.find(GlButton).props('icon')).toBe('close'); + }); + + it('renders gl-search-box-by-type component', () => { + const searchEl = findDropdownEl().find(GlSearchBoxByType); + + expect(searchEl.exists()).toBe(true); + expect(searchEl.attributes()).toMatchObject({ + placeholder: 'Search project', + debounce: '300', + }); + }); + + it('renders gl-loading-icon component when projectsListLoading prop is true', async () => { + wrapper.setData({ + projectsListLoading: true, + }); + + await wrapper.vm.$nextTick(); + + expect( + findDropdownEl() + .find(GlLoadingIcon) + .exists(), + ).toBe(true); + }); + + it('renders gl-dropdown-item components for available projects', async () => { + wrapper.setData({ + projects: mockProjects, + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + const dropdownItems = wrapper.findAll(GlDropdownItem); + + expect(dropdownItems).toHaveLength(mockProjects.length); + expect(dropdownItems.at(0).props()).toMatchObject({ + isCheckItem: true, + isChecked: true, + }); + expect(dropdownItems.at(0).text()).toBe(mockProjects[0].name_with_namespace); + }); + + it('renders string "No matching results" when search does not yield any matches', async () => { + wrapper.setData({ + searchKey: 'foo', + }); + + // Wait for `searchKey` watcher to run. + await wrapper.vm.$nextTick(); + + wrapper.setData({ + projects: [], + projectsListLoading: false, + }); + + await wrapper.vm.$nextTick(); + + const dropdownContentEl = wrapper.find('[data-testid="content"]'); + + expect(dropdownContentEl.text()).toContain('No matching results'); + }); + + it('renders string "Failed to load projects" when loading projects list fails', async () => { + wrapper.setData({ + projects: [], + projectsListLoading: false, + projectsListLoadFailed: true, + }); + + await wrapper.vm.$nextTick(); + + const dropdownContentEl = wrapper.find('[data-testid="content"]'); + + expect(dropdownContentEl.text()).toContain('Failed to load projects'); + }); + + it('renders gl-button within footer', async () => { + const moveButtonEl = wrapper.find('[data-testid="footer"]').find(GlButton); + + expect(moveButtonEl.text()).toBe('Move'); + expect(moveButtonEl.attributes('disabled')).toBe('true'); + + wrapper.setData({ + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + expect( + wrapper + .find('[data-testid="footer"]') + .find(GlButton) + .attributes('disabled'), + ).not.toBeDefined(); + }); + }); + + describe('events', () => { + it('collapsed state element emits `toggle-collapse` event on component when clicked', () => { + wrapper.find('[data-testid="move-collapsed"]').trigger('click'); + + expect(wrapper.emitted('toggle-collapse')).toBeTruthy(); + }); + + it('gl-dropdown component calls `fetchProjects` on `shown` event', () => { + jest.spyOn(axios, 'get').mockResolvedValue({ + data: mockProjects, + }); + + findDropdownEl().vm.$emit('shown'); + + expect(axios.get).toHaveBeenCalled(); + }); + + it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => { + wrapper.setData({ + projectItemClick: true, + }); + + findDropdownEl().vm.$emit('hide', mockEvent); + + expect(mockEvent.preventDefault).toHaveBeenCalled(); + expect(wrapper.vm.projectItemClick).toBe(false); + }); + + it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', async () => { + findDropdownEl().vm.$emit('hide'); + + expect(wrapper.emitted('dropdown-close')).toBeTruthy(); + }); + + it('close icon in dropdown header closes the dropdown when clicked', () => { + wrapper + .find('[data-testid="header"]') + .find(GlButton) + .vm.$emit('click', mockEvent); + + expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); + }); + + it('sets project for clicked gl-dropdown-item to selectedProject', async () => { + wrapper.setData({ + projects: mockProjects, + }); + + await wrapper.vm.$nextTick(); + + wrapper + .findAll(GlDropdownItem) + .at(0) + .vm.$emit('click', mockEvent); + + expect(wrapper.vm.selectedProject).toBe(mockProjects[0]); + }); + + it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => { + wrapper.setData({ + selectedProject: mockProjects[0], + }); + + await wrapper.vm.$nextTick(); + + wrapper + .find('[data-testid="footer"]') + .find(GlButton) + .vm.$emit('click'); + + expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); + expect(wrapper.emitted('move-issuable')).toBeTruthy(); + expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]); + }); + }); + }); +}); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js index 7847e0ee71d..71c040c6633 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js @@ -81,9 +81,7 @@ describe('DropdownValueCollapsedComponent', () => { describe('template', () => { it('renders component container element with tooltip`', () => { - expect(vm.$el.dataset.placement).toBe('left'); - expect(vm.$el.dataset.container).toBe('body'); - expect(vm.$el.dataset.originalTitle).toBe(vm.labelsList); + expect(vm.$el.title).toBe(vm.labelsList); }); it('renders tags icon element', () => { diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js index e8a126d8774..78367b3a5b4 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js @@ -128,6 +128,16 @@ describe('DropdownContentsLabelsView', () => { }); }); + describe('handleComponentAppear', () => { + it('calls `focusInput` on searchInput field', async () => { + wrapper.vm.$refs.searchInput.focusInput = jest.fn(); + + await wrapper.vm.handleComponentAppear(); + + expect(wrapper.vm.$refs.searchInput.focusInput).toHaveBeenCalled(); + }); + }); + describe('handleComponentDisappear', () => { it('calls action `receiveLabelsSuccess` with empty array', () => { jest.spyOn(wrapper.vm, 'receiveLabelsSuccess'); @@ -301,7 +311,6 @@ describe('DropdownContentsLabelsView', () => { const searchInputEl = wrapper.find(GlSearchBoxByType); expect(searchInputEl.exists()).toBe(true); - expect(searchInputEl.attributes('autofocus')).toBe('true'); }); it('renders label elements for all labels', () => { |