355 lines
11 KiB
JavaScript
355 lines
11 KiB
JavaScript
import {
|
|
GlIcon,
|
|
GlLoadingIcon,
|
|
GlDropdown,
|
|
GlDropdownForm,
|
|
GlDropdownItem,
|
|
GlSearchBoxByType,
|
|
GlButton,
|
|
} from '@gitlab/ui';
|
|
import { shallowMount } from '@vue/test-utils';
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
|
|
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]]);
|
|
});
|
|
});
|
|
});
|
|
});
|