Commit 397363ad authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '273797-multi-schedules-dry-2' into 'master'

Multiple Schedules - load one rotation set at a time

See merge request gitlab-org/gitlab!60441
parents 02cbc9ab 85b9536a
......@@ -24,7 +24,7 @@ import {
editRotationModalId,
PRESET_TYPES,
} from '../constants';
import getShiftsForRotations from '../graphql/queries/get_oncall_schedules_with_rotations_shifts.query.graphql';
import getShiftsForRotationsQuery from '../graphql/queries/get_oncall_schedules_with_rotations_shifts.query.graphql';
import EditScheduleModal from './add_edit_schedule_modal.vue';
import DeleteScheduleModal from './delete_schedule_modal.vue';
import AddEditRotationModal from './rotations/components/add_edit_rotation_modal.vue';
......@@ -80,10 +80,17 @@ export default {
type: Object,
required: true,
},
scheduleIndex: {
type: Number,
required: true,
},
},
apollo: {
rotations: {
query: getShiftsForRotations,
query: getShiftsForRotationsQuery,
skip() {
return !this.scheduleVisible;
},
variables() {
this.timeframeStartDate.setHours(0, 0, 0, 0);
const startsAt = this.timeframeStartDate;
......@@ -115,7 +122,7 @@ export default {
timeframeStartDate: getStartOfWeek(new Date()),
rotations: this.schedule.rotations.nodes,
rotationToUpdate: {},
scheduleVisible: true,
scheduleVisible: this.scheduleIndex === 0,
};
},
computed: {
......@@ -318,27 +325,25 @@ export default {
</gl-card>
</gl-collapse>
</gl-card>
<div v-if="scheduleVisible">
<delete-schedule-modal :schedule="schedule" :modal-id="deleteScheduleModalId" />
<edit-schedule-modal :schedule="schedule" :modal-id="editScheduleModalId" is-edit-mode />
<add-edit-rotation-modal
:schedule="schedule"
:modal-id="addRotationModalId"
@fetch-rotation-shifts="fetchRotationShifts"
/>
<add-edit-rotation-modal
:schedule="schedule"
:modal-id="editRotationModalId"
:rotation="rotationToUpdate"
is-edit-mode
@fetch-rotation-shifts="fetchRotationShifts"
/>
<delete-rotation-modal
:rotation="rotationToUpdate"
:schedule="schedule"
:modal-id="deleteRotationModalId"
@fetch-rotation-shifts="fetchRotationShifts"
/>
</div>
<delete-schedule-modal :schedule="schedule" :modal-id="deleteScheduleModalId" />
<edit-schedule-modal :schedule="schedule" :modal-id="editScheduleModalId" is-edit-mode />
<add-edit-rotation-modal
:schedule="schedule"
:modal-id="addRotationModalId"
@fetch-rotation-shifts="fetchRotationShifts"
/>
<add-edit-rotation-modal
:schedule="schedule"
:modal-id="editRotationModalId"
:rotation="rotationToUpdate"
is-edit-mode
@fetch-rotation-shifts="fetchRotationShifts"
/>
<delete-rotation-modal
:rotation="rotationToUpdate"
:schedule="schedule"
:modal-id="deleteRotationModalId"
@fetch-rotation-shifts="fetchRotationShifts"
/>
</div>
</template>
......@@ -118,7 +118,12 @@ export default {
>
{{ $options.i18n.successNotification.description }}
</gl-alert>
<oncall-schedule v-for="schedule in schedules" :key="schedule.iid" :schedule="schedule" />
<oncall-schedule
v-for="(schedule, scheduleIndex) in schedules"
:key="schedule.iid"
:schedule="schedule"
:schedule-index="scheduleIndex"
/>
</template>
<gl-empty-state
......
......@@ -35,7 +35,7 @@ export const getOncallSchedulesQueryResponse = {
name: 'Test schedule from query',
description: 'Description 1 lives here',
timezone: 'Pacific/Honolulu',
rotations: { nodes: [mockRotations] },
rotations: { nodes: mockRotations },
},
],
},
......
......@@ -27,11 +27,10 @@
"nodes": [
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/49",
"colorWeight": "500",
"colorPalette": "blue",
"user": {
"id": "1",
"id": "gid://gitlab/User/1",
"username": "nora.schaden",
"avatarUrl": "/url",
"name": "nora"
......@@ -42,11 +41,10 @@
},
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/232",
"colorWeight": "500",
"colorPalette": "orange",
"user": {
"id": "2",
"id": "gid://gitlab/User/2",
"username": "racheal.loving",
"avatarUrl": "/url",
"name": "racheal"
......@@ -87,10 +85,11 @@
"nodes": [
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/99",
"colorWeight": "500",
"colorPalette": "aqua",
"user": {
"id": "gid://gitlab/User/38",
"avatarUrl": "url",
"username": "david.oregan",
"name": "david"
}
......@@ -100,10 +99,11 @@
},
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/300",
"colorWeight": "500",
"colorPalette": "green",
"user": {
"id": "gid://gitlab/User/39",
"avatarUrl": "url",
"username": "david.keagan",
"name": "david k"
}
......@@ -143,10 +143,11 @@
"nodes": [
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/100",
"colorWeight": "500",
"colorPalette": "magenta",
"user": {
"id": "gid://gitlab/User/40",
"avatarUrl": "url",
"username": "root",
"name": "Administrator"
}
......@@ -156,10 +157,11 @@
},
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/109",
"colorWeight": "600",
"colorPalette": "blue",
"user": {
"id": "gid://gitlab/User/41",
"avatarUrl": "url",
"username": "root2",
"name": "Administrator 2"
}
......@@ -199,10 +201,11 @@
"nodes": [
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/52",
"colorWeight": "600",
"colorPalette": "orange",
"user": {
"id": "gid://gitlab/User/43",
"avatarUrl": "url",
"username": "oregand",
"name": "david"
}
......@@ -212,10 +215,11 @@
},
{
"participant": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/77",
"colorWeight": "600",
"colorPalette": "aqua",
"user": {
"id": "gid://gitlab/User/44",
"avatarUrl": "url",
"username": "sarah.w",
"name": "sarah"
}
......
import { GlCard, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlButton, GlCard, GlCollapse } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import OnCallSchedule, { i18n } from 'ee/oncall_schedules/components/oncall_schedule.vue';
import RotationsListSection from 'ee/oncall_schedules/components/schedule/components/rotations_list_section.vue';
import ScheduleTimelineSection from 'ee/oncall_schedules/components/schedule/components/schedule_timeline_section.vue';
import * as utils from 'ee/oncall_schedules/components/schedule/utils';
import { PRESET_TYPES } from 'ee/oncall_schedules/constants';
import getShiftsForRotationsQuery from 'ee/oncall_schedules/graphql/queries/get_oncall_schedules_with_rotations_shifts.query.graphql';
import * as commonUtils from 'ee/oncall_schedules/utils/common_utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import * as dateTimeUtility from '~/lib/utils/datetime_utility';
import { getOncallSchedulesQueryResponse } from './mocks/apollo_mock';
import mockTimezones from './mocks/mock_timezones.json';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('On-call schedule', () => {
let wrapper;
let fakeApollo;
const lastTz = mockTimezones[mockTimezones.length - 1];
const mockRotations = [{ name: 'rotation1' }, { name: 'rotation2' }];
const mockSchedule = {
description: 'monitor description',
iid: '3',
name: 'monitor schedule',
timezone: lastTz.identifier,
rotations: {
nodes: mockRotations,
nodes: [],
},
};
......@@ -32,44 +42,54 @@ describe('On-call schedule', () => {
];
const formattedTimezone = '(UTC-09:00) AKST Alaska';
function createComponent({ schedule, loading } = {}) {
const $apollo = {
queries: {
rotations: {
loading,
},
},
};
const createComponent = ({
schedule = mockSchedule,
scheduleIndex = 0,
getShiftsForRotationsQueryHandler = jest
.fn()
.mockResolvedValue(getOncallSchedulesQueryResponse),
props = {},
provide = {},
} = {}) => {
fakeApollo = createMockApollo([
[getShiftsForRotationsQuery, getShiftsForRotationsQueryHandler],
]);
wrapper = extendedWrapper(
shallowMount(OnCallSchedule, {
localVue,
apolloProvider: fakeApollo,
propsData: {
schedule,
},
provide: {
timezones: mockTimezones,
projectPath,
scheduleIndex,
...props,
},
data() {
return {
rotations: mockRotations,
rotations: schedule.rotations.nodes,
};
},
provide: {
timezones: mockTimezones,
projectPath,
...provide,
},
stubs: {
GlCard,
},
mocks: { $apollo },
}),
);
}
};
beforeEach(() => {
jest.spyOn(utils, 'getTimeframeForWeeksView').mockReturnValue(mockWeeksTimeFrame);
jest.spyOn(commonUtils, 'getFormattedTimezone').mockReturnValue(formattedTimezone);
createComponent({ schedule: mockSchedule, loading: false });
createComponent();
});
afterEach(() => {
wrapper.destroy();
fakeApollo = null;
});
const findScheduleHeader = () => wrapper.findByTestId('scheduleHeader');
......@@ -83,6 +103,7 @@ describe('On-call schedule', () => {
const findRotationsList = () => findRotations().find(RotationsListSection);
const findLoadPreviousTimeframeBtn = () => wrapper.findByTestId('previous-timeframe-btn');
const findLoadNextTimeframeBtn = () => wrapper.findByTestId('next-timeframe-btn');
const findCollapsible = () => wrapper.findComponent(GlCollapse);
it('shows schedule title', () => {
expect(findScheduleHeader().text()).toBe(mockSchedule.name);
......@@ -102,7 +123,11 @@ describe('On-call schedule', () => {
});
it('does not show schedule description if none present', () => {
createComponent({ schedule: { ...mockSchedule, description: null }, loading: false });
createComponent({
schedule: { ...mockSchedule, description: null },
loading: false,
scheduleIndex: 0,
});
expect(findScheduleDescription()).not.toContain(mockSchedule.description);
});
});
......@@ -133,6 +158,15 @@ describe('On-call schedule', () => {
});
});
it('renders a open card for the first in the list by default', () => {
expect(findCollapsible().attributes('visible')).toBe('true');
});
it('renders a collapsed card if not the first in the list by default', () => {
createComponent({ scheduleIndex: 1 });
expect(findCollapsible().attributes('visible')).toBeUndefined();
});
describe('Timeframe shift preset type', () => {
it('renders rotation shift preset type buttons', () => {
expect(findRotationsShiftPreset().exists()).toBe(true);
......@@ -196,4 +230,27 @@ describe('On-call schedule', () => {
});
});
});
describe('with Apollo mock', () => {
it('renders rotations list from API response when resolved', async () => {
createComponent();
await waitForPromises();
expect(findRotationsList().props('rotations')).toHaveLength(4);
expect(findRotationsList().props('rotations')).toEqual(
getOncallSchedulesQueryResponse.data.project.incidentManagementOncallSchedules.nodes[0]
.rotations.nodes,
);
});
it('does not renders rotations list from API response when skipped', async () => {
createComponent({ scheduleIndex: 1 });
await nextTick();
await waitForPromises();
expect(findRotationsList().props('rotations')).toHaveLength(0);
expect(findRotationsList().props('rotations')).toEqual([]);
});
});
});
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