@@ -363,7 +363,7 @@ QUnit.module('typesense-minibar', hooks => {
363
363
assert . false ( listbox . hidden , 'listbox re-opened' ) ;
364
364
} ) ;
365
365
366
- QUnit . test ( 'listbox [no stale result leak on refocus ]' , async assert => {
366
+ QUnit . test ( 'listbox [no stale result leak after empty input ]' , async assert => {
367
367
const form = parseHTML ( '<form><input type="search"></form>' ) ;
368
368
const input = form . firstChild ;
369
369
bar = tsminibar ( form ) ;
@@ -404,6 +404,42 @@ QUnit.module('typesense-minibar', hooks => {
404
404
assert . equal ( listbox . querySelector ( 'mark' ) ?. textContent , null , 'stale snippet gone' ) ;
405
405
} ) ;
406
406
407
+ QUnit . test ( 'listbox [no stale result leak after close button]' , async assert => {
408
+ const form = parseHTML ( '<form><input type="search"></form>' ) ;
409
+ const input = form . firstChild ;
410
+ bar = tsminibar ( form ) ;
411
+ const listbox = form . querySelector ( '[role=listbox]' ) ;
412
+
413
+ mockFetchResponse = API_RESP_FULL_MATCH_SOMETHING ;
414
+ input . value = 'something' ;
415
+ await expectRender ( form , ( ) => {
416
+ simulate ( input , 'input' ) ;
417
+ } ) ;
418
+ assert . false ( listbox . hidden , 'listbox not hidden' ) ;
419
+ assert . equal ( listbox . querySelector ( 'mark' ) . outerHTML , '<mark>something</mark>' , 'snippet' ) ;
420
+
421
+ // NOTE: close button empties programmatically without "input" event.
422
+ // This means clean up of "input" event handler isn't reached.
423
+ // The close button is responsible for clearing state.hits instead.
424
+ mockFetchResponse = null ;
425
+ simulate ( form . querySelector ( 'svg.tsmb-icon-close' ) , 'click' , { bubbles : true } ) ;
426
+ assert . true ( listbox . hidden , 'listbox hidden' ) ;
427
+
428
+ mockFetchResponse = null ;
429
+ simulate ( document . body , 'click' , { bubbles : true } ) ;
430
+ assert . true ( listbox . hidden , 'listbox remains hidden (document)' ) ;
431
+
432
+ simulate ( input , 'click' , { bubbles : true } ) ;
433
+ assert . true ( listbox . hidden , 'listbox remains hidden (refocus)' ) ;
434
+ // It would be fine if render() was more lazy and left innerHTML populated
435
+ // when rendering a close() that sets `state.open = false`. It only matters
436
+ // that state.hits is cleared and that any future render() call will not make
437
+ // the element visible, unless it also replaces innerHTML then.
438
+ // But.. for simplicity, right now, we do clear the HTML unconditonally,
439
+ // so let's assert that, and detect potentially unintended changes in the future.
440
+ assert . equal ( listbox . querySelector ( 'mark' ) ?. textContent , null , 'stale snippet gone' ) ;
441
+ } ) ;
442
+
407
443
QUnit . test ( 'listbox [arrow key cursor]' , async assert => {
408
444
const form = parseHTML ( '<form><input type="search"></form>' ) ;
409
445
const input = form . firstChild ;
0 commit comments