@@ -6,149 +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 ( ) ;
46+ expect (
47+ screen . getByRole ( "button" , { name : "Clear user filter" } ) ,
48+ ) . toBeInTheDocument ( ) ;
3649 } ) ;
3750
38- it ( "should have switch checked when value is set" , ( ) => {
39- render ( < CreatedByFilter value = "me" 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+ ) ;
4059
41- expect ( screen . getByRole ( "switch" ) ) . toBeChecked ( ) ;
60+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
61+ "existing-user" ,
62+ ) ;
4263 } ) ;
4364 } ) ;
4465
45- describe ( "toggle behavior" , ( ) => {
46- it ( "should call onChange with 'me' when toggle is turned on " , async ( ) => {
66+ describe ( "typing behavior" , ( ) => {
67+ it ( "should call onChange when typing " , async ( ) => {
4768 const user = userEvent . setup ( ) ;
4869 const onChange = vi . fn ( ) ;
49- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
50-
51- await user . click ( screen . getByRole ( "switch" ) ) ;
52-
53- expect ( onChange ) . toHaveBeenCalledWith ( "me" ) ;
54- } ) ;
55-
56- it ( "should call onChange with undefined when toggle is turned off" , async ( ) => {
57- const user = userEvent . setup ( ) ;
58- const onChange = vi . fn ( ) ;
59- render ( < CreatedByFilter value = "me" onChange = { onChange } /> ) ;
70+ render (
71+ < CreatedByFilter
72+ value = { undefined }
73+ onChange = { onChange }
74+ onClear = { vi . fn ( ) }
75+ /> ,
76+ ) ;
6077
61- await user . click ( screen . getByRole ( "switch" ) ) ;
78+ await user . type ( screen . getByPlaceholderText ( "Search by user..." ) , "jane" ) ;
6279
63- expect ( onChange ) . toHaveBeenCalledWith ( undefined ) ;
80+ // Called for each character typed
81+ expect ( onChange ) . toHaveBeenCalledTimes ( 4 ) ;
82+ expect ( onChange ) . toHaveBeenLastCalledWith ( "jane" ) ;
6483 } ) ;
6584
66- it ( "should not change value when toggle on with existing value " , async ( ) => {
85+ it ( "should call onChange with undefined when input is cleared by typing " , async ( ) => {
6786 const user = userEvent . setup ( ) ;
6887 const onChange = vi . fn ( ) ;
69- render ( < CreatedByFilter value = "john.doe" onChange = { onChange } /> ) ;
88+ render (
89+ < CreatedByFilter value = "j" onChange = { onChange } onClear = { vi . fn ( ) } /> ,
90+ ) ;
7091
71- // Toggle is already on, clicking it should turn it off
72- await user . click ( screen . getByRole ( "switch" ) ) ;
92+ await user . clear ( screen . getByPlaceholderText ( "Search by user..." ) ) ;
7393
74- expect ( onChange ) . toHaveBeenCalledWith ( undefined ) ;
94+ expect ( onChange ) . toHaveBeenLastCalledWith ( undefined ) ;
7595 } ) ;
7696 } ) ;
7797
78- describe ( "search behavior" , ( ) => {
79- it ( "should have search button disabled when input is empty" , ( ) => {
80- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
81-
82- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeDisabled ( ) ;
83- } ) ;
84-
85- 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 ( ) => {
86100 const user = userEvent . setup ( ) ;
87- render ( < CreatedByFilter value = { undefined } onChange = { vi . fn ( ) } /> ) ;
88-
89- await user . type ( screen . getByPlaceholderText ( "Search by user" ) , "jane" ) ;
90-
91- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeEnabled ( ) ;
92- } ) ;
93-
94- it ( "should call onChange with typed user when search clicked" , async ( ) => {
95- const user = userEvent . setup ( ) ;
96- const onChange = vi . fn ( ) ;
97- 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+ ) ;
98109
99- await user . type (
100- screen . getByPlaceholderText ( "Search by user" ) ,
101- "jane.doe" ,
110+ await user . click (
111+ screen . getByRole ( "button" , { name : "Clear user filter" } ) ,
102112 ) ;
103- await user . click ( screen . getByRole ( "button" , { name : "Search" } ) ) ;
104113
105- expect ( onChange ) . toHaveBeenCalledWith ( "jane.doe" ) ;
114+ expect ( onClear ) . toHaveBeenCalled ( ) ;
106115 } ) ;
107116
108- it ( "should call onChange when Enter is pressed in input " , async ( ) => {
117+ it ( "should hide clear button after clearing " , async ( ) => {
109118 const user = userEvent . setup ( ) ;
110- const onChange = vi . fn ( ) ;
111- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
119+ render (
120+ < CreatedByFilter
121+ value = "john.doe"
122+ onChange = { vi . fn ( ) }
123+ onClear = { vi . fn ( ) }
124+ /> ,
125+ ) ;
112126
113- const input = screen . getByPlaceholderText ( "Search by user" ) ;
114- await user . type ( input , "jane.doe{Enter}" ) ;
127+ await user . click (
128+ screen . getByRole ( "button" , { name : "Clear user filter" } ) ,
129+ ) ;
115130
116- expect ( onChange ) . toHaveBeenCalledWith ( "jane.doe" ) ;
131+ expect (
132+ screen . queryByRole ( "button" , { name : "Clear user filter" } ) ,
133+ ) . not . toBeInTheDocument ( ) ;
117134 } ) ;
135+ } ) ;
118136
119- it ( "should trim whitespace from search input" , async ( ) => {
120- const user = userEvent . setup ( ) ;
121- const onChange = vi . fn ( ) ;
122- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
123-
124- await user . type (
125- screen . getByPlaceholderText ( "Search by user" ) ,
126- " 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+ /> ,
127145 ) ;
128- await user . click ( screen . getByRole ( "button" , { name : "Search" } ) ) ;
129-
130- expect ( onChange ) . toHaveBeenCalledWith ( "jane" ) ;
131- } ) ;
132146
133- it ( "should not call onChange when searching with only whitespace" , async ( ) => {
134- const user = userEvent . setup ( ) ;
135- const onChange = vi . fn ( ) ;
136- render ( < CreatedByFilter value = { undefined } onChange = { onChange } /> ) ;
147+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
148+ "initial" ,
149+ ) ;
137150
138- 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+ ) ;
139158
140- // Button should still be disabled
141- expect ( screen . getByRole ( "button" , { name : "Search" } ) ) . toBeDisabled ( ) ;
159+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue (
160+ "updated" ,
161+ ) ;
142162 } ) ;
143- } ) ;
144163
145- describe ( "initial state" , ( ) => {
146- it ( "should populate search input with current value" , ( ) => {
147- 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+ ) ;
148172
149- expect ( screen . getByPlaceholderText ( "Search by user" ) ) . toHaveValue (
150- "existing-user" ,
173+ rerender (
174+ < CreatedByFilter
175+ value = { undefined }
176+ onChange = { vi . fn ( ) }
177+ onClear = { vi . fn ( ) }
178+ /> ,
151179 ) ;
180+
181+ expect ( screen . getByPlaceholderText ( "Search by user..." ) ) . toHaveValue ( "" ) ;
152182 } ) ;
153183 } ) ;
154184} ) ;
0 commit comments