From 8467b943778333f189d5c0b41cb8bf4ce18f8d17 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Tue, 26 May 2026 14:12:16 +0400 Subject: [PATCH 1/7] test(scheduler): migrate Validation tests to isolated popup env --- .../appointment_popup.integration.test.ts | 47 ---------------- .../appointment_popup.test.ts | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts index d2b376b01c8e..a2184a6ea1cf 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts @@ -343,53 +343,6 @@ describe('Appointment Form', () => { }); }); - describe('Validation', () => { - it.each([ - 'startDateEditor', 'startTimeEditor', 'endDateEditor', 'endTimeEditor', - ])('should not close popup on save button click when %s is empty', async (editorName) => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ ...commonAppointment }); - - POM.popup.setInputValue(editorName, null); - POM.popup.saveButton.click(); - await Promise.resolve(); - - expect(POM.isPopupVisible()).toBe(true); - }); - - it.each([ - 'startTimeEditor', 'endDateEditor', 'endTimeEditor', - ])('should not close popup on save button click in recurrence form when %s editor is empty', async (editorName) => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ ...commonAppointment }); - - POM.popup.setInputValue(editorName, null); - POM.popup.selectRepeatValue('daily'); - POM.popup.saveButton.click(); - await Promise.resolve(); - - expect(POM.isPopupVisible()).toBe(true); - }); - - it('should close popup on save button click in recurrence form when startEditor editor is empty', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ ...commonAppointment }); - - POM.popup.setInputValue('startDateEditor', null); - POM.popup.selectRepeatValue('daily'); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/9/2017'); - - POM.popup.saveButton.click(); - await Promise.resolve(); - - expect(POM.isPopupVisible()).toBe(false); - }); - }); - describe('State', () => { it('should have correct editor values when opening for empty date cell - 1', async () => { const { POM } = await createScheduler({ diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index b638a730c170..1259554a97e3 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -350,4 +350,60 @@ describe('Isolated AppointmentPopup environment', () => { expect(recurrenceStartFDOW).toBe(1); expect(weekDayButtonsText).toBe('MTWTFSS'); }); + + describe('Validation', () => { + const commonAppointment = { + text: 'common-app', + startDate: new Date(2017, 4, 9, 9, 30), + endDate: new Date(2017, 4, 9, 11), + }; + + it.each([ + 'startDateEditor', 'startTimeEditor', 'endDateEditor', 'endTimeEditor', + ])('should not close popup on save button click when %s is empty', async (editorName) => { + const { popup, POM } = await createAppointmentPopup({ + appointmentData: + { ...commonAppointment }, + }); + + POM.setInputValue(editorName, null); + POM.saveButton.click(); + await Promise.resolve(); + + expect(popup.visible).toBe(true); + }); + + it.each([ + 'startTimeEditor', 'endDateEditor', 'endTimeEditor', + ])('should not close popup on save button click in recurrence form when %s editor is empty', async (editorName) => { + const { popup, POM } = await createAppointmentPopup({ + appointmentData: + { ...commonAppointment }, + }); + + POM.setInputValue(editorName, null); + POM.selectRepeatValue('daily'); + POM.saveButton.click(); + await Promise.resolve(); + + expect(popup.visible).toBe(true); + }); + + it('should not block save in recurrence form when startDateEditor is empty', async () => { + const { POM, callbacks } = await createAppointmentPopup({ + appointmentData: + { ...commonAppointment }, + }); + + POM.setInputValue('startDateEditor', null); + POM.selectRepeatValue('daily'); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/9/2017'); + + POM.saveButton.click(); + await Promise.resolve(); + + expect(callbacks.onSave).toHaveBeenCalledTimes(1); + }); + }); }); From 3c07295c9886f5b0a2df6ef71d55c1d0ec2f0767 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Tue, 26 May 2026 21:32:57 +0400 Subject: [PATCH 2/7] test(scheduler): migrate Recurrence Form tests to isolated env --- .../appointment_popup.integration.test.ts | 503 +----------------- .../appointment_popup.test.ts | 402 +++++++++++++- 2 files changed, 397 insertions(+), 508 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts index a2184a6ea1cf..1f61902a4719 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts @@ -4,8 +4,7 @@ import { import dateLocalization from '@js/common/core/localization/date'; import { CustomStore } from '@js/common/data/custom_store'; import $ from '@js/core/renderer'; -import { loadMessages, locale } from '@js/localization'; -import type { GroupItem, Item as FormItem, SimpleItem } from '@js/ui/form'; +import type { GroupItem, Item as FormItem } from '@js/ui/form'; import type { ToolbarItem } from '@js/ui/popup'; import { toMilliseconds } from '@ts/utils/toMilliseconds'; @@ -987,506 +986,6 @@ describe('Appointment Form', () => { .toBe((scheduler as any).resourceManager); }); }); - - describe('Recurrence Form', () => { - it('should allow opening recurrence settings when allowUpdating is false', async () => { - const appointment = { - text: 'Recurrent Appointment', - startDate: new Date(2017, 4, 1, 9, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10', - }; - - const { POM, scheduler } = await createScheduler({ - ...getDefaultConfig(), - editing: { allowUpdating: false }, - }); - - scheduler.showAppointmentPopup(appointment); - - expect(POM.popup.isRecurrenceGroupVisible()).toBe(false); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); - }); - - it('should close repeat selectbox popup when navigating to recurrence group via settings button', async () => { - const { POM, scheduler } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [{ ...recurringAppointment }], - }); - - const dataSource = (scheduler as any).getDataSource(); - const appointment = dataSource.items()[0]; - - scheduler.showAppointmentPopup(appointment); - POM.popup.editSeriesButton.click(); - - const repeatEditor = POM.popup.dxForm.getEditor('repeatEditor'); - POM.popup.getInput('repeatEditor').click(); - - expect(repeatEditor?.option('opened')).toBe(true); - - POM.popup.recurrenceSettingsButton.click(); - - expect(repeatEditor?.option('opened')).toBe(false); - }); - - it('should have disabled week day buttons when allowUpdating is false', async () => { - const { POM, scheduler } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [{ ...recurringAppointment, recurrenceRule: 'FREQ=WEEKLY;BYDAY=WE,TU,TH,FR,SA' }], - editing: { allowUpdating: false }, - }); - - const dataSource = (scheduler as any).getDataSource(); - const appointment = dataSource.items()[0]; - - scheduler.showAppointmentPopup(appointment); - POM.popup.recurrenceSettingsButton.click(); - - const weekDayButtons = $(POM.popup.recurrenceWeekDayButtons); - const disabledButtons = weekDayButtons?.find('.dx-button.dx-state-disabled'); - - expect(disabledButtons.length).toBe(7); - }); - - it('should be visible after changing repeat editor\'s value', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup(); - - expect(POM.popup.isMainGroupVisible()).toBe(true); - expect(POM.popup.mainGroup.getAttribute('inert')).toBeNull(); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(false); - expect(POM.popup.recurrenceGroup.getAttribute('inert')).toBe('true'); - - POM.popup.selectRepeatValue('weekly'); - - const popupHeight = POM.popup.component.option('height'); - expect(popupHeight).toBeDefined(); - expect(typeof popupHeight).toBe('number'); - - expect(POM.popup.isMainGroupVisible()).toBe(false); - expect(POM.popup.mainGroup.getAttribute('inert')).toBe('true'); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); - expect(POM.popup.recurrenceGroup.getAttribute('inert')).toBeNull(); - - POM.popup.backButton.click(); - - expect(POM.popup.component.option('height')).toBe('auto'); - expect(POM.popup.isMainGroupVisible()).toBe(true); - expect(POM.popup.mainGroup.getAttribute('inert')).toBeNull(); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(false); - expect(POM.popup.recurrenceGroup.getAttribute('inert')).toBe('true'); - }); - - it('should open main form when opening recurring appointment', async () => { - const appointment = { - text: 'Recurrent Appointment', - startDate: new Date(2017, 4, 1, 9, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10', - }; - - const { POM, scheduler } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup(appointment); - - POM.popup.editSeriesButton.click(); - - expect(POM.popup.isMainGroupVisible()).toBe(true); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(false); - }); - - describe('State', () => { - it('should have correct input values for appointment with hour frequency', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=HOURLY;INTERVAL=2;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Hourly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('2'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Hour(s)'); - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - - it('should have correct input values for appointment with daily frequency', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Daily'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('2'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Day(s)'); - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - - it('should have correct input values for appointment with week frequency', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Weekly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('2'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Week(s)'); - - const expectedWeekDaysSelection = [true, false, true, false, true, false, false]; - expect(POM.popup.getWeekDaysSelection()).toEqual(expectedWeekDaysSelection); - - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - - it('should have correct input values for appointment with monthly frequency', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=MONTHLY;INTERVAL=2;BYMONTHDAY=1;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Monthly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('2'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Month(s)'); - expect(POM.popup.getInputValue('recurrenceDayOfMonthEditor')).toBe('1'); - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - - it('should have correct input values for appointment with yearly frequency', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=YEARLY;INTERVAL=2;BYMONTHDAY=1;BYMONTH=5;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('Yearly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); - expect(POM.popup.getInputValue('recurrenceCountEditor')).toBe('2'); - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Year(s)'); - expect(POM.popup.getInputValue('recurrenceDayOfYearDayEditor')).toBe('1'); - expect(POM.popup.getInputValue('recurrenceDayOfYearMonthEditor')).toBe('May'); - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - - it('T1325870: should use current locale for recurrence editors after locale change', async () => { - const currentLocale = locale(); - - loadMessages({ - de: { - 'dxScheduler-recurrenceYearly': 'custom yearly', - 'dxScheduler-recurrenceRepeatYearly': 'custom repeat yearly', - }, - }); - locale('de'); - - try { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=YEARLY;INTERVAL=2;BYMONTHDAY=1;BYMONTH=5;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - - expect(POM.popup.getInputValue('repeatEditor')).toBe('custom yearly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrencePeriodEditor')).toBe('Custom repeat yearly'); - expect(POM.popup.getInputValue('recurrenceDayOfYearMonthEditor')).toBe(dateLocalization.getMonthNames()[4]); - } finally { - locale(currentLocale); - } - }); - - it('should have correct input values for appointment with no end', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=DAILY;INTERVAL=2', - repeatEnd: 'never', - }); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceRepeatEndEditor')).toBe('never'); - }); - - it('should have correct input values for appointment with end by date', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=DAILY;INTERVAL=2;UNTIL=20170601T000000Z', - repeatEnd: 'until', - }); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceRepeatEndEditor')).toBe('until'); - expect(POM.popup.getInputValue('recurrenceEndUntilEditor')).toBe('6/1/2017'); - }); - - it('should have correct input values for appointment with end by count', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=10', - repeatEnd: 'count', - }); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceRepeatEndEditor')).toBe('count'); - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); - }); - }); - - describe('Repeat End Values Preservation', () => { - it('should preserve count value when switching between recurrence types', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - const testCount = 15; - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - }); - - POM.popup.selectRepeatValue('daily'); - - POM.popup.setInputValue('recurrenceRepeatEndEditor', 'count'); - POM.popup.setInputValue('recurrenceEndCountEditor', testCount); - - POM.popup.backButton.click(); - - POM.popup.selectRepeatValue('weekly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceEndCountEditor')).toBe(`${testCount} occurrence(s)`); - - scheduler.hideAppointmentPopup(); - }); - - it('should preserve until value when switching between recurrence types', async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - const testUntilDate = new Date(2017, 5, 16); - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - }); - - POM.popup.selectRepeatValue('daily'); - - POM.popup.setInputValue('recurrenceRepeatEndEditor', 'until'); - POM.popup.setInputValue('recurrenceEndUntilEditor', testUntilDate); - - POM.popup.backButton.click(); - - POM.popup.selectRepeatValue('weekly'); - - POM.popup.recurrenceSettingsButton.click(); - - expect(POM.popup.getInputValue('recurrenceEndUntilEditor')).toBe('6/16/2017'); - - scheduler.hideAppointmentPopup(); - }); - }); - - describe('Repeat End Editors Disabled State', () => { - ['never', 'until', 'count'].forEach((repeatEndValue) => { - it(`should set correct disabled state when repeatEnd is ${repeatEndValue}`, async () => { - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - let recurrenceRule = ''; - switch (repeatEndValue) { - case 'count': - recurrenceRule = 'FREQ=DAILY;COUNT=10'; - break; - case 'until': - recurrenceRule = 'FREQ=DAILY;UNTIL=20170615T000000Z'; - break; - default: - recurrenceRule = 'FREQ=DAILY'; - } - - scheduler.showAppointmentPopup({ - text: 'Meeting', - startDate: new Date(2017, 4, 1, 10, 30), - endDate: new Date(2017, 4, 1, 11), - recurrenceRule, - }); - - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - const untilEditor = POM.popup.dxForm.getEditor('recurrenceEndUntilEditor'); - const countEditor = POM.popup.dxForm.getEditor('recurrenceEndCountEditor'); - - expect(untilEditor?.option('disabled')).toBe(repeatEndValue !== 'until'); - expect(countEditor?.option('disabled')).toBe(repeatEndValue !== 'count'); - }); - }); - }); - - describe('FrequencyEditor focus', () => { - it('should not be focused when value is changed via API', async () => { - const { POM, scheduler } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [], - views: ['week'], - currentView: 'week', - currentDate: new Date(2021, 2, 25), - }); - - scheduler.showAppointmentPopup(recurringAppointment); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - const frequencyEditor = POM.popup.dxForm.getEditor('recurrencePeriodEditor'); - const frequencyEditorInputElement = POM.popup.getInput('recurrencePeriodEditor'); - - frequencyEditor?.option('value', 'yearly'); - - expect(document.activeElement).not.toBe(frequencyEditorInputElement); - }); - - it('should be focused when value is changed via keyboard', async () => { - const { POM, scheduler, keydown } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [], - views: ['week'], - currentView: 'week', - currentDate: new Date(2021, 2, 25), - }); - - scheduler.showAppointmentPopup(recurringAppointment); - POM.popup.editSeriesButton.click(); - POM.popup.recurrenceSettingsButton.click(); - - const frequencyEditorInputElement = POM.popup.getInput('recurrencePeriodEditor'); - - frequencyEditorInputElement.click(); - jest.useFakeTimers(); - keydown(frequencyEditorInputElement, 'ArrowDown'); - jest.runAllTimers(); - - expect(document.activeElement).toBe(frequencyEditorInputElement); - }); - }); - - it('should set animation offset CSS variable when switching to recurrence form', async () => { - setupSchedulerTestEnvironment({ - height: 600, - classRects: { - 'dx-form': { top: 10 }, - 'dx-scheduler-form-main-group': { top: 60 }, - }, - }); - - const { scheduler, POM } = await createScheduler(getDefaultConfig()); - - scheduler.showAppointmentPopup(); - POM.popup.selectRepeatValue('weekly'); - - const animationTop = POM.popup.dxForm.$element()[0].style.getPropertyValue('--dx-scheduler-animation-top'); - expect(animationTop).toBe('50px'); - }); - }); - - it('recurrence editors with hidden outer label must have editorOptions.labelMode set to hidden (T1318550)', async () => { - const flattenBy = ( - items: T[], - getChildren: (item: T) => T[] | undefined, - ): T[] => items.flatMap((item) => { - const children = getChildren(item); - return children?.length ? flattenBy(children, getChildren) : [item]; - }); - - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - dataSource: [{ ...recurringAppointment }], - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const formItems = POM.popup.dxForm.option('items') ?? []; - const recurrenceGroup = formItems[1] as GroupItem; - const allItems = flattenBy( - recurrenceGroup.items as SimpleItem[], - (i) => (i as unknown as GroupItem).items as SimpleItem[] | undefined, - ); - - const missingLabelMode = allItems - .filter((i) => i.label?.visible === false && i.editorOptions) - .filter((i) => (i.editorOptions as Record).labelMode !== 'hidden'); - - expect(missingLabelMode.length).toEqual(0); - }); - describe('firstDayOfWeek', () => { beforeEach(() => { jest.spyOn(dateLocalization, 'firstDayOfWeekIndex').mockReturnValue(3); diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 1259554a97e3..06cb58a0f722 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -1,12 +1,15 @@ import { afterEach, beforeEach, describe, expect, it, jest, } from '@jest/globals'; +import { loadMessages, locale } from '@js/localization'; +import { fireEvent } from '@testing-library/dom'; import fx from '../../../common/core/animation/fx'; import { createAppointmentPopup, disposeAppointmentPopups, } from '../__tests__/__mock__/create_appointment_popup'; +import { setupSchedulerTestEnvironment } from '../__tests__/__mock__/m_mock_scheduler'; describe('Isolated AppointmentPopup environment', () => { beforeEach(() => { @@ -362,8 +365,7 @@ describe('Isolated AppointmentPopup environment', () => { 'startDateEditor', 'startTimeEditor', 'endDateEditor', 'endTimeEditor', ])('should not close popup on save button click when %s is empty', async (editorName) => { const { popup, POM } = await createAppointmentPopup({ - appointmentData: - { ...commonAppointment }, + appointmentData: { ...commonAppointment }, }); POM.setInputValue(editorName, null); @@ -377,8 +379,7 @@ describe('Isolated AppointmentPopup environment', () => { 'startTimeEditor', 'endDateEditor', 'endTimeEditor', ])('should not close popup on save button click in recurrence form when %s editor is empty', async (editorName) => { const { popup, POM } = await createAppointmentPopup({ - appointmentData: - { ...commonAppointment }, + appointmentData: { ...commonAppointment }, }); POM.setInputValue(editorName, null); @@ -391,8 +392,7 @@ describe('Isolated AppointmentPopup environment', () => { it('should not block save in recurrence form when startDateEditor is empty', async () => { const { POM, callbacks } = await createAppointmentPopup({ - appointmentData: - { ...commonAppointment }, + appointmentData: { ...commonAppointment }, }); POM.setInputValue('startDateEditor', null); @@ -406,4 +406,394 @@ describe('Isolated AppointmentPopup environment', () => { expect(callbacks.onSave).toHaveBeenCalledTimes(1); }); }); + + describe('Recurrence Form', () => { + const recurringAppointment = { + text: 'recurring-app', + startDate: new Date(2017, 4, 1, 9, 30), + endDate: new Date(2017, 4, 1, 11), + recurrenceRule: 'FREQ=DAILY;COUNT=5', + }; + + const baseAppointment = { + text: 'Meeting', + startDate: new Date(2017, 4, 1, 10, 30), + endDate: new Date(2017, 4, 1, 11), + }; + + it('should allow opening recurrence settings when allowUpdating is false', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...recurringAppointment }, + editing: { allowUpdating: false }, + readOnly: true, + }); + + expect(POM.isRecurrenceGroupVisible()).toBe(false); + + POM.recurrenceSettingsButton.click(); + + expect(POM.isRecurrenceGroupVisible()).toBe(true); + }); + + it('should close repeat selectbox popup when navigating to recurrence group via settings button', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...recurringAppointment }, + }); + + const repeatEditor = POM.dxForm.getEditor('repeatEditor'); + POM.getInput('repeatEditor').click(); + + expect(repeatEditor?.option('opened')).toBe(true); + + POM.recurrenceSettingsButton.click(); + + expect(repeatEditor?.option('opened')).toBe(false); + }); + + it('should have disabled week day buttons when allowUpdating is false', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...recurringAppointment, recurrenceRule: 'FREQ=WEEKLY;BYDAY=WE,TU,TH,FR,SA' }, + editing: { allowUpdating: false }, + readOnly: true, + }); + + POM.recurrenceSettingsButton.click(); + + const disabledButtons = POM.recurrenceWeekDayButtons.querySelectorAll('.dx-button.dx-state-disabled'); + + expect(disabledButtons.length).toBe(7); + }); + + it('should show recurrence group when repeat value is selected', async () => { + const { POM } = await createAppointmentPopup(); + + POM.selectRepeatValue('weekly'); + + expect(POM.isMainGroupVisible()).toBe(false); + expect(POM.isRecurrenceGroupVisible()).toBe(true); + }); + + it('should restore main group when back button is clicked', async () => { + const { POM } = await createAppointmentPopup(); + POM.selectRepeatValue('weekly'); + + POM.backButton.click(); + + expect(POM.isMainGroupVisible()).toBe(true); + expect(POM.isRecurrenceGroupVisible()).toBe(false); + }); + + it('should set inert attribute on hidden group when switching forms', async () => { + const { POM } = await createAppointmentPopup(); + + POM.selectRepeatValue('weekly'); + + expect(POM.mainGroup.getAttribute('inert')).toBe('true'); + expect(POM.recurrenceGroup.getAttribute('inert')).toBeNull(); + }); + + it('should adjust popup height when switching to recurrence form', async () => { + const { POM } = await createAppointmentPopup(); + + POM.selectRepeatValue('weekly'); + + expect(typeof POM.component.option('height')).toBe('number'); + }); + + it('should reset popup height to auto when returning to main form', async () => { + const { POM } = await createAppointmentPopup(); + POM.selectRepeatValue('weekly'); + + POM.backButton.click(); + + expect(POM.component.option('height')).toBe('auto'); + }); + + it('should open main form when opening recurring appointment', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: + { ...recurringAppointment }, + }); + + expect(POM.isMainGroupVisible()).toBe(true); + expect(POM.isRecurrenceGroupVisible()).toBe(false); + }); + + describe('State', () => { + it('should have correct input values for appointment with hour frequency', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=HOURLY;INTERVAL=2;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('Hourly'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); + expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Hour(s)'); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + + it('should have correct input values for appointment with daily frequency', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('Daily'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); + expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Day(s)'); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + + it('should have correct input values for appointment with week frequency', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE,FR;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('Weekly'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); + expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Week(s)'); + // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] — MO,WE,FR → positions 1,3,5 + expect(POM.getWeekDaysSelection()).toEqual([false, true, false, true, false, true, false]); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + + it('should have correct input values for appointment with monthly frequency', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=MONTHLY;INTERVAL=2;BYMONTHDAY=1;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('Monthly'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); + expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Month(s)'); + expect(POM.getInputValue('recurrenceDayOfMonthEditor')).toBe('1'); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + + it('should have correct input values for appointment with yearly frequency', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=YEARLY;INTERVAL=2;BYMONTHDAY=1;BYMONTH=5;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('Yearly'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); + expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Year(s)'); + expect(POM.getInputValue('recurrenceDayOfYearDayEditor')).toBe('1'); + expect(POM.getInputValue('recurrenceDayOfYearMonthEditor')).toBe('May'); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + + it('T1325870: should use current locale for recurrence editors after locale change', async () => { + const currentLocale = locale(); + + loadMessages({ + de: { + 'dxScheduler-recurrenceYearly': 'custom yearly', + 'dxScheduler-recurrenceRepeatYearly': 'custom repeat yearly', + }, + }); + locale('de'); + + try { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=YEARLY;INTERVAL=2;BYMONTHDAY=1;BYMONTH=5;COUNT=10' }, + }); + + expect(POM.getInputValue('repeatEditor')).toBe('custom yearly'); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Custom repeat yearly'); + expect(POM.getInputValue('recurrenceDayOfYearMonthEditor')).toBe('Mai'); + } finally { + locale(currentLocale); + } + }); + + it('should have correct input values for appointment with no end', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=DAILY;INTERVAL=2' }, + }); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceRepeatEndEditor')).toBe('never'); + }); + + it('should have correct input values for appointment with end by date', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=DAILY;INTERVAL=2;UNTIL=20170601T000000Z' }, + }); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceRepeatEndEditor')).toBe('until'); + expect(POM.getInputValue('recurrenceEndUntilEditor')).toBe('6/1/2017'); + }); + + it('should have correct input values for appointment with end by count', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=10' }, + }); + + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceRepeatEndEditor')).toBe('count'); + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); + }); + }); + + describe('Repeat End Values Preservation', () => { + it('should preserve count value when switching between recurrence types', async () => { + const testCount = 15; + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment }, + }); + + POM.selectRepeatValue('daily'); + + POM.setInputValue('recurrenceRepeatEndEditor', 'count'); + POM.setInputValue('recurrenceEndCountEditor', testCount); + + POM.backButton.click(); + + POM.selectRepeatValue('weekly'); + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceEndCountEditor')).toBe(`${testCount} occurrence(s)`); + }); + + it('should preserve until value when switching between recurrence types', async () => { + const testUntilDate = new Date(2017, 5, 16); + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment }, + }); + + POM.selectRepeatValue('daily'); + + POM.setInputValue('recurrenceRepeatEndEditor', 'until'); + POM.setInputValue('recurrenceEndUntilEditor', testUntilDate); + + POM.backButton.click(); + + POM.selectRepeatValue('weekly'); + POM.recurrenceSettingsButton.click(); + + expect(POM.getInputValue('recurrenceEndUntilEditor')).toBe('6/16/2017'); + }); + }); + + describe('Repeat End Editors Disabled State', () => { + it.each([ + ['never', 'FREQ=DAILY'], + ['until', 'FREQ=DAILY;UNTIL=20170615T000000Z'], + ['count', 'FREQ=DAILY;COUNT=10'], + ] as ['never' | 'until' | 'count', string][])( + 'should set correct disabled state when repeatEnd is %s', + async (repeatEndValue, recurrenceRule) => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule }, + }); + + POM.recurrenceSettingsButton.click(); + + const untilEditor = POM.dxForm.getEditor('recurrenceEndUntilEditor'); + const countEditor = POM.dxForm.getEditor('recurrenceEndCountEditor'); + + expect(untilEditor?.option('disabled')).toBe(repeatEndValue !== 'until'); + expect(countEditor?.option('disabled')).toBe(repeatEndValue !== 'count'); + }, + ); + }); + + describe('FrequencyEditor focus', () => { + afterEach(() => { + jest.useRealTimers(); + }); + + it('should not be focused when value is changed via API', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: + { ...recurringAppointment }, + }); + POM.recurrenceSettingsButton.click(); + + const frequencyEditor = POM.dxForm.getEditor('recurrencePeriodEditor'); + const frequencyEditorInputElement = POM.getInput('recurrencePeriodEditor'); + + frequencyEditor?.option('value', 'yearly'); + + expect(document.activeElement).not.toBe(frequencyEditorInputElement); + }); + + it('should be focused when value is changed via keyboard', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: + { ...recurringAppointment }, + }); + POM.recurrenceSettingsButton.click(); + + const frequencyEditorInputElement = POM.getInput('recurrencePeriodEditor'); + + frequencyEditorInputElement.click(); + jest.useFakeTimers(); + fireEvent.keyDown(frequencyEditorInputElement, { key: 'ArrowDown' }); + jest.runAllTimers(); + + expect(document.activeElement).toBe(frequencyEditorInputElement); + }); + }); + + it('should set animation offset CSS variable when switching to recurrence form', async () => { + setupSchedulerTestEnvironment({ + height: 600, + classRects: { + 'dx-form': { top: 10 }, + 'dx-scheduler-form-main-group': { top: 60 }, + }, + }); + + const { POM } = await createAppointmentPopup(); + POM.selectRepeatValue('weekly'); + + const animationTop = POM.dxForm.$element()[0].style.getPropertyValue('--dx-scheduler-animation-top'); + expect(animationTop).toBe('50px'); + }); + + it('T1318550: editors with hidden outer label must have labelMode: hidden', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=YEARLY;BYMONTHDAY=1;BYMONTH=5' }, + }); + POM.recurrenceSettingsButton.click(); + + const editorsWithHiddenLabel = [ + 'recurrencePeriodEditor', + 'recurrenceRepeatEndEditor', + 'recurrenceEndUntilEditor', + 'recurrenceEndCountEditor', + 'recurrenceDayOfYearDayEditor', + ]; + + editorsWithHiddenLabel.forEach((name) => { + expect(POM.dxForm.getEditor(name)?.option('labelMode')).toBe('hidden'); + }); + }); + }); }); From 4801c81f774723d55e16789174baf1df23bae1f5 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Wed, 27 May 2026 10:39:52 +0400 Subject: [PATCH 3/7] test(scheduler): migrate Customize form items tests to isolated env --- .../appointment_popup.integration.test.ts | 392 +----------------- .../appointment_popup.test.ts | 130 +++++- 2 files changed, 125 insertions(+), 397 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts index 1f61902a4719..ea5e8a50ea10 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts @@ -4,7 +4,7 @@ import { import dateLocalization from '@js/common/core/localization/date'; import { CustomStore } from '@js/common/data/custom_store'; import $ from '@js/core/renderer'; -import type { GroupItem, Item as FormItem } from '@js/ui/form'; +import type { GroupItem } from '@js/ui/form'; import type { ToolbarItem } from '@js/ui/popup'; import { toMilliseconds } from '@ts/utils/toMilliseconds'; @@ -1949,393 +1949,3 @@ describe('Timezone Editors', () => { it.todo('timeZone editor should have correct display value for timezones with different offsets'); it.todo('dataSource of timezoneEditor should be filtered'); }); - -describe('Customize form items', () => { - beforeEach(() => { - fx.off = true; - setupSchedulerTestEnvironment({ height: 600 }); - }); - - afterEach(() => { - const $scheduler = $(document.querySelector(`.${CLASSES.scheduler}`)); - // @ts-expect-error - $scheduler.dxScheduler('dispose'); - document.body.innerHTML = ''; - fx.off = false; - jest.useRealTimers(); - }); - - describe('Basic form customization', () => { - it('should use default form when editing.items is not set', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems).toBeDefined(); - expect(formItems?.length).toBeGreaterThan(0); - }); - - it('should show empty form when editing.items is empty array', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems?.length ?? 0).toBe(0); - }); - - it('should show mainGroup when specified in string array', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: ['mainGroup'], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems?.length).toBe(1); - expect(formItems?.[0]?.name).toBe('mainGroup'); - }); - - it('should hide group when visible is false', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ name: 'mainGroup', visible: false }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems?.length).toBe(1); - expect(formItems?.[0]?.visible).toBe(false); - }); - - it('should show group when visible is true', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ name: 'mainGroup', visible: true }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems?.length).toBe(1); - expect(formItems?.[0]?.visible).toBe(true); - }); - - it('should filter children when items array is specified', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ - name: 'mainGroup', - visible: true, - items: ['subjectGroup'], - }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - const mainGroup = formItems?.[0] as GroupItem; - - expect(formItems?.length).toBe(1); - expect(mainGroup?.items?.length).toBe(1); - expect(mainGroup?.items?.[0]?.name).toBe('subjectGroup'); - }); - - it('should handle non-existent groups gracefully', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: ['nonExistentGroup'], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - - expect(formItems?.length ?? 0).toBe(1); - }); - - it('should call custom onContentReady and onInitialized and preserving default', async () => { - const onContentReady = jest.fn(); - const onInitialized = jest.fn(); - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - ...{ - editing: { - form: { - onContentReady, - onInitialized, - }, - }, - }, - }); - - scheduler.showAppointmentPopup(); - - POM.popup.selectRepeatValue('weekly'); - - expect(POM.popup.isMainGroupVisible()).toBe(false); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); - - expect(onContentReady).toHaveBeenCalled(); - expect(onInitialized).toHaveBeenCalled(); - }); - }); - - it('should call custom onContentReady and onInitialized and preserving default', async () => { - const onContentReady = jest.fn(); - const onInitialized = jest.fn(); - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - ...{ - editing: { - form: { - onContentReady, - onInitialized, - }, - }, - }, - }); - - scheduler.showAppointmentPopup(); - - POM.popup.selectRepeatValue('weekly'); - - expect(POM.popup.isMainGroupVisible()).toBe(false); - expect(POM.popup.isRecurrenceGroupVisible()).toBe(true); - - expect(onContentReady).toHaveBeenCalled(); - expect(onInitialized).toHaveBeenCalled(); - }); - - describe('Form customization with editing.items', () => { - it('should handle empty items array', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBe(0); - }); - - it('should handle string array configuration', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: ['mainGroup'], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBe(1); - expect((formItems?.[0] as GroupItem)?.name).toBe('mainGroup'); - }); - - it('should handle object configuration with visible false', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ name: 'mainGroup', visible: false }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBe(1); - expect(formItems?.[0]?.visible).toBe(false); - }); - - it('should handle object configuration with custom items', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ - name: 'mainGroup', - items: ['subjectGroup', 'dateGroup'], - }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - const mainGroup = formItems?.[0] as GroupItem; - expect(mainGroup?.items?.length).toBe(2); - expect((mainGroup?.items?.[0] as GroupItem)?.name).toBe('subjectGroup'); - expect((mainGroup?.items?.[1] as GroupItem)?.name).toBe('dateGroup'); - }); - - it('should handle non-existent group names', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: ['nonExistentGroup'], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBe(1); - }); - - it('should handle undefined items', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: undefined, - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBeGreaterThan(0); - }); - - it('should handle mixed configurations', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [ - 'mainGroup', - { name: 'mainGroup', visible: false }, - ], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - expect(formItems?.length).toBe(2); - expect((formItems?.[0] as any)?.name).toBe('mainGroup'); - expect((formItems?.[1] as any)?.name).toBe('mainGroup'); - expect(formItems?.[1]?.visible).toBe(false); - }); - - it('should handle empty items array in object config', async () => { - const { scheduler, POM } = await createScheduler({ - ...getDefaultConfig(), - editing: { - allowAdding: true, - allowUpdating: true, - form: { - items: [{ - name: 'mainGroup', - items: [], - }], - }, - }, - }); - - scheduler.showAppointmentPopup(commonAppointment); - - const { dxForm: form } = POM.popup; - const formItems = form.option('items') as FormItem[]; - const mainGroup = formItems?.[0] as any; - expect(mainGroup?.items?.length).toBe(0); - }); - }); -}); diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 06cb58a0f722..de61f0fa091e 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -428,11 +428,14 @@ describe('Isolated AppointmentPopup environment', () => { readOnly: true, }); - expect(POM.isRecurrenceGroupVisible()).toBe(false); + const visibleBefore = POM.isRecurrenceGroupVisible(); POM.recurrenceSettingsButton.click(); - expect(POM.isRecurrenceGroupVisible()).toBe(true); + const visibleAfter = POM.isRecurrenceGroupVisible(); + + expect(visibleBefore).toBe(false); + expect(visibleAfter).toBe(true); }); it('should close repeat selectbox popup when navigating to recurrence group via settings button', async () => { @@ -443,11 +446,14 @@ describe('Isolated AppointmentPopup environment', () => { const repeatEditor = POM.dxForm.getEditor('repeatEditor'); POM.getInput('repeatEditor').click(); - expect(repeatEditor?.option('opened')).toBe(true); + const openedBefore = repeatEditor?.option('opened'); POM.recurrenceSettingsButton.click(); - expect(repeatEditor?.option('opened')).toBe(false); + const openedAfter = repeatEditor?.option('opened'); + + expect(openedBefore).toBe(true); + expect(openedAfter).toBe(false); }); it('should have disabled week day buttons when allowUpdating is false', async () => { @@ -511,8 +517,7 @@ describe('Isolated AppointmentPopup environment', () => { it('should open main form when opening recurring appointment', async () => { const { POM } = await createAppointmentPopup({ - appointmentData: - { ...recurringAppointment }, + appointmentData: { ...recurringAppointment }, }); expect(POM.isMainGroupVisible()).toBe(true); @@ -796,4 +801,117 @@ describe('Isolated AppointmentPopup environment', () => { }); }); }); + + describe('Customize form items', () => { + const appointment = { + text: 'Meeting', + startDate: new Date(2017, 4, 9, 9, 30), + endDate: new Date(2017, 4, 9, 11), + }; + + it('should use default form items when editing.form.items is not configured', async () => { + const { POM } = await createAppointmentPopup({ appointmentData: { ...appointment } }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems.length).toBeGreaterThan(0); + }); + + it('should produce empty form when editing.form.items is empty array', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: [] } }, + }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems.length).toBe(0); + }); + + it('should resolve named group when specified as string in items array', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: ['mainGroup'] } }, + }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems.length).toBe(1); + expect(formItems[0]?.name).toBe('mainGroup'); + }); + + it.each([true, false])('should set group visibility to %s when specified in object config', async (visible) => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: [{ name: 'mainGroup', visible }] } }, + }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems[0]?.visible).toBe(visible); + }); + + it('should filter group children to specified named items', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: [{ name: 'mainGroup', items: ['subjectGroup', 'dateGroup'] }] } }, + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const mainGroup = (POM.dxForm.option('items') ?? [])[0] as any; + + expect(mainGroup?.items?.length).toBe(2); + expect(mainGroup?.items?.[0]?.name).toBe('subjectGroup'); + expect(mainGroup?.items?.[1]?.name).toBe('dateGroup'); + }); + + it('should produce empty children when items array in group config is empty', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: [{ name: 'mainGroup', items: [] }] } }, + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const mainGroup = (POM.dxForm.option('items') ?? [])[0] as any; + + expect(mainGroup?.items?.length).toBe(0); + }); + + it('should create placeholder item for non-existent group name', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: ['nonExistentGroup'] } }, + }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems.length).toBe(1); + }); + + it('should handle mixed string and object items', async () => { + const { POM } = await createAppointmentPopup({ + appointmentData: { ...appointment }, + editing: { form: { items: ['mainGroup', { name: 'mainGroup', visible: false }] } }, + }); + + const formItems = POM.dxForm.option('items') ?? []; + + expect(formItems.length).toBe(2); + expect(formItems[0]?.name).toBe('mainGroup'); + expect(formItems[1]?.name).toBe('mainGroup'); + expect(formItems[1]?.visible).toBe(false); + }); + + it('should call custom onContentReady and onInitialized form callbacks', async () => { + const onContentReady = jest.fn(); + const onInitialized = jest.fn(); + + await createAppointmentPopup({ + editing: { form: { onContentReady, onInitialized } }, + }); + + expect(onContentReady).toHaveBeenCalled(); + expect(onInitialized).toHaveBeenCalled(); + }); + }); }); From aa1929c2730726dc48d00312f57e992fcc9b2643 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Wed, 27 May 2026 10:51:03 +0400 Subject: [PATCH 4/7] test(scheduler): fix AAA violations and formatting in popup tests --- .../appointment_popup/appointment_popup.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index de61f0fa091e..3b309831c778 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -398,11 +398,12 @@ describe('Isolated AppointmentPopup environment', () => { POM.setInputValue('startDateEditor', null); POM.selectRepeatValue('daily'); - expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/9/2017'); + const recurrenceStartDate = POM.getInputValue('recurrenceStartDateEditor'); POM.saveButton.click(); await Promise.resolve(); + expect(recurrenceStartDate).toBe('5/9/2017'); expect(callbacks.onSave).toHaveBeenCalledTimes(1); }); }); @@ -567,7 +568,6 @@ describe('Isolated AppointmentPopup environment', () => { expect(POM.getInputValue('recurrenceStartDateEditor')).toBe('5/1/2017'); expect(POM.getInputValue('recurrenceCountEditor')).toBe('2'); expect(POM.getInputValue('recurrencePeriodEditor')).toBe('Week(s)'); - // [Sun, Mon, Tue, Wed, Thu, Fri, Sat] — MO,WE,FR → positions 1,3,5 expect(POM.getWeekDaysSelection()).toEqual([false, true, false, true, false, true, false]); expect(POM.getInputValue('recurrenceEndCountEditor')).toBe('10 occurrence(s)'); }); @@ -735,9 +735,9 @@ describe('Isolated AppointmentPopup environment', () => { it('should not be focused when value is changed via API', async () => { const { POM } = await createAppointmentPopup({ - appointmentData: - { ...recurringAppointment }, + appointmentData: { ...recurringAppointment }, }); + POM.recurrenceSettingsButton.click(); const frequencyEditor = POM.dxForm.getEditor('recurrencePeriodEditor'); @@ -750,9 +750,9 @@ describe('Isolated AppointmentPopup environment', () => { it('should be focused when value is changed via keyboard', async () => { const { POM } = await createAppointmentPopup({ - appointmentData: - { ...recurringAppointment }, + appointmentData: { ...recurringAppointment }, }); + POM.recurrenceSettingsButton.click(); const frequencyEditorInputElement = POM.getInput('recurrencePeriodEditor'); @@ -786,6 +786,7 @@ describe('Isolated AppointmentPopup environment', () => { const { POM } = await createAppointmentPopup({ appointmentData: { ...baseAppointment, recurrenceRule: 'FREQ=YEARLY;BYMONTHDAY=1;BYMONTH=5' }, }); + POM.recurrenceSettingsButton.click(); const editorsWithHiddenLabel = [ From 181c1aaecf7bcdf9823291c1cdfcd6e96433cafd Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Wed, 27 May 2026 11:22:23 +0400 Subject: [PATCH 5/7] test(scheduler): address review comments on popup validation tests --- .../appointment_popup.integration.test.ts | 18 +++++++ .../appointment_popup.test.ts | 50 ++++++++++++------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts index ea5e8a50ea10..d2b7fb7f3bd6 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.integration.test.ts @@ -342,6 +342,24 @@ describe('Appointment Form', () => { }); }); + describe('Validation', () => { + it('should close popup on save when startDateEditor is empty in recurrence form', async () => { + const { scheduler, POM } = await createScheduler(getDefaultConfig()); + + scheduler.showAppointmentPopup({ ...commonAppointment }); + + POM.popup.setInputValue('startDateEditor', null); + POM.popup.selectRepeatValue('daily'); + + expect(POM.popup.getInputValue('recurrenceStartDateEditor')).toBe('5/9/2017'); + + POM.popup.saveButton.click(); + await Promise.resolve(); + + expect(POM.isPopupVisible()).toBe(false); + }); + }); + describe('State', () => { it('should have correct editor values when opening for empty date cell - 1', async () => { const { POM } = await createScheduler({ diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 3b309831c778..87caf93117b0 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -3,6 +3,7 @@ import { } from '@jest/globals'; import { loadMessages, locale } from '@js/localization'; import { fireEvent } from '@testing-library/dom'; +import DOMComponent from '@ts/core/widget/dom_component'; import fx from '../../../common/core/animation/fx'; import { @@ -363,8 +364,8 @@ describe('Isolated AppointmentPopup environment', () => { it.each([ 'startDateEditor', 'startTimeEditor', 'endDateEditor', 'endTimeEditor', - ])('should not close popup on save button click when %s is empty', async (editorName) => { - const { popup, POM } = await createAppointmentPopup({ + ])('should block save when %s is empty', async (editorName) => { + const { POM, callbacks } = await createAppointmentPopup({ appointmentData: { ...commonAppointment }, }); @@ -372,13 +373,13 @@ describe('Isolated AppointmentPopup environment', () => { POM.saveButton.click(); await Promise.resolve(); - expect(popup.visible).toBe(true); + expect(callbacks.onSave).not.toHaveBeenCalled(); }); it.each([ 'startTimeEditor', 'endDateEditor', 'endTimeEditor', - ])('should not close popup on save button click in recurrence form when %s editor is empty', async (editorName) => { - const { popup, POM } = await createAppointmentPopup({ + ])('should block save in recurrence form when %s is empty', async (editorName) => { + const { POM, callbacks } = await createAppointmentPopup({ appointmentData: { ...commonAppointment }, }); @@ -387,7 +388,7 @@ describe('Isolated AppointmentPopup environment', () => { POM.saveButton.click(); await Promise.resolve(); - expect(popup.visible).toBe(true); + expect(callbacks.onSave).not.toHaveBeenCalled(); }); it('should not block save in recurrence form when startDateEditor is empty', async () => { @@ -400,10 +401,11 @@ describe('Isolated AppointmentPopup environment', () => { const recurrenceStartDate = POM.getInputValue('recurrenceStartDateEditor'); + expect(recurrenceStartDate).toBe('5/9/2017'); + POM.saveButton.click(); await Promise.resolve(); - expect(recurrenceStartDate).toBe('5/9/2017'); expect(callbacks.onSave).toHaveBeenCalledTimes(1); }); }); @@ -767,19 +769,31 @@ describe('Isolated AppointmentPopup environment', () => { }); it('should set animation offset CSS variable when switching to recurrence form', async () => { - setupSchedulerTestEnvironment({ - height: 600, - classRects: { - 'dx-form': { top: 10 }, - 'dx-scheduler-form-main-group': { top: 60 }, - }, - }); + const originalGetComputedStyle = window.getComputedStyle; + const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect; + const originalGetClientRects = Element.prototype.getClientRects; + const originalIsVisible = DOMComponent.prototype._isVisible; + + try { + setupSchedulerTestEnvironment({ + height: 600, + classRects: { + 'dx-form': { top: 10 }, + 'dx-scheduler-form-main-group': { top: 60 }, + }, + }); - const { POM } = await createAppointmentPopup(); - POM.selectRepeatValue('weekly'); + const { POM } = await createAppointmentPopup(); + POM.selectRepeatValue('weekly'); - const animationTop = POM.dxForm.$element()[0].style.getPropertyValue('--dx-scheduler-animation-top'); - expect(animationTop).toBe('50px'); + const animationTop = POM.dxForm.$element()[0].style.getPropertyValue('--dx-scheduler-animation-top'); + expect(animationTop).toBe('50px'); + } finally { + window.getComputedStyle = originalGetComputedStyle; + Element.prototype.getBoundingClientRect = originalGetBoundingClientRect; + Element.prototype.getClientRects = originalGetClientRects; + DOMComponent.prototype._isVisible = originalIsVisible; + } }); it('T1318550: editors with hidden outer label must have labelMode: hidden', async () => { From 7f872433cdcbbd7fe2e71bb33a3a8be42c270fc9 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Wed, 27 May 2026 13:10:32 +0400 Subject: [PATCH 6/7] fix(scheduler): remove any type --- .../scheduler/appointment_popup/appointment_popup.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 87caf93117b0..1d88fcbdc504 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, jest, } from '@jest/globals'; import { loadMessages, locale } from '@js/localization'; +import type { GroupItem } from '@js/ui/form'; import { fireEvent } from '@testing-library/dom'; import DOMComponent from '@ts/core/widget/dom_component'; @@ -872,8 +873,7 @@ describe('Isolated AppointmentPopup environment', () => { editing: { form: { items: [{ name: 'mainGroup', items: ['subjectGroup', 'dateGroup'] }] } }, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mainGroup = (POM.dxForm.option('items') ?? [])[0] as any; + const mainGroup = (POM.dxForm.option('items') ?? [])[0] as GroupItem; expect(mainGroup?.items?.length).toBe(2); expect(mainGroup?.items?.[0]?.name).toBe('subjectGroup'); @@ -886,8 +886,7 @@ describe('Isolated AppointmentPopup environment', () => { editing: { form: { items: [{ name: 'mainGroup', items: [] }] } }, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mainGroup = (POM.dxForm.option('items') ?? [])[0] as any; + const mainGroup = (POM.dxForm.option('items') ?? [])[0] as GroupItem; expect(mainGroup?.items?.length).toBe(0); }); From f3227c56981116d112b6074d06fe9893c50341c3 Mon Sep 17 00:00:00 2001 From: Maksim Zakharov <251575087+bit-byte0@users.noreply.github.com> Date: Wed, 27 May 2026 16:47:16 +0400 Subject: [PATCH 7/7] test(scheduler): strengthen inert and custom callback assertions --- .../appointment_popup/appointment_popup.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts index 1d88fcbdc504..4d93cde96b41 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts @@ -500,6 +500,11 @@ describe('Isolated AppointmentPopup environment', () => { expect(POM.mainGroup.getAttribute('inert')).toBe('true'); expect(POM.recurrenceGroup.getAttribute('inert')).toBeNull(); + + POM.backButton.click(); + + expect(POM.mainGroup.getAttribute('inert')).toBeNull(); + expect(POM.recurrenceGroup.getAttribute('inert')).toBe('true'); }); it('should adjust popup height when switching to recurrence form', async () => { @@ -920,12 +925,17 @@ describe('Isolated AppointmentPopup environment', () => { const onContentReady = jest.fn(); const onInitialized = jest.fn(); - await createAppointmentPopup({ + const { POM } = await createAppointmentPopup({ editing: { form: { onContentReady, onInitialized } }, }); expect(onContentReady).toHaveBeenCalled(); expect(onInitialized).toHaveBeenCalled(); + + POM.selectRepeatValue('weekly'); + + expect(POM.isMainGroupVisible()).toBe(false); + expect(POM.isRecurrenceGroupVisible()).toBe(true); }); }); });