diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index ddf7c789c0968376a6c39ae83d697a8557a7f4cd..ef99001c0e80dcd93f637ae7e1ad08381bc27039 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -46,7 +46,8 @@ import workItemAssigneesSubscription from '../graphql/work_item_assignees.subscr import workItemMilestoneSubscription from '../graphql/work_item_milestone.subscription.graphql'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql'; -import { findHierarchyWidgetChildren, getWorkItemQuery } from '../utils'; +import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; +import { findHierarchyWidgetChildren } from '../utils'; import WorkItemTree from './work_item_links/work_item_tree.vue'; import WorkItemActions from './work_item_actions.vue'; @@ -137,18 +138,15 @@ export default { }, apollo: { workItem: { - query() { - return getWorkItemQuery(this.fetchByIid); - }, + query: workItemByIidQuery, variables() { return this.queryVariables; }, skip() { - return !this.workItemId && !this.workItemIid; + return !this.workItemIid; }, update(data) { - const workItem = this.fetchByIid ? data.workspace.workItems.nodes[0] : data.workItem; - return workItem ?? {}; + return data.workspace.workItems.nodes[0] ?? {}; }, error() { this.setEmptyState(); @@ -316,18 +314,11 @@ export default { workItemNotes() { return this.isWidgetPresent(WIDGET_TYPE_NOTES); }, - fetchByIid() { - return true; - }, queryVariables() { - return this.fetchByIid - ? { - fullPath: this.fullPath, - iid: this.workItemIid, - } - : { - id: this.workItemId, - }; + return { + fullPath: this.fullPath, + iid: this.workItemIid, + }; }, children() { return this.workItem ? findHierarchyWidgetChildren(this.workItem) : []; @@ -408,14 +399,12 @@ export default { }, toggleChildFromCache(workItem, childId, store) { const sourceData = store.readQuery({ - query: getWorkItemQuery(this.fetchByIid), + query: workItemByIidQuery, variables: this.queryVariables, }); const newData = produce(sourceData, (draftState) => { - const widgets = this.fetchByIid - ? draftState.workspace.workItems.nodes[0].widgets - : draftState.workItem.widgets; + const { widgets } = draftState.workspace.workItems.nodes[0]; const widgetHierarchy = widgets.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY); const index = widgetHierarchy.children.nodes.findIndex((child) => child.id === childId); @@ -428,7 +417,7 @@ export default { }); store.writeQuery({ - query: getWorkItemQuery(this.fetchByIid), + query: workItemByIidQuery, variables: this.queryVariables, data: newData, }); @@ -475,12 +464,8 @@ export default { this.$emit('has-notes'); }, updateUrl(modalWorkItem) { - const params = this.fetchByIid - ? { work_item_iid: modalWorkItem?.iid } - : { work_item_id: getIdFromGraphQLId(modalWorkItem?.id) }; - updateHistory({ - url: setUrlParams(params), + url: setUrlParams({ work_item_iid: modalWorkItem?.iid }), replace: true, }); }, @@ -722,7 +707,6 @@ export default { :can-update="canUpdate" :project-path="fullPath" :confidential="workItem.confidential" - :fetch-by-iid="fetchByIid" @addWorkItemChild="addChild" @removeChild="removeChild" @show-modal="openInModal" diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue index 3e5d9453fef8e7d1b81912ac7e337afd21d63326..4dcc4d51957c5a1b8a7d93466ee0c7305f2d4e09 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue @@ -61,11 +61,6 @@ export default { type: String, required: true, }, - fetchByIid: { - type: Boolean, - required: false, - default: false, - }, }, data() { return { @@ -174,7 +169,7 @@ export default { :work-item-id="workItemId" :work-item-iid="workItemIid" :work-item-type="workItemType" - :fetch-by-iid="fetchByIid" + fetch-by-iid @removeChild="$emit('removeChild', $event)" @show-modal="showModal" /> diff --git a/ee/spec/frontend/work_items/components/work_item_detail_spec.js b/ee/spec/frontend/work_items/components/work_item_detail_spec.js index a6bf7214f943ef2aeb3981cbd104ced550938983..5204a130500cbb423bad7478cbf967f01f7cff30 100644 --- a/ee/spec/frontend/work_items/components/work_item_detail_spec.js +++ b/ee/spec/frontend/work_items/components/work_item_detail_spec.js @@ -12,7 +12,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import { workItemDatesSubscriptionResponse, workItemTitleSubscriptionResponse, - workItemByIidResponseFactory as workItemResponseFactory, + workItemByIidResponseFactory, workItemWeightSubscriptionResponse, workItemAssigneesSubscriptionResponse, workItemIterationSubscriptionResponse, @@ -34,7 +34,7 @@ describe('WorkItemDetail component', () => { Vue.use(VueApollo); - const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true }); + const workItemQueryResponse = workItemByIidResponseFactory({ canUpdate: true, canDelete: true }); const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse); const datesSubscriptionHandler = jest.fn().mockResolvedValue(workItemDatesSubscriptionResponse); const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse); @@ -103,7 +103,7 @@ describe('WorkItemDetail component', () => { it(`${ iterationWidgetPresent ? 'renders' : 'does not render' } iteration component`, async () => { - const response = workItemResponseFactory({ iterationWidgetPresent }); + const response = workItemByIidResponseFactory({ iterationWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -131,7 +131,7 @@ describe('WorkItemDetail component', () => { ${'when widget is not returned from API'} | ${false} | ${false} `('$description', ({ weightWidgetPresent, exists }) => { it(`${weightWidgetPresent ? 'renders' : 'does not render'} weight component`, async () => { - const response = workItemResponseFactory({ weightWidgetPresent }); + const response = workItemByIidResponseFactory({ weightWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -161,7 +161,7 @@ describe('WorkItemDetail component', () => { it(`${ healthStatusWidgetPresent ? 'renders' : 'does not render' } healthStatus component`, async () => { - const response = workItemResponseFactory({ healthStatusWidgetPresent }); + const response = workItemByIidResponseFactory({ healthStatusWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -191,7 +191,7 @@ describe('WorkItemDetail component', () => { it(`${ progressWidgetPresent ? 'renders' : 'does not render' } progress component`, async () => { - const response = workItemResponseFactory({ progressWidgetPresent }); + const response = workItemByIidResponseFactory({ progressWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js index c6869573b2ebfb8d6fc79e76bf52ce62ca02296e..9a0cde15b24b92be9c6eb606b5f87103c5edd290 100644 --- a/spec/frontend/issues/show/components/description_spec.js +++ b/spec/frontend/issues/show/components/description_spec.js @@ -8,7 +8,6 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createAlert } from '~/alert'; import Description from '~/issues/show/components/description.vue'; import eventHub from '~/issues/show/event_hub'; -import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql'; import workItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql'; import TaskList from '~/task_list'; @@ -35,13 +34,6 @@ const $toast = { }; const issueDetailsResponse = getIssueDetailsResponse(); -const workItemQueryResponse = { - data: { - workItem: null, - }, -}; - -const queryHandler = jest.fn().mockResolvedValue(workItemQueryResponse); const workItemTypesQueryHandler = jest.fn().mockResolvedValue(projectWorkItemTypesQueryResponse); describe('Description component', () => { @@ -72,7 +64,6 @@ describe('Description component', () => { ...provide, }, apolloProvider: createMockApollo([ - [workItemQuery, queryHandler], [workItemTypesQuery, workItemTypesQueryHandler], [getIssueDetailsQuery, issueDetailsQueryHandler], [createWorkItemMutation, createWorkItemMutationHandler], diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js index 6e8f6defb17615cd1792f356ef612b2d1956309a..0045abe50d01324255e28080e277f3abc45ceb78 100644 --- a/spec/frontend/work_items/components/work_item_actions_spec.js +++ b/spec/frontend/work_items/components/work_item_actions_spec.js @@ -18,10 +18,10 @@ import updateWorkItemNotificationsMutation from '~/work_items/graphql/update_wor import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql'; import convertWorkItemMutation from '~/work_items/graphql/work_item_convert.mutation.graphql'; import { - workItemResponseFactory, convertWorkItemMutationResponse, projectWorkItemTypesQueryResponse, convertWorkItemMutationErrorResponse, + workItemByIidResponseFactory, } from '../mock_data'; jest.mock('~/lib/utils/common_utils'); @@ -211,45 +211,45 @@ describe('WorkItemActions component', () => { describe('notifications action', () => { const errorMessage = 'Failed to subscribe'; + const id = 'gid://gitlab/WorkItem/1'; const notificationToggledOffMessage = 'Notifications turned off.'; const notificationToggledOnMessage = 'Notifications turned on.'; - const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true }); const inputVariablesOff = { - id: workItemQueryResponse.data.workItem.id, + id, notificationsWidget: { subscribed: false, }, }; const inputVariablesOn = { - id: workItemQueryResponse.data.workItem.id, + id, notificationsWidget: { subscribed: true, }, }; - const notificationsOffExpectedResponse = workItemResponseFactory({ + const notificationsOffExpectedResponse = workItemByIidResponseFactory({ subscribed: false, }); const toggleNotificationsOffHandler = jest.fn().mockResolvedValue({ data: { workItemUpdate: { - workItem: notificationsOffExpectedResponse.data.workItem, + workItem: notificationsOffExpectedResponse.data.workspace.workItems.nodes[0], errors: [], }, }, }); - const notificationsOnExpectedResponse = workItemResponseFactory({ + const notificationsOnExpectedResponse = workItemByIidResponseFactory({ subscribed: true, }); const toggleNotificationsOnHandler = jest.fn().mockResolvedValue({ data: { workItemUpdate: { - workItem: notificationsOnExpectedResponse.data.workItem, + workItem: notificationsOnExpectedResponse.data.workspace.workItems.nodes[0], errors: [], }, }, diff --git a/spec/frontend/work_items/components/work_item_assignees_spec.js b/spec/frontend/work_items/components/work_item_assignees_spec.js index af97b3680f966facdf3a3e59b3c89b7ae0361ad8..1e336a928a009d28d431b19af1a8588650df5446 100644 --- a/spec/frontend/work_items/components/work_item_assignees_spec.js +++ b/spec/frontend/work_items/components/work_item_assignees_spec.js @@ -8,9 +8,7 @@ import { mockTracking } from 'helpers/tracking_helper'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql'; import currentUserQuery from '~/graphql_shared/queries/current_user.query.graphql'; -import { config } from '~/graphql_shared/issuable_client'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; -import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql'; import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue'; import { @@ -22,7 +20,6 @@ import { import { projectMembersResponseWithCurrentUser, mockAssignees, - workItemQueryResponse, currentUserResponse, currentUserNullResponse, projectMembersResponseWithoutCurrentUser, @@ -78,25 +75,11 @@ describe('WorkItemAssignees component', () => { canInviteMembers = false, canUpdate = true, } = {}) => { - const apolloProvider = createMockApollo( - [ - [userSearchQuery, searchQueryHandler], - [currentUserQuery, currentUserQueryHandler], - [updateWorkItemMutation, updateWorkItemMutationHandler], - ], - {}, - { - typePolicies: config.cacheConfig.typePolicies, - }, - ); - - apolloProvider.clients.defaultClient.writeQuery({ - query: workItemQuery, - variables: { - id: workItemId, - }, - data: workItemQueryResponse.data, - }); + const apolloProvider = createMockApollo([ + [userSearchQuery, searchQueryHandler], + [currentUserQuery, currentUserQueryHandler], + [updateWorkItemMutation, updateWorkItemMutationHandler], + ]); wrapper = mountExtended(WorkItemAssignees, { propsData: { diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js index 291dacfd844d1c99a757c9ad355862e5c330d32d..630eb78d5c05840c6a432a64aee556b23b9f36c6 100644 --- a/spec/frontend/work_items/components/work_item_detail_spec.js +++ b/spec/frontend/work_items/components/work_item_detail_spec.js @@ -39,7 +39,7 @@ import updateWorkItemTaskMutation from '~/work_items/graphql/update_work_item_ta import { mockParent, workItemDatesSubscriptionResponse, - workItemByIidResponseFactory as workItemResponseFactory, + workItemByIidResponseFactory, workItemTitleSubscriptionResponse, workItemAssigneesSubscriptionResponse, workItemMilestoneSubscriptionResponse, @@ -52,8 +52,8 @@ describe('WorkItemDetail component', () => { Vue.use(VueApollo); - const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true }); - const workItemQueryResponseWithoutParent = workItemResponseFactory({ + const workItemQueryResponse = workItemByIidResponseFactory({ canUpdate: true, canDelete: true }); + const workItemQueryResponseWithoutParent = workItemByIidResponseFactory({ parent: null, canUpdate: true, canDelete: true, @@ -221,7 +221,7 @@ describe('WorkItemDetail component', () => { describe('confidentiality', () => { const errorMessage = 'Mutation failed'; - const confidentialWorkItem = workItemResponseFactory({ + const confidentialWorkItem = workItemByIidResponseFactory({ confidential: true, }); const workItem = confidentialWorkItem.data.workspace.workItems.nodes[0]; @@ -398,7 +398,7 @@ describe('WorkItemDetail component', () => { describe('with parent', () => { beforeEach(() => { - const parentResponse = workItemResponseFactory(mockParent); + const parentResponse = workItemByIidResponseFactory(mockParent); createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) }); return waitForPromises(); @@ -437,7 +437,7 @@ describe('WorkItemDetail component', () => { }, }, }; - const parentResponse = workItemResponseFactory(mockParentObjective); + const parentResponse = workItemByIidResponseFactory(mockParentObjective); createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) }); await waitForPromises(); @@ -492,7 +492,7 @@ describe('WorkItemDetail component', () => { describe('when the assignees widget does not exist', () => { it('does not call the assignees subscription', async () => { - const response = workItemResponseFactory({ assigneesWidgetPresent: false }); + const response = workItemByIidResponseFactory({ assigneesWidgetPresent: false }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -514,7 +514,7 @@ describe('WorkItemDetail component', () => { describe('when the due date widget does not exist', () => { it('does not call the dates subscription', async () => { - const response = workItemResponseFactory({ datesWidgetPresent: false }); + const response = workItemByIidResponseFactory({ datesWidgetPresent: false }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -537,7 +537,7 @@ describe('WorkItemDetail component', () => { createComponent({ handler: jest .fn() - .mockResolvedValue(workItemResponseFactory({ assigneesWidgetPresent: false })), + .mockResolvedValue(workItemByIidResponseFactory({ assigneesWidgetPresent: false })), }); await waitForPromises(); @@ -551,7 +551,7 @@ describe('WorkItemDetail component', () => { ${'renders when widget is returned from API'} | ${true} | ${true} ${'does not render when widget is not returned from API'} | ${false} | ${false} `('$description', async ({ labelsWidgetPresent, exists }) => { - const response = workItemResponseFactory({ labelsWidgetPresent }); + const response = workItemByIidResponseFactory({ labelsWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -567,7 +567,7 @@ describe('WorkItemDetail component', () => { ${'when widget is not returned from API'} | ${false} | ${false} `('$description', ({ datesWidgetPresent, exists }) => { it(`${datesWidgetPresent ? 'renders' : 'does not render'} due date component`, async () => { - const response = workItemResponseFactory({ datesWidgetPresent }); + const response = workItemByIidResponseFactory({ datesWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -594,7 +594,7 @@ describe('WorkItemDetail component', () => { ${'renders when widget is returned from API'} | ${true} | ${true} ${'does not render when widget is not returned from API'} | ${false} | ${false} `('$description', async ({ milestoneWidgetPresent, exists }) => { - const response = workItemResponseFactory({ milestoneWidgetPresent }); + const response = workItemByIidResponseFactory({ milestoneWidgetPresent }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -614,7 +614,7 @@ describe('WorkItemDetail component', () => { describe('when the assignees widget does not exist', () => { it('does not call the milestone subscription', async () => { - const response = workItemResponseFactory({ milestoneWidgetPresent: false }); + const response = workItemByIidResponseFactory({ milestoneWidgetPresent: false }); const handler = jest.fn().mockResolvedValue(response); createComponent({ handler }); await waitForPromises(); @@ -632,6 +632,13 @@ describe('WorkItemDetail component', () => { expect(successHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' }); }); + it('skips the work item query when there is no workItemIid', async () => { + createComponent({ workItemIid: null }); + await waitForPromises(); + + expect(successHandler).not.toHaveBeenCalled(); + }); + it('calls the work item query when isModal=true', async () => { createComponent({ isModal: true }); await waitForPromises(); @@ -648,7 +655,7 @@ describe('WorkItemDetail component', () => { }); describe('work item has children', () => { - const objectiveWorkItem = workItemResponseFactory({ + const objectiveWorkItem = workItemByIidResponseFactory({ workItemType: objectiveType, confidential: true, }); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 2ab31908577be4985a202bb911e3a245bbea7c1d..1c319844af302c12f4e9cf08708a7cd1a75e3512 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -684,87 +684,6 @@ export const createWorkItemMutationErrorResponse = { }, }; -export const createWorkItemFromTaskMutationResponse = { - data: { - workItemCreateFromTask: { - __typename: 'WorkItemCreateFromTaskPayload', - errors: [], - workItem: { - __typename: 'WorkItem', - description: 'New description', - id: 'gid://gitlab/WorkItem/1', - iid: '1', - title: 'Updated title', - state: 'OPEN', - confidential: false, - createdAt: '2022-08-03T12:41:54Z', - closedAt: null, - project: { - __typename: 'Project', - id: '1', - fullPath: 'test-project-path', - archived: false, - }, - workItemType: { - __typename: 'WorkItemType', - id: 'gid://gitlab/WorkItems::Type/5', - name: 'Task', - iconName: 'issue-type-task', - }, - userPermissions: { - deleteWorkItem: false, - updateWorkItem: false, - setWorkItemMetadata: false, - __typename: 'WorkItemPermissions', - }, - widgets: [ - { - __typename: 'WorkItemWidgetDescription', - type: 'DESCRIPTION', - description: 'New description', - descriptionHtml: '<p>New description</p>', - lastEditedAt: '2022-09-21T06:18:42Z', - lastEditedBy: { - name: 'Administrator', - webPath: '/root', - }, - }, - ], - }, - newWorkItem: { - __typename: 'WorkItem', - id: 'gid://gitlab/WorkItem/1000000', - iid: '100', - title: 'Updated title', - state: 'OPEN', - createdAt: '2022-08-03T12:41:54Z', - closedAt: null, - description: '', - confidential: false, - project: { - __typename: 'Project', - id: '1', - fullPath: 'test-project-path', - archived: false, - }, - workItemType: { - __typename: 'WorkItemType', - id: 'gid://gitlab/WorkItems::Type/5', - name: 'Task', - iconName: 'issue-type-task', - }, - userPermissions: { - deleteWorkItem: false, - updateWorkItem: false, - setWorkItemMetadata: false, - __typename: 'WorkItemPermissions', - }, - widgets: [], - }, - }, - }, -}; - export const deleteWorkItemResponse = { data: { workItemDelete: { errors: [], __typename: 'WorkItemDeletePayload' } }, }; @@ -1831,18 +1750,6 @@ export const projectMilestonesResponseWithNoMilestones = { }, }; -export const projectWorkItemResponse = { - data: { - workspace: { - id: 'gid://gitlab/Project/1', - workItems: { - nodes: [workItemQueryResponse.data.workItem], - }, - __typename: 'Project', - }, - }, -}; - export const mockWorkItemNotesResponse = { data: { workItem: { diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js index 86e890ea809b6ebacdf4fbf9b131126826ae82d2..b5d54a7c3192286a51580c90fd30bc103bfa8df2 100644 --- a/spec/frontend/work_items/router_spec.js +++ b/spec/frontend/work_items/router_spec.js @@ -6,7 +6,7 @@ import { currentUserResponse, workItemAssigneesSubscriptionResponse, workItemDatesSubscriptionResponse, - workItemByIidResponseFactory as workItemResponseFactory, + workItemByIidResponseFactory, workItemTitleSubscriptionResponse, workItemLabelsSubscriptionResponse, workItemMilestoneSubscriptionResponse, @@ -32,7 +32,7 @@ describe('Work items router', () => { Vue.use(VueApollo); - const workItemQueryHandler = jest.fn().mockResolvedValue(workItemResponseFactory()); + const workItemQueryHandler = jest.fn().mockResolvedValue(workItemByIidResponseFactory()); const currentUserQueryHandler = jest.fn().mockResolvedValue(currentUserResponse); const datesSubscriptionHandler = jest.fn().mockResolvedValue(workItemDatesSubscriptionResponse); const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);