Commit 52e7bdbe authored by Natalia Tepluhina's avatar Natalia Tepluhina Committed by Simon Knox

Fix occasional failure when updating labels from sidebar

Changelog: fixed
parent 7e76ac08
#import "~/graphql_shared/fragments/label.fragment.graphql"
mutation issueSetLabels($input: UpdateIssueInput!) { mutation issueSetLabels($input: UpdateIssueInput!) {
updateIssue(input: $input) { updateIssuableLabels: updateIssue(input: $input) {
issue { issuable: issue {
id id
labels { labels {
nodes { nodes {
id ...Label
title
color
description
} }
} }
} }
......
mutation mergeRequestSetLabels($input: MergeRequestSetLabelsInput!) { mutation mergeRequestSetLabels($input: MergeRequestSetLabelsInput!) {
mergeRequestSetLabels(input: $input) { updateIssuableLabels: mergeRequestSetLabels(input: $input) {
errors errors
mergeRequest { issuable: mergeRequest {
id id
labels { labels {
nodes { nodes {
......
#import "~/graphql_shared/fragments/label.fragment.graphql" #import "~/graphql_shared/fragments/label.fragment.graphql"
mutation updateEpicLabels($input: UpdateEpicInput!) { mutation updateEpicLabels($input: UpdateEpicInput!) {
updateEpic(input: $input) { updateIssuableLabels: updateEpic(input: $input) {
epic { issuable: epic {
id id
labels { labels {
nodes { nodes {
......
...@@ -225,16 +225,13 @@ export default { ...@@ -225,16 +225,13 @@ export default {
variables: { input: inputVariables }, variables: { input: inputVariables },
}) })
.then(({ data }) => { .then(({ data }) => {
const { mutationName } = issuableLabelsQueries[this.issuableType]; if (data.updateIssuableLabels?.errors?.length) {
if (data[mutationName]?.errors?.length) {
throw new Error(); throw new Error();
} }
this.issuableLabels = data[mutationName]?.[this.issuableType]?.labels?.nodes;
this.$emit('updateSelectedLabels', { this.$emit('updateSelectedLabels', {
id: data[mutationName]?.[this.issuableType]?.id, id: data.updateIssuableLabels?.issuable?.id,
labels: this.issuableLabels, labels: data.updateIssuableLabels?.issuable?.labels?.nodes,
}); });
}) })
.catch((error) => .catch((error) =>
......
...@@ -10,16 +10,26 @@ import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widg ...@@ -10,16 +10,26 @@ import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widg
import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue'; import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue'; import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue';
import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql'; import issueLabelsQuery from '~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql';
import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import updateMergeRequestLabelsMutation from '~/sidebar/queries/update_merge_request_labels.mutation.graphql';
import updateEpicLabelsMutation from '~/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql';
import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue'; import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import { mockConfig, issuableLabelsQueryResponse } from './mock_data'; import { mockConfig, issuableLabelsQueryResponse, updateLabelsMutationResponse } from './mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
Vue.use(VueApollo); Vue.use(VueApollo);
const successfulQueryHandler = jest.fn().mockResolvedValue(issuableLabelsQueryResponse); const successfulQueryHandler = jest.fn().mockResolvedValue(issuableLabelsQueryResponse);
const successfulMutationHandler = jest.fn().mockResolvedValue(updateLabelsMutationResponse);
const errorQueryHandler = jest.fn().mockRejectedValue('Houston, we have a problem'); const errorQueryHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
const updateLabelsMutation = {
[IssuableType.Issue]: updateIssueLabelsMutation,
[IssuableType.MergeRequest]: updateMergeRequestLabelsMutation,
[IssuableType.Epic]: updateEpicLabelsMutation,
};
describe('LabelsSelectRoot', () => { describe('LabelsSelectRoot', () => {
let wrapper; let wrapper;
...@@ -31,16 +41,21 @@ describe('LabelsSelectRoot', () => { ...@@ -31,16 +41,21 @@ describe('LabelsSelectRoot', () => {
const createComponent = ({ const createComponent = ({
config = mockConfig, config = mockConfig,
slots = {}, slots = {},
issuableType = IssuableType.Issue,
queryHandler = successfulQueryHandler, queryHandler = successfulQueryHandler,
mutationHandler = successfulMutationHandler,
} = {}) => { } = {}) => {
const mockApollo = createMockApollo([[issueLabelsQuery, queryHandler]]); const mockApollo = createMockApollo([
[issueLabelsQuery, queryHandler],
[updateLabelsMutation[issuableType], mutationHandler],
]);
wrapper = shallowMount(LabelsSelectRoot, { wrapper = shallowMount(LabelsSelectRoot, {
slots, slots,
apolloProvider: mockApollo, apolloProvider: mockApollo,
propsData: { propsData: {
...config, ...config,
issuableType: IssuableType.Issue, issuableType,
labelCreateType: 'project', labelCreateType: 'project',
workspaceType: 'project', workspaceType: 'project',
}, },
...@@ -133,4 +148,46 @@ describe('LabelsSelectRoot', () => { ...@@ -133,4 +148,46 @@ describe('LabelsSelectRoot', () => {
findDropdownContents().vm.$emit('setLabels', [label]); findDropdownContents().vm.$emit('setLabels', [label]);
expect(wrapper.emitted('updateSelectedLabels')).toEqual([[{ labels: [label] }]]); expect(wrapper.emitted('updateSelectedLabels')).toEqual([[{ labels: [label] }]]);
}); });
describe.each`
issuableType
${IssuableType.Issue}
${IssuableType.MergeRequest}
${IssuableType.Epic}
`('when updating labels for $issuableType', ({ issuableType }) => {
const label = { id: 'gid://gitlab/ProjectLabel/2' };
it('sets the loading state', async () => {
createComponent({ issuableType });
await nextTick();
findDropdownContents().vm.$emit('setLabels', [label]);
await nextTick();
expect(findSidebarEditableItem().props('loading')).toBe(true);
});
it('updates labels correctly after successful mutation', async () => {
createComponent({ issuableType });
await nextTick();
findDropdownContents().vm.$emit('setLabels', [label]);
await waitForPromises();
expect(findDropdownValue().props('selectedLabels')).toEqual(
updateLabelsMutationResponse.data.updateIssuableLabels.issuable.labels.nodes,
);
});
it('displays an error if mutation was rejected', async () => {
createComponent({ issuableType, mutationHandler: errorQueryHandler });
await nextTick();
findDropdownContents().vm.$emit('setLabels', [label]);
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
captureError: true,
error: expect.anything(),
message: 'An error occurred while updating labels.',
});
});
});
}); });
...@@ -120,6 +120,7 @@ export const issuableLabelsQueryResponse = { ...@@ -120,6 +120,7 @@ export const issuableLabelsQueryResponse = {
workspace: { workspace: {
id: 'workspace-1', id: 'workspace-1',
issuable: { issuable: {
__typename: 'Issue',
id: '1', id: '1',
labels: { labels: {
nodes: [ nodes: [
...@@ -136,3 +137,18 @@ export const issuableLabelsQueryResponse = { ...@@ -136,3 +137,18 @@ export const issuableLabelsQueryResponse = {
}, },
}, },
}; };
export const updateLabelsMutationResponse = {
data: {
updateIssuableLabels: {
errors: [],
issuable: {
__typename: 'Issue',
id: '1',
labels: {
nodes: [],
},
},
},
},
};
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