@@ -6,154 +6,179 @@ import { CreatedByFilter } from "./CreatedByFilter";
66
77describe ( "CreatedByFilter" , ( ) => {
88 describe ( "rendering" , ( ) => {
9- it ( "should render toggle and search input" , ( ) => {
10- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
9+ it ( "should render search input with placeholder" , ( ) => {
10+ render (
11+ < CreatedByFilter
12+ value = { undefined }
13+ onChange = { vi . fn ( ) }
14+ onClear = { vi . fn ( ) }
15+ /> ,
16+ ) ;
1117
12- expect ( screen . getByRole ( "switch" ) ) . toBeInTheDocument ( ) ;
13- expect ( screen . getByLabelText ( "Created by me" ) ) . toBeInTheDocument ( ) ;
14- expect ( screen . getByPlaceholderText ( "Search by user" ) ) . toBeInTheDocument ( ) ;
1518 expect (
16- screen . getByRole ( "button" , { name : "Search" } ) ,
19+ screen . getByPlaceholderText ( "Search by user..." ) ,
1720 ) . toBeInTheDocument ( ) ;
1821 } ) ;
1922
20- it ( "should show 'Created by me' when no value " , ( ) => {
21- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
22-
23- expect ( screen . getByLabelText ( "Created by me" ) ) . toBeInTheDocument ( ) ;
24- } ) ;
25-
26- it ( "should show 'Created by {user}' when value is set" , ( ) => {
27- render ( < CreatedByFilter value = "john.doe" onChange = { vi . fn ( ) } /> ) ;
23+ it ( "should not show clear button when empty " , ( ) => {
24+ render (
25+ < CreatedByFilter
26+ value = { undefined }
27+ onChange = { vi . fn ( ) }
28+ onClear = { vi . fn ( ) }
29+ /> ,
30+ ) ;
2831
29- expect ( screen . getByLabelText ( "Created by john.doe" ) ) . toBeInTheDocument ( ) ;
32+ expect (
33+ screen . queryByRole ( "button" , { name : "Clear user filter" } ) ,
34+ ) . not . toBeInTheDocument ( ) ;
3035 } ) ;
3136
32- it ( "should have switch unchecked when no value" , ( ) => {
33- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
37+ it ( "should show clear button when value is set" , ( ) => {
38+ render (
39+ < CreatedByFilter
40+ value = "john.doe"
41+ onChange = { vi . fn ( ) }
42+ onClear = { vi . fn ( ) }
43+ /> ,
44+ ) ;
3445
35- expect ( screen . getByRole ( "switch" ) ) . not . toBeChecked ( ) ;
36- expect ( screen . getByLabelText ( "Created by me" ) ) . toBeInTheDocument ( ) ;
37- expect ( screen . getByPlaceholderText ( "Search by user" ) ) . toBeInTheDocument ( ) ;
3846 expect (
39- screen . getByRole ( "button" , { name : "Search " } ) ,
47+ screen . getByRole ( "button" , { name : "Clear user filter " } ) ,
4048 ) . toBeInTheDocument ( ) ;
4149 } ) ;
4250
43- it ( "should show 'Created by {user}' and checked switch when value is set" , ( ) => {
44- render ( < CreatedByFilter value = "john.doe" onChange = { vi . fn ( ) } /> ) ;
51+ it ( "should populate input with current value" , ( ) => {
52+ render (
53+ < CreatedByFilter
54+ value = "existing-user"
55+ onChange = { vi . fn ( ) }
56+ onClear = { vi . fn ( ) }
57+ /> ,
58+ ) ;
4559
46- expect ( screen . getByRole ( "switch" ) ) . toBeChecked ( ) ;
47- expect ( screen . getByLabelText ( "Created by john.doe" ) ) . toBeInTheDocument ( ) ;
60+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
61+ "existing-user" ,
62+ ) ;
4863 } ) ;
4964 } ) ;
5065
51- describe ( "toggle behavior" , ( ) => {
52- it ( "should call onChange with 'me' when toggle is turned on" , async ( ) => {
53- const user = userEvent . setup ( ) ;
54- const onChange = vi . fn ( ) ;
55- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
56-
57- await user . click ( screen . getByRole ( "switch" ) ) ;
58-
59- expect ( onChange ) . toHaveBeenCalledWith ( "me" ) ;
60- } ) ;
61-
62- it ( "should call onChange with undefined when toggle is turned off" , async ( ) => {
66+ describe ( "typing behavior" , ( ) => {
67+ it ( "should call onChange when typing" , async ( ) => {
6368 const user = userEvent . setup ( ) ;
6469 const onChange = vi . fn ( ) ;
65- render ( < CreatedByFilter value = "me" onChange = { onChange } /> ) ;
70+ render (
71+ < CreatedByFilter
72+ value = { undefined }
73+ onChange = { onChange }
74+ onClear = { vi . fn ( ) }
75+ /> ,
76+ ) ;
6677
67- await user . click ( screen . getByRole ( "switch" ) ) ;
78+ await user . type ( screen . getByPlaceholderText ( "Search by user..." ) , "jane" ) ;
6879
69- expect ( onChange ) . toHaveBeenCalledWith ( undefined ) ;
80+ // Called for each character typed
81+ expect ( onChange ) . toHaveBeenCalledTimes ( 4 ) ;
82+ expect ( onChange ) . toHaveBeenLastCalledWith ( "jane" ) ;
7083 } ) ;
7184
72- it ( "should clear filter when toggling off with existing value " , async ( ) => {
85+ it ( "should call onChange with undefined when input is cleared by typing " , async ( ) => {
7386 const user = userEvent . setup ( ) ;
7487 const onChange = vi . fn ( ) ;
75- render ( < CreatedByFilter value = "john.doe" onChange = { onChange } /> ) ;
88+ render (
89+ < CreatedByFilter value = "j" onChange = { onChange } onClear = { vi . fn ( ) } /> ,
90+ ) ;
7691
77- await user . click ( screen . getByRole ( "switch ") ) ;
92+ await user . clear ( screen . getByPlaceholderText ( "Search by user... ") ) ;
7893
79- expect ( onChange ) . toHaveBeenCalledWith ( undefined ) ;
94+ expect ( onChange ) . toHaveBeenLastCalledWith ( undefined ) ;
8095 } ) ;
8196 } ) ;
8297
83- describe ( "search behavior" , ( ) => {
84- it ( "should have search button disabled when input is empty" , ( ) => {
85- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
86-
87- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeDisabled ( ) ;
88- } ) ;
89-
90- it ( "should enable search button when input has text" , async ( ) => {
98+ describe ( "clear button behavior" , ( ) => {
99+ it ( "should call onClear and clear input when clear button is clicked" , async ( ) => {
91100 const user = userEvent . setup ( ) ;
92- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
93-
94- await user . type ( screen . getByPlaceholderText ( "Search by user" ) , "jane" ) ;
95-
96- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeEnabled ( ) ;
97- } ) ;
98-
99- it ( "should call onChange with typed user when search clicked" , async ( ) => {
100- const user = userEvent . setup ( ) ;
101- const onChange = vi . fn ( ) ;
102- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
101+ const onClear = vi . fn ( ) ;
102+ render (
103+ < CreatedByFilter
104+ value = "john.doe"
105+ onChange = { vi . fn ( ) }
106+ onClear = { onClear }
107+ /> ,
108+ ) ;
103109
104- await user . type (
105- screen . getByPlaceholderText ( "Search by user" ) ,
106- "jane.doe" ,
110+ await user . click (
111+ screen . getByRole ( "button" , { name : "Clear user filter" } ) ,
107112 ) ;
108- await user . click ( screen . getByRole ( "button" , { name : "Search" } ) ) ;
109113
110- expect ( onChange ) . toHaveBeenCalledWith ( "jane.doe" ) ;
114+ expect ( onClear ) . toHaveBeenCalled ( ) ;
111115 } ) ;
112116
113- it ( "should call onChange when Enter is pressed in input " , async ( ) => {
117+ it ( "should hide clear button after clearing " , async ( ) => {
114118 const user = userEvent . setup ( ) ;
115- const onChange = vi . fn ( ) ;
116- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
119+ render (
120+ < CreatedByFilter
121+ value = "john.doe"
122+ onChange = { vi . fn ( ) }
123+ onClear = { vi . fn ( ) }
124+ /> ,
125+ ) ;
117126
118- const input = screen . getByPlaceholderText ( "Search by user" ) ;
119- await user . type ( input , "jane.doe{Enter}" ) ;
127+ await user . click (
128+ screen . getByRole ( "button" , { name : "Clear user filter" } ) ,
129+ ) ;
120130
121- expect ( onChange ) . toHaveBeenCalledWith ( "jane.doe" ) ;
131+ expect (
132+ screen . queryByRole ( "button" , { name : "Clear user filter" } ) ,
133+ ) . not . toBeInTheDocument ( ) ;
122134 } ) ;
135+ } ) ;
123136
124- it ( "should trim whitespace from search input" , async ( ) => {
125- const user = userEvent . setup ( ) ;
126- const onChange = vi . fn ( ) ;
127- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
128-
129- await user . type (
130- screen . getByPlaceholderText ( "Search by user" ) ,
131- " jane " ,
137+ describe ( "external value sync" , ( ) => {
138+ it ( "should sync input when value prop changes" , ( ) => {
139+ const { rerender } = render (
140+ < CreatedByFilter
141+ value = "initial"
142+ onChange = { vi . fn ( ) }
143+ onClear = { vi . fn ( ) }
144+ /> ,
132145 ) ;
133- await user . click ( screen . getByRole ( "button" , { name : "Search" } ) ) ;
134146
135- expect ( onChange ) . toHaveBeenCalledWith ( "jane" ) ;
136- } ) ;
137-
138- it ( "should not call onChange when searching with only whitespace" , async ( ) => {
139- const user = userEvent . setup ( ) ;
140- const onChange = vi . fn ( ) ;
141- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
147+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
148+ "initial" ,
149+ ) ;
142150
143- await user . type ( screen . getByPlaceholderText ( "Search by user" ) , " " ) ;
151+ rerender (
152+ < CreatedByFilter
153+ value = "updated"
154+ onChange = { vi . fn ( ) }
155+ onClear = { vi . fn ( ) }
156+ /> ,
157+ ) ;
144158
145- // Button should still be disabled
146- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeDisabled ( ) ;
159+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
160+ "updated" ,
161+ ) ;
147162 } ) ;
148- } ) ;
149163
150- describe ( "initial state" , ( ) => {
151- it ( "should populate search input with current value" , ( ) => {
152- render ( < CreatedByFilter value = "existing-user" onChange = { vi . fn ( ) } /> ) ;
164+ it ( "should clear input when value prop becomes undefined" , ( ) => {
165+ const { rerender } = render (
166+ < CreatedByFilter
167+ value = "some-user"
168+ onChange = { vi . fn ( ) }
169+ onClear = { vi . fn ( ) }
170+ /> ,
171+ ) ;
153172
154- expect ( screen . getByPlaceholderText ( "Search by user" ) ) . toHaveValue (
155- "existing-user" ,
173+ rerender (
174+ < CreatedByFilter
175+ value = { undefined }
176+ onChange = { vi . fn ( ) }
177+ onClear = { vi . fn ( ) }
178+ /> ,
156179 ) ;
180+
181+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue ( "" ) ;
157182 } ) ;
158183 } ) ;
159184} ) ;
0 commit comments