diff options
Diffstat (limited to 'spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js')
-rw-r--r-- | spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js | 157 |
1 files changed, 149 insertions, 8 deletions
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js index c9a2499b127..7178fa1aae7 100644 --- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js @@ -9,12 +9,17 @@ import waitForPromises from 'helpers/wait_for_promises'; import WidgetWrapper from '~/work_items/components/widget_wrapper.vue'; import WorkItemRelationships from '~/work_items/components/work_item_relationships/work_item_relationships.vue'; import WorkItemRelationshipList from '~/work_items/components/work_item_relationships/work_item_relationship_list.vue'; +import WorkItemAddRelationshipForm from '~/work_items/components/work_item_relationships/work_item_add_relationship_form.vue'; +import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; +import removeLinkedItemsMutation from '~/work_items/graphql/remove_linked_items.mutation.graphql'; import { + groupWorkItemByIidResponseFactory, workItemByIidResponseFactory, mockLinkedItems, mockBlockingLinkedItem, + removeLinkedWorkItemResponse, } from '../../mock_data'; describe('WorkItemRelationships', () => { @@ -24,23 +29,44 @@ describe('WorkItemRelationships', () => { const emptyLinkedWorkItemsQueryHandler = jest .fn() .mockResolvedValue(workItemByIidResponseFactory()); - const linkedWorkItemsQueryHandler = jest + const groupWorkItemsQueryHandler = jest .fn() - .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })); - const blockingLinkedWorkItemQueryHandler = jest + .mockResolvedValue(groupWorkItemByIidResponseFactory()); + const removeLinkedWorkItemSuccessMutationHandler = jest .fn() - .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockBlockingLinkedItem })); + .mockResolvedValue(removeLinkedWorkItemResponse('Successfully unlinked IDs: 2.')); + const removeLinkedWorkItemErrorMutationHandler = jest + .fn() + .mockResolvedValue(removeLinkedWorkItemResponse(null, ['Linked item removal failed'])); + const $toast = { + show: jest.fn(), + }; const createComponent = async ({ workItemQueryHandler = emptyLinkedWorkItemsQueryHandler, + workItemType = 'Task', + isGroup = false, + removeLinkedWorkItemMutationHandler = removeLinkedWorkItemSuccessMutationHandler, } = {}) => { - const mockApollo = createMockApollo([[workItemByIidQuery, workItemQueryHandler]]); + const mockApollo = createMockApollo([ + [workItemByIidQuery, workItemQueryHandler], + [removeLinkedItemsMutation, removeLinkedWorkItemMutationHandler], + [groupWorkItemByIidQuery, groupWorkItemsQueryHandler], + ]); wrapper = shallowMountExtended(WorkItemRelationships, { apolloProvider: mockApollo, propsData: { + workItemId: 'gid://gitlab/WorkItem/1', workItemIid: '1', workItemFullPath: 'test-project-path', + workItemType, + }, + provide: { + isGroup, + }, + mocks: { + $toast, }, }); @@ -51,8 +77,11 @@ describe('WorkItemRelationships', () => { const findWidgetWrapper = () => wrapper.findComponent(WidgetWrapper); const findEmptyRelatedMessageContainer = () => wrapper.findByTestId('links-empty'); const findLinkedItemsCountContainer = () => wrapper.findByTestId('linked-items-count'); + const findLinkedItemsHelpLink = () => wrapper.findByTestId('help-link'); const findAllWorkItemRelationshipListComponents = () => wrapper.findAllComponents(WorkItemRelationshipList); + const findAddButton = () => wrapper.findByTestId('link-item-add-button'); + const findWorkItemRelationshipForm = () => wrapper.findComponent(WorkItemAddRelationshipForm); it('shows loading icon when query is not processed', () => { createComponent(); @@ -60,22 +89,35 @@ describe('WorkItemRelationships', () => { expect(findLoadingIcon().exists()).toBe(true); }); - it('renders the component with empty message when there are no items', async () => { + it('renders the component with with defaults', async () => { await createComponent(); expect(wrapper.find('.work-item-relationships').exists()).toBe(true); expect(findEmptyRelatedMessageContainer().exists()).toBe(true); + expect(findAddButton().exists()).toBe(true); + expect(findWorkItemRelationshipForm().exists()).toBe(false); + expect(findLinkedItemsHelpLink().attributes('href')).toBe( + '/help/user/okrs.md#linked-items-in-okrs', + ); }); it('renders blocking linked item lists', async () => { - await createComponent({ workItemQueryHandler: blockingLinkedWorkItemQueryHandler }); + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockBlockingLinkedItem })), + }); expect(findAllWorkItemRelationshipListComponents().length).toBe(1); expect(findLinkedItemsCountContainer().text()).toBe('1'); }); it('renders blocking, blocked by and related to linked item lists with proper count', async () => { - await createComponent({ workItemQueryHandler: linkedWorkItemsQueryHandler }); + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })), + }); // renders all 3 lists: blocking, blocked by and related to expect(findAllWorkItemRelationshipListComponents().length).toBe(3); @@ -90,4 +132,103 @@ describe('WorkItemRelationships', () => { expect(findWidgetWrapper().props('error')).toBe(errorMessage); }); + + it('does not render add button when there is no permission', async () => { + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ canAdminWorkItemLink: false })), + }); + + expect(findAddButton().exists()).toBe(false); + }); + + it('shows form on add button and hides when cancel button is clicked', async () => { + await createComponent(); + + await findAddButton().vm.$emit('click'); + expect(findWorkItemRelationshipForm().exists()).toBe(true); + + await findWorkItemRelationshipForm().vm.$emit('cancel'); + expect(findWorkItemRelationshipForm().exists()).toBe(false); + }); + + describe('when project context', () => { + it('calls the project work item query', () => { + createComponent(); + + expect(emptyLinkedWorkItemsQueryHandler).toHaveBeenCalled(); + }); + + it('skips calling the group work item query', () => { + createComponent(); + + expect(groupWorkItemsQueryHandler).not.toHaveBeenCalled(); + }); + }); + + describe('when group context', () => { + it('skips calling the project work item query', () => { + createComponent({ isGroup: true }); + + expect(emptyLinkedWorkItemsQueryHandler).not.toHaveBeenCalled(); + }); + + it('calls the group work item query', () => { + createComponent({ isGroup: true }); + + expect(groupWorkItemsQueryHandler).toHaveBeenCalled(); + }); + }); + + it('removes linked item and shows toast message when removeLinkedItem event is emitted', async () => { + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })), + }); + + expect(findLinkedItemsCountContainer().text()).toBe('3'); + + await findAllWorkItemRelationshipListComponents() + .at(0) + .vm.$emit('removeLinkedItem', { id: 'gid://gitlab/WorkItem/2' }); + + await waitForPromises(); + + expect(removeLinkedWorkItemSuccessMutationHandler).toHaveBeenCalledWith({ + input: { + id: 'gid://gitlab/WorkItem/1', + workItemsIds: ['gid://gitlab/WorkItem/2'], + }, + }); + + expect($toast.show).toHaveBeenCalledWith('Linked item removed'); + + expect(findLinkedItemsCountContainer().text()).toBe('2'); + }); + + it.each` + errorType | mutationMock | errorMessage + ${'an error in the mutation response'} | ${removeLinkedWorkItemErrorMutationHandler} | ${'Linked item removal failed'} + ${'a network error'} | ${jest.fn().mockRejectedValue(new Error('Network Error'))} | ${'Something went wrong when removing item. Please refresh this page.'} + `( + 'shows an error message when there is $errorType while removing items', + async ({ mutationMock, errorMessage }) => { + await createComponent({ + workItemQueryHandler: jest + .fn() + .mockResolvedValue(workItemByIidResponseFactory({ linkedItems: mockLinkedItems })), + removeLinkedWorkItemMutationHandler: mutationMock, + }); + + await findAllWorkItemRelationshipListComponents() + .at(0) + .vm.$emit('removeLinkedItem', { id: 'gid://gitlab/WorkItem/2' }); + + await waitForPromises(); + + expect(findWidgetWrapper().props('error')).toBe(errorMessage); + }, + ); }); |