diff options
Diffstat (limited to 'spec/frontend/groups')
-rw-r--r-- | spec/frontend/groups/components/app_spec.js | 124 | ||||
-rw-r--r-- | spec/frontend/groups/components/item_actions_spec.js | 89 | ||||
-rw-r--r-- | spec/frontend/groups/members/index_spec.js | 13 | ||||
-rw-r--r-- | spec/frontend/groups/mock_data.js | 5 |
4 files changed, 105 insertions, 126 deletions
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js index 5d34bc48ed5..691f8896d74 100644 --- a/spec/frontend/groups/components/app_spec.js +++ b/spec/frontend/groups/components/app_spec.js @@ -2,6 +2,8 @@ import '~/flash'; import $ from 'jquery'; import Vue from 'vue'; import AxiosMockAdapter from 'axios-mock-adapter'; +import { GlModal, GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import appComponent from '~/groups/components/app.vue'; @@ -23,47 +25,51 @@ import { mockPageInfo, } from '../mock_data'; -const createComponent = (hideProjects = false) => { - const Component = Vue.extend(appComponent); - const store = new GroupsStore(false); - const service = new GroupsService(mockEndpoint); - - store.state.pageInfo = mockPageInfo; - - return new Component({ - propsData: { - store, - service, - hideProjects, - }, - }); +const $toast = { + show: jest.fn(), }; describe('AppComponent', () => { + let wrapper; let vm; let mock; let getGroupsSpy; + const store = new GroupsStore(false); + const service = new GroupsService(mockEndpoint); + + const createShallowComponent = (hideProjects = false) => { + store.state.pageInfo = mockPageInfo; + wrapper = shallowMount(appComponent, { + propsData: { + store, + service, + hideProjects, + }, + mocks: { + $toast, + }, + }); + vm = wrapper.vm; + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + beforeEach(() => { mock = new AxiosMockAdapter(axios); mock.onGet('/dashboard/groups.json').reply(200, mockGroups); Vue.component('group-folder', groupFolderComponent); Vue.component('group-item', groupItemComponent); - vm = createComponent(); + createShallowComponent(); getGroupsSpy = jest.spyOn(vm.service, 'getGroups'); return vm.$nextTick(); }); describe('computed', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - describe('groups', () => { it('should return list of groups from store', () => { jest.spyOn(vm.store, 'getGroups').mockImplementation(() => {}); @@ -88,14 +94,6 @@ describe('AppComponent', () => { }); describe('methods', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - describe('fetchGroups', () => { it('should call `getGroups` with all the params provided', () => { return vm @@ -284,29 +282,15 @@ describe('AppComponent', () => { it('updates props which show modal confirmation dialog', () => { const group = { ...mockParentGroupItem }; - expect(vm.showModal).toBe(false); expect(vm.groupLeaveConfirmationMessage).toBe(''); vm.showLeaveGroupModal(group, mockParentGroupItem); - expect(vm.showModal).toBe(true); expect(vm.groupLeaveConfirmationMessage).toBe( `Are you sure you want to leave the "${group.fullName}" group?`, ); }); }); - describe('hideLeaveGroupModal', () => { - it('hides modal confirmation which is shown before leaving the group', () => { - const group = { ...mockParentGroupItem }; - vm.showLeaveGroupModal(group, mockParentGroupItem); - - expect(vm.showModal).toBe(true); - vm.hideLeaveGroupModal(); - - expect(vm.showModal).toBe(false); - }); - }); - describe('leaveGroup', () => { let groupItem; let childGroupItem; @@ -324,18 +308,16 @@ describe('AppComponent', () => { const notice = `You left the "${childGroupItem.fullName}" group.`; jest.spyOn(vm.service, 'leaveGroup').mockResolvedValue({ data: { notice } }); jest.spyOn(vm.store, 'removeGroup'); - jest.spyOn(window, 'Flash').mockImplementation(() => {}); jest.spyOn($, 'scrollTo').mockImplementation(() => {}); vm.leaveGroup(); - expect(vm.showModal).toBe(false); expect(vm.targetGroup.isBeingRemoved).toBe(true); expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath); return waitForPromises().then(() => { expect($.scrollTo).toHaveBeenCalledWith(0); expect(vm.store.removeGroup).toHaveBeenCalledWith(vm.targetGroup, vm.targetParentGroup); - expect(window.Flash).toHaveBeenCalledWith(notice, 'notice'); + expect($toast.show).toHaveBeenCalledWith(notice); }); }); @@ -417,8 +399,7 @@ describe('AppComponent', () => { it('should bind event listeners on eventHub', () => { jest.spyOn(eventHub, '$on').mockImplementation(() => {}); - const newVm = createComponent(); - newVm.$mount(); + createShallowComponent(); return vm.$nextTick().then(() => { expect(eventHub.$on).toHaveBeenCalledWith('fetchPage', expect.any(Function)); @@ -426,25 +407,20 @@ describe('AppComponent', () => { expect(eventHub.$on).toHaveBeenCalledWith('showLeaveGroupModal', expect.any(Function)); expect(eventHub.$on).toHaveBeenCalledWith('updatePagination', expect.any(Function)); expect(eventHub.$on).toHaveBeenCalledWith('updateGroups', expect.any(Function)); - newVm.$destroy(); }); }); it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `false`', () => { - const newVm = createComponent(); - newVm.$mount(); + createShallowComponent(); return vm.$nextTick().then(() => { - expect(newVm.searchEmptyMessage).toBe('No groups or projects matched your search'); - newVm.$destroy(); + expect(vm.searchEmptyMessage).toBe('No groups or projects matched your search'); }); }); it('should initialize `searchEmptyMessage` prop with correct string when `hideProjects` is `true`', () => { - const newVm = createComponent(true); - newVm.$mount(); + createShallowComponent(true); return vm.$nextTick().then(() => { - expect(newVm.searchEmptyMessage).toBe('No groups matched your search'); - newVm.$destroy(); + expect(vm.searchEmptyMessage).toBe('No groups matched your search'); }); }); }); @@ -453,9 +429,8 @@ describe('AppComponent', () => { it('should unbind event listeners on eventHub', () => { jest.spyOn(eventHub, '$off').mockImplementation(() => {}); - const newVm = createComponent(); - newVm.$mount(); - newVm.$destroy(); + createShallowComponent(); + wrapper.destroy(); return vm.$nextTick().then(() => { expect(eventHub.$off).toHaveBeenCalledWith('fetchPage', expect.any(Function)); @@ -468,19 +443,10 @@ describe('AppComponent', () => { }); describe('template', () => { - beforeEach(() => { - vm.$mount(); - }); - - afterEach(() => { - vm.$destroy(); - }); - it('should render loading icon', () => { vm.isLoading = true; return vm.$nextTick().then(() => { - expect(vm.$el.querySelector('.loading-animation')).toBeDefined(); - expect(vm.$el.querySelector('span').getAttribute('aria-label')).toBe('Loading groups'); + expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); }); }); @@ -493,15 +459,13 @@ describe('AppComponent', () => { }); it('renders modal confirmation dialog', () => { - vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?'; - vm.showModal = true; - return vm.$nextTick().then(() => { - const modalDialogEl = vm.$el.querySelector('.modal'); + createShallowComponent(); - expect(modalDialogEl).not.toBe(null); - expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); - expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); - }); + const findGlModal = wrapper.find(GlModal); + + expect(findGlModal.exists()).toBe(true); + expect(findGlModal.attributes('title')).toBe('Are you sure?'); + expect(findGlModal.props('actionPrimary').text).toBe('Leave group'); }); }); }); diff --git a/spec/frontend/groups/components/item_actions_spec.js b/spec/frontend/groups/components/item_actions_spec.js index d4aa29eaadd..9adbc9abe13 100644 --- a/spec/frontend/groups/components/item_actions_spec.js +++ b/spec/frontend/groups/components/item_actions_spec.js @@ -1,5 +1,4 @@ import { shallowMount } from '@vue/test-utils'; -import { GlIcon } from '@gitlab/ui'; import ItemActions from '~/groups/components/item_actions.vue'; import eventHub from '~/groups/event_hub'; import { mockParentGroupItem, mockChildren } from '../mock_data'; @@ -20,18 +19,25 @@ describe('ItemActions', () => { }; afterEach(() => { - if (wrapper) { - wrapper.destroy(); - wrapper = null; - } + wrapper.destroy(); + wrapper = null; }); const findEditGroupBtn = () => wrapper.find('[data-testid="edit-group-btn"]'); - const findEditGroupIcon = () => findEditGroupBtn().find(GlIcon); const findLeaveGroupBtn = () => wrapper.find('[data-testid="leave-group-btn"]'); - const findLeaveGroupIcon = () => findLeaveGroupBtn().find(GlIcon); describe('template', () => { + let group; + + beforeEach(() => { + group = { + ...mockParentGroupItem, + canEdit: true, + canLeave: true, + }; + createComponent({ group }); + }); + it('renders component template correctly', () => { createComponent(); @@ -39,49 +45,46 @@ describe('ItemActions', () => { }); it('renders "Edit group" button with correct attribute values', () => { - const group = { - ...mockParentGroupItem, - canEdit: true, - }; - - createComponent({ group }); - - expect(findEditGroupBtn().exists()).toBe(true); - expect(findEditGroupBtn().classes()).toContain('no-expand'); - expect(findEditGroupBtn().attributes('href')).toBe(group.editPath); - expect(findEditGroupBtn().attributes('aria-label')).toBe('Edit group'); - expect(findEditGroupBtn().attributes('data-original-title')).toBe('Edit group'); - expect(findEditGroupIcon().exists()).toBe(true); - expect(findEditGroupIcon().props('name')).toBe('settings'); + const button = findEditGroupBtn(); + expect(button.exists()).toBe(true); + expect(button.props('icon')).toBe('pencil'); + expect(button.attributes('aria-label')).toBe('Edit group'); }); - describe('`canLeave` is true', () => { - const group = { - ...mockParentGroupItem, - canLeave: true, - }; + it('renders "Leave this group" button with correct attribute values', () => { + const button = findLeaveGroupBtn(); + expect(button.exists()).toBe(true); + expect(button.props('icon')).toBe('leave'); + expect(button.attributes('aria-label')).toBe('Leave this group'); + }); - beforeEach(() => { - createComponent({ group }); - }); + it('emits `showLeaveGroupModal` event in the event hub', () => { + jest.spyOn(eventHub, '$emit'); + findLeaveGroupBtn().vm.$emit('click', { stopPropagation: () => {} }); - it('renders "Leave this group" button with correct attribute values', () => { - expect(findLeaveGroupBtn().exists()).toBe(true); - expect(findLeaveGroupBtn().classes()).toContain('no-expand'); - expect(findLeaveGroupBtn().attributes('href')).toBe(group.leavePath); - expect(findLeaveGroupBtn().attributes('aria-label')).toBe('Leave this group'); - expect(findLeaveGroupBtn().attributes('data-original-title')).toBe('Leave this group'); - expect(findLeaveGroupIcon().exists()).toBe(true); - expect(findLeaveGroupIcon().props('name')).toBe('leave'); - }); + expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', group, parentGroup); + }); + }); - it('emits event on "Leave this group" button click', () => { - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); + it('does not render leave button if group can not be left', () => { + createComponent({ + group: { + ...mockParentGroupItem, + canLeave: false, + }, + }); - findLeaveGroupBtn().trigger('click'); + expect(findLeaveGroupBtn().exists()).toBe(false); + }); - expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', group, parentGroup); - }); + it('does not render edit button if group can not be edited', () => { + createComponent({ + group: { + ...mockParentGroupItem, + canEdit: false, + }, }); + + expect(findEditGroupBtn().exists()).toBe(false); }); }); diff --git a/spec/frontend/groups/members/index_spec.js b/spec/frontend/groups/members/index_spec.js index 2fb7904bcfe..aaa36665c45 100644 --- a/spec/frontend/groups/members/index_spec.js +++ b/spec/frontend/groups/members/index_spec.js @@ -9,7 +9,12 @@ describe('initGroupMembersApp', () => { let wrapper; const setup = () => { - vm = initGroupMembersApp(el, ['account'], () => ({})); + vm = initGroupMembersApp( + el, + ['account'], + { table: { 'data-qa-selector': 'members_list' } }, + () => ({}), + ); wrapper = createWrapper(vm); }; @@ -68,6 +73,12 @@ describe('initGroupMembersApp', () => { expect(vm.$store.state.tableFields).toEqual(['account']); }); + it('sets `tableAttrs` in Vuex store', () => { + setup(); + + expect(vm.$store.state.tableAttrs).toEqual({ table: { 'data-qa-selector': 'members_list' } }); + }); + it('sets `requestFormatter` in Vuex store', () => { setup(); diff --git a/spec/frontend/groups/mock_data.js b/spec/frontend/groups/mock_data.js index 380dda9f7b1..603cb27deec 100644 --- a/spec/frontend/groups/mock_data.js +++ b/spec/frontend/groups/mock_data.js @@ -7,13 +7,14 @@ export const ITEM_TYPE = { export const GROUP_VISIBILITY_TYPE = { public: 'Public - The group and any public projects can be viewed without any authentication.', - internal: 'Internal - The group and any internal projects can be viewed by any logged in user.', + internal: + 'Internal - The group and any internal projects can be viewed by any logged in user except external users.', private: 'Private - The group and its projects can only be viewed by members.', }; export const PROJECT_VISIBILITY_TYPE = { public: 'Public - The project can be accessed without any authentication.', - internal: 'Internal - The project can be accessed by any logged in user.', + internal: 'Internal - The project can be accessed by any logged in user except external users.', private: 'Private - Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.', }; |