1717 * under the License.
1818 */
1919import "@testing-library/jest-dom" ;
20- import { render , screen , fireEvent , waitFor } from "@testing-library/react" ;
20+ import { render , screen , fireEvent , waitFor , cleanup } from "@testing-library/react" ;
2121import dayjs from "dayjs" ;
2222import timezone from "dayjs/plugin/timezone" ;
2323import utc from "dayjs/plugin/utc" ;
2424import { useMemo } from "react" ;
25- import { describe , it , expect , vi , beforeEach } from "vitest" ;
25+ import { describe , it , expect , vi , beforeEach , afterEach } from "vitest" ;
2626
2727import { TimezoneContext } from "src/context/timezone" ;
2828import { ChakraWrapper } from "src/utils/ChakraWrapper" ;
2929
3030import type { FilterPluginProps } from "../types" ;
3131import { DateRangeFilter } from "./DateRangeFilter" ;
3232
33- // Initialize dayjs plugins
3433dayjs . extend ( timezone ) ;
3534dayjs . extend ( utc ) ;
3635
37- // Mock useTranslation
3836const mockTranslate = vi . fn ( ( key : string ) => {
3937 const translations : Record < string , string > = {
4038 "common:filters.endTime" : "End Time" ,
@@ -75,7 +73,6 @@ const TestWrapper = ({ children }: { readonly children: React.ReactNode }) => {
7573 ) ;
7674} ;
7775
78- // Mock data
7976const mockFilter = {
8077 config : {
8178 icon : undefined ,
@@ -93,155 +90,161 @@ const defaultProps: FilterPluginProps = {
9390 onRemove : vi . fn ( ) ,
9491} ;
9592
96- describe ( "DateRangeFilter - Input Validation" , ( ) => {
97- beforeEach ( ( ) => {
98- vi . clearAllMocks ( ) ;
99- } ) ;
93+ const getInputs = ( ) => {
94+ const dateInputs = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
95+ const timeInputs = screen . getAllByPlaceholderText ( "HH:mm" ) ;
10096
101- it ( "shows error for invalid date format in start date" , async ( ) => {
102- render (
103- < TestWrapper >
104- < DateRangeFilter { ...defaultProps } />
105- </ TestWrapper > ,
106- ) ;
97+ return {
98+ endDateInput : dateInputs [ 1 ] ,
99+ endTimeInput : timeInputs [ 1 ] ,
100+ startDateInput : dateInputs [ 0 ] ,
101+ startTimeInput : timeInputs [ 0 ] ,
102+ } ;
103+ } ;
107104
108- const [ startDateInput ] = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
105+ const changeDateInput = ( input : HTMLElement | undefined , value : string ) => {
106+ if ( input ) {
107+ fireEvent . change ( input , { target : { value } } ) ;
108+ }
109+ } ;
109110
110- if ( startDateInput ) {
111- fireEvent . change ( startDateInput , { target : { value : "invalid-date" } } ) ;
112- }
111+ const changeTimeInput = ( input : HTMLElement | undefined , value : string ) => {
112+ if ( input ) {
113+ fireEvent . change ( input , { target : { value } } ) ;
114+ }
115+ } ;
113116
114- await waitFor ( ( ) => {
115- expect ( screen . getByText ( "Invalid date format." ) ) . toBeInTheDocument ( ) ;
116- } ) ;
117+ const waitForError = async ( errorText : string ) => {
118+ await waitFor ( ( ) => {
119+ expect ( screen . getByText ( errorText ) ) . toBeInTheDocument ( ) ;
117120 } ) ;
121+ } ;
118122
119- it ( "shows error for invalid date format in end date" , async ( ) => {
120- render (
121- < TestWrapper >
122- < DateRangeFilter { ...defaultProps } />
123- </ TestWrapper > ,
124- ) ;
125-
126- const [ , endDateInput ] = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
123+ const waitForNoError = async ( errorText : string ) => {
124+ await waitFor ( ( ) => {
125+ expect ( screen . queryByText ( errorText ) ) . not . toBeInTheDocument ( ) ;
126+ } ) ;
127+ } ;
127128
128- if ( endDateInput ) {
129- fireEvent . change ( endDateInput , { target : { value : "invalid-date" } } ) ;
129+ const waitForNoErrors = async ( errorTexts : Array < string > ) => {
130+ await waitFor ( ( ) => {
131+ for ( const errorText of errorTexts ) {
132+ expect ( screen . queryByText ( errorText ) ) . not . toBeInTheDocument ( ) ;
130133 }
131-
132- await waitFor ( ( ) => {
133- expect ( screen . getByText ( "Invalid date format." ) ) . toBeInTheDocument ( ) ;
134- } ) ;
135134 } ) ;
135+ } ;
136136
137- it ( "shows error for invalid time format in start time" , async ( ) => {
138- render (
139- < TestWrapper >
140- < DateRangeFilter { ...defaultProps } />
141- </ TestWrapper > ,
142- ) ;
137+ const renderFilter = ( props : FilterPluginProps = defaultProps ) =>
138+ render (
139+ < TestWrapper >
140+ < DateRangeFilter { ...props } />
141+ </ TestWrapper > ,
142+ ) ;
143143
144- const [ startTimeInput ] = screen . getAllByPlaceholderText ( "HH:mm" ) ;
144+ describe ( "DateRangeFilter" , ( ) => {
145+ beforeEach ( ( ) => {
146+ vi . clearAllMocks ( ) ;
147+ } ) ;
145148
146- if ( startTimeInput ) {
147- fireEvent . change ( startTimeInput , { target : { value : "invalid-time" } } ) ;
148- }
149+ afterEach ( ( ) => {
150+ cleanup ( ) ;
151+ } ) ;
149152
150- await waitFor ( ( ) => {
151- expect ( screen . getByText ( "Invalid time format." ) ) . toBeInTheDocument ( ) ;
153+ describe ( "Input Validation" , ( ) => {
154+ it ( "validates date and time formats" , async ( ) => {
155+ renderFilter ( ) ;
156+ const { startDateInput, startTimeInput } = getInputs ( ) ;
157+
158+ changeDateInput ( startDateInput , "invalid-date" ) ;
159+ await waitForError ( "Invalid date format." ) ;
160+ changeDateInput ( startDateInput , "2024/13/01" ) ;
161+ await waitForError ( "Invalid date format." ) ;
162+ changeTimeInput ( startTimeInput , "25:00" ) ;
163+ await waitForError ( "Invalid time format." ) ;
152164 } ) ;
153- } ) ;
154165
155- it ( "shows error for invalid time format in end time" , async ( ) => {
156- render (
157- < TestWrapper >
158- < DateRangeFilter { ...defaultProps } />
159- </ TestWrapper > ,
160- ) ;
166+ it ( "validates date range" , async ( ) => {
167+ renderFilter ( ) ;
168+ const { endDateInput, endTimeInput, startDateInput, startTimeInput } = getInputs ( ) ;
161169
162- const [ , endTimeInput ] = screen . getAllByPlaceholderText ( "HH:mm" ) ;
170+ changeDateInput ( startDateInput , "2024/01/15" ) ;
171+ changeTimeInput ( startTimeInput , "10:00" ) ;
172+ changeDateInput ( endDateInput , "2024/01/14" ) ;
173+ changeTimeInput ( endTimeInput , "09:00" ) ;
174+ await waitForError ( "Start date/time must be before end date/time" ) ;
175+ } ) ;
163176
164- if ( endTimeInput ) {
165- fireEvent . change ( endTimeInput , { target : { value : "invalid-time" } } ) ;
166- }
177+ it ( "accepts valid inputs" , async ( ) => {
178+ renderFilter ( ) ;
179+ const { endDateInput , endTimeInput , startDateInput , startTimeInput } = getInputs ( ) ;
167180
168- await waitFor ( ( ) => {
169- expect ( screen . getByText ( "Invalid time format." ) ) . toBeInTheDocument ( ) ;
181+ changeDateInput ( startDateInput , "2024/01/15" ) ;
182+ changeTimeInput ( startTimeInput , "09:00" ) ;
183+ changeDateInput ( endDateInput , "2024/01/20" ) ;
184+ changeTimeInput ( endTimeInput , "17:00" ) ;
185+ await waitForNoErrors ( [ "Invalid date format." , "Start date/time must be before end date/time" ] ) ;
170186 } ) ;
171187 } ) ;
172188
173- it ( "shows error when start date/time is after end date/time" , async ( ) => {
174- render (
175- < TestWrapper >
176- < DateRangeFilter { ...defaultProps } />
177- </ TestWrapper > ,
178- ) ;
179-
180- const [ startDateInput , endDateInput ] = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
181- const [ startTimeInput , endTimeInput ] = screen . getAllByPlaceholderText ( "HH:mm" ) ;
182-
183- // Set start date/time to be after end date/time
184- if ( startDateInput && startTimeInput && endDateInput && endTimeInput ) {
185- fireEvent . change ( startDateInput , { target : { value : "2024/01/15" } } ) ;
186- fireEvent . change ( startTimeInput , { target : { value : "10:00" } } ) ;
187- fireEvent . change ( endDateInput , { target : { value : "2024/01/14" } } ) ;
188- fireEvent . change ( endTimeInput , { target : { value : "09:00" } } ) ;
189- }
189+ describe ( "Display Value Formatting" , ( ) => {
190+ it ( "displays placeholder when no value is set" , ( ) => {
191+ renderFilter ( ) ;
192+ expect ( screen . getByText ( "Select Date Range" ) ) . toBeInTheDocument ( ) ;
193+ } ) ;
190194
191- await waitFor ( ( ) => {
192- expect ( screen . getByText ( "Start date/time must be before end date/time" ) ) . toBeInTheDocument ( ) ;
195+ it ( "displays formatted date range" , ( ) => {
196+ const props = {
197+ ...defaultProps ,
198+ filter : {
199+ ...mockFilter ,
200+ value : { endDate : "2024-01-20T17:00:00Z" , startDate : "2024-01-15T09:00:00Z" } ,
201+ } ,
202+ } ;
203+
204+ renderFilter ( props ) ;
205+ expect ( screen . getByText ( / J a n 1 5 , 2 0 2 4 / u) ) . toBeInTheDocument ( ) ;
193206 } ) ;
194- } ) ;
195207
196- it ( "accepts valid date and time inputs" , async ( ) => {
197- render (
198- < TestWrapper >
199- < DateRangeFilter { ...defaultProps } />
200- </ TestWrapper > ,
201- ) ;
202-
203- const [ startDateInput , endDateInput ] = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
204- const [ startTimeInput , endTimeInput ] = screen . getAllByPlaceholderText ( "HH:mm" ) ;
205-
206- // Set valid inputs
207- if ( startDateInput && startTimeInput && endDateInput && endTimeInput ) {
208- fireEvent . change ( startDateInput , { target : { value : "2024/01/15" } } ) ;
209- fireEvent . change ( startTimeInput , { target : { value : "09:00" } } ) ;
210- fireEvent . change ( endDateInput , { target : { value : "2024/01/20" } } ) ;
211- fireEvent . change ( endTimeInput , { target : { value : "17:00" } } ) ;
212- }
208+ it ( "displays 'From' when only start date is set" , ( ) => {
209+ const props = {
210+ ...defaultProps ,
211+ filter : { ...mockFilter , value : { endDate : undefined , startDate : "2024-01-15T09:00:00Z" } } ,
212+ } ;
213213
214- await waitFor ( ( ) => {
215- // Should not show any validation errors
216- expect ( screen . queryByText ( "Invalid date format." ) ) . not . toBeInTheDocument ( ) ;
217- expect ( screen . queryByText ( "Invalid time format." ) ) . not . toBeInTheDocument ( ) ;
218- expect ( screen . queryByText ( "Start date/time must be before end date/time" ) ) . not . toBeInTheDocument ( ) ;
214+ renderFilter ( props ) ;
215+ expect ( screen . getAllByText ( / F r o m / u) . length ) . toBeGreaterThan ( 0 ) ;
219216 } ) ;
220- } ) ;
221-
222- it ( "clears validation errors when invalid input is corrected" , async ( ) => {
223- render (
224- < TestWrapper >
225- < DateRangeFilter { ...defaultProps } />
226- </ TestWrapper > ,
227- ) ;
228217
229- const [ startDateInput ] = screen . getAllByPlaceholderText ( "YYYY/MM/DD" ) ;
218+ it ( "displays 'To' when only end date is set" , ( ) => {
219+ const props = {
220+ ...defaultProps ,
221+ filter : { ...mockFilter , value : { endDate : "2024-01-20T17:00:00Z" , startDate : undefined } } ,
222+ } ;
230223
231- if ( startDateInput ) {
232- // First, set an invalid date
233- fireEvent . change ( startDateInput , { target : { value : "invalid-date" } } ) ;
224+ renderFilter ( props ) ;
225+ expect ( screen . getAllByText ( / T o / u) . length ) . toBeGreaterThan ( 0 ) ;
226+ } ) ;
227+ } ) ;
234228
235- await waitFor ( ( ) => {
236- expect ( screen . getByText ( "Invalid date format." ) ) . toBeInTheDocument ( ) ;
237- } ) ;
229+ describe ( "Edge Cases" , ( ) => {
230+ it ( "handles leap years and boundary dates" , async ( ) => {
231+ renderFilter ( ) ;
232+ const { endDateInput, startDateInput } = getInputs ( ) ;
233+
234+ changeDateInput ( startDateInput , "2024/02/29" ) ;
235+ await waitForNoError ( "Invalid date format." ) ;
236+ changeDateInput ( startDateInput , "2023/02/29" ) ;
237+ await waitForError ( "Invalid date format." ) ;
238+ changeDateInput ( startDateInput , "2024/01/31" ) ;
239+ changeDateInput ( endDateInput , "2024/02/01" ) ;
240+ await waitForNoError ( "Invalid date format." ) ;
241+ } ) ;
238242
239- // Then, correct it to a valid date
240- fireEvent . change ( startDateInput , { target : { value : "2024/01/15" } } ) ;
241- }
243+ it ( "handles undefined filter value" , ( ) => {
244+ const props = { ...defaultProps , filter : { ...mockFilter , value : undefined } } ;
242245
243- await waitFor ( ( ) => {
244- expect ( screen . queryByText ( "Invalid date format." ) ) . not . toBeInTheDocument ( ) ;
246+ // Should not throw when filter value is undefined
247+ renderFilter ( props ) ;
245248 } ) ;
246249 } ) ;
247250} ) ;
0 commit comments