@@ -546,7 +546,7 @@ describe('useThread', () => {
546546 } ) ;
547547 } ) ;
548548
549- it ( 'handles onUserBanned action correctly' , async ( ) => {
549+ it ( 'handles onUserBanned action correctly when current user is banned ' , async ( ) => {
550550 const wrapper = ( { children } ) => (
551551 < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
552552 ) ;
@@ -562,7 +562,7 @@ describe('useThread', () => {
562562 const { onUserBanned } = result . current . actions ;
563563
564564 await act ( ( ) => {
565- onUserBanned ( ) ;
565+ onUserBanned ( mockChannel , { userId : 'test-user-id' } ) ;
566566 } ) ;
567567
568568 await waitFor ( ( ) => {
@@ -572,6 +572,46 @@ describe('useThread', () => {
572572 } ) ;
573573 } ) ;
574574
575+ it ( 'handles onUserBanned action correctly when another user is banned' , async ( ) => {
576+ const wrapper = ( { children } ) => (
577+ < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
578+ ) ;
579+
580+ let result ;
581+ await act ( async ( ) => {
582+ result = renderHook ( ( ) => useThread ( ) , { wrapper } ) . result ;
583+
584+ waitFor ( ( ) => {
585+ expect ( result . current . state . currentChannel ) . not . toBe ( undefined ) ;
586+ } ) ;
587+ } ) ;
588+ const { onUserBanned } = result . current . actions ;
589+
590+ // Channel object reflecting the ban: 'other-user-id' is no longer a member.
591+ const channelAfterBan = {
592+ url : 'test-channel' ,
593+ members : [ { userId : 'test-user-id' , nickname : 'me' } ] ,
594+ } ;
595+
596+ await act ( ( ) => {
597+ onUserBanned ( channelAfterBan , { userId : 'other-user-id' } ) ;
598+ } ) ;
599+
600+ await waitFor ( ( ) => {
601+ // Thread state must not be reset when another user is banned.
602+ expect ( result . current . state . channelState ) . not . toBe ( ChannelStateTypes . NIL ) ;
603+ expect ( result . current . state . currentChannel ) . not . toBeNull ( ) ;
604+ // currentChannel must be updated so the membership change is reflected.
605+ expect ( result . current . state . currentChannel ) . toBe ( channelAfterBan ) ;
606+ expect (
607+ result . current . state . currentChannel . members . find ( ( m : { userId : string } ) => m . userId === 'other-user-id' ) ,
608+ ) . toBeUndefined ( ) ;
609+ // nicknamesMap must be regenerated so downstream consumers (e.g. mention list) see the change.
610+ expect ( result . current . state . nicknamesMap . get ( 'other-user-id' ) ) . toBeUndefined ( ) ;
611+ expect ( result . current . state . nicknamesMap . get ( 'test-user-id' ) ) . toBe ( 'me' ) ;
612+ } ) ;
613+ } ) ;
614+
575615 it ( 'handles onUserUnbanned action correctly' , async ( ) => {
576616 const wrapper = ( { children } ) => (
577617 < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
@@ -592,7 +632,7 @@ describe('useThread', () => {
592632 } ) ;
593633 } ) ;
594634
595- it ( 'handles onUserLeft action correctly' , async ( ) => {
635+ it ( 'handles onUserLeft action correctly when current user has left ' , async ( ) => {
596636 const wrapper = ( { children } ) => (
597637 < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
598638 ) ;
@@ -608,7 +648,7 @@ describe('useThread', () => {
608648 const { onUserLeft } = result . current . actions ;
609649
610650 await act ( ( ) => {
611- onUserLeft ( ) ;
651+ onUserLeft ( mockChannel , { userId : 'test-user-id' } ) ;
612652 } ) ;
613653
614654 await waitFor ( ( ) => {
@@ -618,6 +658,46 @@ describe('useThread', () => {
618658 } ) ;
619659 } ) ;
620660
661+ it ( 'handles onUserLeft action correctly when another user has left' , async ( ) => {
662+ const wrapper = ( { children } ) => (
663+ < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
664+ ) ;
665+
666+ let result ;
667+ await act ( async ( ) => {
668+ result = renderHook ( ( ) => useThread ( ) , { wrapper } ) . result ;
669+
670+ waitFor ( ( ) => {
671+ expect ( result . current . state . currentChannel ) . not . toBe ( undefined ) ;
672+ } ) ;
673+ } ) ;
674+ const { onUserLeft } = result . current . actions ;
675+
676+ // Channel object reflecting the leave: 'other-user-id' is no longer a member.
677+ const channelAfterLeave = {
678+ url : 'test-channel' ,
679+ members : [ { userId : 'test-user-id' , nickname : 'me' } ] ,
680+ } ;
681+
682+ await act ( ( ) => {
683+ onUserLeft ( channelAfterLeave , { userId : 'other-user-id' } ) ;
684+ } ) ;
685+
686+ await waitFor ( ( ) => {
687+ // Thread state must not be reset when another user leaves.
688+ expect ( result . current . state . channelState ) . not . toBe ( ChannelStateTypes . NIL ) ;
689+ expect ( result . current . state . currentChannel ) . not . toBeNull ( ) ;
690+ // currentChannel must be updated so the membership change is reflected.
691+ expect ( result . current . state . currentChannel ) . toBe ( channelAfterLeave ) ;
692+ expect (
693+ result . current . state . currentChannel . members . find ( ( m : { userId : string } ) => m . userId === 'other-user-id' ) ,
694+ ) . toBeUndefined ( ) ;
695+ // nicknamesMap must be regenerated so downstream consumers (e.g. mention list) see the change.
696+ expect ( result . current . state . nicknamesMap . get ( 'other-user-id' ) ) . toBeUndefined ( ) ;
697+ expect ( result . current . state . nicknamesMap . get ( 'test-user-id' ) ) . toBe ( 'me' ) ;
698+ } ) ;
699+ } ) ;
700+
621701 it ( 'handles onChannelFrozen action correctly' , async ( ) => {
622702 const wrapper = ( { children } ) => (
623703 < ThreadProvider channelUrl = "test-channel" message = { mockParentMessage } > { children } </ ThreadProvider >
0 commit comments