261 lines
7.9 KiB
JavaScript
261 lines
7.9 KiB
JavaScript
import {
|
|
GlButton,
|
|
GlDropdown,
|
|
GlDropdownItem,
|
|
GlDropdownSectionHeader,
|
|
GlSearchBoxByType,
|
|
} from '@gitlab/ui';
|
|
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
|
|
import VueApollo from 'vue-apollo';
|
|
import createMockApollo from 'helpers/mock_apollo_helper';
|
|
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
|
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
|
import eventHub from '~/projects/new/event_hub';
|
|
import NewProjectUrlSelect from '~/projects/new/components/new_project_url_select.vue';
|
|
import searchQuery from '~/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql';
|
|
|
|
describe('NewProjectUrlSelect component', () => {
|
|
let wrapper;
|
|
|
|
const data = {
|
|
currentUser: {
|
|
groups: {
|
|
nodes: [
|
|
{
|
|
id: 'gid://gitlab/Group/26',
|
|
fullPath: 'flightjs',
|
|
name: 'Flight JS',
|
|
visibility: 'public',
|
|
webUrl: 'http://127.0.0.1:3000/flightjs',
|
|
},
|
|
{
|
|
id: 'gid://gitlab/Group/28',
|
|
fullPath: 'h5bp',
|
|
name: 'H5BP',
|
|
visibility: 'public',
|
|
webUrl: 'http://127.0.0.1:3000/h5bp',
|
|
},
|
|
{
|
|
id: 'gid://gitlab/Group/30',
|
|
fullPath: 'h5bp/subgroup',
|
|
name: 'H5BP Subgroup',
|
|
visibility: 'private',
|
|
webUrl: 'http://127.0.0.1:3000/h5bp/subgroup',
|
|
},
|
|
],
|
|
},
|
|
namespace: {
|
|
id: 'gid://gitlab/Namespace/1',
|
|
fullPath: 'root',
|
|
},
|
|
},
|
|
};
|
|
|
|
const localVue = createLocalVue();
|
|
localVue.use(VueApollo);
|
|
|
|
const defaultProvide = {
|
|
namespaceFullPath: 'h5bp',
|
|
namespaceId: '28',
|
|
rootUrl: 'https://gitlab.com/',
|
|
trackLabel: 'blank_project',
|
|
userNamespaceFullPath: 'root',
|
|
userNamespaceId: '1',
|
|
};
|
|
|
|
const mountComponent = ({
|
|
search = '',
|
|
queryResponse = data,
|
|
provide = defaultProvide,
|
|
mountFn = shallowMount,
|
|
} = {}) => {
|
|
const requestHandlers = [[searchQuery, jest.fn().mockResolvedValue({ data: queryResponse })]];
|
|
const apolloProvider = createMockApollo(requestHandlers);
|
|
|
|
return mountFn(NewProjectUrlSelect, {
|
|
localVue,
|
|
apolloProvider,
|
|
provide,
|
|
data() {
|
|
return {
|
|
search,
|
|
};
|
|
},
|
|
});
|
|
};
|
|
|
|
const findButtonLabel = () => wrapper.findComponent(GlButton);
|
|
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
|
const findInput = () => wrapper.findComponent(GlSearchBoxByType);
|
|
const findHiddenInput = () => wrapper.find('input');
|
|
const clickDropdownItem = async () => {
|
|
wrapper.findComponent(GlDropdownItem).vm.$emit('click');
|
|
await wrapper.vm.$nextTick();
|
|
};
|
|
|
|
afterEach(() => {
|
|
wrapper.destroy();
|
|
});
|
|
|
|
it('renders the root url as a label', () => {
|
|
wrapper = mountComponent();
|
|
|
|
expect(findButtonLabel().text()).toBe(defaultProvide.rootUrl);
|
|
expect(findButtonLabel().props('label')).toBe(true);
|
|
});
|
|
|
|
describe('when namespaceId is provided', () => {
|
|
beforeEach(() => {
|
|
wrapper = mountComponent();
|
|
});
|
|
|
|
it('renders a dropdown with the given namespace full path as the text', () => {
|
|
expect(findDropdown().props('text')).toBe(defaultProvide.namespaceFullPath);
|
|
});
|
|
|
|
it('renders a dropdown with the given namespace id in the hidden input', () => {
|
|
expect(findHiddenInput().attributes('value')).toBe(defaultProvide.namespaceId);
|
|
});
|
|
});
|
|
|
|
describe('when namespaceId is not provided', () => {
|
|
const provide = {
|
|
...defaultProvide,
|
|
namespaceFullPath: undefined,
|
|
namespaceId: undefined,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
wrapper = mountComponent({ provide });
|
|
});
|
|
|
|
it("renders a dropdown with the user's namespace full path as the text", () => {
|
|
expect(findDropdown().props('text')).toBe(defaultProvide.userNamespaceFullPath);
|
|
});
|
|
|
|
it("renders a dropdown with the user's namespace id in the hidden input", () => {
|
|
expect(findHiddenInput().attributes('value')).toBe(defaultProvide.userNamespaceId);
|
|
});
|
|
});
|
|
|
|
it('focuses on the input when the dropdown is opened', async () => {
|
|
wrapper = mountComponent({ mountFn: mount });
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const spy = jest.spyOn(findInput().vm, 'focusInput');
|
|
|
|
findDropdown().vm.$emit('shown');
|
|
|
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('renders expected dropdown items', async () => {
|
|
wrapper = mountComponent({ mountFn: mount });
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const listItems = wrapper.findAll('li');
|
|
|
|
expect(listItems).toHaveLength(6);
|
|
expect(listItems.at(0).findComponent(GlDropdownSectionHeader).text()).toBe('Groups');
|
|
expect(listItems.at(1).text()).toBe(data.currentUser.groups.nodes[0].fullPath);
|
|
expect(listItems.at(2).text()).toBe(data.currentUser.groups.nodes[1].fullPath);
|
|
expect(listItems.at(3).text()).toBe(data.currentUser.groups.nodes[2].fullPath);
|
|
expect(listItems.at(4).findComponent(GlDropdownSectionHeader).text()).toBe('Users');
|
|
expect(listItems.at(5).text()).toBe(data.currentUser.namespace.fullPath);
|
|
});
|
|
|
|
describe('when selecting from a group template', () => {
|
|
const groupId = getIdFromGraphQLId(data.currentUser.groups.nodes[1].id);
|
|
|
|
beforeEach(async () => {
|
|
wrapper = mountComponent({ mountFn: mount });
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
eventHub.$emit('select-template', groupId);
|
|
});
|
|
|
|
it('filters the dropdown items to the selected group and children', async () => {
|
|
const listItems = wrapper.findAll('li');
|
|
|
|
expect(listItems).toHaveLength(3);
|
|
expect(listItems.at(0).findComponent(GlDropdownSectionHeader).text()).toBe('Groups');
|
|
expect(listItems.at(1).text()).toBe(data.currentUser.groups.nodes[1].fullPath);
|
|
expect(listItems.at(2).text()).toBe(data.currentUser.groups.nodes[2].fullPath);
|
|
});
|
|
|
|
it('sets the selection to the group', async () => {
|
|
expect(findDropdown().props('text')).toBe(data.currentUser.groups.nodes[1].fullPath);
|
|
});
|
|
});
|
|
|
|
it('renders `No matches found` when there are no matching dropdown items', async () => {
|
|
const queryResponse = {
|
|
currentUser: {
|
|
groups: {
|
|
nodes: [],
|
|
},
|
|
namespace: {
|
|
id: 'gid://gitlab/Namespace/1',
|
|
fullPath: 'root',
|
|
},
|
|
},
|
|
};
|
|
|
|
wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount });
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
expect(wrapper.find('li').text()).toBe('No matches found');
|
|
});
|
|
|
|
it('emits `update-visibility` event to update the visibility radio options', async () => {
|
|
wrapper = mountComponent();
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const spy = jest.spyOn(eventHub, '$emit');
|
|
|
|
await clickDropdownItem();
|
|
|
|
const namespace = data.currentUser.groups.nodes[0];
|
|
|
|
expect(spy).toHaveBeenCalledWith('update-visibility', {
|
|
name: namespace.name,
|
|
visibility: namespace.visibility,
|
|
showPath: namespace.webUrl,
|
|
editPath: `${namespace.webUrl}/-/edit`,
|
|
});
|
|
});
|
|
|
|
it('updates hidden input with selected namespace', async () => {
|
|
wrapper = mountComponent();
|
|
jest.runOnlyPendingTimers();
|
|
await wrapper.vm.$nextTick();
|
|
|
|
await clickDropdownItem();
|
|
|
|
expect(findHiddenInput().attributes()).toMatchObject({
|
|
name: 'project[namespace_id]',
|
|
value: getIdFromGraphQLId(data.currentUser.groups.nodes[0].id).toString(),
|
|
});
|
|
});
|
|
|
|
it('tracks clicking on the dropdown', () => {
|
|
wrapper = mountComponent();
|
|
|
|
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
|
|
|
findDropdown().vm.$emit('show');
|
|
|
|
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'activate_form_input', {
|
|
label: defaultProvide.trackLabel,
|
|
property: 'project_path',
|
|
});
|
|
|
|
unmockTracking();
|
|
});
|
|
});
|