@@ -20,7 +20,13 @@ limitations under the License.
20
20
import { type MockedFunction , type Mock } from "jest-mock" ;
21
21
22
22
import { 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" ;
24
30
import { LegacyMembershipManager } from "../../../src/matrixrtc/LegacyMembershipManager" ;
25
31
import { makeMockClient , makeMockRoom , membershipTemplate , mockCallMembership , type MockClient } from "./mocks" ;
26
32
import { MembershipManager } from "../../../src/matrixrtc/NewMembershipManager" ;
@@ -34,6 +40,14 @@ function waitForMockCall(method: MockedFunction<any>, returnVal?: Promise<any>)
34
40
} ) ;
35
41
} ) ;
36
42
}
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
+ }
37
51
38
52
function createAsyncHandle ( method : MockedFunction < any > ) {
39
53
const { reject, resolve, promise } = defer ( ) ;
@@ -78,16 +92,16 @@ describe.each([
78
92
// There is no need to clean up mocks since we will recreate the client.
79
93
} ) ;
80
94
81
- describe ( "isJoined ()" , ( ) => {
95
+ describe ( "isActivated ()" , ( ) => {
82
96
it ( "defaults to false" , ( ) => {
83
97
const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
84
- expect ( manager . isJoined ( ) ) . toEqual ( false ) ;
98
+ expect ( manager . isActivated ( ) ) . toEqual ( false ) ;
85
99
} ) ;
86
100
87
101
it ( "returns true after join()" , ( ) => {
88
102
const manager = new TestMembershipManager ( { } , room , client , ( ) => undefined ) ;
89
103
manager . join ( [ ] ) ;
90
- expect ( manager . isJoined ( ) ) . toEqual ( true ) ;
104
+ expect ( manager . isActivated ( ) ) . toEqual ( true ) ;
91
105
} ) ;
92
106
} ) ;
93
107
@@ -125,6 +139,23 @@ describe.each([
125
139
{ } ,
126
140
"_@alice:example.org_AAAAAAA" ,
127
141
) ;
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 ) ;
128
159
} ) ;
129
160
130
161
describe ( "does not prefix the state key with _ for rooms that support user-owned state events" , ( ) => {
@@ -505,7 +536,39 @@ describe.each([
505
536
await testExpires ( 10_000 , 1_000 ) ;
506
537
} ) ;
507
538
} ) ;
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 ) ;
508
547
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
+ } ) ;
509
572
describe ( "server error handling" , ( ) => {
510
573
// 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
511
574
describe ( "retries sending delayed leave event" , ( ) => {
0 commit comments