Commit 80b9d7c4 authored by David O'Regan's avatar David O'Regan Committed by Natalia Tepluhina

Allow schedule update

Hook schedule update to the backend
and allow for a schedule to be
updated via GraphQL
parent 25f534f1
...@@ -66,7 +66,9 @@ export default { ...@@ -66,7 +66,9 @@ export default {
editScheduleVariables() { editScheduleVariables() {
return { return {
projectPath: this.projectPath, projectPath: this.projectPath,
...this.form, iid: this.schedule.iid,
name: this.form.name,
description: this.form.description,
timezone: this.form.timezone.identifier, timezone: this.form.timezone.identifier,
}; };
}, },
...@@ -79,14 +81,12 @@ export default { ...@@ -79,14 +81,12 @@ export default {
this.$apollo this.$apollo
.mutate({ .mutate({
mutation: updateOncallScheduleMutation, mutation: updateOncallScheduleMutation,
variables: { variables: this.editScheduleVariables,
oncallScheduleEditInput: this.editScheduleVariables,
},
update(store, { data }) { update(store, { data }) {
updateStoreAfterScheduleEdit(store, getOncallSchedulesQuery, data, { projectPath }); updateStoreAfterScheduleEdit(store, getOncallSchedulesQuery, data, { projectPath });
}, },
}) })
.then(({ data: { oncallScheduleEdit: { errors: [error] } } }) => { .then(({ data: { oncallScheduleUpdate: { errors: [error] } } }) => {
if (error) { if (error) {
throw error; throw error;
} }
...@@ -114,6 +114,7 @@ export default { ...@@ -114,6 +114,7 @@ export default {
ref="updateScheduleModal" ref="updateScheduleModal"
modal-id="updateScheduleModal" modal-id="updateScheduleModal"
size="sm" size="sm"
:data-testid="`update-schedule-modal-${schedule.iid}`"
:title="$options.i18n.editSchedule" :title="$options.i18n.editSchedule"
:action-primary="actionsProps.primary" :action-primary="actionsProps.primary"
:action-cancel="actionsProps.cancel" :action-cancel="actionsProps.cancel"
......
mutation oncallScheduleUpdate($oncallScheduleUpdateInput: oncallScheduleUpdateInput!) { mutation oncallScheduleUpdate(
oncallScheduleUpdate(input: $oncallScheduleUpdateInput) { $iid: String!
$projectPath: ID!
$name: String
$description: String
$timezone: String
) {
oncallScheduleUpdate(
input: {
iid: $iid
projectPath: $projectPath
name: $name
description: $description
timezone: $timezone
}
) {
errors errors
oncallSchedule { oncallSchedule {
iid iid
......
...@@ -4,6 +4,7 @@ exports[`UpdateScheduleModal renders update schedule modal layout 1`] = ` ...@@ -4,6 +4,7 @@ exports[`UpdateScheduleModal renders update schedule modal layout 1`] = `
<gl-modal-stub <gl-modal-stub
actioncancel="[object Object]" actioncancel="[object Object]"
actionprimary="[object Object]" actionprimary="[object Object]"
data-testid="update-schedule-modal-37"
modalclass="" modalclass=""
modalid="updateScheduleModal" modalid="updateScheduleModal"
size="sm" size="sm"
......
/* eslint-disable no-unused-vars */
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import createMockApollo from 'jest/helpers/mock_apollo_helper'; import createMockApollo from 'jest/helpers/mock_apollo_helper';
import { GlModal } from '@gitlab/ui'; import { GlModal, GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import getOncallSchedulesQuery from 'ee/oncall_schedules/graphql/queries/get_oncall_schedules.query.graphql';
import updateOncallScheduleMutation from 'ee/oncall_schedules/graphql/mutations/update_oncall_schedule.mutation.graphql'; import updateOncallScheduleMutation from 'ee/oncall_schedules/graphql/mutations/update_oncall_schedule.mutation.graphql';
import UpdateScheduleModal, { i18n } from 'ee/oncall_schedules/components/edit_schedule_modal.vue'; import UpdateScheduleModal, { i18n } from 'ee/oncall_schedules/components/edit_schedule_modal.vue';
import { UPDATE_SCHEDULE_ERROR } from 'ee/oncall_schedules/utils/error_messages'; import {
import { getOncallSchedulesQueryResponse, updateScheduleResponse } from './mocks/apollo_mock'; getOncallSchedulesQueryResponse,
updateScheduleResponse,
updateScheduleResponseWithErrors,
} from './mocks/apollo_mock';
import mockTimezones from './mocks/mockTimezones.json'; import mockTimezones from './mocks/mockTimezones.json';
const localVue = createLocalVue(); const localVue = createLocalVue();
const projectPath = 'group/project'; const projectPath = 'group/project';
const mutate = jest.fn(); const mutate = jest.fn();
const mockHideModal = jest.fn(); const mockHideModal = jest.fn();
const schedule =
getOncallSchedulesQueryResponse.data.project.incidentManagementOncallSchedules.nodes[0];
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -23,6 +28,7 @@ describe('UpdateScheduleModal', () => { ...@@ -23,6 +28,7 @@ describe('UpdateScheduleModal', () => {
let updateScheduleHandler; let updateScheduleHandler;
const findModal = () => wrapper.find(GlModal); const findModal = () => wrapper.find(GlModal);
const findAlert = () => wrapper.find(GlAlert);
async function awaitApolloDomMock() { async function awaitApolloDomMock() {
await wrapper.vm.$nextTick(); // kick off the DOM update await wrapper.vm.$nextTick(); // kick off the DOM update
...@@ -30,11 +36,11 @@ describe('UpdateScheduleModal', () => { ...@@ -30,11 +36,11 @@ describe('UpdateScheduleModal', () => {
await wrapper.vm.$nextTick(); // kick off the DOM update for flash await wrapper.vm.$nextTick(); // kick off the DOM update for flash
} }
async function destroySchedule(localWrapper) { async function updateSchedule(localWrapper) {
await jest.runOnlyPendingTimers(); await jest.runOnlyPendingTimers();
await localWrapper.vm.$nextTick(); await localWrapper.vm.$nextTick();
localWrapper.vm.$emit('primary'); localWrapper.find(GlModal).vm.$emit('primary', { preventDefault: jest.fn() });
} }
const createComponent = ({ data = {}, props = {} } = {}) => { const createComponent = ({ data = {}, props = {} } = {}) => {
...@@ -42,13 +48,11 @@ describe('UpdateScheduleModal', () => { ...@@ -42,13 +48,11 @@ describe('UpdateScheduleModal', () => {
data() { data() {
return { return {
...data, ...data,
form: form: schedule,
getOncallSchedulesQueryResponse.data.project.incidentManagementOncallSchedules.nodes[0],
}; };
}, },
propsData: { propsData: {
schedule: schedule,
getOncallSchedulesQueryResponse.data.project.incidentManagementOncallSchedules.nodes[0],
...props, ...props,
}, },
provide: { provide: {
...@@ -70,13 +74,32 @@ describe('UpdateScheduleModal', () => { ...@@ -70,13 +74,32 @@ describe('UpdateScheduleModal', () => {
localVue.use(VueApollo); localVue.use(VueApollo);
updateScheduleHandler = updateHandler; updateScheduleHandler = updateHandler;
const requestHandlers = [[updateOncallScheduleMutation, updateScheduleHandler]]; const requestHandlers = [
[getOncallSchedulesQuery, jest.fn().mockResolvedValue(getOncallSchedulesQueryResponse)],
[updateOncallScheduleMutation, updateScheduleHandler],
];
fakeApollo = createMockApollo(requestHandlers); fakeApollo = createMockApollo(requestHandlers);
fakeApollo.clients.defaultClient.cache.writeQuery({
query: getOncallSchedulesQuery,
variables: {
projectPath: 'group/project',
},
data: getOncallSchedulesQueryResponse.data,
});
wrapper = shallowMount(UpdateScheduleModal, { wrapper = shallowMount(UpdateScheduleModal, {
localVue, localVue,
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
data() {
return {
form: schedule,
};
},
propsData: {
schedule,
},
provide: { provide: {
projectPath, projectPath,
timezones: mockTimezones, timezones: mockTimezones,
...@@ -114,8 +137,13 @@ describe('UpdateScheduleModal', () => { ...@@ -114,8 +137,13 @@ describe('UpdateScheduleModal', () => {
expect(mutate).toHaveBeenCalledWith({ expect(mutate).toHaveBeenCalledWith({
mutation: expect.any(Object), mutation: expect.any(Object),
update: expect.anything(), update: expect.anything(),
// TODO: Once the BE is complete for the mutation update this spec to use the correct params variables: {
variables: expect.anything(), iid: schedule.iid,
projectPath,
name: schedule.name,
description: schedule.description,
timezone: schedule.timezone.identifier,
},
}); });
}); });
...@@ -123,8 +151,7 @@ describe('UpdateScheduleModal', () => { ...@@ -123,8 +151,7 @@ describe('UpdateScheduleModal', () => {
mutate.mockResolvedValueOnce({ data: { oncallScheduleUpdate: { errors: [] } } }); mutate.mockResolvedValueOnce({ data: { oncallScheduleUpdate: { errors: [] } } });
findModal().vm.$emit('primary', { preventDefault: jest.fn() }); findModal().vm.$emit('primary', { preventDefault: jest.fn() });
await waitForPromises(); await waitForPromises();
// TODO: Once the BE is complete for the mutation update this spec to use the call expect(mockHideModal).toHaveBeenCalled();
expect(mockHideModal).not.toHaveBeenCalled();
}); });
it("doesn't hide the modal on fail", async () => { it("doesn't hide the modal on fail", async () => {
...@@ -137,6 +164,34 @@ describe('UpdateScheduleModal', () => { ...@@ -137,6 +164,34 @@ describe('UpdateScheduleModal', () => {
}); });
describe('with mocked Apollo client', () => { describe('with mocked Apollo client', () => {
// TODO: Once the BE is complete for the mutation add specs here for that via a destroyHandler it('has the name of the schedule to update based on getOncallSchedulesQuery', async () => {
createComponentWithApollo();
await jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
expect(findModal().attributes('data-testid')).toBe(`update-schedule-modal-${schedule.iid}`);
});
it('calls a mutation with correct parameters and updates a schedule', async () => {
createComponentWithApollo();
await updateSchedule(wrapper);
expect(updateScheduleHandler).toHaveBeenCalled();
});
it('displays alert if mutation had a recoverable error', async () => {
createComponentWithApollo({
updateHandler: jest.fn().mockResolvedValue(updateScheduleResponseWithErrors),
});
await updateSchedule(wrapper);
await awaitApolloDomMock();
const alert = findAlert();
expect(alert.exists()).toBe(true);
expect(alert.text()).toContain('Houston, we have a problem');
});
}); });
}); });
...@@ -68,7 +68,7 @@ export const destroyScheduleResponseWithErrors = { ...@@ -68,7 +68,7 @@ export const destroyScheduleResponseWithErrors = {
export const updateScheduleResponse = { export const updateScheduleResponse = {
data: { data: {
oncallScheduleDestroy: { oncallScheduleUpdate: {
errors: [], errors: [],
oncallSchedule: { oncallSchedule: {
__typename: 'IncidentManagementOncallSchedule', __typename: 'IncidentManagementOncallSchedule',
...@@ -81,6 +81,21 @@ export const updateScheduleResponse = { ...@@ -81,6 +81,21 @@ export const updateScheduleResponse = {
}, },
}; };
export const updateScheduleResponseWithErrors = {
data: {
oncallScheduleUpdate: {
errors: ['Houston, we have a problem'],
oncallSchedule: {
__typename: 'IncidentManagementOncallSchedule',
iid: '37',
name: 'Test schedule 2',
description: 'Description 2 lives here',
timezone: 'Pacific/Honolulu',
},
},
},
};
export const preExistingSchedule = { export const preExistingSchedule = {
description: 'description', description: 'description',
iid: '1', iid: '1',
......
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