Commit 00c3b985 authored by Robert Knight's avatar Robert Knight

Add additional tests for group list item

This covers the functionality related to the submenu for group list items.
parent 9fdd6737
...@@ -16,13 +16,20 @@ const MenuItem = require('./menu-item'); ...@@ -16,13 +16,20 @@ const MenuItem = require('./menu-item');
* The item has a primary action which selects the group, along with a set of * The item has a primary action which selects the group, along with a set of
* secondary actions accessible via a toggle menu. * secondary actions accessible via a toggle menu.
*/ */
function GroupListItem({ analytics, group, groups: groupsService }) { function GroupListItem({
analytics,
defaultSubmenuOpen = false,
group,
groups: groupsService,
}) {
const canLeaveGroup = group.type === 'private'; const canLeaveGroup = group.type === 'private';
const activityUrl = group.links.html; const activityUrl = group.links.html;
const hasActionMenu = activityUrl || canLeaveGroup; const hasActionMenu = activityUrl || canLeaveGroup;
const isSelectable = !group.scopes.enforced || group.isScopedToUri; const isSelectable = !group.scopes.enforced || group.isScopedToUri;
const [isExpanded, setExpanded] = useState(hasActionMenu ? false : undefined); const [isExpanded, setExpanded] = useState(
hasActionMenu ? defaultSubmenuOpen : undefined
);
const focusedGroupId = useStore(store => store.focusedGroupId()); const focusedGroupId = useStore(store => store.focusedGroupId());
const isSelected = group.id === focusedGroupId; const isSelected = group.id === focusedGroupId;
...@@ -111,6 +118,9 @@ function GroupListItem({ analytics, group, groups: groupsService }) { ...@@ -111,6 +118,9 @@ function GroupListItem({ analytics, group, groups: groupsService }) {
GroupListItem.propTypes = { GroupListItem.propTypes = {
group: propTypes.object.isRequired, group: propTypes.object.isRequired,
/** Whether the submenu is open when the item is initially rendered. */
defaultSubmenuOpen: propTypes.bool,
// Injected services. // Injected services.
analytics: propTypes.object.isRequired, analytics: propTypes.object.isRequired,
groups: propTypes.object.isRequired, groups: propTypes.object.isRequired,
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { act } = require('preact/test-utils');
const { mount } = require('enzyme'); const { mount } = require('enzyme');
const GroupListItem = require('../group-list-item'); const GroupListItem = require('../group-list-item');
...@@ -12,19 +13,21 @@ describe('GroupListItem', () => { ...@@ -12,19 +13,21 @@ describe('GroupListItem', () => {
let fakeGroupsService; let fakeGroupsService;
let fakeStore; let fakeStore;
let fakeGroupListItemCommon; let fakeGroupListItemCommon;
let fakeGroup;
const fakeGroup = {
id: 'groupid',
name: 'Test',
links: {
html: 'https://annotate.com/groups/groupid',
},
scopes: {
enforced: false,
},
};
beforeEach(() => { beforeEach(() => {
fakeGroup = {
id: 'groupid',
name: 'Test',
links: {
html: 'https://annotate.com/groups/groupid',
},
scopes: {
enforced: false,
},
type: 'private',
};
fakeStore = { fakeStore = {
focusGroup: sinon.stub(), focusGroup: sinon.stub(),
focusedGroupId: sinon.stub().returns('groupid'), focusedGroupId: sinon.stub().returns('groupid'),
...@@ -49,18 +52,22 @@ describe('GroupListItem', () => { ...@@ -49,18 +52,22 @@ describe('GroupListItem', () => {
'../util/group-list-item-common': fakeGroupListItemCommon, '../util/group-list-item-common': fakeGroupListItemCommon,
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
sinon.stub(window, 'confirm').returns(false);
}); });
afterEach(() => { afterEach(() => {
GroupListItem.$imports.$restore(); GroupListItem.$imports.$restore();
window.confirm.restore();
}); });
const createGroupListItem = fakeGroup => { const createGroupListItem = (fakeGroup, props = {}) => {
return mount( return mount(
<GroupListItem <GroupListItem
group={fakeGroup} group={fakeGroup}
groups={fakeGroupsService} groups={fakeGroupsService}
analytics={fakeAnalytics} analytics={fakeAnalytics}
{...props}
/> />
); );
}; };
...@@ -138,4 +145,125 @@ describe('GroupListItem', () => { ...@@ -138,4 +145,125 @@ describe('GroupListItem', () => {
}); });
}); });
}); });
it('toggles submenu when toggle is clicked', () => {
const wrapper = createGroupListItem(fakeGroup);
const toggleSubmenu = () => {
const dummyEvent = new Event();
act(() => {
wrapper
.find('MenuItem')
.first()
.props()
.onToggleSubmenu(dummyEvent);
});
wrapper.update();
};
toggleSubmenu();
assert.isTrue(wrapper.exists('ul'));
toggleSubmenu();
assert.isFalse(wrapper.exists('ul'));
});
it('does not show submenu toggle if there are no available actions', () => {
fakeGroup.links.html = null;
fakeGroup.type = 'open';
const wrapper = createGroupListItem(fakeGroup);
assert.isUndefined(wrapper.find('MenuItem').prop('isExpanded'));
});
it('does not show link to activity page if not available', () => {
fakeGroup.links.html = null;
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
assert.isFalse(wrapper.exists('MenuItem[label="View group activity"]'));
});
it('shows link to activity page if available', () => {
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
assert.isTrue(wrapper.exists('MenuItem[label="View group activity"]'));
});
it('does not show "Leave" action if user cannot leave', () => {
fakeGroup.type = 'open';
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
assert.isFalse(wrapper.exists('MenuItem[label="Leave group"]'));
});
it('shows "Leave" action if user can leave', () => {
fakeGroup.type = 'private';
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
assert.isTrue(wrapper.exists('MenuItem[label="Leave group"]'));
});
it('prompts to leave group if "Leave" action is clicked', () => {
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
act(() => {
wrapper
.find('MenuItem[label="Leave group"]')
.props()
.onClick();
});
assert.called(window.confirm);
assert.notCalled(fakeGroupsService.leave);
});
it('leaves group if "Leave" is clicked and user confirms', () => {
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
window.confirm.returns(true);
act(() => {
wrapper
.find('MenuItem[label="Leave group"]')
.props()
.onClick();
});
assert.called(window.confirm);
assert.calledWith(fakeGroupsService.leave, fakeGroup.id);
});
[
{
enforced: false,
isScopedToUri: false,
expectDisabled: false,
},
{
enforced: true,
isScopedToUri: false,
expectDisabled: true,
},
{
enforced: true,
isScopedToUri: true,
expectDisabled: false,
},
].forEach(({ enforced, isScopedToUri, expectDisabled }) => {
it('disables menu item and shows note in submenu if group is not selectable', () => {
fakeGroup.scopes.enforced = enforced;
fakeGroup.isScopedToUri = isScopedToUri;
const wrapper = createGroupListItem(fakeGroup, {
defaultSubmenuOpen: true,
});
assert.equal(
wrapper
.find('MenuItem')
.first()
.prop('isDisabled'),
expectDisabled
);
assert.equal(wrapper.exists('.group-list-item__footer'), expectDisabled);
});
});
}); });
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