diff --git a/src/test/calendar_icon.test.tsx b/src/test/calendar_icon.test.tsx index 27f5c241e..1235d0c04 100644 --- a/src/test/calendar_icon.test.tsx +++ b/src/test/calendar_icon.test.tsx @@ -4,6 +4,7 @@ import React from "react"; import CalendarIcon from "../calendar_icon"; import { IconParkSolidApplication } from "./helper_components/calendar_icon"; +import { safeQuerySelector } from "./test_utils"; describe("CalendarIcon", () => { let onClickMock: jest.Mock; @@ -38,9 +39,11 @@ describe("CalendarIcon", () => { it("should fire onClick event when the icon is clicked", () => { const { container } = render(); - const icon = container.querySelector("svg.react-datepicker__calendar-icon"); - expect(icon).not.toBeNull(); - fireEvent.click(icon ?? new Element()); + const icon = safeQuerySelector( + container, + "svg.react-datepicker__calendar-icon", + ); + fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -50,9 +53,8 @@ describe("CalendarIcon", () => { , ); - const icon = container.querySelector("i.fa-example-icon"); - expect(icon).not.toBeNull(); - fireEvent.click(icon ?? new Element()); + const icon = safeQuerySelector(container, "i.fa-example-icon"); + fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -75,8 +77,11 @@ describe("CalendarIcon", () => { />, ); - const icon = container.querySelector("svg.react-datepicker__calendar-icon"); - fireEvent.click(icon ?? new Element()); + const icon = safeQuerySelector( + container, + "svg.react-datepicker__calendar-icon", + ); + fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); expect(onClickCustomIcon).toHaveBeenCalledTimes(1); diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index 0e55b3b83..f0912d721 100644 --- a/src/test/calendar_test.test.tsx +++ b/src/test/calendar_test.test.tsx @@ -31,7 +31,12 @@ import { } from "../date_utils"; import DatePicker from "../index"; -import { getKey } from "./test_utils"; +import { + getKey, + SafeElementWrapper, + safeQuerySelector, + safeQuerySelectorAll, +} from "./test_utils"; import type { ReactDatePickerCustomHeaderProps } from "../calendar"; import type { Locale } from "../date_utils"; @@ -229,8 +234,9 @@ describe("Calendar", () => { it("should render month and year as a header of datepicker by default", () => { const { calendar } = getCalendar(); - const header = calendar.querySelector("h2.react-datepicker__current-month"); - expect(header).not.toBeNull(); + expect(() => + calendar.querySelector("h2.react-datepicker__current-month"), + ).not.toThrow(); }); it("should not show the year dropdown menu by default", () => { @@ -652,8 +658,7 @@ describe("Calendar", () => { }); const selected = newDate(instance?.state.date); - const prevMonth = - calendar.querySelector(".prevMonth") ?? new HTMLElement(); + const prevMonth = safeQuerySelector(calendar, ".prevMonth"); fireEvent.click(prevMonth); @@ -668,8 +673,7 @@ describe("Calendar", () => { }); const selected = newDate(instance?.state.date); - const nextMonth = - calendar.querySelector(".nextMonth") ?? new HTMLElement(); + const nextMonth = safeQuerySelector(calendar, ".nextMonth"); fireEvent.click(nextMonth); @@ -717,8 +721,7 @@ describe("Calendar", () => { renderCustomHeader, }); - const monthSelect = - calendar.querySelector(".month-select") ?? new HTMLElement(); + const monthSelect = safeQuerySelector(calendar, ".month-select"); fireEvent.change(monthSelect, { target: { value: 4 } }); const selected = newDate(instance?.state.date); @@ -731,8 +734,7 @@ describe("Calendar", () => { renderCustomHeader, }); - const yearSelect = - calendar.querySelector(".year-select") ?? new HTMLElement(); + const yearSelect = safeQuerySelector(calendar, ".year-select"); fireEvent.change(yearSelect, { target: { value: 2017 } }); @@ -877,13 +879,15 @@ describe("Calendar", () => { showDisabledMonthNavigation: true, onMonthChange: onMonthChangeSpy, }); - const prevNavigationButton = - calendar.querySelector(".react-datepicker__navigation--previous") ?? - new HTMLElement(); + const prevNavigationButton = safeQuerySelector( + calendar, + ".react-datepicker__navigation--previous", + ); - const nextNavigationButton = - calendar.querySelector(".react-datepicker__navigation--next") ?? - new HTMLElement(); + const nextNavigationButton = safeQuerySelector( + calendar, + ".react-datepicker__navigation--next", + ); fireEvent.click(prevNavigationButton); @@ -902,13 +906,15 @@ describe("Calendar", () => { showDisabledMonthNavigation: true, onMonthChange: onMonthChangeSpy, }); - const prevNavigationButton = - calendar.querySelector(".react-datepicker__navigation--previous") ?? - new HTMLElement(); + const prevNavigationButton = safeQuerySelector( + calendar, + ".react-datepicker__navigation--previous", + ); - const nextNavigationButton = - calendar.querySelector(".react-datepicker__navigation--next") ?? - new HTMLElement(); + const nextNavigationButton = safeQuerySelector( + calendar, + ".react-datepicker__navigation--next", + ); fireEvent.click(prevNavigationButton); fireEvent.click(nextNavigationButton); @@ -996,9 +1002,10 @@ describe("Calendar", () => { it("should set the date when pressing todayButton", () => { const { calendar, instance } = getCalendar({ todayButton: "Vandaag" }); - const todayButton = - calendar.querySelector(".react-datepicker__today-button") ?? - new HTMLElement(); + const todayButton = safeQuerySelector( + calendar, + ".react-datepicker__today-button", + ); fireEvent.click(todayButton); expect(isSameDay(instance?.state.date, newDate())).toBeTruthy(); }); @@ -1031,8 +1038,7 @@ describe("Calendar", () => { />, ); - const day = - container.querySelector(".react-datepicker__day") ?? new HTMLElement(); + const day = safeQuerySelector(container, ".react-datepicker__day"); fireEvent.mouseEnter(day); expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith( @@ -1054,8 +1060,7 @@ describe("Calendar", () => { />, ); - const day = - container.querySelector(".react-datepicker__day") ?? new HTMLElement(); + const day = safeQuerySelector(container, ".react-datepicker__day"); fireEvent.pointerEnter(day); expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith( @@ -1094,11 +1099,11 @@ describe("Calendar", () => { onSelect={() => {}} />, ); - const month = container.querySelector(".react-datepicker__month"); + const month = safeQuerySelector(container, ".react-datepicker__month"); expect( month?.classList.contains("react-datepicker__month--selecting-range"), ).toBeTruthy(); - fireEvent.mouseLeave(month ?? new HTMLElement()); + fireEvent.mouseLeave(month); expect( container @@ -1153,10 +1158,12 @@ describe("Calendar", () => { }} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - const previousButton = - container.querySelector(".react-datepicker__navigation--previous") ?? - new HTMLElement(); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const previousButton = safeQuerySelector( + container, + ".react-datepicker__navigation--previous", + ); fireEvent.click(previousButton); expect(date).not.toBeNull(); @@ -1175,11 +1182,14 @@ describe("Calendar", () => { }} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - const nextButton = container.querySelector( + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const nextButton = safeQuerySelector( + container, ".react-datepicker__navigation--next", ); - fireEvent.click(nextButton ?? new HTMLElement()); + fireEvent.click(nextButton); expect(date).not.toBeNull(); expect(formatDate(date!, "dd.MM.yyyy")).toBe(expectedDate); }); @@ -1196,11 +1206,14 @@ describe("Calendar", () => { }} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - const previousButton = container.querySelector( + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const previousButton = safeQuerySelector( + container, ".react-datepicker__navigation--previous", ); - fireEvent.click(previousButton ?? new HTMLElement()); + fireEvent.click(previousButton); expect(date).not.toBeNull(); expect(formatDate(date!, "dd.MM.yyyy")).toBe(expectedDate); }); @@ -1216,7 +1229,7 @@ describe("Calendar", () => { />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.focus(input); expect(onCalendarOpen).toHaveBeenCalled(); @@ -1246,28 +1259,31 @@ describe("Calendar", () => { }); it("calls onMonthChange prop when previous month button clicked", () => { - const select = - calendar.querySelector(".react-datepicker__navigation--previous") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__navigation--previous", + ); fireEvent.click(select); expect(onMonthChangeSpy).toHaveBeenCalled(); }); it("calls onMonthChange prop when next month button clicked", () => { - const select = - calendar.querySelector(".react-datepicker__navigation--next") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__navigation--next", + ); fireEvent.click(select); expect(onMonthChangeSpy).toHaveBeenCalled(); }); it("calls onMonthChange prop when month changed from month dropdown", () => { - const select = - calendar - .querySelector(".react-datepicker__month-dropdown-container") - ?.querySelector("select") ?? new HTMLElement(); + const select = new SafeElementWrapper(calendar) + .safeQuerySelector(".react-datepicker__month-dropdown-container") + .safeQuerySelector("select") + .getElement(); + fireEvent.change(select, { target: { value: Array.from( @@ -1299,10 +1315,11 @@ describe("Calendar", () => { }); it("calls onYearChange prop when year changed from year dropdown", () => { - const select = - calendar - .querySelector(".react-datepicker__year-dropdown-container") - ?.querySelector("select") ?? new HTMLElement(); + const yearDropdownContainer = safeQuerySelector( + calendar, + ".react-datepicker__year-dropdown-container", + ); + const select = safeQuerySelector(yearDropdownContainer, "select"); fireEvent.change(select, { target: { value: Array.from( @@ -1351,12 +1368,20 @@ describe("Calendar", () => { it("calls onYearChange prop when selection is changed from month-year dropdown", () => { const { calendar } = renderCalendar(); - const select = - calendar - .querySelector(".react-datepicker__month-year-dropdown-container") - ?.querySelector("select") ?? new HTMLElement(); - const option = - select?.querySelectorAll("option")[3] ?? new HTMLOptionElement(); + const monthYearDropdownContainer = safeQuerySelector( + calendar, + ".react-datepicker__month-year-dropdown-container", + ); + + const select = safeQuerySelector(monthYearDropdownContainer, "select"); + const minMonthYearOptionsLen = 4; + const options = safeQuerySelectorAll( + select, + "option", + minMonthYearOptionsLen, + ); + + const option = options[3]!; fireEvent.change(select, { target: { value: option.value, @@ -1368,12 +1393,18 @@ describe("Calendar", () => { it("calls onMonthChange prop when selection is changed from month-year dropdown", () => { const { calendar } = renderCalendar(); - const select = - calendar - .querySelector(".react-datepicker__month-year-dropdown-container") - ?.querySelector("select") ?? new HTMLElement(); - const option = - select.querySelectorAll("option")[3] ?? new HTMLOptionElement(); + + const monthYearDropdownContainer = safeQuerySelector( + calendar, + ".react-datepicker__month-year-dropdown-container", + ); + const select = safeQuerySelector(monthYearDropdownContainer, "select"); + const options = safeQuerySelectorAll(select, "option"); + const option = options[3]; + if (!option) { + throw new Error("option is undefined"); + } + fireEvent.change(select, { target: { value: option.value, @@ -1407,36 +1438,40 @@ describe("Calendar", () => { }); it("calls onDropdownFocus prop when year select is focused", () => { - const select = - calendar.querySelector(".react-datepicker__year-select") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__year-select", + ); fireEvent.focus(select); expect(onDropdownFocusSpy).toHaveBeenCalled(); }); it("calls onDropdownFocus prop when month select is focused", () => { - const select = - calendar.querySelector(".react-datepicker__month-select") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__month-select", + ); fireEvent.focus(select); expect(onDropdownFocusSpy).toHaveBeenCalled(); }); it("calls onDropdownFocus prop when year-month select is focused", () => { - const select = - calendar.querySelector(".react-datepicker__month-year-select") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__month-year-select", + ); fireEvent.focus(select); expect(onDropdownFocusSpy).toHaveBeenCalled(); }); it("does not call onDropdownFocus prop when the dropdown container div is focused", () => { - const select = - calendar.querySelector(".react-datepicker__header__dropdown") ?? - new HTMLElement(); + const select = safeQuerySelector( + calendar, + ".react-datepicker__header__dropdown", + ); fireEvent.focus(select); expect(onDropdownFocusSpy).toHaveBeenCalledTimes(0); @@ -2272,10 +2307,12 @@ describe("Calendar", () => { onKeyDown={onKeyDownSpy} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - const prevMonthButton = - container.querySelector(".react-datepicker__navigation--previous") ?? - new HTMLElement(); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const prevMonthButton = safeQuerySelector( + container, + ".react-datepicker__navigation--previous", + ); fireEvent.keyDown(prevMonthButton, getKey(KeyType.Tab)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -2288,10 +2325,12 @@ describe("Calendar", () => { onKeyDown={onKeyDownSpy} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - const nextMonthButton = - container.querySelector(".react-datepicker__navigation--next") ?? - new HTMLElement(); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const nextMonthButton = safeQuerySelector( + container, + ".react-datepicker__navigation--next", + ); fireEvent.keyDown(nextMonthButton, getKey(KeyType.Tab)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -2347,11 +2386,13 @@ describe("Calendar", () => { describe("should render aria live region after month/year change", () => { it("should render aria live region after month change", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); - const nextNavigationButton = - container.querySelector(".react-datepicker__navigation--next") ?? - new HTMLElement(); + const nextNavigationButton = safeQuerySelector( + container, + ".react-datepicker__navigation--next", + ); fireEvent.click(nextNavigationButton); const currentMonthText = container.querySelector( diff --git a/src/test/datepicker_test.test.tsx b/src/test/datepicker_test.test.tsx index d395e82b1..ec3f7ccbf 100644 --- a/src/test/datepicker_test.test.tsx +++ b/src/test/datepicker_test.test.tsx @@ -27,7 +27,7 @@ import DatePicker, { registerLocale } from "../index"; import CustomInput from "./helper_components/custom_input"; import TestWrapper from "./helper_components/test_wrapper"; -import { getKey } from "./test_utils"; +import { getKey, safeQuerySelector } from "./test_utils"; function getSelectedDayNode(container: HTMLElement) { return ( @@ -87,7 +87,7 @@ describe("DatePicker", () => { it("should retain the calendar open status when the document visibility change", () => { const { container } = render(); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); if (!input) { throw new Error("Input element not found"); @@ -104,12 +104,7 @@ describe("DatePicker", () => { it("should retain the calendar close status when the document visibility change", () => { const { container } = render(); - const input = container.querySelector("input"); - - if (!input) { - throw new Error("Input element not found"); - } - + const input = safeQuerySelector(container, "input"); fireEvent.click(input); expect(container.querySelector(".react-datepicker")).toBeTruthy(); @@ -124,14 +119,17 @@ describe("DatePicker", () => { it("should show the calendar when focusing on the date input", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); }); it("should allow the user to supply a wrapper component for the popper", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelectorAll(".test-wrapper").length).toBe(1); expect(container.querySelector(".react-datepicker")).not.toBeNull(); @@ -142,7 +140,8 @@ describe("DatePicker", () => { , ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelectorAll(".test-wrapper").length).toBe(1); expect(container.querySelector(".react-datepicker")).not.toBeNull(); @@ -153,7 +152,8 @@ describe("DatePicker", () => { , ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); const popper = container.querySelectorAll(".react-datepicker-popper"); expect(popper.length).toBe(1); @@ -163,7 +163,8 @@ describe("DatePicker", () => { it("should show the calendar when clicking on the date input", () => { const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); }); @@ -198,7 +199,8 @@ describe("DatePicker", () => { it("should not set open state when it is disabled and gets clicked", () => { const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); expect(container.querySelector(".react-datepicker")).toBeNull(); }); @@ -226,15 +228,13 @@ describe("DatePicker", () => { fireEvent.click(instance!.input!); // user may tab or arrow down to the current day (or some other element in the popper) - const today = div.querySelector( - ".react-datepicker__day--today", - ); + const today = safeQuerySelector(div, ".react-datepicker__day--today"); act(() => { today?.focus(); }); // user hits Escape - fireEvent.keyDown(today ?? new HTMLElement(), getKey(KeyType.Escape)); + fireEvent.keyDown(today, getKey(KeyType.Escape)); expect(instance!.calendar).toBeFalsy(); @@ -267,15 +267,13 @@ describe("DatePicker", () => { fireEvent.focus(instance!.input!); // user may tab or arrow down to the current day (or some other element in the popper) - const today = div.querySelector( - ".react-datepicker__day--today", - ); + const today = safeQuerySelector(div, ".react-datepicker__day--today"); act(() => { today?.focus(); }); // user hits Enter - fireEvent.keyDown(today ?? new HTMLElement(), getKey(KeyType.Enter)); + fireEvent.keyDown(today, getKey(KeyType.Enter)); expect(instance!.calendar).toBeFalsy(); @@ -307,15 +305,13 @@ describe("DatePicker", () => { fireEvent.focus(instance!.input!); // user may tab or arrow down to the current day (or some other element in the popper) - const today = div.querySelector( - ".react-datepicker__day--today", - ); + const today = safeQuerySelector(div, ".react-datepicker__day--today"); act(() => { - today?.focus(); + today.focus(); }); // user hits Enter - fireEvent.keyDown(today ?? new HTMLElement(), getKey(KeyType.Enter)); + fireEvent.keyDown(today, getKey(KeyType.Enter)); expect(instance!.calendar).toBeTruthy(); @@ -334,14 +330,15 @@ describe("DatePicker", () => { onBlur={onBlurSpy} />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); const focusSpy = jest.spyOn(input, "focus"); fireEvent.focus(input); - const yearSelect = - container.querySelector(".react-datepicker__year-select") ?? - new HTMLElement(); + const yearSelect = safeQuerySelector( + container, + ".react-datepicker__year-select", + ); fireEvent.blur(input); fireEvent.focus(yearSelect); @@ -358,12 +355,13 @@ describe("DatePicker", () => { onYearChange={onYearChangeSpy} />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.click(input); - const yearSelect = - container.querySelector(".react-datepicker__year-select") ?? - new HTMLElement(); + const yearSelect = safeQuerySelector( + container, + ".react-datepicker__year-select", + ); fireEvent.change(yearSelect, { target: { value: Array.from(yearSelect.querySelectorAll("option")).at(-2)?.value, @@ -374,7 +372,7 @@ describe("DatePicker", () => { it("should keep the calendar shown when clicking the calendar", () => { const { container } = render(); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.focus(input); fireEvent.click(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); @@ -382,31 +380,33 @@ describe("DatePicker", () => { it("should not set open state when it is disabled and gets clicked.", () => { const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should not set open state when it is readOnly and gets clicked", () => { const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should hide the calendar when clicking a day on the calendar", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const day = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(day); expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should not hide the calendar when clicking a day on the calendar and shouldCloseOnSelect prop is false", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const day = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(day); expect(container.querySelector(".react-datepicker")).not.toBeNull(); }); @@ -418,19 +418,18 @@ describe("DatePicker", () => { }); // user focuses the input field, the calendar opens - const dateInput = div.querySelector("input") ?? new HTMLElement(); + const dateInput = safeQuerySelector(div, "input"); fireEvent.focus(dateInput); // user may tab or arrow down to the current day (or some other element in the popper) - const today = div.querySelector( - ".react-datepicker__day--today", - ); + + const today = safeQuerySelector(div, ".react-datepicker__day--today"); act(() => { today?.focus(); }); // user hits Enter - fireEvent.keyDown(today ?? new HTMLElement(), getKey(KeyType.Enter)); + fireEvent.keyDown(today, getKey(KeyType.Enter)); expect(document.activeElement).toBe(today); }); @@ -538,7 +537,7 @@ describe("DatePicker", () => { it("should hide the calendar when pressing enter in the date input", () => { const { container } = render(); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.focus(input); fireEvent.keyDown(input, getKey(KeyType.Enter)); expect(container.querySelector(".react-datepicker")).toBeNull(); @@ -546,7 +545,7 @@ describe("DatePicker", () => { it("should hide the calendar when the pressing escape in the date input", () => { const { container } = render(); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.focus(input); fireEvent.keyDown(input, getKey(KeyType.Escape)); expect(container.querySelector(".react-datepicker")).toBeNull(); @@ -590,7 +589,7 @@ describe("DatePicker", () => { onBlurSpy(event); }; const { container } = render(); - const input = container.querySelector("input") ?? new HTMLInputElement(); + const input = safeQuerySelector(container, "input"); onBlurSpy = jest.spyOn(input, "blur"); fireEvent.focus(input); fireEvent.keyDown(input, getKey(KeyType.Tab, true)); @@ -600,7 +599,7 @@ describe("DatePicker", () => { it("should not apply the react-datepicker-ignore-onclickoutside class to the date input when closed", () => { const { container } = render(); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); expect( input?.classList.contains("react-datepicker-ignore-onclickoutside"), ).toBeFalsy(); @@ -608,8 +607,8 @@ describe("DatePicker", () => { it("should apply the react-datepicker-ignore-onclickoutside class to date input when open", () => { const { container } = render(); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect( input?.classList.contains("react-datepicker-ignore-onclickoutside"), ).toBeTruthy(); @@ -624,10 +623,11 @@ describe("DatePicker", () => { />, ); - const calendarIcon = container.querySelector( + const calendarIcon = safeQuerySelector( + container, "svg.react-datepicker__calendar-icon", ); - fireEvent.click(calendarIcon ?? new HTMLElement()); + fireEvent.click(calendarIcon); const reactCalendar = container.querySelector( "div.react-datepicker-popper .react-datepicker", @@ -645,10 +645,11 @@ describe("DatePicker", () => { />, ); - const calendarIcon = container.querySelector( + const calendarIcon = safeQuerySelector( + container, "svg.react-datepicker__calendar-icon", ); - fireEvent.click(calendarIcon ?? new HTMLElement()); + fireEvent.click(calendarIcon); const reactCalendar = container.querySelector( "div.react-datepicker-popper .react-datepicker", @@ -681,12 +682,14 @@ describe("DatePicker", () => { />, ); - let calendarIcon = container.querySelector( + let calendarIcon = safeQuerySelector( + container, "svg.react-datepicker__calendar-icon", ); - fireEvent.click(calendarIcon ?? new HTMLElement()); + fireEvent.click(calendarIcon); - calendarIcon = container.querySelector( + calendarIcon = safeQuerySelector( + container, "svg.react-datepicker__calendar-icon", ); @@ -782,10 +785,12 @@ describe("DatePicker", () => { onChange={handleChange} />, ); - const clearButton = container.querySelector( + + const clearButton = safeQuerySelector( + container, ".react-datepicker__close-icon", ); - fireEvent.click(clearButton ?? new HTMLElement()); + fireEvent.click(clearButton); expect(cleared).toBe(true); }); @@ -801,10 +806,12 @@ describe("DatePicker", () => { />, ); expect(instance).toBeTruthy(); - const clearButton = container.querySelector( + + const clearButton = safeQuerySelector( + container, ".react-datepicker__close-icon", ); - fireEvent.click(clearButton ?? new HTMLElement()); + fireEvent.click(clearButton); expect(instance!.state.inputValue).toBeNull(); }); @@ -835,10 +842,11 @@ describe("DatePicker", () => { }, ); - const clearButton = container.querySelector( + const clearButton = safeQuerySelector( + container, ".react-datepicker__close-icon", ); - fireEvent.click(clearButton ?? new HTMLElement()); + fireEvent.click(clearButton); await waitFor(() => { expect(document.activeElement).toBe(div.querySelector("input")); @@ -888,8 +896,9 @@ describe("DatePicker", () => { }} />, ); - const dayButton = container.querySelector(".react-datepicker__day"); - fireEvent.click(dayButton ?? new HTMLElement()); + + const dayButton = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(dayButton); expect(date).toBeTruthy(); expect(getHours(date!)).toBe(10); @@ -909,8 +918,8 @@ describe("DatePicker", () => { }} />, ); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLInputElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: newDate("2014-01-02"), }, @@ -986,7 +995,8 @@ describe("DatePicker", () => { it("should render Calendar in portal when withPortal is set and input has focus", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect( document.body.querySelector(".react-datepicker__portal"), @@ -1037,7 +1047,8 @@ describe("DatePicker", () => { , ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(document.body.querySelector("#portal-id-dom-test")).not.toBeNull(); }); @@ -1687,9 +1698,8 @@ describe("DatePicker", () => { it("should auto update calendar when the updated date text is after props.minDate", () => { const { container } = getCalendar(); - const input = container.querySelector("input"); - - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: "1801/01/01", }, @@ -1703,9 +1713,9 @@ describe("DatePicker", () => { it("should not auto update calendar when the updated date text is before props.minDate", () => { const { container } = getCalendar(); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); - fireEvent.change(input ?? new HTMLElement(), { + fireEvent.change(input, { target: { value: "1799/01/01", }, @@ -1895,7 +1905,7 @@ describe("DatePicker", () => { }); // user focuses the input field, the calendar opens - const dateInput = div.querySelector("input") ?? new HTMLElement(); + const dateInput = safeQuerySelector(div, "input"); fireEvent.focus(dateInput); fireEvent.keyDown(dateInput, getKey(KeyType.ArrowDown)); @@ -2058,8 +2068,8 @@ describe("DatePicker", () => { const { container } = render( , ); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: "", }, @@ -2174,8 +2184,8 @@ describe("DatePicker", () => { ); expect(onChangeRawSpy).not.toHaveBeenCalled(); expect(onSelectSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: inputValue, }, @@ -2197,10 +2207,10 @@ describe("DatePicker", () => { ); expect(onChangeRawSpy).not.toHaveBeenCalled(); expect(onSelectSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); - const day = container.querySelector(".react-datepicker__day"); - fireEvent.click(day ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const day = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(day); expect(onChangeRawSpy).toHaveBeenCalledTimes(1); expect(onSelectSpy).toHaveBeenCalledTimes(1); }); @@ -2212,7 +2222,7 @@ describe("DatePicker", () => { (event.target as unknown as HTMLInputElement).value > "2" && event.preventDefault(); const { container } = render(); - const input = container.querySelector("input") ?? new HTMLInputElement(); + const input = safeQuerySelector(container, "input"); expect(input.value).toBe(""); fireEvent.change(input, { target: { @@ -2239,8 +2249,8 @@ describe("DatePicker", () => { />, ); expect(onChangeRawSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: inputValue, }, @@ -2264,8 +2274,8 @@ describe("DatePicker", () => { />, ); expect(onChangeRawSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: inputValue, }, @@ -2303,11 +2313,15 @@ describe("DatePicker", () => { , ); expect(container.querySelector(".react-datepicker")).toBeNull(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + expect(container.querySelector(".react-datepicker")).not.toBeNull(); - fireEvent.mouseDown( - container.querySelector(".testText") ?? new HTMLElement(), - ); + + const testText = safeQuerySelector(container, ".testText"); + fireEvent.mouseDown(testText); + expect(container.querySelector(".react-datepicker")).toBeNull(); expect(onClickOutsideSpy).toHaveBeenCalledTimes(1); }); @@ -2318,9 +2332,11 @@ describe("DatePicker", () => { , ); expect(container.querySelector(".react-datepicker")).toBeNull(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); - fireEvent.mouseDown(container.querySelector("input") ?? new HTMLElement()); + + fireEvent.mouseDown(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); expect(onClickOutsideSpy).not.toHaveBeenCalled(); }); @@ -2513,15 +2529,16 @@ describe("DatePicker", () => { }); it("should not set open state when focusing on the date input and the preventOpenOnFocus prop is set", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should not set open state onInputKeyDown when preventOpenOnFocus prop is set", () => { const { container } = render(); - fireEvent.keyDown( - container.querySelector("input") ?? new HTMLElement(), - getKey(KeyType.ArrowLeft), - ); + + const input = safeQuerySelector(container, "input"); + fireEvent.keyDown(input, getKey(KeyType.ArrowLeft)); + expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should clear the input when clear() member function is called", () => { @@ -2543,7 +2560,8 @@ describe("DatePicker", () => { }); it("should not open when open is false and input is focused", () => { const { container } = render(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).toBeNull(); }); it("should open when open is true", () => { @@ -2553,7 +2571,8 @@ describe("DatePicker", () => { it("should fire onInputClick when input is clicked", () => { const onInputClickSpy = jest.fn(); const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); expect(onInputClickSpy).toHaveBeenCalledTimes(1); }); @@ -2612,8 +2631,8 @@ describe("DatePicker", () => { it("should show the popper arrow when showPopperArrow is true", () => { const { container } = render(); - const input = container.querySelector("input"); - fireEvent.click(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const arrow = container.querySelector(".react-datepicker__triangle"); @@ -2622,8 +2641,8 @@ describe("DatePicker", () => { it("should not show the popper arrow when showPopperArrow is false", () => { const { container } = render(); - const input = container.querySelector("input"); - fireEvent.click(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const arrows = container.querySelectorAll(".react-datepicker__triangle"); @@ -2694,8 +2713,8 @@ describe("DatePicker", () => { it("should close the calendar after scrolling", () => { const { container } = render(); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); fireEvent.scroll(document); expect(container.querySelector(".react-datepicker")).toBeNull(); @@ -2707,8 +2726,8 @@ describe("DatePicker", () => { const { container } = render(, { container: div, }); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); fireEvent.scroll(div); expect(container.querySelector(".react-datepicker")).not.toBeNull(); @@ -2716,8 +2735,8 @@ describe("DatePicker", () => { it("should close the calendar after scrolling.", () => { const { container } = render( true} />); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); expect(container.querySelector(".react-datepicker")).not.toBeNull(); fireEvent.scroll(document); expect(container.querySelector(".react-datepicker")).toBeNull(); @@ -2725,8 +2744,8 @@ describe("DatePicker", () => { it("should not close the calendar after scrolling.", () => { const { container } = render( false} />); - const input = container.querySelector("input"); - fireEvent.focus(input ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); fireEvent.scroll(document); expect(container.querySelector(".react-datepicker")).not.toBeNull(); }); @@ -2753,7 +2772,8 @@ describe("DatePicker", () => { onChange={onChange} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); const days = container.querySelectorAll(".react-datepicker__day"); @@ -2775,7 +2795,10 @@ describe("DatePicker", () => { onChange={onChange} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const days = container.querySelectorAll(".react-datepicker__day"); expect(days[5]).toBeTruthy(); @@ -2797,7 +2820,10 @@ describe("DatePicker", () => { onChange={onChange} />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const days = container.querySelectorAll(".react-datepicker__day"); expect(days[5]).toBeTruthy(); @@ -2830,8 +2856,11 @@ describe("DatePicker", () => { />, ); - const day = container.querySelector(".react-datepicker__day--selected"); - fireEvent.click(day ?? new HTMLElement()); + const selectedDay = safeQuerySelector( + container, + ".react-datepicker__day--selected", + ); + fireEvent.click(selectedDay); expect(startDate).toBeTruthy(); expect(formatDate(startDate!, "yyyy-MM-dd")).toBe( formatDate(selected, "yyyy-MM-dd"), @@ -2858,10 +2887,11 @@ describe("DatePicker", () => { />, ); - const day = container.querySelector( + const day = safeQuerySelector( + container, ".react-datepicker__day--selected + .react-datepicker__day", ); - fireEvent.click(day ?? new HTMLElement()); + fireEvent.click(day); expect(formatDate(startDate, "yyyy-MM-dd")).toBe( formatDate(startDate, "yyyy-MM-dd"), ); @@ -2892,8 +2922,11 @@ describe("DatePicker", () => { />, ); - const day = container.querySelector(".react-datepicker__day--selected"); - fireEvent.click(day ?? new HTMLElement()); + const day = safeQuerySelector( + container, + ".react-datepicker__day--selected", + ); + fireEvent.click(day); expect(formatDate(startDate, "yyyy-MM-dd")).toBe( formatDate(selected, "yyyy-MM-dd"), ); @@ -3008,10 +3041,15 @@ describe("DatePicker", () => { const { container } = render( , ); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), + + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); + + const datePickerDay = safeQuerySelector( + container, + ".react-datepicker__day", ); + fireEvent.click(datePickerDay); expect(container.querySelector(".react-datepicker")).not.toBeNull(); }); @@ -3022,7 +3060,9 @@ describe("DatePicker", () => { const { container } = render( , ); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const days = container.querySelectorAll(".react-datepicker__day"); const day = days[Math.floor(days.length / 2)]; @@ -3042,7 +3082,9 @@ describe("DatePicker", () => { endDate={endDate} />, ); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const days = container.querySelectorAll(".react-datepicker__day"); const day = days[Math.floor(days.length / 2)]; @@ -3113,9 +3155,9 @@ describe("DatePicker", () => { />, ); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); expect(input).toBeTruthy(); - fireEvent.click(input ?? new HTMLElement()); + fireEvent.click(input); const calendar = container.querySelector(".react-datepicker"); expect(calendar).toBeTruthy(); @@ -3124,10 +3166,11 @@ describe("DatePicker", () => { const startDatePrefixedWithZeros = formatDayWithZeros( startDate.getDate(), ); - const endDateElement = container.querySelector( + const endDateElement = safeQuerySelector( + container, `.react-datepicker__day--${startDatePrefixedWithZeros}`, ); - fireEvent.click(endDateElement ?? new HTMLElement()); + fireEvent.click(endDateElement); expect(onChangeSpy).toHaveBeenCalled(); }); @@ -3155,7 +3198,7 @@ describe("DatePicker", () => { />, ); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); expect(input).toBeTruthy(); fireEvent.click(input!); @@ -3166,10 +3209,12 @@ describe("DatePicker", () => { const startDatePrefixedWithZeros = formatDayWithZeros( startDate.getDate(), ); - const endDateElement = container.querySelector( + + const endDateElement = safeQuerySelector( + container, `.react-datepicker__day--${startDatePrefixedWithZeros}`, ); - fireEvent.click(endDateElement ?? new HTMLElement()); + fireEvent.click(endDateElement); calendar = container.querySelector(".react-datepicker"); expect(calendar).toBeFalsy(); @@ -3186,7 +3231,8 @@ describe("DatePicker", () => { const { container, rerender } = render( , ); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const months = container.querySelectorAll(".react-datepicker__month"); expect(months).toHaveLength(2); // 2023-05 monthShowsDuplicateDaysEnd:true @@ -3206,7 +3252,7 @@ describe("DatePicker", () => { // moreThanTwoMonths rerender(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + fireEvent.click(input); const monthsMore = container.querySelectorAll(".react-datepicker__month"); expect(monthsMore).toHaveLength(4); // 2023-05 monthShowsDuplicateDaysEnd:true @@ -3244,7 +3290,8 @@ describe("DatePicker", () => { const { container, rerender } = render( , ); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const months = container.querySelectorAll(".react-datepicker__month"); expect(months).toHaveLength(2); @@ -3261,7 +3308,7 @@ describe("DatePicker", () => { // moreThanTwoMonths rerender(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + fireEvent.click(input); const monthsMore = container.querySelectorAll(".react-datepicker__month"); expect(monthsMore).toHaveLength(4); // 2023-05 monthShowsDuplicateDaysStart:false @@ -3288,7 +3335,8 @@ describe("DatePicker", () => { it("should not find duplicates when single month displayed", () => { const { container } = render(); - fireEvent.click(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.click(input); const months = container.querySelectorAll(".react-datepicker__month"); expect(months).toHaveLength(1); @@ -3476,7 +3524,7 @@ describe("DatePicker", () => { registerLocale("en-GB", enGB); const { container } = render(); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); jest.spyOn(input, "focus"); fireEvent.focus(input); @@ -3489,7 +3537,7 @@ describe("DatePicker", () => { registerLocale("en-US", enUS); const { container } = render(); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); jest.spyOn(input, "focus"); fireEvent.focus(input); @@ -3518,7 +3566,7 @@ describe("DatePicker", () => { />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.change(input, { target: { value: "8:22 AM", @@ -3547,9 +3595,10 @@ describe("DatePicker", () => { />, ); - const input = - datepicker.querySelector(".react-datepicker__input-container > input") ?? - new HTMLElement(); + const input = safeQuerySelector( + datepicker, + ".react-datepicker__input-container > input", + ); fireEvent.change(input, { target: { value: "" } }); expect(date).toBe(null); @@ -3573,9 +3622,10 @@ describe("DatePicker", () => { />, ); - const input = - datepicker.querySelector(".react-datepicker__input-container > input") ?? - new HTMLElement(); + const input = safeQuerySelector( + datepicker, + ".react-datepicker__input-container > input", + ); fireEvent.change(input, { target: { value: "" } }); expect(date).toBe(null); @@ -3594,7 +3644,7 @@ describe("DatePicker", () => { />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.change(input, { target: { value: "11/2022", @@ -3623,7 +3673,7 @@ describe("DatePicker", () => { />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.change(input, { target: { value: "2021", @@ -3741,11 +3791,12 @@ describe("DatePicker", () => { />, ); - const dateInput = container.querySelector("input"); - fireEvent.focus(dateInput ?? new HTMLElement()); - const selectedYear = - container.querySelector(".react-datepicker__year-text--selected") ?? - new HTMLElement(); + const dateInput = safeQuerySelector(container, "input"); + fireEvent.focus(dateInput); + const selectedYear = safeQuerySelector( + container, + ".react-datepicker__year-text--selected", + ); fireEvent.mouseEnter(selectedYear); fireEvent.mouseLeave(selectedYear); @@ -3767,11 +3818,12 @@ describe("DatePicker", () => { />, ); - const dateInput = container.querySelector("input"); - fireEvent.focus(dateInput ?? new HTMLElement()); - const selectedYear = - container.querySelector(".react-datepicker__year-text--selected") ?? - new HTMLElement(); + const dateInput = safeQuerySelector(container, "input"); + fireEvent.focus(dateInput); + const selectedYear = safeQuerySelector( + container, + ".react-datepicker__year-text--selected", + ); fireEvent.pointerEnter(selectedYear); fireEvent.pointerLeave(selectedYear); diff --git a/src/test/day_test.test.tsx b/src/test/day_test.test.tsx index 3fff9254e..12d2692a0 100644 --- a/src/test/day_test.test.tsx +++ b/src/test/day_test.test.tsx @@ -15,6 +15,8 @@ import { } from "../date_utils"; import Day from "../day"; +import { safeQuerySelector } from "./test_utils"; + function renderDay(day: Date, props = {}) { return render( {}} {...props} />, @@ -1226,9 +1228,9 @@ describe("Day", () => { onClick={onClick} />, ); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(dayElement); expect(onClickCalled).toBe(true); }); @@ -1243,9 +1245,9 @@ describe("Day", () => { onClick={onClick} />, ); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(dayElement); expect(onClickCalled).toBe(false); }); @@ -1262,9 +1264,9 @@ describe("Day", () => { onClick={onClick} />, ); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(dayElement); expect(onClickCalled).toBe(false); }); }); @@ -1284,9 +1286,8 @@ describe("Day", () => { />, ); - fireEvent.mouseEnter( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.mouseEnter(dayElement); expect(onMouseEnterSpy).toHaveBeenCalled(); }); @@ -1305,9 +1306,8 @@ describe("Day", () => { />, ); - fireEvent.pointerEnter( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.pointerEnter(dayElement); expect(onMouseEnterSpy).toHaveBeenCalled(); }); }); diff --git a/src/test/min_time_test.test.tsx b/src/test/min_time_test.test.tsx index c73051bd3..0360a1022 100644 --- a/src/test/min_time_test.test.tsx +++ b/src/test/min_time_test.test.tsx @@ -3,6 +3,8 @@ import React, { useState } from "react"; import DatePicker from "../index"; +import { safeQuerySelector } from "./test_utils"; + import type { DatePickerProps } from "../index"; // see https://github.com/microsoft/TypeScript/issues/31501 @@ -78,7 +80,7 @@ describe("Datepicker minTime", () => { const { container } = render( , ); - const input = container.querySelector("input") ?? new HTMLInputElement(); + const input = safeQuerySelector(container, "input"); fireEvent.change(input, { target: { value: "2023-03-10 16:00" } }); fireEvent.focusOut(input); diff --git a/src/test/month_dropdown_test.test.tsx b/src/test/month_dropdown_test.test.tsx index a6344719f..ac3d918c1 100644 --- a/src/test/month_dropdown_test.test.tsx +++ b/src/test/month_dropdown_test.test.tsx @@ -8,12 +8,12 @@ import { getMonthInLocale, registerLocale } from "../date_utils"; import MonthDropdown from "../month_dropdown"; import MonthDropdownOptions from "../month_dropdown_options"; -import { range } from "./test_utils"; +import { range, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; type MonthDropdownProps = React.ComponentProps; describe("MonthDropdown", () => { - let monthDropdown: HTMLElement | null = null; + let monthDropdown: HTMLElement; let handleChangeResult: number | null; const mockHandleChange = function (changeInput: number) { handleChangeResult = changeInput; @@ -49,10 +49,11 @@ describe("MonthDropdown", () => { }); it("opens a list when read view is clicked", () => { - fireEvent.click( - monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? - new HTMLElement(), + const monthReadView = safeQuerySelector( + monthDropdown, + ".react-datepicker__month-read-view", ); + fireEvent.click(monthReadView); const optionsView = monthDropdown?.querySelector( ".react-datepicker__month-dropdown", ); @@ -63,10 +64,11 @@ describe("MonthDropdown", () => { let selectedMonth: HTMLSelectElement | null | undefined; beforeEach(() => { - fireEvent.click( - monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? - new HTMLElement(), + const monthReadView = safeQuerySelector( + monthDropdown, + ".react-datepicker__month-read-view", ); + fireEvent.click(monthReadView); selectedMonth = monthDropdown?.querySelector( ".react-datepicker__month-option--selected_month", ); @@ -90,7 +92,8 @@ describe("MonthDropdown", () => { monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? new HTMLSelectElement(), ); - notSelectedMonth = monthDropdown?.querySelector( + notSelectedMonth = safeQuerySelector( + monthDropdown, ".react-datepicker__month-option", ); }); @@ -106,14 +109,20 @@ describe("MonthDropdown", () => { }); it("closes the dropdown when a month is clicked", () => { - fireEvent.click( - monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? - new HTMLElement(), + const monthReadView = safeQuerySelector( + monthDropdown, + ".react-datepicker__month-read-view", ); - fireEvent.click( - (monthDropdown?.querySelectorAll(".react-datepicker__month-option") ?? - [])[1] ?? new HTMLElement(), + fireEvent.click(monthReadView); + + const minMonthOptionsLen = 2; + const monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", + minMonthOptionsLen, ); + + fireEvent.click(monthOptions[1]!); expect( monthDropdown?.querySelectorAll(".react-datepicker__month-dropdown"), ).toHaveLength(0); @@ -137,26 +146,36 @@ describe("MonthDropdown", () => { }); it("does not call the supplied onChange function when the same month is clicked", () => { - fireEvent.click( - monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? - new HTMLElement(), + const monthReadView = safeQuerySelector( + monthDropdown, + ".react-datepicker__month-read-view", ); - fireEvent.click( - (monthDropdown?.querySelectorAll(".react-datepicker__month-option") ?? - [])[11] ?? new HTMLElement(), + fireEvent.click(monthReadView); + + const monthOptionsLen = 12; + const monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", + monthOptionsLen, ); + fireEvent.click(monthOptions[11]!); expect(handleChangeResult).toBeNull(); }); it("calls the supplied onChange function when a different month is clicked", () => { - fireEvent.click( - monthDropdown?.querySelector(".react-datepicker__month-read-view") ?? - new HTMLElement(), + const monthReadView = safeQuerySelector( + monthDropdown, + ".react-datepicker__month-read-view", ); - fireEvent.click( - (monthDropdown?.querySelectorAll(".react-datepicker__month-option") ?? - [])[2] ?? new HTMLElement(), + fireEvent.click(monthReadView); + + const minRequiredMonthsLen = 3; + const monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", + minRequiredMonthsLen, ); + fireEvent.click(monthOptions[2]!); expect(handleChangeResult).toEqual(2); }); diff --git a/src/test/month_year_dropdown_test.test.tsx b/src/test/month_year_dropdown_test.test.tsx index 916e12d96..ea5067eed 100644 --- a/src/test/month_year_dropdown_test.test.tsx +++ b/src/test/month_year_dropdown_test.test.tsx @@ -13,10 +13,12 @@ import { import MonthYearDropdown from "../month_year_dropdown"; import MonthYearDropdownOptions from "../month_year_dropdown_options"; +import { safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; + type MonthYearDropdownProps = React.ComponentProps; describe("MonthYearDropdown", () => { - let monthYearDropdown: HTMLElement | null = null; + let monthYearDropdown: HTMLElement; let handleChangeResult: Date | null = null; const mockHandleChange = function (changeInput: Date) { handleChangeResult = changeInput; @@ -81,11 +83,11 @@ describe("MonthYearDropdown", () => { }); it("opens a list when read view is clicked", () => { - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); + fireEvent.click(monthYearReadView); const optionsView = monthYearDropdown?.querySelector( ".react-datepicker__month-year-dropdown", ); @@ -93,16 +95,18 @@ describe("MonthYearDropdown", () => { }); it("closes the dropdown when a month year is clicked", () => { - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); - fireEvent.click( - (monthYearDropdown?.querySelectorAll( - ".react-datepicker__month-year-option", - ) ?? [])[1] ?? new HTMLElement(), + fireEvent.click(monthYearReadView); + + const monthYearOptions = safeQuerySelectorAll( + monthYearDropdown, + ".react-datepicker__month-year-option", ); + const monthYearOption = monthYearOptions[0]!; + fireEvent.click(monthYearOption); expect( monthYearDropdown?.querySelectorAll( ".react-datepicker__month-year-dropdown", @@ -131,25 +135,27 @@ describe("MonthYearDropdown", () => { }); it("does not call the supplied onChange function when the same month year is clicked", () => { - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-option--selected_month-year", - ) ?? new HTMLElement(), + fireEvent.click(monthYearReadView); + + const selectedMonthYear = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-option--selected_month-year", ); + fireEvent.click(selectedMonthYear); + expect(handleChangeResult).toBeNull(); }); it("adds aria-selected to selected option", () => { - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); + fireEvent.click(monthYearReadView); const ariaSelected = monthYearDropdown ?.querySelector( @@ -161,12 +167,11 @@ describe("MonthYearDropdown", () => { }); it("does not add aria-selected to non-selected option", () => { - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); - + fireEvent.click(monthYearReadView); const ariaSelected = monthYearDropdown ?.querySelector(".react-datepicker__month-year-option") ?.getAttribute("aria-selected"); @@ -177,18 +182,22 @@ describe("MonthYearDropdown", () => { it("calls the supplied onChange function when a different month year is clicked", () => { const expected_date = newDate("2017-12"); - fireEvent.click( - monthYearDropdown?.querySelector( - ".react-datepicker__month-year-read-view", - ) ?? new HTMLElement(), + const monthYearReadView = safeQuerySelector( + monthYearDropdown, + ".react-datepicker__month-year-read-view", ); + fireEvent.click(monthYearReadView); - fireEvent.click( - (monthYearDropdown?.querySelectorAll( - ".react-datepicker__month-year-option", - ) ?? [])[5] ?? new HTMLElement(), + const minRequiredMonthYearOptionsLen = 6; + const monthYearOptions = safeQuerySelectorAll( + monthYearDropdown, + ".react-datepicker__month-year-option", + minRequiredMonthYearOptionsLen, ); + const monthYearOption = monthYearOptions[5]!; + fireEvent.click(monthYearOption); + expect(handleChangeResult?.toString()).toBe(expected_date.toString()); }); @@ -297,10 +306,11 @@ describe("MonthYearDropdown", () => { dropdownMode: "select", date: selectedMonth, }); - const select = monthYearDropdown.querySelector( + const select = safeQuerySelector( + monthYearDropdown, ".react-datepicker__month-year-select", ); - fireEvent.change(select ?? new HTMLElement(), { + fireEvent.change(select, { target: { value: selectedMonth.valueOf() }, }); expect(handleChangeResult).toBeFalsy(); @@ -313,10 +323,11 @@ describe("MonthYearDropdown", () => { dropdownMode: "select", date: selectedMonth, }); - const select = monthYearDropdown.querySelector( + const select = safeQuerySelector( + monthYearDropdown, ".react-datepicker__month-year-select", ); - fireEvent.change(select ?? new HTMLElement(), { + fireEvent.change(select, { target: { value: monthToClick.valueOf() }, }); expect(handleChangeResult?.valueOf()).toBe(monthToClick.valueOf()); diff --git a/src/test/show_time_test.test.tsx b/src/test/show_time_test.test.tsx index 742cc7ab5..0fdfbf442 100644 --- a/src/test/show_time_test.test.tsx +++ b/src/test/show_time_test.test.tsx @@ -4,6 +4,8 @@ import React from "react"; import DatePicker from "../index"; import TimeComponent from "../time"; +import { safeQuerySelector } from "./test_utils"; + describe("DatePicker", () => { it("should show time component when showTimeSelect prop is present", () => { const { container } = render(); @@ -26,7 +28,9 @@ describe("DatePicker", () => { datePicker = render( , ).container; - fireEvent.click(datePicker.querySelector("input") ?? new HTMLElement()); + + const input = safeQuerySelector(datePicker, "input"); + fireEvent.click(input); }); it("should not show month container when showTimeSelectOnly prop is present", () => { diff --git a/src/test/test_utils.test.ts b/src/test/test_utils.test.ts new file mode 100644 index 000000000..4503cb0cf --- /dev/null +++ b/src/test/test_utils.test.ts @@ -0,0 +1,174 @@ +import { + SafeElementWrapper, + safeQuerySelector, + safeQuerySelectorAll, +} from "./test_utils"; + +describe("test_utils", () => { + describe("safeQuerySelector", () => { + let container: HTMLElement; + + beforeEach(() => { + container = document.createElement("div"); + container.innerHTML = ` +
+
+ Span 1 +
+
+ `; + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + }); + + it("should return the element when the selector matches", () => { + const result = safeQuerySelector(container, ".childDiv"); + expect(result).toBeInstanceOf(HTMLDivElement); + expect(result.className).toBe("childDiv"); + }); + + it("should throw an error if the element is not found", () => { + expect(() => safeQuerySelector(container, ".nonExistent")).toThrow( + "Element with selector '.nonExistent' not found", + ); + }); + }); + + describe("safeQuerySelectorAll", () => { + let container: HTMLElement; + + beforeEach(() => { + container = document.createElement("div"); + container.innerHTML = ` +
+
+ Span 1 + Span 2 +
+
+ `; + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + }); + + it("should return an array of elements when the selector matches", () => { + const results = safeQuerySelectorAll( + container, + ".childSpan", + ); + expect(results).toHaveLength(2); + expect(results[0]!.textContent).toBe("Span 1"); + expect(results[1]!.textContent).toBe("Span 2"); + }); + + it("should throw an error if no elements are found", () => { + expect(() => safeQuerySelectorAll(container, ".nonExistent")).toThrow( + "Element with selector '.nonExistent' not found", + ); + }); + + it("should throw an error if fewer elements than expected are found", () => { + expect(() => safeQuerySelectorAll(container, ".childSpan", 3)).toThrow( + "Expected at least 3 element(s) for selector '.childSpan'. Only 2 found", + ); + }); + + it("should return elements if the minimum expected number of elements are found", () => { + const results = safeQuerySelectorAll( + container, + ".childSpan", + 2, + ); + expect(results).toHaveLength(2); + }); + }); + + describe("SafeElementWrapper", () => { + let container: HTMLElement; + + beforeEach(() => { + container = document.createElement("div"); + container.innerHTML = ` +
+
+ Span 1 + Span 2 +
+
+ `; + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + }); + + it("should wrap an element using getElement", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + + expect(wrapper).toBeInstanceOf(SafeElementWrapper); + expect(wrapper.getElement()).toBe(element); + }); + + it("should find a child element using safeQuerySelector", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + const childWrapper = wrapper.safeQuerySelector(".childDiv"); + + expect(childWrapper).toBeInstanceOf(SafeElementWrapper); + expect(childWrapper.getElement().className).toBe("childDiv"); + }); + + it("should throw an error if child element is not found using safeQuerySelector", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + + expect(() => wrapper.safeQuerySelector(".nonExistent")).toThrow( + "Element with selector '.nonExistent' not found", + ); + }); + + it("should find all matching child elements using safeQuerySelectorAll", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + const childWrappers = wrapper.safeQuerySelectorAll(".childSpan"); + + expect(childWrappers.length).toBe(2); + expect(childWrappers[0]!.getElement().textContent).toBe("Span 1"); + expect(childWrappers[1]!.getElement().textContent).toBe("Span 2"); + }); + + it("should throw an error if no matching elements are found using safeQuerySelectorAll", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + + expect(() => wrapper.safeQuerySelectorAll(".nonExistent")).toThrow( + "Element with selector '.nonExistent' not found", + ); + }); + + it("should throw an error if fewer elements than expected are found using safeQuerySelectorAll", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + + expect(() => wrapper.safeQuerySelectorAll(".childSpan", 3)).toThrow( + "Expected at least 3 element(s) for selector '.childSpan'. Only 2 found", + ); + }); + + it("should pass if expected number of elements are found using safeQuerySelectorAll", () => { + const element = document.getElementById("parentDiv"); + const wrapper = new SafeElementWrapper(element!); + + const childWrappers = wrapper.safeQuerySelectorAll(".childSpan", 2); + expect(childWrappers.length).toBe(2); + }); + }); +}); diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 72cf1fbb9..5e576795b 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -80,3 +80,71 @@ export const gotoNextView = (container: Element) => { )!; fireEvent.click(nextButton); }; + +export const safeQuerySelector = ( + container: HTMLElement, + selector: string, +): T => { + const element = container.querySelector(selector); + if (element) { + return element as T; + } + + throw new Error(`Element with selector '${selector}' not found`); +}; + +export const safeQuerySelectorAll = ( + container: HTMLElement, + selector: string, + minExpected: number = 1, +): T[] => { + const elements = Array.from(container.querySelectorAll(selector)) as T[]; + + if (!elements.length) { + throw new Error(`Element with selector '${selector}' not found`); + } + if (elements.length < minExpected) { + throw new Error( + `Expected at least ${minExpected} element(s) for selector '${selector}'. Only ${elements.length} found`, + ); + } + + return elements; +}; + +export class SafeElementWrapper { + constructor(private element: T) {} + + getElement(): T { + return this.element; + } + + safeQuerySelector( + selector: string, + ): SafeElementWrapper { + const element = this.element.querySelector(selector) as E; + if (element) { + return new SafeElementWrapper(element); + } + + throw new Error(`Element with selector '${selector}' not found`); + } + + safeQuerySelectorAll( + selector: string, + minExpected = 1, + ): SafeElementWrapper[] { + const elements = Array.from(this.element.querySelectorAll(selector)) as E[]; + + if (!elements.length) { + throw new Error(`Element with selector '${selector}' not found`); + } + if (elements.length < minExpected) { + throw new Error( + `Expected at least ${minExpected} element(s) for selector '${selector}'. Only ${elements.length} found`, + ); + } + + return elements.map((element) => new SafeElementWrapper(element)); + } +} diff --git a/src/test/time_input_test.test.tsx b/src/test/time_input_test.test.tsx index a04ba8906..7dd8fb049 100644 --- a/src/test/time_input_test.test.tsx +++ b/src/test/time_input_test.test.tsx @@ -5,6 +5,7 @@ import DatePicker from "../index"; import InputTimeComponent from "../input_time"; import CustomTimeInput from "./helper_components/custom_time_input"; +import { safeQuerySelector } from "./test_utils"; describe("timeInput", () => { afterEach(() => { @@ -30,11 +31,11 @@ describe("timeInput", () => { it("should trigger onChange event", () => { const onChangeSpy = jest.fn(); const { container } = render(); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: "13:00" }, }); - expect(input?.value).toEqual("13:00"); + expect(input.value).toEqual("13:00"); }); it("should retain the focus on onChange event", () => { @@ -42,18 +43,18 @@ describe("timeInput", () => { const { container } = render( , ); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); act(() => { input?.focus(); }); expect(document.activeElement).toBe(input); - fireEvent.change(input ?? new HTMLElement(), { + fireEvent.change(input, { target: { value: "13:00" }, }); - expect(input?.value).toEqual("13:00"); + expect(input.value).toEqual("13:00"); expect(document.activeElement).toBe(input); }); @@ -61,9 +62,9 @@ describe("timeInput", () => { const { container } = render( {}} />, ); - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { target: { value: "" } }); - expect(input?.value).toEqual("13:00"); + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: "" } }); + expect(input.value).toEqual("13:00"); }); it("should trigger onChange event on a custom time input without using the last valid timeString", () => { @@ -79,8 +80,8 @@ describe("timeInput", () => { ); const newTime = "14:00"; - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: newTime }, }); @@ -104,8 +105,8 @@ describe("timeInput", () => { ); const newTime = "14:00"; - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: newTime }, }); @@ -121,8 +122,8 @@ describe("timeInput", () => { ); const newTime = "13:00"; - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: newTime }, }); @@ -146,8 +147,8 @@ describe("timeInput", () => { ); const newTime = "13:00"; - const input = container.querySelector("input"); - fireEvent.change(input ?? new HTMLElement(), { + const input = safeQuerySelector(container, "input"); + fireEvent.change(input, { target: { value: newTime }, }); diff --git a/src/test/timepicker_test.test.tsx b/src/test/timepicker_test.test.tsx index 0c51ed079..8db6f4baf 100644 --- a/src/test/timepicker_test.test.tsx +++ b/src/test/timepicker_test.test.tsx @@ -4,11 +4,13 @@ import React from "react"; import { formatDate, KeyType } from "../date_utils"; import DatePicker from "../index"; -import { getKey } from "./test_utils"; +import { getKey, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; + +const MIN_TIME_LI_LEN = 2; describe("TimePicker", () => { - let datePicker: HTMLDivElement | undefined; - let div: HTMLDivElement | undefined; + let datePicker: HTMLDivElement; + let div: HTMLDivElement; let onChangeMoment: Date | undefined; let instance: DatePicker | null = null; @@ -29,39 +31,55 @@ describe("TimePicker", () => { it("should allow time changes after input change", () => { renderDatePicker("February 28, 2018 4:43 PM"); setManually("February 28, 2018 4:45 PM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time?.querySelectorAll("li"); - fireEvent.click(lis[1] ?? new HTMLElement()); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + + fireEvent.focus(instance.input); + + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.click(lis[1]!); expect(getInputString()).toBe("February 28, 2018 12:30 AM"); }); it("should allow for injected date if input does not have focus", () => { renderDatePicker("February 28, 2018 4:43 PM"); setManually("February 28, 2018 4:45 PM"); - fireEvent.blur(instance?.input ?? new HTMLElement()); + + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + + fireEvent.blur(instance.input); renderDatePicker("February 28, 2018 4:43 PM"); expect(getInputString()).toBe("February 28, 2018 4:43 PM"); }); it("should not close datepicker after time clicked when shouldCloseOnSelect is false", () => { - let instance: DatePicker | null; + let instance: DatePicker; const { container } = render( { - instance = node; + if (node) { + instance = node; + } }} shouldCloseOnSelect={false} showTimeSelect />, ); fireEvent.focus(instance!.input!); - const time = container.querySelector(".react-datepicker__time-container"); - const lis = time?.querySelectorAll("li") ?? []; - fireEvent.click(lis?.[0] ?? new HTMLElement()); + const time = safeQuerySelector( + container, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li"); + fireEvent.click(lis[0]!); expect(instance!.state.open).toBe(true); }); @@ -95,7 +113,10 @@ describe("TimePicker", () => { }); expect(getInputString()).toBe("February 28, 2018 9:00 AM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); setManually("February 28, 2018 9:20 AM"); expect(getInputString()).toBe("February 28, 2018 9:20 AM"); @@ -108,7 +129,10 @@ describe("TimePicker", () => { }); expect(getInputString()).toBe("February 28, 2018 9:00 AM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); setManually("February 28, 2018 9:53 AM"); expect(getInputString()).toBe("February 28, 2018 9:53 AM"); @@ -121,7 +145,10 @@ describe("TimePicker", () => { }); expect(getInputString()).toBe("July 13, 2020 2:59 PM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); setManually("July 13, 2020 3:00 PM"); expect(getInputString()).toBe("July 13, 2020 3:00 PM"); @@ -147,36 +174,50 @@ describe("TimePicker", () => { it("should select time when Enter is pressed", () => { renderDatePicker("February 28, 2018 4:43 PM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Enter)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); expect(getInputString()).toBe("February 28, 2018 12:30 AM"); }); it("should select time when Space is pressed", () => { renderDatePicker("February 28, 2018 4:43 PM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Space)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Space)); expect(getInputString()).toBe("February 28, 2018 12:30 AM"); }); it("should return focus to input once time is selected", async () => { - document.body.appendChild(div ?? new HTMLElement()); // So we can check the dom later for activeElement + document.body.appendChild(div); // So we can check the dom later for activeElement renderDatePicker("February 28, 2018 4:43 PM"); - const input = datePicker?.querySelector("input") ?? new HTMLElement(); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Enter)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + + const input = safeQuerySelector(datePicker, "input"); + + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); await waitFor(() => { expect(document.activeElement).toBe(input); @@ -185,12 +226,16 @@ describe("TimePicker", () => { it("should not select time when Escape is pressed", () => { renderDatePicker("February 28, 2018 4:43 PM"); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Escape)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Escape)); expect(getInputString()).toBe("February 28, 2018 4:43 PM"); }); @@ -199,12 +244,16 @@ describe("TimePicker", () => { renderDatePicker("February 28, 2018 4:43 PM", { onKeyDown: onKeyDownSpy, }); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Escape)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Escape)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -213,12 +262,16 @@ describe("TimePicker", () => { renderDatePicker("February 28, 2018 4:43 PM", { onKeyDown: onKeyDownSpy, }); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Enter)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -227,12 +280,16 @@ describe("TimePicker", () => { renderDatePicker("February 28, 2018 4:43 PM", { onKeyDown: onKeyDownSpy, }); - fireEvent.focus(instance?.input ?? new HTMLElement()); - const time = - datePicker?.querySelector(".react-datepicker__time-container") ?? - new HTMLElement(); - const lis = time.querySelectorAll("li"); - fireEvent.keyDown(lis[1] ?? new HTMLElement(), getKey(KeyType.Space)); + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + const time = safeQuerySelector( + datePicker, + ".react-datepicker__time-container", + ); + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); + fireEvent.keyDown(lis[1]!, getKey(KeyType.Space)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -242,17 +299,22 @@ describe("TimePicker", () => { onKeyDown: onKeyDownSpy, showTimeSelectOnly: true, }); - fireEvent.focus(instance?.input ?? new HTMLElement()); - fireEvent.keyDown( - instance?.input ?? new HTMLElement(), - getKey(KeyType.ArrowDown), - ); + + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + + fireEvent.focus(instance.input); + fireEvent.keyDown(instance.input, getKey(KeyType.ArrowDown)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); function setManually(string: string) { - fireEvent.focus(instance?.input ?? new HTMLElement()); - fireEvent.change(instance?.input ?? new HTMLElement(), { + if (!instance?.input) { + throw new Error("input is null/undefined"); + } + fireEvent.focus(instance.input); + fireEvent.change(instance.input, { target: { value: string }, }); } diff --git a/src/test/week_number_test.test.tsx b/src/test/week_number_test.test.tsx index ab2d33f6f..baa704cfa 100644 --- a/src/test/week_number_test.test.tsx +++ b/src/test/week_number_test.test.tsx @@ -4,6 +4,8 @@ import React from "react"; import { KeyType, addWeeks, newDate } from "../date_utils"; import WeekNumber from "../week_number"; +import { safeQuerySelector } from "./test_utils"; + type WeekNumberProps = React.ComponentProps; function renderWeekNumber( @@ -37,11 +39,11 @@ describe("WeekNumber", () => { const { container } = render( , ); - const weekNumber = container.querySelector( + const weekNumber = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); - fireEvent.click(weekNumber ?? new HTMLDivElement()); + fireEvent.click(weekNumber); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -104,11 +106,11 @@ describe("WeekNumber", () => { const { container } = render( , ); - const weekNumber = container.querySelector( + const weekNumber = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); - fireEvent.click(weekNumber ?? new HTMLDivElement()); + fireEvent.click(weekNumber); expect(onClickMock).toHaveBeenCalled(); }); diff --git a/src/test/week_picker_test.test.tsx b/src/test/week_picker_test.test.tsx index 76b3f2a60..e5ecb0bc1 100644 --- a/src/test/week_picker_test.test.tsx +++ b/src/test/week_picker_test.test.tsx @@ -3,6 +3,8 @@ import React from "react"; import DatePicker from "../index"; +import { safeQuerySelector } from "./test_utils"; + describe("WeekPicker", () => { it("should change the week when clicked on any option in the picker", () => { const onChangeSpy = jest.fn(); @@ -10,10 +12,12 @@ describe("WeekPicker", () => { , ); expect(onChangeSpy).not.toHaveBeenCalled(); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); - fireEvent.click( - container.querySelector(".react-datepicker__day") ?? new HTMLElement(), - ); + + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + + const dayElement = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.click(dayElement); expect(onChangeSpy).toHaveBeenCalled(); }); @@ -23,14 +27,13 @@ describe("WeekPicker", () => { , ); expect(onChangeSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); - expect(input).not.toBeNull(); - fireEvent.focus(input ?? new HTMLElement()); - const weekNumber = container.querySelector( + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); + const weekNumber = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); - fireEvent.click(weekNumber ?? new HTMLElement()); + fireEvent.click(weekNumber); expect(onChangeSpy).toHaveBeenCalled(); }); diff --git a/src/test/week_test.test.tsx b/src/test/week_test.test.tsx index dedd97367..1a6200c3e 100644 --- a/src/test/week_test.test.tsx +++ b/src/test/week_test.test.tsx @@ -13,6 +13,8 @@ import { } from "../date_utils"; import Week from "../week"; +import { safeQuerySelector } from "./test_utils"; + describe("Week", () => { it("should have the week CSS class", () => { const { container } = render( @@ -109,11 +111,12 @@ describe("Week", () => { month={getMonth(weekStart)} />, ); - const weekNumberElement = container.querySelector( + + const weekNumberElement = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); - fireEvent.click(weekNumberElement ?? new HTMLElement()); + fireEvent.click(weekNumberElement); expect(isEqual(firstDayReceived, weekStart)).toBe(true); }); @@ -131,11 +134,11 @@ describe("Week", () => { month={getMonth(weekStart)} />, ); - const weekNumberElement = container.querySelector( + const weekNumberElement = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); - fireEvent.click(weekNumberElement ?? new HTMLElement()); + fireEvent.click(weekNumberElement); expect(setOpenSpy).toHaveBeenCalledTimes(1); }); @@ -155,12 +158,12 @@ describe("Week", () => { />, ); - const weekNumberElement = container.querySelector( + const weekNumberElement = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); + fireEvent.click(weekNumberElement); - fireEvent.click(weekNumberElement ?? new HTMLElement()); expect(setOnWeekSelect).toHaveBeenCalledTimes(1); expect(setOpenSpy).toHaveBeenCalledTimes(0); }); @@ -183,11 +186,12 @@ describe("Week", () => { month={getMonth(weekStart)} />, ); - const weekNumberElement = container.querySelector( + + const weekNumberElement = safeQuerySelector( + container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); - fireEvent.click(weekNumberElement ?? new HTMLElement()); + fireEvent.click(weekNumberElement); expect(weekNumberReceived).toBe(realWeekNumber); }); @@ -227,9 +231,8 @@ describe("Week", () => { />, ); - const day = container.querySelector(".react-datepicker__day"); - expect(day).not.toBeNull(); - fireEvent.mouseEnter(day ?? new HTMLElement()); + const day = safeQuerySelector(container, ".react-datepicker__day"); + fireEvent.mouseEnter(day); expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith( getStartOfWeek(weekStart), diff --git a/src/test/year_dropdown_options_test.test.tsx b/src/test/year_dropdown_options_test.test.tsx index b9ce438f0..370124cdd 100644 --- a/src/test/year_dropdown_options_test.test.tsx +++ b/src/test/year_dropdown_options_test.test.tsx @@ -4,9 +4,10 @@ import React from "react"; import { addYears, getYear, newDate, subYears } from "../date_utils"; import YearDropdownOptions from "../year_dropdown_options"; +import { safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; + describe("YearDropdownOptions", () => { - let yearDropdown: HTMLElement | null = null, - handleChangeResult: number; + let yearDropdown: HTMLElement, handleChangeResult: number; const mockHandleChange = function (changeInput: number) { handleChangeResult = changeInput; }; @@ -57,11 +58,11 @@ describe("YearDropdownOptions", () => { }); it("increments the available years when the 'upcoming years' button is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector( - ".react-datepicker__navigation--years-upcoming", - ) ?? new HTMLElement(), + const navigationYearsUpcoming = safeQuerySelector( + yearDropdown, + ".react-datepicker__navigation--years-upcoming", ); + fireEvent.click(navigationYearsUpcoming); const textContents = Array.from( yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? [], @@ -87,11 +88,11 @@ describe("YearDropdownOptions", () => { }); it("decrements the available years when the 'previous years' button is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector( - ".react-datepicker__navigation--years-previous", - ) ?? new HTMLElement(), + const navigationYearsPrevious = safeQuerySelector( + yearDropdown, + ".react-datepicker__navigation--years-previous", ); + fireEvent.click(navigationYearsPrevious); const textContents = Array.from( yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? [], @@ -117,11 +118,17 @@ describe("YearDropdownOptions", () => { }); it("calls the supplied onChange function when a year is clicked", () => { - fireEvent.click( - Array.from( - yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? [], - ).find((node) => node.textContent?.includes("2015")) ?? new HTMLElement(), + const yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", ); + const year = yearOptions.find((node) => node.textContent?.includes("2015")); + + if (!year) { + throw new Error("Year 2015 not found!"); + } + + fireEvent.click(year); expect(handleChangeResult).toBe(2015); }); @@ -310,11 +317,11 @@ describe("YearDropdownOptions with scrollable dropwdown", () => { ), ).toBeUndefined(); - fireEvent.click( - container.querySelector( - ".react-datepicker__navigation--years-previous", - ) ?? new HTMLElement(), + const navigationYearsPrevious = safeQuerySelector( + container, + ".react-datepicker__navigation--years-previous", ); + fireEvent.click(navigationYearsPrevious); textContents = Array.from( container.querySelectorAll(".react-datepicker__year-option"), @@ -335,11 +342,12 @@ describe("YearDropdownOptions with scrollable dropwdown", () => { ).length, ).toBe(0); - fireEvent.click( - container.querySelector( - ".react-datepicker__navigation--years-upcoming", - ) ?? new HTMLElement(), + const navigationYearsUpcoming = safeQuerySelector( + container, + ".react-datepicker__navigation--years-upcoming", ); + fireEvent.click(navigationYearsUpcoming); + textContents = Array.from( container.querySelectorAll(".react-datepicker__year-option"), ).filter((node) => node.textContent); @@ -391,11 +399,11 @@ describe("YearDropdownOptions with scrollable dropwdown", () => { ), ).toBeUndefined(); - fireEvent.click( - container.querySelector( - ".react-datepicker__navigation--years-previous", - ) ?? new HTMLElement(), + const navigationYearsPrevious = safeQuerySelector( + container, + ".react-datepicker__navigation--years-previous", ); + fireEvent.click(navigationYearsPrevious); textContents = Array.from( container.querySelectorAll(".react-datepicker__year-option"), @@ -453,11 +461,11 @@ describe("YearDropdownOptions with scrollable dropwdown", () => { ), ).toBeUndefined(); - fireEvent.click( - container.querySelector( - ".react-datepicker__navigation--years-upcoming", - ) ?? new HTMLElement(), + const navigationYearsUpcoming = safeQuerySelector( + container, + ".react-datepicker__navigation--years-upcoming", ); + fireEvent.click(navigationYearsUpcoming); textContents = Array.from( container.querySelectorAll(".react-datepicker__year-option"), diff --git a/src/test/year_dropdown_test.test.tsx b/src/test/year_dropdown_test.test.tsx index b91a9c841..784a7ac21 100644 --- a/src/test/year_dropdown_test.test.tsx +++ b/src/test/year_dropdown_test.test.tsx @@ -4,10 +4,10 @@ import React from "react"; import { newDate } from "../date_utils"; import YearDropdown from "../year_dropdown"; -import { range } from "./test_utils"; +import { range, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; describe("YearDropdown", () => { - let yearDropdown: HTMLElement | null = null; + let yearDropdown: HTMLElement; let lastOnChangeValue: number | null; function onChangeMock(value: number) { @@ -50,10 +50,11 @@ describe("YearDropdown", () => { }); it("opens a list when read view is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector(".react-datepicker__year-read-view") ?? - new HTMLElement(), + const yearReadView = safeQuerySelector( + yearDropdown, + ".react-datepicker__year-read-view", ); + fireEvent.click(yearReadView); const optionsView = yearDropdown?.querySelectorAll( "react-datepicker__year-dropdown", ); @@ -61,40 +62,58 @@ describe("YearDropdown", () => { }); it("closes the dropdown when a year is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector(".react-datepicker__year-read-view") ?? - new HTMLElement(), + const yearReadView = safeQuerySelector( + yearDropdown, + ".react-datepicker__year-read-view", ); - fireEvent.click( - (yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? - [])[0] ?? new HTMLElement(), + fireEvent.click(yearReadView); + + const yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", ); + const yearOption = yearOptions[0]!; + fireEvent.click(yearOption); expect( yearDropdown?.querySelectorAll("react-datepicker__year-dropdown"), ).toHaveLength(0); }); it("does not call the supplied onChange function when the same year is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector(".react-datepicker__year-read-view") ?? - new HTMLElement(), + const yearReadView = safeQuerySelector( + yearDropdown, + ".react-datepicker__year-read-view", ); - fireEvent.click( - (yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? - [])[6] ?? new HTMLElement(), + fireEvent.click(yearReadView); + + const minYearOptionsLen = 7; + const yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", + minYearOptionsLen, ); + + const yearOption = yearOptions[6]!; + fireEvent.click(yearOption); expect(lastOnChangeValue).toBeNull(); }); it("calls the supplied onChange function when a different year is clicked", () => { - fireEvent.click( - yearDropdown?.querySelector(".react-datepicker__year-read-view") ?? - new HTMLElement(), + const yearReadView = safeQuerySelector( + yearDropdown, + ".react-datepicker__year-read-view", ); - fireEvent.click( - (yearDropdown?.querySelectorAll(".react-datepicker__year-option") ?? - [])[7] ?? new HTMLElement(), + fireEvent.click(yearReadView); + + const minYearOptionsLen = 7; + const yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", + minYearOptionsLen, ); + + const yearOption = yearOptions[7]!; + fireEvent.click(yearOption); expect(lastOnChangeValue).toEqual(2014); }); }); diff --git a/src/test/year_picker_test.test.tsx b/src/test/year_picker_test.test.tsx index abfe3e27d..4ea99267c 100644 --- a/src/test/year_picker_test.test.tsx +++ b/src/test/year_picker_test.test.tsx @@ -14,7 +14,13 @@ import { import DatePicker from "../index"; import Year from "../year"; -import { getKey, gotoNextView, openDateInput } from "./test_utils"; +import { + getKey, + gotoNextView, + openDateInput, + safeQuerySelector, + safeQuerySelectorAll, +} from "./test_utils"; const getYearOffset = (calendar: Element, date: Date): number => { const dateNode = calendar.querySelector( @@ -71,10 +77,16 @@ describe("YearPicker", () => { onYearMouseLeave={() => {}} />, ); - const firstYearDiv = container.querySelectorAll( + + const minRequiredYearLen = 2; + const yearDivs = safeQuerySelectorAll( + container, ".react-datepicker__year-text", - )[1]; - fireEvent.click(firstYearDiv ?? new HTMLElement()); + minRequiredYearLen, + ); + + const firstYearDiv = yearDivs[1]!; + fireEvent.click(firstYearDiv); expect(onYearChangeSpy).toHaveBeenCalled(); }); @@ -563,13 +575,15 @@ describe("YearPicker", () => { />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); - const calendar = container.querySelector(".react-datepicker"); - const previousButton = calendar?.querySelector( + const calendar = safeQuerySelector(container, ".react-datepicker"); + const previousButton = safeQuerySelector( + calendar, ".react-datepicker__navigation--previous", ); - fireEvent.click(previousButton ?? new HTMLElement()); + fireEvent.click(previousButton); const year = container.querySelector(".react-datepicker__year"); const allPreselectedYears = year?.querySelectorAll(`.${className}`) ?? []; @@ -594,14 +608,16 @@ describe("YearPicker", () => { />, ); - fireEvent.focus(container.querySelector("input") ?? new HTMLElement()); + const input = safeQuerySelector(container, "input"); + fireEvent.focus(input); - const calendar = container.querySelector(".react-datepicker"); - const nextButton = calendar?.querySelector( + const calendar = safeQuerySelector(container, ".react-datepicker"); + const nextButton = safeQuerySelector( + calendar, ".react-datepicker__navigation--next", ); - fireEvent.click(nextButton ?? new HTMLElement()); + fireEvent.click(nextButton); const year = container.querySelector(".react-datepicker__year"); const allPreselectedYears = year?.querySelectorAll(`.${className}`) ?? []; @@ -683,131 +699,144 @@ describe("YearPicker", () => { it("should preSelect and set 2020 on left arrow press", () => { const yearPicker = getPicker("2021-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateLeft(target ?? new HTMLElement()); + simulateLeft(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2020); }); it("should preSelect and set 2022 on left arrow press", () => { const yearPicker = getPicker("2021-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateRight(target ?? new HTMLElement()); + simulateRight(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2022); }); it("should preSelect and set 2021 on up arrow press", () => { const yearPicker = getPicker("2024-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateUp(target ?? new HTMLElement()); + simulateUp(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2021); }); it("should preSelect and set 2027 on down arrow press", () => { const yearPicker = getPicker("2024-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateDown(target ?? new HTMLElement()); + simulateDown(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2027); }); it("should paginate from 2018 to 2015", () => { const yearPicker = getPicker("2018-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateUp(target ?? new HTMLElement()); + simulateUp(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2015); }); it("should paginate from 2018 to 2016 with custom yearItemNumber", () => { const yearPicker = getPicker("2018-01-01", { yearItemNumber: 8 }); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateUp(target ?? new HTMLElement()); + simulateUp(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2016); }); it("should paginate from 2019 to 2014 with custom yearItemNumber", () => { const yearPicker = getPicker("2019-01-01", { yearItemNumber: 8 }); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateUp(target ?? new HTMLElement()); + simulateUp(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2014); }); it("should paginate from 2028 to 2031", () => { const yearPicker = getPicker("2028-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateDown(target ?? new HTMLElement()); + simulateDown(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2031); }); it("should paginate from 2024 to 2026 with custom yearItemNumber", () => { const yearPicker = getPicker("2024-01-01", { yearItemNumber: 8 }); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateDown(target ?? new HTMLElement()); + simulateDown(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2026); }); it("should paginate from 2022 to 2027 with custom yearItemNumber", () => { const yearPicker = getPicker("2022-01-01", { yearItemNumber: 8 }); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateDown(target ?? new HTMLElement()); + simulateDown(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2027); }); it("should paginate from 2017 to 2016", () => { const yearPicker = getPicker("2017-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateLeft(target ?? new HTMLElement()); + simulateLeft(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2016); }); it("should paginate from 2028 to 2029", () => { const yearPicker = getPicker("2028-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateRight(target ?? new HTMLElement()); + simulateRight(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2029); }); it("should select 2021 when Enter key is pressed", () => { const yearPicker = getPicker("2021-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - fireEvent.keyDown(target ?? new HTMLElement(), getKey(KeyType.Enter)); + fireEvent.keyDown(target, getKey(KeyType.Enter)); expect(selectedDay ? getYear(selectedDay) : selectedDay).toBe(2021); }); @@ -823,12 +852,12 @@ describe("YearPicker", () => { />, ); - const dateInput = container.querySelector("input"); - fireEvent.focus(dateInput ?? new HTMLElement()); + const dateInput = safeQuerySelector(container, "input"); + fireEvent.focus(dateInput); - const year = container.querySelector(".react-datepicker__year-text"); + const year = safeQuerySelector(container, ".react-datepicker__year-text"); - fireEvent.keyDown(year ?? new HTMLElement(), getKey(KeyType.ArrowDown)); + fireEvent.keyDown(year, getKey(KeyType.ArrowDown)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -836,11 +865,12 @@ describe("YearPicker", () => { it("should select 2021 when Space key is pressed", () => { const yearPicker = getPicker("2021-01-01"); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - fireEvent.keyDown(target ?? new HTMLElement(), getKey(KeyType.Space)); + fireEvent.keyDown(target, getKey(KeyType.Space)); expect(selectedDay ? getYear(selectedDay) : selectedDay).toBe(2021); }); @@ -849,10 +879,11 @@ describe("YearPicker", () => { disabledKeyboardNavigation: true, }); - const target = yearPicker.querySelector( + const target = safeQuerySelector( + yearPicker, ".react-datepicker__year-text--selected", ); - simulateRight(target ?? new HTMLElement()); + simulateRight(target); expect(preSelected ? getYear(preSelected) : preSelected).toBe(2021); });