summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js')
-rw-r--r--spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js375
1 files changed, 375 insertions, 0 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]]);
+ });
+ });
+ });
+});