@@ -14,10 +14,18 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import { IProtocol } from 'matrix-js-sdk/src/matrix' ;
18- import { CallEvent , CallState , CallType } from 'matrix-js-sdk/src/webrtc/call' ;
17+ import {
18+ IProtocol ,
19+ LOCAL_NOTIFICATION_SETTINGS_PREFIX ,
20+ MatrixEvent ,
21+ PushRuleKind ,
22+ RuleId ,
23+ TweakName ,
24+ } from 'matrix-js-sdk/src/matrix' ;
25+ import { CallEvent , CallState , CallType , MatrixCall } from 'matrix-js-sdk/src/webrtc/call' ;
1926import EventEmitter from 'events' ;
2027import { mocked } from 'jest-mock' ;
28+ import { CallEventHandlerEvent } from 'matrix-js-sdk/src/webrtc/callEventHandler' ;
2129
2230import LegacyCallHandler , {
2331 LegacyCallHandlerEvent , PROTOCOL_PSTN , PROTOCOL_PSTN_PREFIXED , PROTOCOL_SIP_NATIVE , PROTOCOL_SIP_VIRTUAL ,
@@ -28,6 +36,8 @@ import DMRoomMap from '../src/utils/DMRoomMap';
2836import SdkConfig from '../src/SdkConfig' ;
2937import { Action } from "../src/dispatcher/actions" ;
3038import { getFunctionalMembers } from "../src/utils/room/getFunctionalMembers" ;
39+ import SettingsStore from '../src/settings/SettingsStore' ;
40+ import { UIFeature } from '../src/settings/UIFeature' ;
3141
3242jest . mock ( "../src/utils/room/getFunctionalMembers" , ( ) => ( {
3343 getFunctionalMembers : jest . fn ( ) ,
@@ -126,6 +136,7 @@ describe('LegacyCallHandler', () => {
126136 // what addresses the app has looked up via pstn and native lookup
127137 let pstnLookup : string ;
128138 let nativeLookup : string ;
139+ const deviceId = 'my-device' ;
129140
130141 beforeEach ( async ( ) => {
131142 stubClient ( ) ;
@@ -136,6 +147,7 @@ describe('LegacyCallHandler', () => {
136147 fakeCall = new FakeCall ( roomId ) ;
137148 return fakeCall ;
138149 } ;
150+ MatrixClientPeg . get ( ) . deviceId = deviceId ;
139151
140152 MatrixClientPeg . get ( ) . getThirdpartyProtocols = ( ) => {
141153 return Promise . resolve ( {
@@ -426,4 +438,137 @@ describe('LegacyCallHandler without third party protocols', () => {
426438 // but it should appear to the user to be in thw native room for Bob
427439 expect ( callHandler . roomIdForCall ( fakeCall ) ) . toEqual ( NATIVE_ROOM_ALICE ) ;
428440 } ) ;
441+
442+ describe ( 'incoming calls' , ( ) => {
443+ const roomId = 'test-room-id' ;
444+
445+ const mockAudioElement = {
446+ play : jest . fn ( ) ,
447+ pause : jest . fn ( ) ,
448+ } as unknown as HTMLMediaElement ;
449+ beforeEach ( ( ) => {
450+ jest . clearAllMocks ( ) ;
451+ jest . spyOn ( SettingsStore , 'getValue' ) . mockImplementation ( setting =>
452+ setting === UIFeature . Voip ) ;
453+
454+ jest . spyOn ( MatrixClientPeg . get ( ) , 'supportsVoip' ) . mockReturnValue ( true ) ;
455+
456+ MatrixClientPeg . get ( ) . isFallbackICEServerAllowed = jest . fn ( ) ;
457+ MatrixClientPeg . get ( ) . prepareToEncrypt = jest . fn ( ) ;
458+
459+ MatrixClientPeg . get ( ) . pushRules = {
460+ global : {
461+ [ PushRuleKind . Override ] : [ {
462+ rule_id : RuleId . IncomingCall ,
463+ default : false ,
464+ enabled : true ,
465+ actions : [
466+ {
467+ set_tweak : TweakName . Sound ,
468+ value : 'ring' ,
469+ } ,
470+ ]
471+ ,
472+ } ] ,
473+ } ,
474+ } ;
475+
476+ jest . spyOn ( document , 'getElementById' ) . mockReturnValue ( mockAudioElement ) ;
477+
478+ // silence local notifications by default
479+ jest . spyOn ( MatrixClientPeg . get ( ) , 'getAccountData' ) . mockImplementation ( ( eventType ) => {
480+ if ( eventType . includes ( LOCAL_NOTIFICATION_SETTINGS_PREFIX . name ) ) {
481+ return new MatrixEvent ( {
482+ type : eventType ,
483+ content : {
484+ is_silenced : true ,
485+ } ,
486+ } ) ;
487+ }
488+ } ) ;
489+ } ) ;
490+
491+ it ( 'listens for incoming call events when voip is enabled' , ( ) => {
492+ const call = new MatrixCall ( {
493+ client : MatrixClientPeg . get ( ) ,
494+ roomId,
495+ } ) ;
496+ const cli = MatrixClientPeg . get ( ) ;
497+
498+ cli . emit ( CallEventHandlerEvent . Incoming , call ) ;
499+
500+ // call added to call map
501+ expect ( callHandler . getCallForRoom ( roomId ) ) . toEqual ( call ) ;
502+ } ) ;
503+
504+ it ( 'rings when incoming call state is ringing and notifications set to ring' , ( ) => {
505+ // remove local notification silencing mock for this test
506+ jest . spyOn ( MatrixClientPeg . get ( ) , 'getAccountData' ) . mockReturnValue ( undefined ) ;
507+ const call = new MatrixCall ( {
508+ client : MatrixClientPeg . get ( ) ,
509+ roomId,
510+ } ) ;
511+ const cli = MatrixClientPeg . get ( ) ;
512+
513+ cli . emit ( CallEventHandlerEvent . Incoming , call ) ;
514+
515+ // call added to call map
516+ expect ( callHandler . getCallForRoom ( roomId ) ) . toEqual ( call ) ;
517+ call . emit ( CallEvent . State , CallState . Ringing , CallState . Connected ) ;
518+
519+ // ringer audio element started
520+ expect ( mockAudioElement . play ) . toHaveBeenCalled ( ) ;
521+ } ) ;
522+
523+ it ( 'does not ring when incoming call state is ringing but local notifications are silenced' , ( ) => {
524+ const call = new MatrixCall ( {
525+ client : MatrixClientPeg . get ( ) ,
526+ roomId,
527+ } ) ;
528+ const cli = MatrixClientPeg . get ( ) ;
529+
530+ cli . emit ( CallEventHandlerEvent . Incoming , call ) ;
531+
532+ // call added to call map
533+ expect ( callHandler . getCallForRoom ( roomId ) ) . toEqual ( call ) ;
534+ call . emit ( CallEvent . State , CallState . Ringing , CallState . Connected ) ;
535+
536+ // ringer audio element started
537+ expect ( mockAudioElement . play ) . not . toHaveBeenCalled ( ) ;
538+ expect ( callHandler . isCallSilenced ( call . callId ) ) . toEqual ( true ) ;
539+ } ) ;
540+
541+ it ( 'should force calls to silent when local notifications are silenced' , async ( ) => {
542+ const call = new MatrixCall ( {
543+ client : MatrixClientPeg . get ( ) ,
544+ roomId,
545+ } ) ;
546+ const cli = MatrixClientPeg . get ( ) ;
547+
548+ cli . emit ( CallEventHandlerEvent . Incoming , call ) ;
549+
550+ expect ( callHandler . isForcedSilent ( ) ) . toEqual ( true ) ;
551+ expect ( callHandler . isCallSilenced ( call . callId ) ) . toEqual ( true ) ;
552+ } ) ;
553+
554+ it ( 'does not unsilence calls when local notifications are silenced' , async ( ) => {
555+ const call = new MatrixCall ( {
556+ client : MatrixClientPeg . get ( ) ,
557+ roomId,
558+ } ) ;
559+ const cli = MatrixClientPeg . get ( ) ;
560+ const callHandlerEmitSpy = jest . spyOn ( callHandler , 'emit' ) ;
561+
562+ cli . emit ( CallEventHandlerEvent . Incoming , call ) ;
563+ // reset emit call count
564+ callHandlerEmitSpy . mockClear ( ) ;
565+
566+ callHandler . unSilenceCall ( call . callId ) ;
567+ expect ( callHandlerEmitSpy ) . not . toHaveBeenCalled ( ) ;
568+ // call still silenced
569+ expect ( callHandler . isCallSilenced ( call . callId ) ) . toEqual ( true ) ;
570+ // ringer not played
571+ expect ( mockAudioElement . play ) . not . toHaveBeenCalled ( ) ;
572+ } ) ;
573+ } ) ;
429574} ) ;
0 commit comments