Commit f10a3654 authored by Mark Florian's avatar Mark Florian

Merge branch '262857-validation' into 'master'

Feat(oncallschedules): add form validation - schedule / rotations

See merge request gitlab-org/gitlab!50819
parents 173bc9f9 18d0b537
...@@ -52,12 +52,8 @@ export default { ...@@ -52,12 +52,8 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
isNameInvalid: { validationState: {
type: Boolean, type: Object,
required: true,
},
isTimezoneInvalid: {
type: Boolean,
required: true, required: true,
}, },
schedule: { schedule: {
...@@ -69,6 +65,7 @@ export default { ...@@ -69,6 +65,7 @@ export default {
data() { data() {
return { return {
tzSearchTerm: '', tzSearchTerm: '',
selectedDropdownTimezone: null,
}; };
}, },
computed: { computed: {
...@@ -94,6 +91,9 @@ export default { ...@@ -94,6 +91,9 @@ export default {
isTimezoneSelected(tz) { isTimezoneSelected(tz) {
return isEqual(tz, this.form.timezone); return isEqual(tz, this.form.timezone);
}, },
setTimezone(timezone) {
this.selectedDropdownTimezone = timezone;
},
}, },
}; };
</script> </script>
...@@ -105,12 +105,13 @@ export default { ...@@ -105,12 +105,13 @@ export default {
:invalid-feedback="$options.i18n.fields.name.validation.empty" :invalid-feedback="$options.i18n.fields.name.validation.empty"
label-size="sm" label-size="sm"
label-for="schedule-name" label-for="schedule-name"
:state="validationState.name"
requried
> >
<gl-form-input <gl-form-input
id="schedule-name" id="schedule-name"
:value="form.name" :value="form.name"
:state="!isNameInvalid" @blur="$emit('update-schedule-form', { type: 'name', value: $event.target.value })"
@input="$emit('update-schedule-form', { type: 'name', value: $event })"
/> />
</gl-form-group> </gl-form-group>
...@@ -122,7 +123,7 @@ export default { ...@@ -122,7 +123,7 @@ export default {
<gl-form-input <gl-form-input
id="schedule-description" id="schedule-description"
:value="form.description" :value="form.description"
@input="$emit('update-schedule-form', { type: 'description', value: $event })" @blur="$emit('update-schedule-form', { type: 'description', value: $event.target.value })"
/> />
</gl-form-group> </gl-form-group>
...@@ -131,15 +132,17 @@ export default { ...@@ -131,15 +132,17 @@ export default {
label-size="sm" label-size="sm"
label-for="schedule-timezone" label-for="schedule-timezone"
:description="$options.i18n.fields.timezone.description" :description="$options.i18n.fields.timezone.description"
:state="!isTimezoneInvalid" :state="validationState.timezone"
:invalid-feedback="$options.i18n.fields.timezone.validation.empty" :invalid-feedback="$options.i18n.fields.timezone.validation.empty"
requried
> >
<gl-dropdown <gl-dropdown
id="schedule-timezone" id="schedule-timezone"
:text="selectedTimezone" :text="selectedTimezone"
class="timezone-dropdown gl-w-full" class="timezone-dropdown gl-w-full"
:header-text="$options.i18n.selectTimezone" :header-text="$options.i18n.selectTimezone"
:class="{ 'invalid-dropdown': isTimezoneInvalid }" :class="{ 'invalid-dropdown': !validationState.timezone }"
@hide="$emit('update-schedule-form', { type: 'timezone', value: selectedDropdownTimezone })"
> >
<gl-search-box-by-type v-model.trim="tzSearchTerm" /> <gl-search-box-by-type v-model.trim="tzSearchTerm" />
<gl-dropdown-item <gl-dropdown-item
...@@ -147,7 +150,7 @@ export default { ...@@ -147,7 +150,7 @@ export default {
:key="getFormattedTimezone(tz)" :key="getFormattedTimezone(tz)"
:is-checked="isTimezoneSelected(tz)" :is-checked="isTimezoneSelected(tz)"
is-check-item is-check-item
@click="$emit('update-schedule-form', { type: 'timezone', value: tz })" @click="setTimezone(tz)"
> >
<span class="gl-white-space-nowrap"> {{ getFormattedTimezone(tz) }}</span> <span class="gl-white-space-nowrap"> {{ getFormattedTimezone(tz) }}</span>
</gl-dropdown-item> </gl-dropdown-item>
......
...@@ -7,6 +7,7 @@ import createOncallScheduleMutation from '../graphql/mutations/create_oncall_sch ...@@ -7,6 +7,7 @@ import createOncallScheduleMutation from '../graphql/mutations/create_oncall_sch
import updateOncallScheduleMutation from '../graphql/mutations/update_oncall_schedule.mutation.graphql'; import updateOncallScheduleMutation from '../graphql/mutations/update_oncall_schedule.mutation.graphql';
import AddEditScheduleForm from './add_edit_schedule_form.vue'; import AddEditScheduleForm from './add_edit_schedule_form.vue';
import { updateStoreOnScheduleCreate, updateStoreAfterScheduleEdit } from '../utils/cache_updates'; import { updateStoreOnScheduleCreate, updateStoreAfterScheduleEdit } from '../utils/cache_updates';
import { isNameFieldValid } from '../utils/common_utils';
export const i18n = { export const i18n = {
cancel: __('Cancel'), cancel: __('Cancel'),
...@@ -48,7 +49,11 @@ export default { ...@@ -48,7 +49,11 @@ export default {
description: this.schedule?.description, description: this.schedule?.description,
timezone: this.timezones.find(({ identifier }) => this.schedule?.timezone === identifier), timezone: this.timezones.find(({ identifier }) => this.schedule?.timezone === identifier),
}, },
error: null, error: '',
validationState: {
name: true,
timezone: true,
},
}; };
}, },
computed: { computed: {
...@@ -59,7 +64,7 @@ export default { ...@@ -59,7 +64,7 @@ export default {
attributes: [ attributes: [
{ variant: 'info' }, { variant: 'info' },
{ loading: this.loading }, { loading: this.loading },
{ disabled: this.isFormInvalid }, { disabled: !this.isFormValid },
], ],
}, },
cancel: { cancel: {
...@@ -67,14 +72,8 @@ export default { ...@@ -67,14 +72,8 @@ export default {
}, },
}; };
}, },
isNameInvalid() { isFormValid() {
return !this.form.name?.length; return Object.values(this.validationState).every(Boolean);
},
isTimezoneInvalid() {
return isEmpty(this.form.timezone);
},
isFormInvalid() {
return this.isNameInvalid || this.isTimezoneInvalid;
}, },
editScheduleVariables() { editScheduleVariables() {
return { return {
...@@ -169,10 +168,18 @@ export default { ...@@ -169,10 +168,18 @@ export default {
}); });
}, },
hideErrorAlert() { hideErrorAlert() {
this.error = null; this.error = '';
}, },
updateScheduleForm({ type, value }) { updateScheduleForm({ type, value }) {
this.form[type] = value; this.form[type] = value;
this.validateForm(type);
},
validateForm(key) {
if (key === 'name') {
this.validationState.name = isNameFieldValid(this.form.name);
} else if (key === 'timezone') {
this.validationState.timezone = !isEmpty(this.form.timezone);
}
}, },
}, },
}; };
...@@ -192,8 +199,7 @@ export default { ...@@ -192,8 +199,7 @@ export default {
{{ errorMsg }} {{ errorMsg }}
</gl-alert> </gl-alert>
<add-edit-schedule-form <add-edit-schedule-form
:is-name-invalid="isNameInvalid" :validation-state="validationState"
:is-timezone-invalid="isTimezoneInvalid"
:form="form" :form="form"
:schedule="schedule" :schedule="schedule"
@update-schedule-form="updateScheduleForm" @update-schedule-form="updateScheduleForm"
......
...@@ -143,7 +143,6 @@ export default { ...@@ -143,7 +143,6 @@ export default {
</gl-card> </gl-card>
</gl-card> </gl-card>
<delete-schedule-modal :schedule="schedule" :modal-id="$options.deleteScheduleModalId" /> <delete-schedule-modal :schedule="schedule" :modal-id="$options.deleteScheduleModalId" />
<edit-schedule-modal :schedule="schedule" :modal-id="$options.editScheduleModalId" />
<edit-schedule-modal <edit-schedule-modal
:schedule="schedule" :schedule="schedule"
:modal-id="$options.editScheduleModalId" :modal-id="$options.editScheduleModalId"
......
...@@ -10,13 +10,13 @@ import { ...@@ -10,13 +10,13 @@ import {
GlAvatar, GlAvatar,
GlAvatarLabeled, GlAvatarLabeled,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { import {
LENGTH_ENUM, LENGTH_ENUM,
HOURS_IN_DAY, HOURS_IN_DAY,
CHEVRON_SKIPPING_SHADE_ENUM, CHEVRON_SKIPPING_SHADE_ENUM,
CHEVRON_SKIPPING_PALETTE_ENUM, CHEVRON_SKIPPING_PALETTE_ENUM,
} from '../../../constants'; } from 'ee/oncall_schedules/constants';
import { s__, __ } from '~/locale';
import { format24HourTimeStringFromInt } from '~/lib/utils/datetime_utility'; import { format24HourTimeStringFromInt } from '~/lib/utils/datetime_utility';
export const i18n = { export const i18n = {
...@@ -65,16 +65,8 @@ export default { ...@@ -65,16 +65,8 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
rotationNameIsValid: { validationState: {
type: Boolean, type: Object,
required: true,
},
rotationParticipantsAreValid: {
type: Boolean,
required: true,
},
rotationStartsAtIsValid: {
type: Boolean,
required: true, required: true,
}, },
participants: { participants: {
...@@ -105,11 +97,11 @@ export default { ...@@ -105,11 +97,11 @@ export default {
label-size="sm" label-size="sm"
label-for="rotation-name" label-for="rotation-name"
:invalid-feedback="$options.i18n.fields.name.error" :invalid-feedback="$options.i18n.fields.name.error"
:state="rotationNameIsValid" :state="validationState.name"
> >
<gl-form-input <gl-form-input
id="rotation-name" id="rotation-name"
@input="$emit('update-rotation-form', { type: 'name', value: $event })" @blur="$emit('update-rotation-form', { type: 'name', value: $event.target.value })"
/> />
</gl-form-group> </gl-form-group>
...@@ -118,7 +110,7 @@ export default { ...@@ -118,7 +110,7 @@ export default {
label-size="sm" label-size="sm"
label-for="rotation-participants" label-for="rotation-participants"
:invalid-feedback="$options.i18n.fields.participants.error" :invalid-feedback="$options.i18n.fields.participants.error"
:state="rotationParticipantsAreValid" :state="validationState.participants"
> >
<gl-token-selector <gl-token-selector
v-model="participantsArr" v-model="participantsArr"
...@@ -126,6 +118,7 @@ export default { ...@@ -126,6 +118,7 @@ export default {
:loading="isLoading" :loading="isLoading"
container-class="gl-h-13! gl-overflow-y-auto" container-class="gl-h-13! gl-overflow-y-auto"
@text-input="$emit('filter-participants', $event)" @text-input="$emit('filter-participants', $event)"
@blur="$emit('update-rotation-form', { type: 'participants', value: participantsArr })"
@input="$emit('update-rotation-form', { type: 'participants', value: participantsArr })" @input="$emit('update-rotation-form', { type: 'participants', value: participantsArr })"
> >
<template #token-content="{ token }"> <template #token-content="{ token }">
...@@ -176,13 +169,24 @@ export default { ...@@ -176,13 +169,24 @@ export default {
label-size="sm" label-size="sm"
label-for="rotation-time" label-for="rotation-time"
:invalid-feedback="$options.i18n.fields.startsAt.error" :invalid-feedback="$options.i18n.fields.startsAt.error"
:state="rotationStartsAtIsValid" :state="validationState.startsAt"
> >
<div class="gl-display-flex gl-align-items-center"> <div class="gl-display-flex gl-align-items-center">
<gl-datepicker <gl-datepicker
class="gl-mr-3" class="gl-mr-3"
@input="$emit('update-rotation-form', { type: 'startsAt.date', value: $event })" @input="$emit('update-rotation-form', { type: 'startsAt.date', value: $event })"
/> >
<template #default="{ formattedDate }">
<gl-form-input
class="gl-w-full"
:value="formattedDate"
:placeholder="__(`YYYY-MM-DD`)"
@blur="
$emit('update-rotation-form', { type: 'startsAt.date', value: $event.target.value })
"
/>
</template>
</gl-datepicker>
<span> {{ __('at') }} </span> <span> {{ __('at') }} </span>
<gl-dropdown <gl-dropdown
id="rotation-time" id="rotation-time"
......
<script> <script>
import { GlModal, GlAlert } from '@gitlab/ui'; import { GlModal, GlAlert } from '@gitlab/ui';
import { set } from 'lodash'; import { set } from 'lodash';
import getOncallSchedulesQuery from 'ee/oncall_schedules/graphql/queries/get_oncall_schedules.query.graphql';
import createOncallScheduleRotationMutation from 'ee/oncall_schedules/graphql/mutations/create_oncall_schedule_rotation.mutation.graphql';
import updateOncallScheduleRotationMutation from 'ee/oncall_schedules/graphql/mutations/update_oncall_schedule_rotation.mutation.graphql';
import { LENGTH_ENUM } from 'ee/oncall_schedules/constants';
import {
updateStoreAfterRotationAdd,
updateStoreAfterRotationEdit,
} from 'ee/oncall_schedules/utils/cache_updates';
import { isNameFieldValid } from 'ee/oncall_schedules/utils/common_utils';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import createFlash, { FLASH_TYPES } from '~/flash'; import createFlash, { FLASH_TYPES } from '~/flash';
import usersSearchQuery from '~/graphql_shared/queries/users_search.query.graphql'; import usersSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
import getOncallSchedulesQuery from '../../../graphql/queries/get_oncall_schedules.query.graphql';
import createOncallScheduleRotationMutation from '../../../graphql/mutations/create_oncall_schedule_rotation.mutation.graphql';
import updateOncallScheduleRotationMutation from '../../../graphql/mutations/update_oncall_schedule_rotation.mutation.graphql';
import { LENGTH_ENUM } from '../../../constants';
import AddEditRotationForm from './add_edit_rotation_form.vue'; import AddEditRotationForm from './add_edit_rotation_form.vue';
import {
updateStoreAfterRotationAdd,
updateStoreAfterRotationEdit,
} from '../../../utils/cache_updates';
import { format24HourTimeStringFromInt } from '~/lib/utils/datetime_utility'; import { format24HourTimeStringFromInt } from '~/lib/utils/datetime_utility';
export const i18n = { export const i18n = {
...@@ -81,6 +82,11 @@ export default { ...@@ -81,6 +82,11 @@ export default {
}, },
}, },
error: '', error: '',
validationState: {
name: true,
participants: true,
startsAt: true,
},
}; };
}, },
computed: { computed: {
...@@ -99,15 +105,6 @@ export default { ...@@ -99,15 +105,6 @@ export default {
}, },
}; };
}, },
rotationNameIsValid() {
return this.form.name !== '';
},
rotationParticipantsAreValid() {
return this.form.participants.length > 0;
},
rotationStartsAtIsValid() {
return Boolean(this.form.startsAt.date);
},
rotationVariables() { rotationVariables() {
return { return {
projectPath: this.projectPath, projectPath: this.projectPath,
...@@ -130,11 +127,7 @@ export default { ...@@ -130,11 +127,7 @@ export default {
}; };
}, },
isFormValid() { isFormValid() {
return ( return Object.values(this.validationState).every(Boolean);
this.rotationNameIsValid &&
this.rotationParticipantsAreValid &&
this.rotationStartsAtIsValid
);
}, },
isLoading() { isLoading() {
return this.loading || this.$apollo.queries.participants.loading; return this.loading || this.$apollo.queries.participants.loading;
...@@ -226,10 +219,20 @@ export default { ...@@ -226,10 +219,20 @@ export default {
}, },
updateRotationForm({ type, value }) { updateRotationForm({ type, value }) {
set(this.form, type, value); set(this.form, type, value);
this.validateForm(type);
}, },
filterParticipants(query) { filterParticipants(query) {
this.ptSearchTerm = query; this.ptSearchTerm = query;
}, },
validateForm(key) {
if (key === 'name') {
this.validationState.name = isNameFieldValid(this.form.name);
} else if (key === 'participants') {
this.validationState.participants = this.form.participants.length > 0;
} else if (key === 'startsAt.date') {
this.validationState.startsAt = Boolean(this.form.startsAt.date);
}
},
}, },
}; };
</script> </script>
...@@ -248,9 +251,7 @@ export default { ...@@ -248,9 +251,7 @@ export default {
{{ error || $options.i18n.errorMsg }} {{ error || $options.i18n.errorMsg }}
</gl-alert> </gl-alert>
<add-edit-rotation-form <add-edit-rotation-form
:rotation-name-is-valid="rotationNameIsValid" :validation-state="validationState"
:rotation-participants-are-valid="rotationParticipantsAreValid"
:rotation-starts-at-is-valid="rotationStartsAtIsValid"
:form="form" :form="form"
:schedule="schedule" :schedule="schedule"
:participants="participants" :participants="participants"
......
<script> <script>
import { GlToken, GlAvatarLabeled, GlPopover } from '@gitlab/ui'; import { GlToken, GlAvatarLabeled, GlPopover } from '@gitlab/ui';
import { assigneeScheduleDateStart } from 'ee/oncall_schedules/utils/common_utils';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { assigneeScheduleDateStart } from '../../../utils/common_utils';
export default { export default {
components: { components: {
......
...@@ -30,3 +30,14 @@ export const getFormattedTimezone = (tz) => { ...@@ -30,3 +30,14 @@ export const getFormattedTimezone = (tz) => {
export const assigneeScheduleDateStart = (startDate, daysToAdd) => { export const assigneeScheduleDateStart = (startDate, daysToAdd) => {
return getDateInFuture(startDate, daysToAdd); return getDateInFuture(startDate, daysToAdd);
}; };
/**
* Returns `true` for non-empty string, otherwise returns `false`
*
* @param {String} startDate
*
* @returns {Boolean}
*/
export const isNameFieldValid = (nameField) => {
return Boolean(nameField?.length);
};
...@@ -9,10 +9,11 @@ exports[`AddEditScheduleForm renders modal layout 1`] = ` ...@@ -9,10 +9,11 @@ exports[`AddEditScheduleForm renders modal layout 1`] = `
label="Name" label="Name"
label-for="schedule-name" label-for="schedule-name"
label-size="sm" label-size="sm"
requried=""
state="true"
> >
<gl-form-input-stub <gl-form-input-stub
id="schedule-name" id="schedule-name"
state="true"
value="Test schedule" value="Test schedule"
/> />
</gl-form-group-stub> </gl-form-group-stub>
...@@ -34,6 +35,7 @@ exports[`AddEditScheduleForm renders modal layout 1`] = ` ...@@ -34,6 +35,7 @@ exports[`AddEditScheduleForm renders modal layout 1`] = `
label="Timezone" label="Timezone"
label-for="schedule-timezone" label-for="schedule-timezone"
label-size="sm" label-size="sm"
requried=""
state="true" state="true"
> >
<gl-dropdown-stub <gl-dropdown-stub
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlSearchBoxByType, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlSearchBoxByType, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui';
import AddEditScheduleForm, { import AddEditScheduleForm, {
i18n, i18n,
} from 'ee/oncall_schedules/components/add_edit_schedule_form.vue'; } from 'ee/oncall_schedules/components/add_edit_schedule_form.vue';
...@@ -22,8 +22,10 @@ describe('AddEditScheduleForm', () => { ...@@ -22,8 +22,10 @@ describe('AddEditScheduleForm', () => {
description: mockSchedule.description, description: mockSchedule.description,
timezone: mockTimezones[0], timezone: mockTimezones[0],
}, },
isNameInvalid: false, validationState: {
isTimezoneInvalid: false, name: true,
timezone: true,
},
schedule: mockSchedule, schedule: mockSchedule,
...props, ...props,
}, },
...@@ -36,9 +38,6 @@ describe('AddEditScheduleForm', () => { ...@@ -36,9 +38,6 @@ describe('AddEditScheduleForm', () => {
mutate, mutate,
}, },
}, },
stubs: {
GlFormGroup: false,
},
}); });
}; };
...@@ -52,13 +51,25 @@ describe('AddEditScheduleForm', () => { ...@@ -52,13 +51,25 @@ describe('AddEditScheduleForm', () => {
}); });
const findTimezoneDropdown = () => wrapper.find(GlDropdown); const findTimezoneDropdown = () => wrapper.find(GlDropdown);
const findDropdownOptions = () => wrapper.findAll(GlDropdownItem); const findDropdownOptions = () => wrapper.findAllComponents(GlDropdownItem);
const findTimezoneSearchBox = () => wrapper.find(GlSearchBoxByType); const findTimezoneSearchBox = () => wrapper.find(GlSearchBoxByType);
const findScheduleName = () => wrapper.find(GlFormGroup);
it('renders modal layout', () => { it('renders modal layout', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
describe('Schedule form validation', () => {
it('should show feedback for an invalid name input validation state', async () => {
createComponent({
props: {
validationState: { name: false },
},
});
expect(findScheduleName().attributes('state')).toBeFalsy();
});
});
describe('Timezone select', () => { describe('Timezone select', () => {
it('has options based on provided BE data', () => { it('has options based on provided BE data', () => {
expect(findDropdownOptions()).toHaveLength(mockTimezones.length); expect(findDropdownOptions()).toHaveLength(mockTimezones.length);
...@@ -102,20 +113,23 @@ describe('AddEditScheduleForm', () => { ...@@ -102,20 +113,23 @@ describe('AddEditScheduleForm', () => {
describe('Form validation', () => { describe('Form validation', () => {
describe('Timezone select', () => { describe('Timezone select', () => {
it('has red border when nothing selected', () => { it('has a validation red border when timezone field is invalid', () => {
createComponent({ createComponent({
props: { props: {
schedule: null, schedule: null,
form: { name: '', description: '', timezone: '' }, form: { name: '', description: '', timezone: '' },
isTimezoneInvalid: true, validationState: { timezone: false },
}, },
}); });
expect(findTimezoneDropdown().classes()).toContain('invalid-dropdown'); expect(findTimezoneDropdown().classes()).toContain('invalid-dropdown');
}); });
it("doesn't have a red border when there is selected option", async () => { it('does not have a validation red border when timezone field is valid', async () => {
findDropdownOptions().at(1).vm.$emit('click'); createComponent({
await wrapper.vm.$nextTick(); props: {
validationState: { timezone: true },
},
});
expect(findTimezoneDropdown().classes()).not.toContain('invalid-dropdown'); expect(findTimezoneDropdown().classes()).not.toContain('invalid-dropdown');
}); });
}); });
......
...@@ -16,6 +16,7 @@ exports[`AddEditRotationModal renders rotation modal layout 1`] = ` ...@@ -16,6 +16,7 @@ exports[`AddEditRotationModal renders rotation modal layout 1`] = `
form="[object Object]" form="[object Object]"
participants="" participants=""
schedule="[object Object]" schedule="[object Object]"
validationstate="[object Object]"
/> />
</gl-modal-stub> </gl-modal-stub>
`; `;
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { GlDropdownItem, GlTokenSelector } from '@gitlab/ui'; import { GlDropdownItem, GlTokenSelector, GlFormGroup } from '@gitlab/ui';
import AddEditRotationForm from 'ee/oncall_schedules/components/rotations/components/add_edit_rotation_form.vue'; import AddEditRotationForm from 'ee/oncall_schedules/components/rotations/components/add_edit_rotation_form.vue';
import { LENGTH_ENUM } from 'ee/oncall_schedules/constants'; import { LENGTH_ENUM } from 'ee/oncall_schedules/constants';
import { participants, getOncallSchedulesQueryResponse } from '../../mocks/apollo_mock'; import { participants, getOncallSchedulesQueryResponse } from '../../mocks/apollo_mock';
...@@ -23,9 +23,11 @@ describe('AddEditRotationForm', () => { ...@@ -23,9 +23,11 @@ describe('AddEditRotationForm', () => {
...props, ...props,
schedule, schedule,
isLoading: false, isLoading: false,
rotationNameIsValid: true, validationState: {
rotationParticipantsAreValid: true, name: true,
rotationStartsAtIsValid: true, participants: false,
startsAt: false,
},
participants, participants,
form: { form: {
name: '', name: '',
...@@ -57,7 +59,23 @@ describe('AddEditRotationForm', () => { ...@@ -57,7 +59,23 @@ describe('AddEditRotationForm', () => {
const findRotationLength = () => wrapper.find('[id = "rotation-length"]'); const findRotationLength = () => wrapper.find('[id = "rotation-length"]');
const findRotationStartsOn = () => wrapper.find('[id = "rotation-time"]'); const findRotationStartsOn = () => wrapper.find('[id = "rotation-time"]');
const findUserSelector = () => wrapper.find(GlTokenSelector); const findUserSelector = () => wrapper.find(GlTokenSelector);
const findDropdownOptions = () => wrapper.findAll(GlDropdownItem); const findDropdownOptions = () => wrapper.findAllComponents(GlDropdownItem);
const findRotationFormGroups = () => wrapper.findAllComponents(GlFormGroup);
describe('Rotation form validation', () => {
it.each`
index | type | validationState | value
${0} | ${'name'} | ${true} | ${'true'}
${1} | ${'participants'} | ${false} | ${undefined}
${3} | ${'startsAt'} | ${false} | ${undefined}
`(
'form validation for $type returns $value when passed validate state of $validationState',
({ index, value }) => {
const formGroup = findRotationFormGroups();
expect(formGroup.at(index).attributes('state')).toBe(value);
},
);
});
describe('Rotation length and start time', () => { describe('Rotation length and start time', () => {
it('renders the rotation length value', async () => { it('renders the rotation length value', async () => {
......
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