@@ -136,8 +136,8 @@ describe('useAssetBrowser', () => {
136136 } )
137137 } )
138138
139- describe ( 'Search Functionality' , ( ) => {
140- it ( 'searches across asset name' , async ( ) => {
139+ describe ( 'Fuzzy Search Functionality' , ( ) => {
140+ it ( 'searches across asset name with exact match ' , async ( ) => {
141141 const assets = [
142142 createApiAsset ( { name : 'realistic_vision.safetensors' } ) ,
143143 createApiAsset ( { name : 'anime_style.ckpt' } ) ,
@@ -149,45 +149,148 @@ describe('useAssetBrowser', () => {
149149 searchQuery . value = 'realistic'
150150 await nextTick ( )
151151
152- expect ( filteredAssets . value ) . toHaveLength ( 2 )
152+ expect ( filteredAssets . value . length ) . toBeGreaterThanOrEqual ( 1 )
153153 expect (
154- filteredAssets . value . every ( ( asset ) =>
154+ filteredAssets . value . some ( ( asset ) =>
155155 asset . name . toLowerCase ( ) . includes ( 'realistic' )
156156 )
157157 ) . toBe ( true )
158158 } )
159159
160- it ( 'searches in user metadata description ' , async ( ) => {
160+ it ( 'searches across asset tags ' , async ( ) => {
161161 const assets = [
162162 createApiAsset ( {
163163 name : 'model1.safetensors' ,
164- user_metadata : { description : 'fantasy artwork model' }
164+ tags : [ 'models' , 'checkpoints' ]
165165 } ) ,
166166 createApiAsset ( {
167167 name : 'model2.safetensors' ,
168- user_metadata : { description : 'portrait photography' }
168+ tags : [ 'models' , 'loras' ]
169169 } )
170170 ]
171171
172172 const { searchQuery, filteredAssets } = useAssetBrowser ( ref ( assets ) )
173173
174- searchQuery . value = 'fantasy '
174+ searchQuery . value = 'checkpoints '
175175 await nextTick ( )
176176
177- expect ( filteredAssets . value ) . toHaveLength ( 1 )
178- expect ( filteredAssets . value [ 0 ] . name ) . toBe ( 'model1.safetensors' )
177+ expect ( filteredAssets . value . length ) . toBeGreaterThanOrEqual ( 1 )
178+ expect ( filteredAssets . value [ 0 ] . tags ) . toContain ( 'checkpoints' )
179+ } )
180+
181+ it ( 'supports fuzzy matching with typos' , async ( ) => {
182+ const assets = [
183+ createApiAsset ( { name : 'checkpoint_model.safetensors' } ) ,
184+ createApiAsset ( { name : 'lora_model.safetensors' } )
185+ ]
186+
187+ const { searchQuery, filteredAssets } = useAssetBrowser ( ref ( assets ) )
188+
189+ // Intentional typo - fuzzy search should still find it
190+ searchQuery . value = 'chckpoint'
191+ await nextTick ( )
192+
193+ expect ( filteredAssets . value . length ) . toBeGreaterThanOrEqual ( 1 )
194+ expect ( filteredAssets . value [ 0 ] . name ) . toContain ( 'checkpoint' )
195+ } )
196+
197+ it ( 'handles empty search by returning all assets' , async ( ) => {
198+ const assets = [
199+ createApiAsset ( { name : 'test1.safetensors' } ) ,
200+ createApiAsset ( { name : 'test2.safetensors' } )
201+ ]
202+
203+ const { searchQuery, filteredAssets } = useAssetBrowser ( ref ( assets ) )
204+
205+ searchQuery . value = ''
206+ await nextTick ( )
207+
208+ expect ( filteredAssets . value ) . toHaveLength ( 2 )
179209 } )
180210
181- it ( 'handles empty search results' , async ( ) => {
211+ it ( 'handles no search results' , async ( ) => {
182212 const assets = [ createApiAsset ( { name : 'test.safetensors' } ) ]
183213
184214 const { searchQuery, filteredAssets } = useAssetBrowser ( ref ( assets ) )
185215
186- searchQuery . value = 'nonexistent '
216+ searchQuery . value = 'completelydifferentstring123 '
187217 await nextTick ( )
188218
189219 expect ( filteredAssets . value ) . toHaveLength ( 0 )
190220 } )
221+
222+ it ( 'performs case-insensitive search' , async ( ) => {
223+ const assets = [
224+ createApiAsset ( { name : 'RealisticVision.safetensors' } ) ,
225+ createApiAsset ( { name : 'anime_style.ckpt' } )
226+ ]
227+
228+ const { searchQuery, filteredAssets } = useAssetBrowser ( ref ( assets ) )
229+
230+ searchQuery . value = 'REALISTIC'
231+ await nextTick ( )
232+
233+ expect ( filteredAssets . value . length ) . toBeGreaterThanOrEqual ( 1 )
234+ expect ( filteredAssets . value [ 0 ] . name ) . toContain ( 'Realistic' )
235+ } )
236+
237+ it ( 'combines fuzzy search with format filter' , async ( ) => {
238+ const assets = [
239+ createApiAsset ( { name : 'my_checkpoint_model.safetensors' } ) ,
240+ createApiAsset ( { name : 'my_checkpoint_model.ckpt' } ) ,
241+ createApiAsset ( { name : 'different_lora.safetensors' } )
242+ ]
243+
244+ const { searchQuery, updateFilters, filteredAssets } = useAssetBrowser (
245+ ref ( assets )
246+ )
247+
248+ searchQuery . value = 'checkpoint'
249+ updateFilters ( {
250+ sortBy : 'name-asc' ,
251+ fileFormats : [ 'safetensors' ] ,
252+ baseModels : [ ]
253+ } )
254+ await nextTick ( )
255+
256+ expect ( filteredAssets . value . length ) . toBeGreaterThanOrEqual ( 1 )
257+ expect (
258+ filteredAssets . value . every ( ( asset ) =>
259+ asset . name . endsWith ( '.safetensors' )
260+ )
261+ ) . toBe ( true )
262+ expect (
263+ filteredAssets . value . some ( ( asset ) => asset . name . includes ( 'checkpoint' ) )
264+ ) . toBe ( true )
265+ } )
266+
267+ it ( 'combines fuzzy search with base model filter' , async ( ) => {
268+ const assets = [
269+ createApiAsset ( {
270+ name : 'realistic_sd15.safetensors' ,
271+ user_metadata : { base_model : 'SD1.5' }
272+ } ) ,
273+ createApiAsset ( {
274+ name : 'realistic_sdxl.safetensors' ,
275+ user_metadata : { base_model : 'SDXL' }
276+ } )
277+ ]
278+
279+ const { searchQuery, updateFilters, filteredAssets } = useAssetBrowser (
280+ ref ( assets )
281+ )
282+
283+ searchQuery . value = 'realistic'
284+ updateFilters ( {
285+ sortBy : 'name-asc' ,
286+ fileFormats : [ ] ,
287+ baseModels : [ 'SDXL' ]
288+ } )
289+ await nextTick ( )
290+
291+ expect ( filteredAssets . value ) . toHaveLength ( 1 )
292+ expect ( filteredAssets . value [ 0 ] . name ) . toBe ( 'realistic_sdxl.safetensors' )
293+ } )
191294 } )
192295
193296 describe ( 'Combined Search and Filtering' , ( ) => {
0 commit comments