@@ -311,6 +311,114 @@ describe('BlockerPanel', () => {
311311 } ) ;
312312 } ) ;
313313
314+ describe ( 'type filtering' , ( ) => {
315+ it ( 'keeps filter buttons visible when ASYNC filter returns 0 results' , ( ) => {
316+ // Render with only SYNC blockers
317+ render ( < BlockerPanel blockers = { [ mockSyncBlocker ] } /> ) ;
318+
319+ // Click ASYNC filter
320+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'ASYNC' } ) ) ;
321+
322+ // All three filter buttons should still be visible
323+ expect ( screen . getByRole ( 'button' , { name : 'All' } ) ) . toBeInTheDocument ( ) ;
324+ expect ( screen . getByRole ( 'button' , { name : 'SYNC' } ) ) . toBeInTheDocument ( ) ;
325+ expect ( screen . getByRole ( 'button' , { name : 'ASYNC' } ) ) . toBeInTheDocument ( ) ;
326+
327+ // Count should show 0
328+ expect ( screen . getByText ( '(0)' ) ) . toBeInTheDocument ( ) ;
329+
330+ // Empty message should be displayed
331+ expect ( screen . getByText ( 'No ASYNC blockers found' ) ) . toBeInTheDocument ( ) ;
332+ expect ( screen . getByText ( 'Try selecting a different filter' ) ) . toBeInTheDocument ( ) ;
333+ } ) ;
334+
335+ it ( 'keeps filter buttons visible when SYNC filter returns 0 results' , ( ) => {
336+ // Render with only ASYNC blockers
337+ render ( < BlockerPanel blockers = { [ mockAsyncBlocker ] } /> ) ;
338+
339+ // Click SYNC filter
340+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'SYNC' } ) ) ;
341+
342+ // All three filter buttons should still be visible
343+ expect ( screen . getByRole ( 'button' , { name : 'All' } ) ) . toBeInTheDocument ( ) ;
344+ expect ( screen . getByRole ( 'button' , { name : 'SYNC' } ) ) . toBeInTheDocument ( ) ;
345+ expect ( screen . getByRole ( 'button' , { name : 'ASYNC' } ) ) . toBeInTheDocument ( ) ;
346+
347+ // Count should show 0
348+ expect ( screen . getByText ( '(0)' ) ) . toBeInTheDocument ( ) ;
349+
350+ // Empty message should be displayed
351+ expect ( screen . getByText ( 'No SYNC blockers found' ) ) . toBeInTheDocument ( ) ;
352+ } ) ;
353+
354+ it ( 'allows switching back to ALL filter after seeing empty filtered results' , ( ) => {
355+ // Render with only SYNC blockers
356+ render ( < BlockerPanel blockers = { [ mockSyncBlocker ] } /> ) ;
357+
358+ // Click ASYNC filter (should show 0 results)
359+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'ASYNC' } ) ) ;
360+ expect ( screen . getByText ( '(0)' ) ) . toBeInTheDocument ( ) ;
361+
362+ // Click ALL filter
363+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'All' } ) ) ;
364+
365+ // SYNC blocker should now be visible
366+ expect ( screen . getByText ( mockSyncBlocker . question ) ) . toBeInTheDocument ( ) ;
367+
368+ // Count should show 1
369+ expect ( screen . getByText ( '(1)' ) ) . toBeInTheDocument ( ) ;
370+ } ) ;
371+
372+ it ( 'shows empty state without buttons only when truly no pending blockers' , ( ) => {
373+ // Render with only resolved/expired blockers
374+ render ( < BlockerPanel blockers = { [ mockResolvedBlocker , mockExpiredBlocker ] } /> ) ;
375+
376+ // Empty state message should be shown
377+ expect ( screen . getByText ( 'No blockers - agents are running smoothly!' ) ) . toBeInTheDocument ( ) ;
378+
379+ // Filter buttons should NOT be present
380+ expect ( screen . queryByRole ( 'button' , { name : 'All' } ) ) . not . toBeInTheDocument ( ) ;
381+ expect ( screen . queryByRole ( 'button' , { name : 'SYNC' } ) ) . not . toBeInTheDocument ( ) ;
382+ expect ( screen . queryByRole ( 'button' , { name : 'ASYNC' } ) ) . not . toBeInTheDocument ( ) ;
383+ } ) ;
384+
385+ it ( 'shows empty state without buttons when blockers array is empty' , ( ) => {
386+ render ( < BlockerPanel blockers = { mockEmptyBlockersList } /> ) ;
387+
388+ // Empty state message should be shown
389+ expect ( screen . getByText ( 'No blockers - agents are running smoothly!' ) ) . toBeInTheDocument ( ) ;
390+
391+ // Filter buttons should NOT be present
392+ expect ( screen . queryByRole ( 'button' , { name : 'All' } ) ) . not . toBeInTheDocument ( ) ;
393+ } ) ;
394+
395+ it ( 'switches between filter states correctly' , ( ) => {
396+ // Render with both SYNC and ASYNC blockers
397+ render ( < BlockerPanel blockers = { [ mockSyncBlocker , mockAsyncBlocker ] } /> ) ;
398+
399+ // Initially ALL filter, both visible
400+ expect ( screen . getByText ( '(2)' ) ) . toBeInTheDocument ( ) ;
401+ expect ( screen . getByText ( mockSyncBlocker . question ) ) . toBeInTheDocument ( ) ;
402+ expect ( screen . getByText ( mockAsyncBlocker . question ) ) . toBeInTheDocument ( ) ;
403+
404+ // Click SYNC filter
405+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'SYNC' } ) ) ;
406+ expect ( screen . getByText ( '(1)' ) ) . toBeInTheDocument ( ) ;
407+ expect ( screen . getByText ( mockSyncBlocker . question ) ) . toBeInTheDocument ( ) ;
408+ expect ( screen . queryByText ( mockAsyncBlocker . question ) ) . not . toBeInTheDocument ( ) ;
409+
410+ // Click ASYNC filter
411+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'ASYNC' } ) ) ;
412+ expect ( screen . getByText ( '(1)' ) ) . toBeInTheDocument ( ) ;
413+ expect ( screen . queryByText ( mockSyncBlocker . question ) ) . not . toBeInTheDocument ( ) ;
414+ expect ( screen . getByText ( mockAsyncBlocker . question ) ) . toBeInTheDocument ( ) ;
415+
416+ // Click ALL filter again
417+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'All' } ) ) ;
418+ expect ( screen . getByText ( '(2)' ) ) . toBeInTheDocument ( ) ;
419+ } ) ;
420+ } ) ;
421+
314422 describe ( 'UI styling and structure' , ( ) => {
315423 it ( 'applies hover styles to blocker buttons' , ( ) => {
316424 render ( < BlockerPanel blockers = { [ mockSyncBlocker ] } /> ) ;
0 commit comments