Commit cc77129a authored by Simon Knox's avatar Simon Knox

Merge branch '341083-integration-create-a-new-task' into 'master'

Unmocking create new task

See merge request gitlab-org/gitlab!81040
parents 1f7bfec5 e40c0224
...@@ -34,7 +34,10 @@ export default { ...@@ -34,7 +34,10 @@ export default {
}; };
}, },
update(data) { update(data) {
return data.localWorkItem; return data.workItem;
},
skip() {
return !this.workItemId;
}, },
error() { error() {
this.$emit( this.$emit(
...@@ -46,10 +49,7 @@ export default { ...@@ -46,10 +49,7 @@ export default {
}, },
computed: { computed: {
workItemTitle() { workItemTitle() {
return this.workItem?.widgets?.nodes.find( return this.workItem?.title;
// eslint-disable-next-line no-underscore-dangle
(widget) => widget.__typename === 'LocalTitleWidget',
)?.contentText;
}, },
}, },
}; };
......
#import './widget.fragment.graphql' #import './widget.fragment.graphql'
mutation createWorkItem($input: LocalCreateWorkItemInput) { mutation createWorkItem($input: WorkItemCreateInput!) {
localCreateWorkItem(input: $input) @client { workItemCreate(input: $input) {
workItem { workItem {
id id
type title
widgets { workItemType {
id
}
widgets @client {
nodes { nodes {
...WidgetBase ...WidgetBase
... on LocalTitleWidget {
contentText
}
} }
} }
} }
......
...@@ -20,24 +20,18 @@ export function createApolloProvider() { ...@@ -20,24 +20,18 @@ export function createApolloProvider() {
defaultClient.cache.writeQuery({ defaultClient.cache.writeQuery({
query: workItemQuery, query: workItemQuery,
variables: { variables: {
id: '1', id: 'gid://gitlab/WorkItem/1',
}, },
data: { data: {
localWorkItem: { localWorkItem: {
__typename: 'LocalWorkItem', __typename: 'LocalWorkItem',
id: '1', id: 'gid://gitlab/WorkItem/1',
type: 'FEATURE', type: 'FEATURE',
// eslint-disable-next-line @gitlab/require-i18n-strings
title: 'Test Work Item',
widgets: { widgets: {
__typename: 'LocalWorkItemWidgetConnection', __typename: 'LocalWorkItemWidgetConnection',
nodes: [ nodes: [],
{
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
// eslint-disable-next-line @gitlab/require-i18n-strings
contentText: 'Test Work Item Title',
},
],
}, },
}, },
}, },
......
import { uuids } from '~/lib/utils/uuids';
import workItemQuery from './work_item.query.graphql'; import workItemQuery from './work_item.query.graphql';
export const resolvers = { export const resolvers = {
Mutation: { Mutation: {
localCreateWorkItem(_, { input }, { cache }) {
const id = uuids()[0];
const workItem = {
__typename: 'LocalWorkItem',
type: 'FEATURE',
id,
widgets: {
__typename: 'LocalWorkItemWidgetConnection',
nodes: [
{
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
contentText: input.title,
},
],
},
};
cache.writeQuery({
query: workItemQuery,
variables: { id },
data: { localWorkItem: workItem },
});
return {
__typename: 'LocalCreateWorkItemPayload',
workItem,
};
},
localUpdateWorkItem(_, { input }, { cache }) { localUpdateWorkItem(_, { input }, { cache }) {
const workItemTitle = {
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
contentText: input.title,
};
const workItem = { const workItem = {
__typename: 'LocalWorkItem', __typename: 'LocalWorkItem',
type: 'FEATURE', type: 'FEATURE',
id: input.id, id: input.id,
title: input.title,
widgets: { widgets: {
__typename: 'LocalWorkItemWidgetConnection', __typename: 'LocalWorkItemWidgetConnection',
nodes: [workItemTitle], nodes: [],
}, },
}; };
......
...@@ -22,14 +22,10 @@ type LocalWorkItemWidgetConnection { ...@@ -22,14 +22,10 @@ type LocalWorkItemWidgetConnection {
pageInfo: PageInfo! pageInfo: PageInfo!
} }
type LocalTitleWidget implements LocalWorkItemWidget {
type: LocalWidgetType!
contentText: String!
}
type LocalWorkItem { type LocalWorkItem {
id: ID! id: ID!
type: LocalWorkItemType! type: LocalWorkItemType!
title: String!
widgets: [LocalWorkItemWidgetConnection] widgets: [LocalWorkItemWidgetConnection]
} }
......
#import './widget.fragment.graphql' #import './widget.fragment.graphql'
mutation updateWorkItem($input: LocalUpdateWorkItemInput) { mutation workItemUpdate($input: WorkItemUpdateInput!) {
localUpdateWorkItem(input: $input) @client { workItemUpdate(input: $input) {
workItem { workItem {
id id
type title
widgets { workItemType {
id
}
widgets @client {
nodes { nodes {
...WidgetBase ...WidgetBase
... on LocalTitleWidget {
contentText
}
} }
} }
} }
......
#import './widget.fragment.graphql' #import './widget.fragment.graphql'
query WorkItem($id: ID!) { query WorkItem($id: ID!) {
localWorkItem(id: $id) @client { workItem(id: $id) {
id id
type title
widgets { workItemType {
id
}
widgets @client {
nodes { nodes {
...WidgetBase ...WidgetBase
... on LocalTitleWidget {
contentText
}
} }
} }
} }
......
<script> <script>
import { GlButton, GlAlert, GlLoadingIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlButton, GlAlert, GlLoadingIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import workItemQuery from '../graphql/work_item.query.graphql';
import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql'; import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql';
import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql'; import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql';
...@@ -67,19 +69,43 @@ export default { ...@@ -67,19 +69,43 @@ export default {
variables: { variables: {
input: { input: {
title: this.title, title: this.title,
projectPath: this.fullPath,
workItemTypeId: this.selectedWorkItemType?.id,
}, },
}, },
update(store, { data: { workItemCreate } }) {
const { id, title, workItemType } = workItemCreate.workItem;
store.writeQuery({
query: workItemQuery,
variables: {
id,
},
data: {
workItem: {
__typename: 'WorkItem',
id,
title,
workItemType,
widgets: {
__typename: 'LocalWorkItemWidgetConnection',
nodes: [],
},
},
},
});
},
}); });
const { const {
data: { data: {
localCreateWorkItem: { workItemCreate: {
workItem: { id, type }, workItem: { id, type },
}, },
}, },
} = response; } = response;
if (!this.isModal) { if (!this.isModal) {
this.$router.push({ name: 'workItem', params: { id } }); this.$router.push({ name: 'workItem', params: { id: `${getIdFromGraphQLId(id)}` } });
} else { } else {
this.$emit('onCreate', { id, title: this.title, type }); this.$emit('onCreate', { id, title: this.title, type });
} }
......
<script> <script>
import { GlAlert } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import workItemQuery from '../graphql/work_item.query.graphql'; import workItemQuery from '../graphql/work_item.query.graphql';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import { widgetTypes, WI_TITLE_TRACK_LABEL } from '../constants'; import { WI_TITLE_TRACK_LABEL } from '../constants';
import ItemTitle from '../components/item_title.vue'; import ItemTitle from '../components/item_title.vue';
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
components: { components: {
ItemTitle, ItemTitle,
GlAlert, GlAlert,
GlLoadingIcon,
}, },
mixins: [trackingMixin], mixins: [trackingMixin],
props: { props: {
...@@ -24,7 +26,7 @@ export default { ...@@ -24,7 +26,7 @@ export default {
}, },
data() { data() {
return { return {
workItem: null, workItem: {},
error: false, error: false,
}; };
}, },
...@@ -33,12 +35,9 @@ export default { ...@@ -33,12 +35,9 @@ export default {
query: workItemQuery, query: workItemQuery,
variables() { variables() {
return { return {
id: this.id, id: this.gid,
}; };
}, },
update(data) {
return data.localWorkItem;
},
}, },
}, },
computed: { computed: {
...@@ -50,19 +49,19 @@ export default { ...@@ -50,19 +49,19 @@ export default {
property: '[type_work_item]', property: '[type_work_item]',
}; };
}, },
titleWidgetData() { gid() {
return this.workItem?.widgets?.nodes?.find((widget) => widget.type === widgetTypes.title); return convertToGraphQLId('WorkItem', this.id);
}, },
}, },
methods: { methods: {
async updateWorkItem(title) { async updateWorkItem(updatedTitle) {
try { try {
await this.$apollo.mutate({ await this.$apollo.mutate({
mutation: updateWorkItemMutation, mutation: updateWorkItemMutation,
variables: { variables: {
input: { input: {
id: this.id, id: this.gid,
title, title: updatedTitle,
}, },
}, },
}); });
...@@ -82,12 +81,18 @@ export default { ...@@ -82,12 +81,18 @@ export default {
}}</gl-alert> }}</gl-alert>
<!-- Title widget placeholder --> <!-- Title widget placeholder -->
<div> <div>
<item-title <gl-loading-icon
v-if="titleWidgetData" v-if="$apollo.queries.workItem.loading"
:initial-title="titleWidgetData.contentText" size="md"
data-testid="title" data-testid="loading-types"
@title-changed="updateWorkItem"
/> />
<template v-else>
<item-title
:initial-title="workItem.title"
data-testid="title"
@title-changed="updateWorkItem"
/>
</template>
</div> </div>
</section> </section>
</template> </template>
export const workItemQueryResponse = { export const workItemQueryResponse = {
localWorkItem: { workItem: {
__typename: 'LocalWorkItem', __typename: 'WorkItem',
id: '1', id: '1',
type: 'FEATURE', title: 'Test',
workItemType: {
__typename: 'WorkItemType',
id: 'work-item-type-1',
},
widgets: { widgets: {
__typename: 'LocalWorkItemWidgetConnection', __typename: 'LocalWorkItemWidgetConnection',
nodes: [ nodes: [
...@@ -17,20 +21,29 @@ export const workItemQueryResponse = { ...@@ -17,20 +21,29 @@ export const workItemQueryResponse = {
}; };
export const updateWorkItemMutationResponse = { export const updateWorkItemMutationResponse = {
__typename: 'LocalUpdateWorkItemPayload', data: {
workItem: { workItemUpdate: {
__typename: 'LocalWorkItem', __typename: 'LocalUpdateWorkItemPayload',
id: '1', workItem: {
widgets: { __typename: 'LocalWorkItem',
__typename: 'LocalWorkItemWidgetConnection', id: '1',
nodes: [ title: 'Updated title',
{ workItemType: {
__typename: 'LocalTitleWidget', __typename: 'WorkItemType',
type: 'TITLE', id: 'work-item-type-1',
enabled: true,
contentText: 'Updated title',
}, },
], widgets: {
__typename: 'LocalWorkItemWidgetConnection',
nodes: [
{
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
contentText: 'Updated title',
},
],
},
},
}, },
}, },
}; };
...@@ -48,3 +61,20 @@ export const projectWorkItemTypesQueryResponse = { ...@@ -48,3 +61,20 @@ export const projectWorkItemTypesQueryResponse = {
}, },
}, },
}; };
export const createWorkItemMutationResponse = {
data: {
workItemCreate: {
__typename: 'WorkItemCreatePayload',
workItem: {
__typename: 'WorkItem',
id: '1',
title: 'Updated title',
workItemType: {
__typename: 'WorkItemType',
id: 'work-item-type-1',
},
},
},
},
};
...@@ -8,7 +8,8 @@ import CreateWorkItem from '~/work_items/pages/create_work_item.vue'; ...@@ -8,7 +8,8 @@ import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import ItemTitle from '~/work_items/components/item_title.vue'; import ItemTitle from '~/work_items/components/item_title.vue';
import { resolvers } from '~/work_items/graphql/resolvers'; import { resolvers } from '~/work_items/graphql/resolvers';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql'; import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
import { projectWorkItemTypesQueryResponse } from '../mock_data'; import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
import { projectWorkItemTypesQueryResponse, createWorkItemMutationResponse } from '../mock_data';
jest.mock('~/lib/utils/uuids', () => ({ uuids: () => ['testuuid'] })); jest.mock('~/lib/utils/uuids', () => ({ uuids: () => ['testuuid'] }));
...@@ -19,6 +20,7 @@ describe('Create work item component', () => { ...@@ -19,6 +20,7 @@ describe('Create work item component', () => {
let fakeApollo; let fakeApollo;
const querySuccessHandler = jest.fn().mockResolvedValue(projectWorkItemTypesQueryResponse); const querySuccessHandler = jest.fn().mockResolvedValue(projectWorkItemTypesQueryResponse);
const mutationSuccessHandler = jest.fn().mockResolvedValue(createWorkItemMutationResponse);
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
const findTitleInput = () => wrapper.findComponent(ItemTitle); const findTitleInput = () => wrapper.findComponent(ItemTitle);
...@@ -30,8 +32,19 @@ describe('Create work item component', () => { ...@@ -30,8 +32,19 @@ describe('Create work item component', () => {
const findContent = () => wrapper.find('[data-testid="content"]'); const findContent = () => wrapper.find('[data-testid="content"]');
const findLoadingTypesIcon = () => wrapper.find('[data-testid="loading-types"]'); const findLoadingTypesIcon = () => wrapper.find('[data-testid="loading-types"]');
const createComponent = ({ data = {}, props = {}, queryHandler = querySuccessHandler } = {}) => { const createComponent = ({
fakeApollo = createMockApollo([[projectWorkItemTypesQuery, queryHandler]], resolvers); data = {},
props = {},
queryHandler = querySuccessHandler,
mutationHandler = mutationSuccessHandler,
} = {}) => {
fakeApollo = createMockApollo(
[
[projectWorkItemTypesQuery, queryHandler],
[createWorkItemMutation, mutationHandler],
],
resolvers,
);
wrapper = shallowMount(CreateWorkItem, { wrapper = shallowMount(CreateWorkItem, {
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
data() { data() {
...@@ -126,7 +139,7 @@ describe('Create work item component', () => { ...@@ -126,7 +139,7 @@ describe('Create work item component', () => {
wrapper.find('form').trigger('submit'); wrapper.find('form').trigger('submit');
await waitForPromises(); await waitForPromises();
const expected = { id: 'testuuid', title: mockTitle, type: 'FEATURE' }; const expected = { id: '1', title: mockTitle };
expect(wrapper.emitted('onCreate')).toEqual([[expected]]); expect(wrapper.emitted('onCreate')).toEqual([[expected]]);
}); });
......
...@@ -9,11 +9,12 @@ import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutati ...@@ -9,11 +9,12 @@ import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutati
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue'; import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
import ItemTitle from '~/work_items/components/item_title.vue'; import ItemTitle from '~/work_items/components/item_title.vue';
import { resolvers } from '~/work_items/graphql/resolvers'; import { resolvers } from '~/work_items/graphql/resolvers';
import { workItemQueryResponse } from '../mock_data'; import { workItemQueryResponse, updateWorkItemMutationResponse } from '../mock_data';
Vue.use(VueApollo); Vue.use(VueApollo);
const WORK_ITEM_ID = '1'; const WORK_ITEM_ID = '1';
const WORK_ITEM_GID = `gid://gitlab/WorkItem/${WORK_ITEM_ID}`;
describe('Work items root component', () => { describe('Work items root component', () => {
const mockUpdatedTitle = 'Updated title'; const mockUpdatedTitle = 'Updated title';
...@@ -23,15 +24,19 @@ describe('Work items root component', () => { ...@@ -23,15 +24,19 @@ describe('Work items root component', () => {
const findTitle = () => wrapper.findComponent(ItemTitle); const findTitle = () => wrapper.findComponent(ItemTitle);
const createComponent = ({ queryResponse = workItemQueryResponse } = {}) => { const createComponent = ({ queryResponse = workItemQueryResponse } = {}) => {
fakeApollo = createMockApollo([], resolvers, { fakeApollo = createMockApollo(
possibleTypes: { [[updateWorkItemMutation, jest.fn().mockResolvedValue(updateWorkItemMutationResponse)]],
LocalWorkItemWidget: ['LocalTitleWidget'], resolvers,
{
possibleTypes: {
LocalWorkItemWidget: ['LocalTitleWidget'],
},
}, },
}); );
fakeApollo.clients.defaultClient.cache.writeQuery({ fakeApollo.clients.defaultClient.cache.writeQuery({
query: workItemQuery, query: workItemQuery,
variables: { variables: {
id: WORK_ITEM_ID, id: WORK_ITEM_GID,
}, },
data: queryResponse, data: queryResponse,
}); });
...@@ -49,7 +54,7 @@ describe('Work items root component', () => { ...@@ -49,7 +54,7 @@ describe('Work items root component', () => {
fakeApollo = null; fakeApollo = null;
}); });
it('renders the title if title is in the widgets list', () => { it('renders the title', () => {
createComponent(); createComponent();
expect(findTitle().exists()).toBe(true); expect(findTitle().exists()).toBe(true);
...@@ -66,35 +71,11 @@ describe('Work items root component', () => { ...@@ -66,35 +71,11 @@ describe('Work items root component', () => {
mutation: updateWorkItemMutation, mutation: updateWorkItemMutation,
variables: { variables: {
input: { input: {
id: WORK_ITEM_ID, id: WORK_ITEM_GID,
title: mockUpdatedTitle, title: mockUpdatedTitle,
}, },
}, },
}); });
await waitForPromises();
expect(findTitle().props('initialTitle')).toBe(mockUpdatedTitle);
});
it('does not render the title if title is not in the widgets list', () => {
const queryResponse = {
workItem: {
...workItemQueryResponse.workItem,
widgets: {
__typename: 'WorkItemWidgetConnection',
nodes: [
{
__typename: 'SomeOtherWidget',
type: 'OTHER',
contentText: 'Test',
},
],
},
},
};
createComponent({ queryResponse });
expect(findTitle().exists()).toBe(false);
}); });
describe('tracking', () => { describe('tracking', () => {
......
...@@ -21,6 +21,7 @@ describe('Work items router', () => { ...@@ -21,6 +21,7 @@ describe('Work items router', () => {
mocks: { mocks: {
$apollo: { $apollo: {
queries: { queries: {
workItem: {},
workItemTypes: {}, workItemTypes: {},
}, },
}, },
......
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