@@ -20,7 +20,13 @@ limitations under the License.
2020import { type MockedFunction , type Mock } from "jest-mock" ;
2121
2222import { EventType , HTTPError , MatrixError , UnsupportedDelayedEventsEndpointError , type Room } from "../../../src" ;
23- import { type Focus , type LivekitFocusActive , type SessionMembershipData } from "../../../src/matrixrtc" ;
23+ import {
24+ MembershipManagerEvent ,
25+ Status ,
26+ type Focus ,
27+ type LivekitFocusActive ,
28+ type SessionMembershipData ,
29+ } from "../../../src/matrixrtc" ;
2430import { LegacyMembershipManager } from "../../../src/matrixrtc/LegacyMembershipManager" ;
2531import { makeMockClient , makeMockRoom , membershipTemplate , mockCallMembership , type MockClient } from "./mocks" ;
2632import { MembershipManager } from "../../../src/matrixrtc/NewMembershipManager" ;
@@ -34,6 +40,14 @@ function waitForMockCall(method: MockedFunction<any>, returnVal?: Promise<any>)
3440 } ) ;
3541 } ) ;
3642}
43+ function waitForMockCallOnce ( method : MockedFunction < any > , returnVal ?: Promise < any > ) {
44+ return new Promise < void > ( ( resolve ) => {
45+ method . mockImplementationOnce ( ( ) => {
46+ resolve ( ) ;
47+ return returnVal ?? Promise . resolve ( ) ;
48+ } ) ;
49+ } ) ;
50+ }
3751
3852function createAsyncHandle ( method : MockedFunction < any > ) {
3953 const { reject, resolve, promise } = defer ( ) ;
@@ -78,16 +92,16 @@ describe.each([
7892 // There is no need to clean up mocks since we will recreate the client.
7993 } ) ;
8094
81- describe ( "isJoined ()" , ( ) => {
95+ describe ( "isActivated ()" , ( ) => {
8296 it ( "defaults to false" , ( ) => {
8397 const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
84- expect ( manager . isJoined ( ) ) . toEqual ( false ) ;
98+ expect ( manager . isActivated ( ) ) . toEqual ( false ) ;
8599 } ) ;
86100
87101 it ( "returns true after join()" , ( ) => {
88102 const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
89103 manager . join ( [ ] ) ;
90- expect ( manager . isJoined ( ) ) . toEqual ( true ) ;
104+ expect ( manager . isActivated ( ) ) . toEqual ( true ) ;
91105 } ) ;
92106 } ) ;
93107
@@ -125,6 +139,23 @@ describe.each([
125139 { } ,
126140 "_@alice:example.org_AAAAAAA" ,
127141 ) ;
142+ expect ( client . _unstable_sendDelayedStateEvent ) . toHaveBeenCalledTimes ( 1 ) ;
143+ } ) ;
144+
145+ it ( "reschedules delayed leave event if sending state cancels it" , async ( ) => {
146+ const memberManager = new TestMembershipManager ( undefined , room , client , ( ) => undefined ) ;
147+ const waitForSendState = waitForMockCall ( client . sendStateEvent ) ;
148+ const waitForUpdateDelaye = waitForMockCallOnce (
149+ client . _unstable_updateDelayedEvent ,
150+ Promise . reject ( new MatrixError ( { errcode : "M_NOT_FOUND" } ) ) ,
151+ ) ;
152+ memberManager . join ( [ focus ] , focusActive ) ;
153+ await waitForSendState ;
154+ await waitForUpdateDelaye ;
155+ await jest . advanceTimersByTimeAsync ( 1 ) ;
156+ // Once for the initial event and once because of the errcode: "M_NOT_FOUND"
157+ // Different to "sends a membership event and schedules delayed leave when joining a call" where its only called once (1)
158+ expect ( client . _unstable_sendDelayedStateEvent ) . toHaveBeenCalledTimes ( 2 ) ;
128159 } ) ;
129160
130161 describe ( "does not prefix the state key with _ for rooms that support user-owned state events" , ( ) => {
@@ -505,7 +536,39 @@ describe.each([
505536 await testExpires ( 10_000 , 1_000 ) ;
506537 } ) ;
507538 } ) ;
539+ describe ( "status updates" , ( ) => {
540+ it ( "starts 'Disconnected' !FailsForLegacy" , ( ) => {
541+ const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
542+ expect ( manager . status ) . toBe ( Status . Disconnected ) ;
543+ } ) ;
544+ it ( "emits 'Connection' and 'Connected' after join !FailsForLegacy" , async ( ) => {
545+ const handleDelayedEvent = createAsyncHandle ( client . _unstable_sendDelayedStateEvent ) ;
546+ const handleStateEvent = createAsyncHandle ( client . sendStateEvent ) ;
508547
548+ const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
549+ expect ( manager . status ) . toBe ( Status . Disconnected ) ;
550+ const connectEmit = jest . fn ( ) ;
551+ manager . on ( MembershipManagerEvent . StatusChanged , connectEmit ) ;
552+ manager . join ( [ focus ] , focusActive ) ;
553+ expect ( manager . status ) . toBe ( Status . Connecting ) ;
554+ handleDelayedEvent . resolve ( ) ;
555+ await jest . advanceTimersByTimeAsync ( 1 ) ;
556+ expect ( connectEmit ) . toHaveBeenCalledWith ( Status . Disconnected , Status . Connecting ) ;
557+ handleStateEvent . resolve ( ) ;
558+ await jest . advanceTimersByTimeAsync ( 1 ) ;
559+ expect ( connectEmit ) . toHaveBeenCalledWith ( Status . Connecting , Status . Connected ) ;
560+ } ) ;
561+ it ( "emits 'Disconnecting' and 'Disconnected' after leave !FailsForLegacy" , async ( ) => {
562+ const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
563+ const connectEmit = jest . fn ( ) ;
564+ manager . on ( MembershipManagerEvent . StatusChanged , connectEmit ) ;
565+ manager . join ( [ focus ] , focusActive ) ;
566+ await jest . advanceTimersByTimeAsync ( 1 ) ;
567+ await manager . leave ( ) ;
568+ expect ( connectEmit ) . toHaveBeenCalledWith ( Status . Connected , Status . Disconnecting ) ;
569+ expect ( connectEmit ) . toHaveBeenCalledWith ( Status . Disconnecting , Status . Disconnected ) ;
570+ } ) ;
571+ } ) ;
509572 describe ( "server error handling" , ( ) => {
510573 // Types of server error: 429 rate limit with no retry-after header, 429 with retry-after, 50x server error (maybe retry every second), connection/socket timeout
511574 describe ( "retries sending delayed leave event" , ( ) => {
0 commit comments