Commit 732467b9 authored by Phil Hughes's avatar Phil Hughes

Merge branch '327403-leave-this-group-button-not-working' into 'master'

Fix "Leave this group" button for subgroups listed in group overview.

See merge request gitlab-org/gitlab!62488
parents 37978b78 07765ef3
...@@ -181,7 +181,12 @@ export default { ...@@ -181,7 +181,12 @@ export default {
<gl-badge variant="warning">{{ __('pending removal') }}</gl-badge> <gl-badge variant="warning">{{ __('pending removal') }}</gl-badge>
</div> </div>
<div class="metadata d-flex flex-grow-1 flex-shrink-0 flex-wrap justify-content-md-between"> <div class="metadata d-flex flex-grow-1 flex-shrink-0 flex-wrap justify-content-md-between">
<item-actions v-if="isGroup" :group="group" :parent-group="parentGroup" /> <item-actions
v-if="isGroup"
:group="group"
:parent-group="parentGroup"
:action="action"
/>
<item-stats <item-stats
:item="group" :item="group"
class="group-stats gl-mt-2 d-none d-md-flex gl-align-items-center" class="group-stats gl-mt-2 d-none d-md-flex gl-align-items-center"
......
import { mount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import mountComponent from 'helpers/vue_mount_component_helper'; import GroupFolder from '~/groups/components/group_folder.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue'; import GroupItem from '~/groups/components/group_item.vue';
import groupItemComponent from '~/groups/components/group_item.vue'; import ItemActions from '~/groups/components/item_actions.vue';
import eventHub from '~/groups/event_hub'; import eventHub from '~/groups/event_hub';
import { getGroupItemMicrodata } from '~/groups/store/utils'; import { getGroupItemMicrodata } from '~/groups/store/utils';
import * as urlUtilities from '~/lib/utils/url_utility'; import * as urlUtilities from '~/lib/utils/url_utility';
import { mockParentGroupItem, mockChildren } from '../mock_data'; import { mockParentGroupItem, mockChildren } from '../mock_data';
const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren[0]) => { const createComponent = (
const Component = Vue.extend(groupItemComponent); propsData = { group: mockParentGroupItem, parentGroup: mockChildren[0] },
) => {
return mountComponent(Component, { return mount(GroupItem, {
group, propsData,
parentGroup, components: { GroupFolder },
}); });
}; };
describe('GroupItemComponent', () => { describe('GroupItemComponent', () => {
let vm; let wrapper;
beforeEach(() => { beforeEach(() => {
Vue.component('GroupFolder', groupFolderComponent); wrapper = createComponent();
vm = createComponent();
return Vue.nextTick(); return Vue.nextTick();
}); });
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
}); });
const withMicrodata = (group) => ({ const withMicrodata = (group) => ({
...@@ -39,14 +38,14 @@ describe('GroupItemComponent', () => { ...@@ -39,14 +38,14 @@ describe('GroupItemComponent', () => {
describe('computed', () => { describe('computed', () => {
describe('groupDomId', () => { describe('groupDomId', () => {
it('should return ID string suffixed with group ID', () => { it('should return ID string suffixed with group ID', () => {
expect(vm.groupDomId).toBe('group-55'); expect(wrapper.vm.groupDomId).toBe('group-55');
}); });
}); });
describe('rowClass', () => { describe('rowClass', () => {
it('should return map of classes based on group details', () => { it('should return map of classes based on group details', () => {
const classes = ['is-open', 'has-children', 'has-description', 'being-removed']; const classes = ['is-open', 'has-children', 'has-description', 'being-removed'];
const { rowClass } = vm; const { rowClass } = wrapper.vm;
expect(Object.keys(rowClass).length).toBe(classes.length); expect(Object.keys(rowClass).length).toBe(classes.length);
Object.keys(rowClass).forEach((className) => { Object.keys(rowClass).forEach((className) => {
...@@ -57,58 +56,55 @@ describe('GroupItemComponent', () => { ...@@ -57,58 +56,55 @@ describe('GroupItemComponent', () => {
describe('hasChildren', () => { describe('hasChildren', () => {
it('should return boolean value representing if group has any children present', () => { it('should return boolean value representing if group has any children present', () => {
let newVm;
const group = { ...mockParentGroupItem }; const group = { ...mockParentGroupItem };
group.childrenCount = 5; group.childrenCount = 5;
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.hasChildren).toBeTruthy(); expect(wrapper.vm.hasChildren).toBe(true);
newVm.$destroy(); wrapper.destroy();
group.childrenCount = 0; group.childrenCount = 0;
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.hasChildren).toBeFalsy(); expect(wrapper.vm.hasChildren).toBe(false);
newVm.$destroy(); wrapper.destroy();
}); });
}); });
describe('hasAvatar', () => { describe('hasAvatar', () => {
it('should return boolean value representing if group has any avatar present', () => { it('should return boolean value representing if group has any avatar present', () => {
let newVm;
const group = { ...mockParentGroupItem }; const group = { ...mockParentGroupItem };
group.avatarUrl = null; group.avatarUrl = null;
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.hasAvatar).toBeFalsy(); expect(wrapper.vm.hasAvatar).toBe(false);
newVm.$destroy(); wrapper.destroy();
group.avatarUrl = '/uploads/group_avatar.png'; group.avatarUrl = '/uploads/group_avatar.png';
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.hasAvatar).toBeTruthy(); expect(wrapper.vm.hasAvatar).toBe(true);
newVm.$destroy(); wrapper.destroy();
}); });
}); });
describe('isGroup', () => { describe('isGroup', () => {
it('should return boolean value representing if group item is of type `group` or not', () => { it('should return boolean value representing if group item is of type `group` or not', () => {
let newVm;
const group = { ...mockParentGroupItem }; const group = { ...mockParentGroupItem };
group.type = 'group'; group.type = 'group';
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.isGroup).toBeTruthy(); expect(wrapper.vm.isGroup).toBe(true);
newVm.$destroy(); wrapper.destroy();
group.type = 'project'; group.type = 'project';
newVm = createComponent(group); wrapper = createComponent({ group });
expect(newVm.isGroup).toBeFalsy(); expect(wrapper.vm.isGroup).toBe(false);
newVm.$destroy(); wrapper.destroy();
}); });
}); });
}); });
...@@ -137,22 +133,22 @@ describe('GroupItemComponent', () => { ...@@ -137,22 +133,22 @@ describe('GroupItemComponent', () => {
it('should emit `toggleChildren` event when expand is clicked on a group and it has children present', () => { it('should emit `toggleChildren` event when expand is clicked on a group and it has children present', () => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm.onClickRowGroup(event); wrapper.vm.onClickRowGroup(event);
expect(eventHub.$emit).toHaveBeenCalledWith('toggleChildren', vm.group); expect(eventHub.$emit).toHaveBeenCalledWith('toggleChildren', wrapper.vm.group);
}); });
it('should navigate page to group homepage if group does not have any children present', () => { it('should navigate page to group homepage if group does not have any children present', () => {
jest.spyOn(urlUtilities, 'visitUrl').mockImplementation(); jest.spyOn(urlUtilities, 'visitUrl').mockImplementation();
const group = { ...mockParentGroupItem }; const group = { ...mockParentGroupItem };
group.childrenCount = 0; group.childrenCount = 0;
const newVm = createComponent(group); wrapper = createComponent({ group });
jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
newVm.onClickRowGroup(event); wrapper.vm.onClickRowGroup(event);
expect(eventHub.$emit).not.toHaveBeenCalled(); expect(eventHub.$emit).not.toHaveBeenCalled();
expect(urlUtilities.visitUrl).toHaveBeenCalledWith(newVm.group.relativePath); expect(urlUtilities.visitUrl).toHaveBeenCalledWith(wrapper.vm.group.relativePath);
}); });
}); });
}); });
...@@ -163,11 +159,11 @@ describe('GroupItemComponent', () => { ...@@ -163,11 +159,11 @@ describe('GroupItemComponent', () => {
describe('for a group pending deletion', () => { describe('for a group pending deletion', () => {
beforeEach(() => { beforeEach(() => {
group = { ...mockParentGroupItem, pendingRemoval: true }; group = { ...mockParentGroupItem, pendingRemoval: true };
vm = createComponent(group); wrapper = createComponent({ group });
}); });
it('renders the group pending removal badge', () => { it('renders the group pending removal badge', () => {
const badgeEl = vm.$el.querySelector('.badge-warning'); const badgeEl = wrapper.vm.$el.querySelector('.badge-warning');
expect(badgeEl).toBeDefined(); expect(badgeEl).toBeDefined();
expect(badgeEl.innerHTML).toContain('pending removal'); expect(badgeEl.innerHTML).toContain('pending removal');
...@@ -177,21 +173,41 @@ describe('GroupItemComponent', () => { ...@@ -177,21 +173,41 @@ describe('GroupItemComponent', () => {
describe('for a group not scheduled for deletion', () => { describe('for a group not scheduled for deletion', () => {
beforeEach(() => { beforeEach(() => {
group = { ...mockParentGroupItem, pendingRemoval: false }; group = { ...mockParentGroupItem, pendingRemoval: false };
vm = createComponent(group); wrapper = createComponent({ group });
}); });
it('does not render the group pending removal badge', () => { it('does not render the group pending removal badge', () => {
const groupTextContainer = vm.$el.querySelector('.group-text-container'); const groupTextContainer = wrapper.vm.$el.querySelector('.group-text-container');
expect(groupTextContainer).not.toContain('pending removal'); expect(groupTextContainer).not.toContain('pending removal');
}); });
it('renders `item-actions` component and passes correct props to it', () => {
wrapper = createComponent({
group: mockParentGroupItem,
parentGroup: mockChildren[0],
action: 'subgroups_and_projects',
});
const itemActionsComponent = wrapper.findComponent(ItemActions);
expect(itemActionsComponent.exists()).toBe(true);
expect(itemActionsComponent.props()).toEqual({
group: mockParentGroupItem,
parentGroup: mockChildren[0],
action: 'subgroups_and_projects',
});
});
}); });
it('should render component template correctly', () => { it('should render component template correctly', () => {
const visibilityIconEl = vm.$el.querySelector('[data-testid="group-visibility-icon"]'); const visibilityIconEl = wrapper.vm.$el.querySelector(
'[data-testid="group-visibility-icon"]',
);
const { vm } = wrapper;
expect(vm.$el.getAttribute('id')).toBe('group-55'); expect(vm.$el.getAttribute('id')).toBe('group-55');
expect(vm.$el.classList.contains('group-row')).toBeTruthy(); expect(vm.$el.classList.contains('group-row')).toBe(true);
expect(vm.$el.querySelector('.group-row-contents')).toBeDefined(); expect(vm.$el.querySelector('.group-row-contents')).toBeDefined();
expect(vm.$el.querySelector('.group-row-contents .controls')).toBeDefined(); expect(vm.$el.querySelector('.group-row-contents .controls')).toBeDefined();
...@@ -220,13 +236,13 @@ describe('GroupItemComponent', () => { ...@@ -220,13 +236,13 @@ describe('GroupItemComponent', () => {
describe('schema.org props', () => { describe('schema.org props', () => {
describe('when showSchemaMarkup is disabled on the group', () => { describe('when showSchemaMarkup is disabled on the group', () => {
it.each(['itemprop', 'itemtype', 'itemscope'], 'it does not set %s', (attr) => { it.each(['itemprop', 'itemtype', 'itemscope'], 'it does not set %s', (attr) => {
expect(vm.$el.getAttribute(attr)).toBeNull(); expect(wrapper.vm.$el.getAttribute(attr)).toBeNull();
}); });
it.each( it.each(
['.js-group-avatar', '.js-group-name', '.js-group-description'], ['.js-group-avatar', '.js-group-name', '.js-group-description'],
'it does not set `itemprop` on sub-nodes', 'it does not set `itemprop` on sub-nodes',
(selector) => { (selector) => {
expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBeNull(); expect(wrapper.vm.$el.querySelector(selector).getAttribute('itemprop')).toBeNull();
}, },
); );
}); });
...@@ -238,7 +254,7 @@ describe('GroupItemComponent', () => { ...@@ -238,7 +254,7 @@ describe('GroupItemComponent', () => {
description: 'Foo Bar', description: 'Foo Bar',
}); });
vm = createComponent(group); wrapper = createComponent({ group });
}); });
it.each` it.each`
...@@ -247,7 +263,7 @@ describe('GroupItemComponent', () => { ...@@ -247,7 +263,7 @@ describe('GroupItemComponent', () => {
${'itemtype'} | ${'https://schema.org/Organization'} ${'itemtype'} | ${'https://schema.org/Organization'}
${'itemprop'} | ${'subOrganization'} ${'itemprop'} | ${'subOrganization'}
`('it does set correct $attr', ({ attr, value } = {}) => { `('it does set correct $attr', ({ attr, value } = {}) => {
expect(vm.$el.getAttribute(attr)).toBe(value); expect(wrapper.vm.$el.getAttribute(attr)).toBe(value);
}); });
it.each` it.each`
...@@ -256,7 +272,7 @@ describe('GroupItemComponent', () => { ...@@ -256,7 +272,7 @@ describe('GroupItemComponent', () => {
${'[data-testid="group-name"]'} | ${'name'} ${'[data-testid="group-name"]'} | ${'name'}
${'[data-testid="group-description"]'} | ${'description'} ${'[data-testid="group-description"]'} | ${'description'}
`('it does set correct $selector', ({ selector, propValue } = {}) => { `('it does set correct $selector', ({ selector, propValue } = {}) => {
expect(vm.$el.querySelector(selector).getAttribute('itemprop')).toBe(propValue); expect(wrapper.vm.$el.querySelector(selector).getAttribute('itemprop')).toBe(propValue);
}); });
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment