1111 * limitations under the License.
1212 */
1313
14+ import type { FilterSelectProps } from '@openmetadata/ui-core-components' ;
1415import {
1516 act ,
1617 fireEvent ,
@@ -22,7 +23,6 @@ import userEvent from '@testing-library/user-event';
2223import { EntityFields } from '../../enums/AdvancedSearch.enum' ;
2324import { SearchIndex } from '../../enums/search.enum' ;
2425import { getAggregationOptions } from '../../utils/ExploreUtils' ;
25- import { SearchDropdownProps } from '../SearchDropdown/SearchDropdown.interface' ;
2626import { ExploreQuickFilterField } from './ExplorePage.interface' ;
2727import ExploreQuickFilters from './ExploreQuickFilters' ;
2828import {
@@ -35,6 +35,18 @@ const mockQueryFilter = {};
3535const mockUseAdvanceSearch = jest . fn ( ) ;
3636const mockUseSearchStore = jest . fn ( ) ;
3737
38+ const mockDebounceCancel = jest . fn ( ) ;
39+
40+ jest . mock ( 'lodash' , ( ) => ( {
41+ ...jest . requireActual ( 'lodash' ) ,
42+ // The component debounces search; tests drive it synchronously and only
43+ // assert that closing a dropdown cancels a pending keystroke.
44+ debounce : ( fn : ( ...args : unknown [ ] ) => unknown ) =>
45+ Object . assign ( ( ...args : unknown [ ] ) => fn ( ...args ) , {
46+ cancel : mockDebounceCancel ,
47+ } ) ,
48+ } ) ) ;
49+
3850jest . mock ( '../../hooks/useCustomLocation/useCustomLocation' , ( ) => ( {
3951 __esModule : true ,
4052 default : ( ) => mockUseCustomLocation ( ) ,
@@ -57,75 +69,67 @@ jest.mock('../../hooks/useSearchStore', () => ({
5769const mockOnFieldValueSelect = jest . fn ( ) ;
5870const mockGetAggregationOptions = jest . fn ( ) ;
5971
60- jest . mock ( '../SearchDropdown/SearchDropdown ' , ( ) => ( {
61- __esModule : true ,
62- default : ( {
72+ jest . mock ( '@openmetadata/ui-core-components ' , ( ) => ( {
73+ FilterSelect : ( {
74+ 'data-testid' : testId ,
6375 options,
64- searchKey,
65- isSuggestionsLoading,
76+ selectedValues,
77+ selectionMode,
78+ nullOption,
79+ hideCounts,
80+ isLoading,
6681 onChange,
6782 onSearch,
68- onGetInitialOptions,
69- selectedKeys,
70- hasNullOption,
71- hideCounts,
72- independent,
73- showSelectedCounts,
74- singleSelect,
75- getPopupContainer,
76- index : dropdownIndex ,
77- } : SearchDropdownProps ) => (
78- < div data-testid = { `search-dropdown-${ searchKey } ` } title = "search-dropdown" >
79- < span data-testid = { `label-${ searchKey } ` } > { searchKey } </ span >
80- < span data-testid = { `single-select-${ searchKey } ` } >
81- { singleSelect ? 'true' : 'false' }
82- </ span >
83- < span data-testid = { `index-${ searchKey } ` } > { dropdownIndex } </ span >
84- < span data-testid = { `has-null-option-${ searchKey } ` } >
85- { hasNullOption ? 'true' : 'false' }
86- </ span >
87- < span data-testid = { `hide-counts-${ searchKey } ` } >
88- { hideCounts ? 'true' : 'false' }
89- </ span >
90- < span data-testid = { `independent-${ searchKey } ` } >
91- { independent ? 'true' : 'false' }
92- </ span >
93- < span data-testid = { `show-selected-counts-${ searchKey } ` } >
94- { showSelectedCounts ? 'true' : 'false' }
95- </ span >
96- < span data-testid = { `popup-container-${ searchKey } ` } >
97- { getPopupContainer ? 'true' : 'false' }
98- </ span >
99- < span data-testid = { `selected-count-${ searchKey } ` } >
100- { selectedKeys ?. length ?? 0 }
101- </ span >
102- < span data-testid = { `suggestions-loading-${ searchKey } ` } >
103- { isSuggestionsLoading ? 'true' : 'false' }
104- </ span >
105- { options . map ( ( option , index ) => (
106- < div data-testid = { `option-${ searchKey } -${ index } ` } key = { option . key } >
107- { option . label } - { option . count }
108- </ div >
109- ) ) }
110- < button
111- data-testid = { `onGetInitialOptions-${ searchKey } ` }
112- onClick = { ( ) => onGetInitialOptions ?.( searchKey ) } >
113- Get Initial Options
114- </ button >
115- < button
116- data-testid = { `onSearch-${ searchKey } ` }
117- onClick = { ( ) => onSearch ( 'test' , searchKey ) } >
118- Search
119- </ button >
120- < button
121- data-testid = { `onChange-${ searchKey } ` }
122- onClick = { ( ) =>
123- onChange ( [ { key : 'test-key' , label : 'test-label' } ] , searchKey )
124- } >
125- Change
126- </ button >
127- </ div >
128- ) ,
83+ onOpenChange,
84+ } : FilterSelectProps ) => {
85+ const searchKey = ( testId ?? '' ) . replace ( 'search-dropdown-' , '' ) ;
86+
87+ return (
88+ < div data-testid = { testId } title = "search-dropdown" >
89+ < span data-testid = { `label-${ searchKey } ` } > { searchKey } </ span >
90+ < span data-testid = { `single-select-${ searchKey } ` } >
91+ { selectionMode === 'single' ? 'true' : 'false' }
92+ </ span >
93+ < span data-testid = { `has-null-option-${ searchKey } ` } >
94+ { nullOption ? 'true' : 'false' }
95+ </ span >
96+ < span data-testid = { `hide-counts-${ searchKey } ` } >
97+ { hideCounts ? 'true' : 'false' }
98+ </ span >
99+ < span data-testid = { `selected-count-${ searchKey } ` } >
100+ { selectedValues ?. length ?? 0 }
101+ </ span >
102+ < span data-testid = { `suggestions-loading-${ searchKey } ` } >
103+ { isLoading ? 'true' : 'false' }
104+ </ span >
105+ { options . map ( ( option , index ) => (
106+ < div data-testid = { `option-${ searchKey } -${ index } ` } key = { option . value } >
107+ { option . label } - { option . count }
108+ </ div >
109+ ) ) }
110+ < button
111+ data-testid = { `onGetInitialOptions-${ searchKey } ` }
112+ onClick = { ( ) => onOpenChange ?.( true ) } >
113+ Get Initial Options
114+ </ button >
115+ < button
116+ data-testid = { `onSearch-${ searchKey } ` }
117+ onClick = { ( ) => onSearch ?.( 'test' ) } >
118+ Search
119+ </ button >
120+ < button
121+ data-testid = { `onClose-${ searchKey } ` }
122+ onClick = { ( ) => onOpenChange ?.( false ) } >
123+ Close
124+ </ button >
125+ < button
126+ data-testid = { `onChange-${ searchKey } ` }
127+ onClick = { ( ) => onChange ( [ 'test-key' ] ) } >
128+ Change
129+ </ button >
130+ </ div >
131+ ) ;
132+ } ,
129133} ) ) ;
130134
131135jest . mock ( '../../utils/ExploreUtils' , ( ) => ( {
@@ -210,36 +214,6 @@ describe('ExploreQuickFilters component', () => {
210214 } ) ;
211215
212216 describe ( 'Props handling' , ( ) => {
213- it ( 'should pass independent prop to SearchDropdown' , ( ) => {
214- render ( < ExploreQuickFilters { ...mockProps } independent /> ) ;
215-
216- mockFields . forEach ( ( field ) => {
217- expect (
218- screen . getByTestId ( `independent-${ field . key } ` )
219- ) . toHaveTextContent ( 'true' ) ;
220- } ) ;
221- } ) ;
222-
223- it ( 'should pass showSelectedCounts prop to SearchDropdown' , ( ) => {
224- render ( < ExploreQuickFilters { ...mockProps } showSelectedCounts /> ) ;
225-
226- mockFields . forEach ( ( field ) => {
227- expect (
228- screen . getByTestId ( `show-selected-counts-${ field . key } ` )
229- ) . toHaveTextContent ( 'true' ) ;
230- } ) ;
231- } ) ;
232-
233- it ( 'should pass popup container override to SearchDropdown' , ( ) => {
234- render ( < ExploreQuickFilters { ...mockProps } /> ) ;
235-
236- mockFields . forEach ( ( field ) => {
237- expect (
238- screen . getByTestId ( `popup-container-${ field . key } ` )
239- ) . toHaveTextContent ( 'true' ) ;
240- } ) ;
241- } ) ;
242-
243217 it ( 'should pass hasNullOption for fields in fieldsWithNullValues' , ( ) => {
244218 const fieldsWithNullValues = [ 'owner.displayName' as EntityFields ] ;
245219 render (
@@ -678,6 +652,18 @@ describe('ExploreQuickFilters component', () => {
678652 } ) ;
679653 } ) ;
680654
655+ describe ( 'Debounce cancellation' , ( ) => {
656+ it ( 'should cancel a pending debounced search when the dropdown closes' , async ( ) => {
657+ render ( < ExploreQuickFilters { ...mockProps } /> ) ;
658+
659+ await act ( async ( ) => {
660+ screen . getByTestId ( 'onClose-database.name' ) . click ( ) ;
661+ } ) ;
662+
663+ expect ( mockDebounceCancel ) . toHaveBeenCalled ( ) ;
664+ } ) ;
665+ } ) ;
666+
681667 describe ( 'onChange handling' , ( ) => {
682668 it ( 'should call onFieldValueSelect when filter value changes' , async ( ) => {
683669 render (
@@ -696,7 +682,7 @@ describe('ExploreQuickFilters component', () => {
696682 expect ( mockOnFieldValueSelect ) . toHaveBeenCalledWith ( {
697683 label : 'Database' ,
698684 key : 'database.name' ,
699- value : [ { key : 'test-key' , label : 'test-label ' } ] ,
685+ value : [ { key : 'test-key' , label : 'test-key ' } ] ,
700686 } ) ;
701687 } ) ;
702688
@@ -711,7 +697,7 @@ describe('ExploreQuickFilters component', () => {
711697
712698 const updatedFields = mockFields . map ( ( f ) =>
713699 f . key === 'database.name'
714- ? { ...f , value : [ { key : 'test-key' , label : 'test-label ' } ] }
700+ ? { ...f , value : [ { key : 'test-key' , label : 'test-key ' } ] }
715701 : f
716702 ) ;
717703
@@ -1150,21 +1136,6 @@ describe('ExploreQuickFilters component', () => {
11501136 } ) ;
11511137 } ) ;
11521138
1153- describe ( 'Multi-index display' , ( ) => {
1154- it ( 'should pass first index as display index to SearchDropdown' , ( ) => {
1155- const multiIndexProps = {
1156- ...mockProps ,
1157- index : [ SearchIndex . TABLE , SearchIndex . TOPIC ] as unknown as SearchIndex ,
1158- } ;
1159-
1160- render ( < ExploreQuickFilters { ...multiIndexProps } /> ) ;
1161-
1162- expect ( screen . getByTestId ( 'index-database.name' ) ) . toHaveTextContent (
1163- SearchIndex . TABLE
1164- ) ;
1165- } ) ;
1166- } ) ;
1167-
11681139 describe ( 'additionalActions' , ( ) => {
11691140 it ( 'should render additionalActions content' , ( ) => {
11701141 render (
0 commit comments