Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/vue_shared/components/list_selector/index_spec.js')
-rw-r--r--spec/frontend/vue_shared/components/list_selector/index_spec.js257
1 files changed, 257 insertions, 0 deletions
diff --git a/spec/frontend/vue_shared/components/list_selector/index_spec.js b/spec/frontend/vue_shared/components/list_selector/index_spec.js
new file mode 100644
index 00000000000..11e64a91eb0
--- /dev/null
+++ b/spec/frontend/vue_shared/components/list_selector/index_spec.js
@@ -0,0 +1,257 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui';
+import Api from '~/api';
+import { createAlert } from '~/alert';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import ListSelector from '~/vue_shared/components/list_selector/index.vue';
+import UserItem from '~/vue_shared/components/list_selector/user_item.vue';
+import GroupItem from '~/vue_shared/components/list_selector/group_item.vue';
+import groupsAutocompleteQuery from '~/graphql_shared/queries/groups_autocomplete.query.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { USERS_RESPONSE_MOCK, GROUPS_RESPONSE_MOCK } from './mock_data';
+
+jest.mock('~/alert');
+Vue.use(VueApollo);
+
+describe('List Selector spec', () => {
+ let wrapper;
+ let fakeApollo;
+
+ const USERS_MOCK_PROPS = {
+ title: 'Users',
+ projectPath: 'some/project/path',
+ groupPath: 'some/group/path',
+ type: 'users',
+ };
+
+ const GROUPS_MOCK_PROPS = {
+ title: 'Groups',
+ projectPath: 'some/project/path',
+ type: 'groups',
+ };
+
+ const groupsAutocompleteQuerySuccess = jest.fn().mockResolvedValue(GROUPS_RESPONSE_MOCK);
+
+ const createComponent = async (props) => {
+ fakeApollo = createMockApollo([[groupsAutocompleteQuery, groupsAutocompleteQuerySuccess]]);
+
+ wrapper = mountExtended(ListSelector, {
+ apolloProvider: fakeApollo,
+ propsData: {
+ ...props,
+ },
+ });
+
+ await waitForPromises();
+ };
+
+ const findCard = () => wrapper.findComponent(GlCard);
+ const findTitle = () => findCard().find('[data-testid="list-selector-title"]');
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const findAllListBoxComponents = () => wrapper.findAllComponents(GlCollapsibleListbox);
+ const findSearchResultsDropdown = () => findAllListBoxComponents().at(0);
+ const findNamespaceDropdown = () => findAllListBoxComponents().at(1);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findAllUserComponents = () => wrapper.findAllComponents(UserItem);
+ const findAllGroupComponents = () => wrapper.findAllComponents(GroupItem);
+
+ beforeEach(() => {
+ jest.spyOn(Api, 'projectUsers').mockResolvedValue(USERS_RESPONSE_MOCK);
+ jest.spyOn(Api, 'groupMembers').mockResolvedValue({ data: USERS_RESPONSE_MOCK });
+ });
+
+ describe('Users type', () => {
+ const search = 'foo';
+
+ beforeEach(() => createComponent(USERS_MOCK_PROPS));
+
+ it('renders a Card component', () => {
+ expect(findCard().exists()).toBe(true);
+ });
+
+ it('renders a correct title', () => {
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().text()).toContain('Users');
+ });
+
+ it('renders the correct icon', () => {
+ expect(findIcon().props('name')).toBe('user');
+ });
+
+ it('renders a Search box component', () => {
+ expect(findSearchBox().exists()).toBe(true);
+ });
+
+ it('renders two namespace dropdown items', () => {
+ expect(findNamespaceDropdown().props('items').length).toBe(2);
+ });
+
+ it('does not call query when search box has not received an input', () => {
+ expect(Api.projectUsers).not.toHaveBeenCalled();
+ expect(Api.groupMembers).not.toHaveBeenCalled();
+ expect(findAllUserComponents().length).toBe(0);
+ });
+
+ describe.each`
+ dropdownItemValue | apiMethod | apiParams | searchResponse
+ ${'false'} | ${'groupMembers'} | ${[USERS_MOCK_PROPS.groupPath, { query: search }]} | ${USERS_RESPONSE_MOCK}
+ ${'true'} | ${'projectUsers'} | ${[USERS_MOCK_PROPS.projectPath, search]} | ${USERS_RESPONSE_MOCK}
+ `(
+ 'searching based on namespace dropdown selection',
+ ({ dropdownItemValue, apiMethod, apiParams, searchResponse }) => {
+ const emitSearchInput = async () => {
+ findSearchBox().vm.$emit('input', search);
+ await waitForPromises();
+ };
+
+ beforeEach(async () => {
+ findNamespaceDropdown().vm.$emit('select', dropdownItemValue);
+ await emitSearchInput();
+ });
+
+ it('shows error alert when API fails', async () => {
+ jest.spyOn(Api, apiMethod).mockRejectedValueOnce();
+ await emitSearchInput();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'An error occurred while fetching. Please try again.',
+ });
+ });
+
+ it('calls query with correct variables when Search box receives an input', () => {
+ expect(Api[apiMethod]).toHaveBeenCalledWith(...apiParams);
+ });
+
+ it('renders a List box component with the correct props', () => {
+ expect(findSearchResultsDropdown().props()).toMatchObject({
+ multiple: true,
+ items: searchResponse,
+ });
+ });
+
+ it('renders a user component for each search result', () => {
+ expect(findAllUserComponents().length).toBe(searchResponse.length);
+ });
+
+ it('emits an event when a search result is selected', () => {
+ const firstSearchResult = searchResponse[0];
+ findAllUserComponents().at(0).vm.$emit('select', firstSearchResult.username);
+
+ expect(wrapper.emitted('select')).toEqual([
+ [{ ...firstSearchResult, text: 'Administrator', value: 'root' }],
+ ]);
+ });
+ },
+ );
+
+ describe('selected items', () => {
+ const selectedUser = { username: 'root' };
+ const selectedItems = [selectedUser];
+ beforeEach(() => createComponent({ ...USERS_MOCK_PROPS, selectedItems }));
+
+ it('renders a heading with the total selected items', () => {
+ expect(findTitle().text()).toContain('Users');
+ expect(findTitle().text()).toContain('1');
+ });
+
+ it('renders a user component for each selected item', () => {
+ expect(findAllUserComponents().length).toBe(selectedItems.length);
+ expect(findAllUserComponents().at(0).props()).toMatchObject({
+ data: selectedUser,
+ canDelete: true,
+ });
+ });
+
+ it('emits a delete event when a delete event is emitted from the user component', () => {
+ const username = 'root';
+ findAllUserComponents().at(0).vm.$emit('delete', username);
+
+ expect(wrapper.emitted('delete')).toEqual([[username]]);
+ });
+ });
+ });
+
+ describe('Groups type', () => {
+ beforeEach(() => createComponent(GROUPS_MOCK_PROPS));
+
+ it('renders a correct title', () => {
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().text()).toContain('Groups');
+ });
+
+ it('renders the correct icon', () => {
+ expect(findIcon().props('name')).toBe('group');
+ });
+
+ it('does not call query when search box has not received an input', () => {
+ expect(groupsAutocompleteQuerySuccess).not.toHaveBeenCalled();
+ expect(findAllGroupComponents().length).toBe(0);
+ });
+
+ describe('searching', () => {
+ const searchResponse = GROUPS_RESPONSE_MOCK.data.groups.nodes;
+ const search = 'foo';
+
+ const emitSearchInput = async () => {
+ findSearchBox().vm.$emit('input', search);
+ await waitForPromises();
+ };
+
+ beforeEach(() => emitSearchInput());
+
+ it('calls query with correct variables when Search box receives an input', () => {
+ expect(groupsAutocompleteQuerySuccess).toHaveBeenCalledWith({
+ search,
+ });
+ });
+
+ it('renders a dropdown for the search results', () => {
+ expect(findSearchResultsDropdown().props()).toMatchObject({
+ multiple: true,
+ items: searchResponse,
+ });
+ });
+
+ it('renders a group component for each search result', () => {
+ expect(findAllGroupComponents().length).toBe(searchResponse.length);
+ });
+
+ it('emits an event when a search result is selected', () => {
+ const firstSearchResult = searchResponse[0];
+ findAllGroupComponents().at(0).vm.$emit('select', firstSearchResult.name);
+
+ expect(wrapper.emitted('select')).toEqual([
+ [{ ...firstSearchResult, text: 'Flightjs', value: 'Flightjs' }],
+ ]);
+ });
+ });
+
+ describe('selected items', () => {
+ const selectedGroup = { name: 'Flightjs' };
+ const selectedItems = [selectedGroup];
+ beforeEach(() => createComponent({ ...GROUPS_MOCK_PROPS, selectedItems }));
+
+ it('renders a heading with the total selected items', () => {
+ expect(findTitle().text()).toContain('Groups');
+ expect(findTitle().text()).toContain('1');
+ });
+
+ it('renders a group component for each selected item', () => {
+ expect(findAllGroupComponents().length).toBe(selectedItems.length);
+ expect(findAllGroupComponents().at(0).props()).toMatchObject({
+ data: selectedGroup,
+ canDelete: true,
+ });
+ });
+
+ it('emits a delete event when a delete event is emitted from the group component', () => {
+ const name = 'Flightjs';
+ findAllGroupComponents().at(0).vm.$emit('delete', name);
+
+ expect(wrapper.emitted('delete')).toEqual([[name]]);
+ });
+ });
+ });
+});