From df7a9730784bb57ffa91a2d761fca494380fd9bf Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:15:03 +0530 Subject: [PATCH 01/24] =?UTF-8?q?=E2=9C=A8=20Added=20a=20utility=20functio?= =?UTF-8?q?n=20to=20ensure=20element=20existence=20with=20querySelector/qu?= =?UTF-8?q?erySelectorAll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5038 --- src/test/test_utils.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 72cf1fbb9..cceca212f 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -80,3 +80,27 @@ 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, +): T[] => { + const elements = Array.from(container.querySelectorAll(selector)) as T[]; + if (elements.length) { + return elements; + } + + throw new Error(`Element with selector '${selector}' not found`); +}; From 0ddb892073dfbb6e97207a3312c1350c5aa08e90 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:15:32 +0530 Subject: [PATCH 02/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20year=5Fpicker=5Ftest's=20querySelector/querySelectorAll=20wi?= =?UTF-8?q?th=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20ele?= =?UTF-8?q?ment=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/year_picker_test.test.tsx | 124 ++++++++++++++++++----------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/src/test/year_picker_test.test.tsx b/src/test/year_picker_test.test.tsx index abfe3e27d..5334b42f4 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,17 @@ describe("YearPicker", () => { onYearMouseLeave={() => {}} />, ); - const firstYearDiv = container.querySelectorAll( + + const yearDivs = safeQuerySelectorAll( + container, ".react-datepicker__year-text", - )[1]; - fireEvent.click(firstYearDiv ?? new HTMLElement()); + ); + if (yearDivs.length < 2) { + throw new Error("yearDivs doesn't have enough length"); + } + + const firstYearDiv = yearDivs[1]!; + fireEvent.click(firstYearDiv); expect(onYearChangeSpy).toHaveBeenCalled(); }); @@ -563,13 +576,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 +609,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 +700,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 +853,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 +866,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 +880,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); }); From 25f08e8e20d42bcd09392f6dba05b83db5777140 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:16:55 +0530 Subject: [PATCH 03/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20year=5Fdropdown=5Ftest's=20querySelector/querySelectorAll=20?= =?UTF-8?q?with=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20e?= =?UTF-8?q?lement=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/year_dropdown_test.test.tsx | 69 ++++++++++++++++++---------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/src/test/year_dropdown_test.test.tsx b/src/test/year_dropdown_test.test.tsx index b91a9c841..e5879b31d 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,62 @@ 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 yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", ); + + if (yearOptions.length < 7) { + throw new Error("Corresponding year option to click is not available!"); + } + + 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 yearOptions = safeQuerySelectorAll( + yearDropdown, + ".react-datepicker__year-option", ); + + if (yearOptions.length < 8) { + throw new Error("Corresponding year option to click is not available!"); + } + + const yearOption = yearOptions[7]!; + fireEvent.click(yearOption); expect(lastOnChangeValue).toEqual(2014); }); }); From 5e387a73cbb3071eab2cfcc4cb1155b84e984a31 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:17:41 +0530 Subject: [PATCH 04/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20year=5Fdropdown=5Foptions=5Ftest's=20querySelector/querySele?= =?UTF-8?q?ctorAll=20with=20safeQuerySelector/safeQuerySelectorAll=20to=20?= =?UTF-8?q?ensure=20element=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/year_dropdown_options_test.test.tsx | 68 +++++++++++--------- 1 file changed, 38 insertions(+), 30 deletions(-) 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"), From ecb15063ea212239b986e6461684cf7221a572e8 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:18:21 +0530 Subject: [PATCH 05/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20month=5Fdropdown=5Ftest's=20querySelector/querySelectorAll?= =?UTF-8?q?=20with=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure?= =?UTF-8?q?=20element=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/month_dropdown_test.test.tsx | 75 +++++++++++++++++---------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/src/test/month_dropdown_test.test.tsx b/src/test/month_dropdown_test.test.tsx index a6344719f..8fea5f33f 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,21 @@ 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 monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", ); + if (monthOptions.length < 2) { + throw new Error("monthOptions must have at least 2 elements"); + } + + fireEvent.click(monthOptions[1]!); expect( monthDropdown?.querySelectorAll(".react-datepicker__month-dropdown"), ).toHaveLength(0); @@ -137,26 +147,37 @@ 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 monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", ); + if (monthOptions.length < 12) { + throw new Error("monthOptions must have at least 12 elements"); + } + 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 monthOptions = safeQuerySelectorAll( + monthDropdown, + ".react-datepicker__month-option", ); + if (monthOptions.length < 3) { + throw new Error("monthOptions must have at least 3 elements"); + } + fireEvent.click(monthOptions[2]!); expect(handleChangeResult).toEqual(2); }); From 1ed38f3992880518d52eb3aea87d3c2248c163fd Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:18:43 +0530 Subject: [PATCH 06/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20month=5Fyear=5Fdropdown=5Ftest's=20querySelector/querySelect?= =?UTF-8?q?orAll=20with=20safeQuerySelector/safeQuerySelectorAll=20to=20en?= =?UTF-8?q?sure=20element=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/month_year_dropdown_test.test.tsx | 97 ++++++++++++---------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/src/test/month_year_dropdown_test.test.tsx b/src/test/month_year_dropdown_test.test.tsx index 916e12d96..98ae059b4 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,24 @@ 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 monthYearOptions = safeQuerySelectorAll( + monthYearDropdown, + ".react-datepicker__month-year-option", ); + if (monthYearOptions.length < 6) { + throw new Error("Specified monthYearOptions offset is not available"); + } + + const monthYearOption = monthYearOptions[5]!; + fireEvent.click(monthYearOption); + expect(handleChangeResult?.toString()).toBe(expected_date.toString()); }); @@ -297,10 +308,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 +325,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()); From b8c98f63aee19bc2cd1d51e839969c1a1a76f7b9 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:20:14 +0530 Subject: [PATCH 07/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20week=5Fpicker=5Ftest's=20querySelector/querySelectorAll=20wi?= =?UTF-8?q?th=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20ele?= =?UTF-8?q?ment=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/week_picker_test.test.tsx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/test/week_picker_test.test.tsx b/src/test/week_picker_test.test.tsx index 76b3f2a60..d92879a5a 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,15 @@ describe("WeekPicker", () => { , ); expect(onChangeSpy).not.toHaveBeenCalled(); - const input = container.querySelector("input"); + const input = safeQuerySelector(container, "input"); expect(input).not.toBeNull(); - fireEvent.focus(input ?? new HTMLElement()); - const weekNumber = container.querySelector( + 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(); }); From a2602dde6e582cc5d9a8995bca641ee98655b4f9 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:20:36 +0530 Subject: [PATCH 08/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20week=5Ftest's=20querySelector/querySelectorAll=20with=20safe?= =?UTF-8?q?QuerySelector/safeQuerySelectorAll=20to=20ensure=20element=20ex?= =?UTF-8?q?istence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/week_test.test.tsx | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/test/week_test.test.tsx b/src/test/week_test.test.tsx index dedd97367..159a0db2b 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,13 @@ 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 +135,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(setOpenSpy).toHaveBeenCalledTimes(1); }); @@ -155,12 +160,13 @@ describe("Week", () => { />, ); - 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(setOnWeekSelect).toHaveBeenCalledTimes(1); expect(setOpenSpy).toHaveBeenCalledTimes(0); }); @@ -183,11 +189,13 @@ 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 +235,9 @@ describe("Week", () => { />, ); - const day = container.querySelector(".react-datepicker__day"); + const day = safeQuerySelector(container, ".react-datepicker__day"); expect(day).not.toBeNull(); - fireEvent.mouseEnter(day ?? new HTMLElement()); + fireEvent.mouseEnter(day); expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith( getStartOfWeek(weekStart), From 3b6d4949a5e8ecdb285e9cac4caa31a308742393 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:20:52 +0530 Subject: [PATCH 09/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20week=5Fnumber=5Ftest's=20querySelector/querySelectorAll=20wi?= =?UTF-8?q?th=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20ele?= =?UTF-8?q?ment=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/week_number_test.test.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/week_number_test.test.tsx b/src/test/week_number_test.test.tsx index ab2d33f6f..433e03bd5 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,12 @@ 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 +107,12 @@ 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(); }); From 7daa8a8865a6a13c7308d89445b379cb6a78e0cd Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:21:16 +0530 Subject: [PATCH 10/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20day=5Ftest's=20querySelector/querySelectorAll=20with=20safeQ?= =?UTF-8?q?uerySelector/safeQuerySelectorAll=20to=20ensure=20element=20exi?= =?UTF-8?q?stence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/day_test.test.tsx | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) 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(); }); }); From f6a4c8d752e622a1fd71ff3b3c307cc563ef0d46 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:58:27 +0530 Subject: [PATCH 11/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20time=5Finput=5Ftest's=20querySelector/querySelectorAll=20wit?= =?UTF-8?q?h=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20elem?= =?UTF-8?q?ent=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/time_input_test.test.tsx | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) 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 }, }); From d85f031cb1af8e4ea82a432c3e7b77c52593e054 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:58:48 +0530 Subject: [PATCH 12/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20show=5Ftime=5Ftest's=20querySelector/querySelectorAll=20with?= =?UTF-8?q?=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20eleme?= =?UTF-8?q?nt=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/show_time_test.test.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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", () => { From 2dd59fb23b0a541b863c2e446acdffe72f1a098b Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 14:59:20 +0530 Subject: [PATCH 13/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20min=5Ftime=5Ftest's=20querySelector/querySelectorAll=20with?= =?UTF-8?q?=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20eleme?= =?UTF-8?q?nt=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/min_time_test.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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); From 7e72bda538c8bbd807b043b5e976a7eb2aa3ce4e Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 15:00:15 +0530 Subject: [PATCH 14/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20timepicker=5Ftest's=20querySelector/querySelectorAll=20with?= =?UTF-8?q?=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20eleme?= =?UTF-8?q?nt=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/timepicker_test.test.tsx | 222 ++++++++++++++++++++---------- 1 file changed, 153 insertions(+), 69 deletions(-) diff --git a/src/test/timepicker_test.test.tsx b/src/test/timepicker_test.test.tsx index 0c51ed079..cbd1bea3e 100644 --- a/src/test/timepicker_test.test.tsx +++ b/src/test/timepicker_test.test.tsx @@ -4,11 +4,11 @@ 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"; 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 +29,58 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + 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 +114,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 +130,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 +146,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 +175,59 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); await waitFor(() => { expect(document.activeElement).toBe(input); @@ -185,12 +236,19 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + fireEvent.keyDown(lis[1]!, getKey(KeyType.Escape)); expect(getInputString()).toBe("February 28, 2018 4:43 PM"); }); @@ -199,12 +257,19 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + fireEvent.keyDown(lis[1]!, getKey(KeyType.Escape)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -213,12 +278,19 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -227,12 +299,19 @@ 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"); + if (lis.length < 2) { + throw new Error("Time list items must be at least 2"); + } + fireEvent.keyDown(lis[1]!, getKey(KeyType.Space)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -242,17 +321,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 }, }); } From 6e99604098a8ac00eb33c13f8ca8158f6467d705 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 15:00:33 +0530 Subject: [PATCH 15/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20calendar=5Ficon's=20querySelector/querySelectorAll=20with=20?= =?UTF-8?q?safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20element?= =?UTF-8?q?=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/calendar_icon.test.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/calendar_icon.test.tsx b/src/test/calendar_icon.test.tsx index 27f5c241e..6dd4b93e0 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,12 @@ describe("CalendarIcon", () => { it("should fire onClick event when the icon is clicked", () => { const { container } = render(); - const icon = container.querySelector("svg.react-datepicker__calendar-icon"); + const icon = safeQuerySelector( + container, + "svg.react-datepicker__calendar-icon", + ); expect(icon).not.toBeNull(); - fireEvent.click(icon ?? new Element()); + fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -50,9 +54,9 @@ describe("CalendarIcon", () => { , ); - const icon = container.querySelector("i.fa-example-icon"); + const icon = safeQuerySelector(container, "i.fa-example-icon"); expect(icon).not.toBeNull(); - fireEvent.click(icon ?? new Element()); + fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -75,8 +79,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); From 9d1fa8fa31e6638e0bb3b5828d495d225d0aa704 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 15:00:50 +0530 Subject: [PATCH 16/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20calendar=5Ftest's=20querySelector/querySelectorAll=20with=20?= =?UTF-8?q?safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20element?= =?UTF-8?q?=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/calendar_test.test.tsx | 216 ++++++++++++++++++-------------- 1 file changed, 125 insertions(+), 91 deletions(-) diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index 0e55b3b83..01d61f26f 100644 --- a/src/test/calendar_test.test.tsx +++ b/src/test/calendar_test.test.tsx @@ -31,7 +31,7 @@ import { } from "../date_utils"; import DatePicker from "../index"; -import { getKey } from "./test_utils"; +import { getKey, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; import type { ReactDatePickerCustomHeaderProps } from "../calendar"; import type { Locale } from "../date_utils"; @@ -652,8 +652,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 +667,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 +715,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 +728,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 +873,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 +900,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 +996,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 +1032,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 +1054,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 +1093,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 +1152,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 +1176,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 +1200,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 +1223,7 @@ describe("Calendar", () => { />, ); - const input = container.querySelector("input") ?? new HTMLElement(); + const input = safeQuerySelector(container, "input"); fireEvent.focus(input); expect(onCalendarOpen).toHaveBeenCalled(); @@ -1246,28 +1253,32 @@ 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 monthDropdownContainer = safeQuerySelector( + calendar, + ".react-datepicker__month-dropdown-container", + ); + const select = safeQuerySelector(monthDropdownContainer, "select"); + fireEvent.change(select, { target: { value: Array.from( @@ -1299,10 +1310,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 +1363,18 @@ 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 options = safeQuerySelectorAll(select, "option"); + + if (options.length < 4) { + throw new Error("Options with the 3rd index not found"); + } + + const option = options[3]!; fireEvent.change(select, { target: { value: option.value, @@ -1368,12 +1386,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 +1431,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 +2300,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 +2318,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 +2379,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( From 26eec628893cb4450e12fa8a9e03f90cb6329be4 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 19 Aug 2024 15:01:09 +0530 Subject: [PATCH 17/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=F0=9F=A7=AA=20Refactor?= =?UTF-8?q?=20datepicker=5Ftest's=20querySelector/querySelectorAll=20with?= =?UTF-8?q?=20safeQuerySelector/safeQuerySelectorAll=20to=20ensure=20eleme?= =?UTF-8?q?nt=20existence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This change prevents tests from passing with unexpected null elements, enhancing test reliability and catching potential issues earlier. Closes #5038 --- src/test/datepicker_test.test.tsx | 386 +++++++++++++++++------------- 1 file changed, 219 insertions(+), 167 deletions(-) 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); From ee2234090dc1f6ee6276b3af97a7b0c0767f8878 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Sun, 25 Aug 2024 17:59:06 +0530 Subject: [PATCH 18/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Remove=20the=20redun?= =?UTF-8?q?dant=20check=20of=20the=20safeQuerySelector=20result=20to=20not?= =?UTF-8?q?=20be=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - As the safeQuerySelector automatically throws if the element is not found, additionally checking for null value is not needed --- src/test/calendar_icon.test.tsx | 2 -- src/test/week_number_test.test.tsx | 2 -- src/test/week_picker_test.test.tsx | 2 -- src/test/week_test.test.tsx | 7 +------ 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/test/calendar_icon.test.tsx b/src/test/calendar_icon.test.tsx index 6dd4b93e0..1235d0c04 100644 --- a/src/test/calendar_icon.test.tsx +++ b/src/test/calendar_icon.test.tsx @@ -43,7 +43,6 @@ describe("CalendarIcon", () => { container, "svg.react-datepicker__calendar-icon", ); - expect(icon).not.toBeNull(); fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); @@ -55,7 +54,6 @@ describe("CalendarIcon", () => { ); const icon = safeQuerySelector(container, "i.fa-example-icon"); - expect(icon).not.toBeNull(); fireEvent.click(icon); expect(onClickMock).toHaveBeenCalledTimes(1); diff --git a/src/test/week_number_test.test.tsx b/src/test/week_number_test.test.tsx index 433e03bd5..baa704cfa 100644 --- a/src/test/week_number_test.test.tsx +++ b/src/test/week_number_test.test.tsx @@ -43,7 +43,6 @@ describe("WeekNumber", () => { container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); fireEvent.click(weekNumber); expect(onClickMock).toHaveBeenCalledTimes(1); }); @@ -111,7 +110,6 @@ describe("WeekNumber", () => { container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); 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 d92879a5a..e5ecb0bc1 100644 --- a/src/test/week_picker_test.test.tsx +++ b/src/test/week_picker_test.test.tsx @@ -28,13 +28,11 @@ describe("WeekPicker", () => { ); expect(onChangeSpy).not.toHaveBeenCalled(); const input = safeQuerySelector(container, "input"); - expect(input).not.toBeNull(); fireEvent.focus(input); const weekNumber = safeQuerySelector( container, ".react-datepicker__week-number", ); - expect(weekNumber).not.toBeNull(); fireEvent.click(weekNumber); expect(onChangeSpy).toHaveBeenCalled(); }); diff --git a/src/test/week_test.test.tsx b/src/test/week_test.test.tsx index 159a0db2b..1a6200c3e 100644 --- a/src/test/week_test.test.tsx +++ b/src/test/week_test.test.tsx @@ -116,7 +116,6 @@ describe("Week", () => { container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); fireEvent.click(weekNumberElement); expect(isEqual(firstDayReceived, weekStart)).toBe(true); }); @@ -139,7 +138,6 @@ describe("Week", () => { container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); fireEvent.click(weekNumberElement); expect(setOpenSpy).toHaveBeenCalledTimes(1); }); @@ -164,9 +162,8 @@ describe("Week", () => { container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); - fireEvent.click(weekNumberElement); + expect(setOnWeekSelect).toHaveBeenCalledTimes(1); expect(setOpenSpy).toHaveBeenCalledTimes(0); }); @@ -194,7 +191,6 @@ describe("Week", () => { container, ".react-datepicker__week-number", ); - expect(weekNumberElement).not.toBeNull(); fireEvent.click(weekNumberElement); expect(weekNumberReceived).toBe(realWeekNumber); }); @@ -236,7 +232,6 @@ describe("Week", () => { ); const day = safeQuerySelector(container, ".react-datepicker__day"); - expect(day).not.toBeNull(); fireEvent.mouseEnter(day); expect(onDayMouseEnterSpy).toHaveBeenLastCalledWith( From 72cc390099dacdde69b3ee52274c30a9aff3abe6 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Sun, 25 Aug 2024 18:01:39 +0530 Subject: [PATCH 19/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20safeQuery?= =?UTF-8?q?Selector=20not=20toBe=20null=20test=20block=20to=20notThrow=20a?= =?UTF-8?q?s=20the=20safeQuerySelector=20automatically=20throws=20an=20err?= =?UTF-8?q?or=20if=20the=20element=20is=20not=20found?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/calendar_test.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index 01d61f26f..db024d94d 100644 --- a/src/test/calendar_test.test.tsx +++ b/src/test/calendar_test.test.tsx @@ -229,8 +229,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", () => { From e5d056838414ffb41a104f2cb60972cdc35cda61 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Sun, 25 Aug 2024 21:31:21 +0530 Subject: [PATCH 20/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20the=20safeQ?= =?UTF-8?q?uerySelectorAll=20helper=20to=20throw=20an=20error=20if=20the?= =?UTF-8?q?=20found=20element=20is=20less=20than=20the=20minExpected=20par?= =?UTF-8?q?am?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/test_utils.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index cceca212f..8b2ee4df3 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -96,11 +96,18 @@ export const safeQuerySelector = ( export const safeQuerySelectorAll = ( container: HTMLElement, selector: string, + minExpected: number = 1, ): T[] => { const elements = Array.from(container.querySelectorAll(selector)) as T[]; - if (elements.length) { - return elements; + + 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`, + ); } - throw new Error(`Element with selector '${selector}' not found`); + return elements; }; From 372338656b83037b9845239af3d0aab79036ef9b Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Sun, 25 Aug 2024 21:33:16 +0530 Subject: [PATCH 21/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20the=20usa?= =?UTF-8?q?ge=20of=20safeQuerySelectorAll=20to=20also=20pass=20the=20requi?= =?UTF-8?q?red=20no=20of=20elements=20and=20remove=20the=20redundant=20thr?= =?UTF-8?q?ow=20error=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/calendar_test.test.tsx | 12 ++++--- src/test/month_dropdown_test.test.tsx | 16 ++++----- src/test/month_year_dropdown_test.test.tsx | 6 ++-- src/test/timepicker_test.test.tsx | 42 ++++++---------------- src/test/year_dropdown_test.test.tsx | 12 +++---- src/test/year_picker_test.test.tsx | 5 ++- 6 files changed, 32 insertions(+), 61 deletions(-) diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index db024d94d..db181fb1c 100644 --- a/src/test/calendar_test.test.tsx +++ b/src/test/calendar_test.test.tsx @@ -1368,12 +1368,14 @@ describe("Calendar", () => { calendar, ".react-datepicker__month-year-dropdown-container", ); - const select = safeQuerySelector(monthYearDropdownContainer, "select"); - const options = safeQuerySelectorAll(select, "option"); - if (options.length < 4) { - throw new Error("Options with the 3rd index not found"); - } + const select = safeQuerySelector(monthYearDropdownContainer, "select"); + const minMonthYearOptionsLen = 4; + const options = safeQuerySelectorAll( + select, + "option", + minMonthYearOptionsLen, + ); const option = options[3]!; fireEvent.change(select, { diff --git a/src/test/month_dropdown_test.test.tsx b/src/test/month_dropdown_test.test.tsx index 8fea5f33f..ac3d918c1 100644 --- a/src/test/month_dropdown_test.test.tsx +++ b/src/test/month_dropdown_test.test.tsx @@ -115,13 +115,12 @@ describe("MonthDropdown", () => { ); fireEvent.click(monthReadView); + const minMonthOptionsLen = 2; const monthOptions = safeQuerySelectorAll( monthDropdown, ".react-datepicker__month-option", + minMonthOptionsLen, ); - if (monthOptions.length < 2) { - throw new Error("monthOptions must have at least 2 elements"); - } fireEvent.click(monthOptions[1]!); expect( @@ -152,13 +151,13 @@ describe("MonthDropdown", () => { ".react-datepicker__month-read-view", ); fireEvent.click(monthReadView); + + const monthOptionsLen = 12; const monthOptions = safeQuerySelectorAll( monthDropdown, ".react-datepicker__month-option", + monthOptionsLen, ); - if (monthOptions.length < 12) { - throw new Error("monthOptions must have at least 12 elements"); - } fireEvent.click(monthOptions[11]!); expect(handleChangeResult).toBeNull(); }); @@ -170,13 +169,12 @@ describe("MonthDropdown", () => { ); fireEvent.click(monthReadView); + const minRequiredMonthsLen = 3; const monthOptions = safeQuerySelectorAll( monthDropdown, ".react-datepicker__month-option", + minRequiredMonthsLen, ); - if (monthOptions.length < 3) { - throw new Error("monthOptions must have at least 3 elements"); - } 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 98ae059b4..ea5067eed 100644 --- a/src/test/month_year_dropdown_test.test.tsx +++ b/src/test/month_year_dropdown_test.test.tsx @@ -188,15 +188,13 @@ describe("MonthYearDropdown", () => { ); fireEvent.click(monthYearReadView); + const minRequiredMonthYearOptionsLen = 6; const monthYearOptions = safeQuerySelectorAll( monthYearDropdown, ".react-datepicker__month-year-option", + minRequiredMonthYearOptionsLen, ); - if (monthYearOptions.length < 6) { - throw new Error("Specified monthYearOptions offset is not available"); - } - const monthYearOption = monthYearOptions[5]!; fireEvent.click(monthYearOption); diff --git a/src/test/timepicker_test.test.tsx b/src/test/timepicker_test.test.tsx index cbd1bea3e..8db6f4baf 100644 --- a/src/test/timepicker_test.test.tsx +++ b/src/test/timepicker_test.test.tsx @@ -6,6 +6,8 @@ import DatePicker from "../index"; import { getKey, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; +const MIN_TIME_LI_LEN = 2; + describe("TimePicker", () => { let datePicker: HTMLDivElement; let div: HTMLDivElement; @@ -40,10 +42,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); fireEvent.click(lis[1]!); expect(getInputString()).toBe("February 28, 2018 12:30 AM"); }); @@ -183,10 +182,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + 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"); }); @@ -201,10 +197,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + 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"); }); @@ -223,10 +216,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); await waitFor(() => { @@ -244,10 +234,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + 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"); }); @@ -265,10 +252,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); fireEvent.keyDown(lis[1]!, getKey(KeyType.Escape)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -286,10 +270,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); fireEvent.keyDown(lis[1]!, getKey(KeyType.Enter)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); @@ -307,10 +288,7 @@ describe("TimePicker", () => { datePicker, ".react-datepicker__time-container", ); - const lis = safeQuerySelectorAll(time, "li"); - if (lis.length < 2) { - throw new Error("Time list items must be at least 2"); - } + const lis = safeQuerySelectorAll(time, "li", MIN_TIME_LI_LEN); fireEvent.keyDown(lis[1]!, getKey(KeyType.Space)); expect(onKeyDownSpy).toHaveBeenCalledTimes(1); }); diff --git a/src/test/year_dropdown_test.test.tsx b/src/test/year_dropdown_test.test.tsx index e5879b31d..784a7ac21 100644 --- a/src/test/year_dropdown_test.test.tsx +++ b/src/test/year_dropdown_test.test.tsx @@ -86,15 +86,13 @@ describe("YearDropdown", () => { ); fireEvent.click(yearReadView); + const minYearOptionsLen = 7; const yearOptions = safeQuerySelectorAll( yearDropdown, ".react-datepicker__year-option", + minYearOptionsLen, ); - if (yearOptions.length < 7) { - throw new Error("Corresponding year option to click is not available!"); - } - const yearOption = yearOptions[6]!; fireEvent.click(yearOption); expect(lastOnChangeValue).toBeNull(); @@ -107,15 +105,13 @@ describe("YearDropdown", () => { ); fireEvent.click(yearReadView); + const minYearOptionsLen = 7; const yearOptions = safeQuerySelectorAll( yearDropdown, ".react-datepicker__year-option", + minYearOptionsLen, ); - if (yearOptions.length < 8) { - throw new Error("Corresponding year option to click is not available!"); - } - 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 5334b42f4..4ea99267c 100644 --- a/src/test/year_picker_test.test.tsx +++ b/src/test/year_picker_test.test.tsx @@ -78,13 +78,12 @@ describe("YearPicker", () => { />, ); + const minRequiredYearLen = 2; const yearDivs = safeQuerySelectorAll( container, ".react-datepicker__year-text", + minRequiredYearLen, ); - if (yearDivs.length < 2) { - throw new Error("yearDivs doesn't have enough length"); - } const firstYearDiv = yearDivs[1]!; fireEvent.click(firstYearDiv); From b98b908cbde666b1b5e569cdb75782af46777864 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 26 Aug 2024 12:11:38 +0530 Subject: [PATCH 22/24] =?UTF-8?q?=E2=9C=A8=20Add=20SafeElementWrapper=20ut?= =?UTF-8?q?ility=20to=20support=20safe=20query=20selection=20chain=20to=20?= =?UTF-8?q?avoid=20unnecessary=20temporary=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/test_utils.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 8b2ee4df3..5e576795b 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -111,3 +111,40 @@ export const safeQuerySelectorAll = ( 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)); + } +} From 26c63bd73b815b014b30eba504f81d71066b831b Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 26 Aug 2024 12:14:13 +0530 Subject: [PATCH 23/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Update=20a=20calenda?= =?UTF-8?q?r=5Ftest=20test=20case=20to=20use=20SafeQuerySelector=20chain?= =?UTF-8?q?=20to=20avoid=20a=20temporary=20variable=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/calendar_test.test.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index db181fb1c..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, safeQuerySelector, safeQuerySelectorAll } from "./test_utils"; +import { + getKey, + SafeElementWrapper, + safeQuerySelector, + safeQuerySelectorAll, +} from "./test_utils"; import type { ReactDatePickerCustomHeaderProps } from "../calendar"; import type { Locale } from "../date_utils"; @@ -1274,11 +1279,10 @@ describe("Calendar", () => { }); it("calls onMonthChange prop when month changed from month dropdown", () => { - const monthDropdownContainer = safeQuerySelector( - calendar, - ".react-datepicker__month-dropdown-container", - ); - const select = safeQuerySelector(monthDropdownContainer, "select"); + const select = new SafeElementWrapper(calendar) + .safeQuerySelector(".react-datepicker__month-dropdown-container") + .safeQuerySelector("select") + .getElement(); fireEvent.change(select, { target: { From 767c3b9d754f1ada165c428c629173378dc6d950 Mon Sep 17 00:00:00 2001 From: Balaji Sridharan Date: Mon, 26 Aug 2024 13:23:56 +0530 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=A7=AA=E2=9C=85=20Add=20test=20case?= =?UTF-8?q?s=20for=20safeQuerySelector,=20safeQuerySelectorAll,=20and=20Sa?= =?UTF-8?q?feElementWrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/test_utils.test.ts | 174 ++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/test/test_utils.test.ts 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); + }); + }); +});