import { mount } from '@vue/test-utils'; import { trimText } from 'helpers/text_helper'; import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue'; describe('Interval Pattern Input Component', () => { let oldWindowGl; let wrapper; const mockHour = 4; const mockWeekDayIndex = 1; const mockDay = 1; const cronIntervalPresets = { everyDay: `0 ${mockHour} * * *`, everyWeek: `0 ${mockHour} * * ${mockWeekDayIndex}`, everyMonth: `0 ${mockHour} ${mockDay} * *`, }; const customKey = 'custom'; const everyDayKey = 'everyDay'; const cronIntervalNotInPreset = `0 12 * * *`; const findEveryDayRadio = () => wrapper.find(`[data-testid=${everyDayKey}]`); const findEveryWeekRadio = () => wrapper.find('[data-testid="everyWeek"]'); const findEveryMonthRadio = () => wrapper.find('[data-testid="everyMonth"]'); const findCustomRadio = () => wrapper.find(`[data-testid="${customKey}"]`); const findCustomInput = () => wrapper.find('#schedule_cron'); const findAllLabels = () => wrapper.findAll('label'); const findSelectedRadio = () => wrapper.findAll('input[type="radio"]').wrappers.find((x) => x.element.checked); const findSelectedRadioKey = () => findSelectedRadio()?.attributes('data-testid'); const selectEveryDayRadio = () => findEveryDayRadio().trigger('click'); const selectEveryWeekRadio = () => findEveryWeekRadio().trigger('click'); const selectEveryMonthRadio = () => findEveryMonthRadio().trigger('click'); const selectCustomRadio = () => findCustomRadio().trigger('click'); const createWrapper = (props = {}, data = {}) => { if (wrapper) { throw new Error('A wrapper already exists'); } wrapper = mount(IntervalPatternInput, { propsData: { ...props }, data() { return { randomHour: data?.hour || mockHour, randomWeekDayIndex: mockWeekDayIndex, randomDay: mockDay, }; }, }); }; beforeEach(() => { oldWindowGl = window.gl; window.gl = { ...(window.gl || {}), pipelineScheduleFieldErrors: { updateFormValidityState: jest.fn(), }, }; }); afterEach(() => { wrapper.destroy(); wrapper = null; window.gl = oldWindowGl; }); describe('the input field defaults', () => { beforeEach(() => { createWrapper(); }); it('defaults to every day value when no `initialCronInterval` is passed', () => { expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyDay); }); }); describe('the input field', () => { const initialCron = '0 * * * *'; beforeEach(() => { createWrapper({ initialCronInterval: initialCron }); }); it('is equal to the prop `initialCronInterval` when passed', () => { expect(findCustomInput().element.value).toBe(initialCron); }); }); describe('The input field is enabled', () => { beforeEach(() => { createWrapper(); }); it('when a default option is selected', async () => { selectEveryDayRadio(); await wrapper.vm.$nextTick(); expect(findCustomInput().attributes('disabled')).toBeUndefined(); }); it('when the custom option is selected', async () => { selectCustomRadio(); await wrapper.vm.$nextTick(); expect(findCustomInput().attributes('disabled')).toBeUndefined(); }); }); describe('formattedTime computed property', () => { it.each` desc | hour | expectedValue ${'returns a time in the afternoon if the value of `random time` is higher than 12'} | ${13} | ${'1:00pm'} ${'returns a time in the morning if the value of `random time` is lower than 12'} | ${11} | ${'11:00am'} ${'returns "12:00pm" if the value of `random time` is exactly 12'} | ${12} | ${'12:00pm'} `('$desc', ({ hour, expectedValue }) => { createWrapper({}, { hour }); expect(wrapper.vm.formattedTime).toBe(expectedValue); }); }); describe('Time strings', () => { beforeEach(() => { createWrapper(); }); it('renders each label for radio options properly', () => { const labels = findAllLabels().wrappers.map((el) => trimText(el.text())); expect(labels).toEqual([ 'Every day (at 4:00am)', 'Every week (Monday at 4:00am)', 'Every month (Day 1 at 4:00am)', 'Custom ( Cron syntax )', ]); }); }); describe('User Actions with radio buttons', () => { describe('Default option', () => { beforeEach(() => { createWrapper(); }); it('when everyday is selected, update value', async () => { selectEveryWeekRadio(); await wrapper.vm.$nextTick(); expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyWeek); selectEveryDayRadio(); await wrapper.vm.$nextTick(); expect(findCustomInput().element.value).toBe(cronIntervalPresets.everyDay); }); }); describe('Other options', () => { it.each` desc | initialCronInterval | act | expectedValue ${'when everyweek is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryWeekRadio} | ${cronIntervalPresets.everyWeek} ${'when everymonth is selected, update value'} | ${'1 2 3 4 5'} | ${selectEveryMonthRadio} | ${cronIntervalPresets.everyMonth} ${'when custom is selected, value remains the same'} | ${cronIntervalPresets.everyMonth} | ${selectCustomRadio} | ${cronIntervalPresets.everyMonth} `('$desc', async ({ initialCronInterval, act, expectedValue }) => { createWrapper({ initialCronInterval }); act(); await wrapper.vm.$nextTick(); expect(findCustomInput().element.value).toBe(expectedValue); }); }); }); describe('User actions with input field for Cron syntax', () => { beforeEach(() => { createWrapper(); }); it('when editing the cron input it selects the custom radio button', async () => { const newValue = '0 * * * *'; expect(findSelectedRadioKey()).toBe(everyDayKey); findCustomInput().setValue(newValue); await wrapper.vm.$nextTick; expect(findSelectedRadioKey()).toBe(customKey); }); }); describe('Edit form field', () => { beforeEach(() => { createWrapper({ initialCronInterval: cronIntervalNotInPreset }); }); it('loads with the custom option being selected', () => { expect(findSelectedRadioKey()).toBe(customKey); }); }); });