@@ -243,4 +243,152 @@ describe('MockSuite', () => {
243243 expect ( actualError . status ) . toStrictEqual ( 429 ) ;
244244 expect ( endTime - startTime ) . toStrictEqual ( 10 ) ;
245245 } ) ;
246+
247+ test ( 'VoiceActivityWebSocket' , ( ) => {
248+ const voiceActivity = DirectLineMock . mockVoiceActivity ( ) ;
249+ directline = new DirectLineExport . DirectLine ( { ...services , webSocket : true } ) ;
250+
251+ const actual : Array < DirectLineExport . Activity > = [ ] ;
252+ subscriptions . push ( directline . activity$ . subscribe ( a => actual . push ( a ) ) ) ;
253+
254+ let postActivityCompleted = false ;
255+ let postActivityError : any ;
256+
257+ const scenario = function * ( ) : IterableIterator < Observable < unknown > > {
258+ yield Observable . timer ( 200 , scheduler ) ;
259+ yield directline . postActivity ( voiceActivity )
260+ . do ( ( ) => postActivityCompleted = true )
261+ . catch ( error => {
262+ postActivityError = error ;
263+ return Observable . empty ( ) ;
264+ } ) ;
265+ yield Observable . timer ( 200 , scheduler ) ;
266+ } ;
267+
268+ subscriptions . push ( lazyConcat ( scenario ( ) ) . observeOn ( scheduler ) . subscribe ( ) ) ;
269+ scheduler . flush ( ) ;
270+
271+ // Assert that voice activity was sent successfully without errors
272+ expect ( postActivityCompleted ) . toBe ( true ) ;
273+ expect ( postActivityError ) . toBeUndefined ( ) ;
274+ } ) ;
275+
276+ test ( 'VoiceActivityWithoutWebSocket' , ( ) => {
277+ const voiceActivity = DirectLineMock . mockVoiceActivity ( ) ;
278+ directline = new DirectLineExport . DirectLine ( { ...services , webSocket : false } ) ;
279+
280+ let actualError : any ;
281+
282+ const scenario = function * ( ) : IterableIterator < Observable < unknown > > {
283+ yield Observable . timer ( 200 , scheduler ) ;
284+ yield directline . postActivity ( voiceActivity ) . catch ( error => {
285+ actualError = error ;
286+ return Observable . empty ( ) ;
287+ } ) ;
288+ } ;
289+
290+ subscriptions . push ( lazyConcat ( scenario ( ) ) . observeOn ( scheduler ) . subscribe ( ) ) ;
291+ scheduler . flush ( ) ;
292+
293+ expect ( actualError . message ) . toContain ( 'Voice activities require WebSocket to be enabled' ) ;
294+ } ) ;
295+
296+ test ( 'VoiceVsTextActivityRouting' , ( ) => {
297+ const voiceActivity = DirectLineMock . mockVoiceActivity ( ) ;
298+ const textActivity = DirectLineMock . mockActivity ( 'hello' ) ;
299+
300+ directline = new DirectLineExport . DirectLine ( { ...services , webSocket : true } ) ;
301+
302+ const actual : Array < DirectLineExport . Activity > = [ ] ;
303+ subscriptions . push ( directline . activity$ . subscribe ( a => actual . push ( a ) ) ) ;
304+
305+ let voiceCompleted = false ;
306+ let textCompleted = false ;
307+ let voiceError : any ;
308+ let textError : any ;
309+
310+ const scenario = function * ( ) : IterableIterator < Observable < unknown > > {
311+ yield Observable . timer ( 200 , scheduler ) ;
312+
313+ // Send text activity (should go through HTTP/Ajax)
314+ yield directline . postActivity ( textActivity )
315+ . do ( ( ) => textCompleted = true )
316+ . catch ( error => {
317+ textError = error ;
318+ return Observable . empty ( ) ;
319+ } ) ;
320+
321+ yield Observable . timer ( 100 , scheduler ) ;
322+
323+ // Send voice activity (should go through WebSocket)
324+ yield directline . postActivity ( voiceActivity )
325+ . do ( ( ) => voiceCompleted = true )
326+ . catch ( error => {
327+ voiceError = error ;
328+ return Observable . empty ( ) ;
329+ } ) ;
330+
331+ yield Observable . timer ( 200 , scheduler ) ;
332+ } ;
333+
334+ subscriptions . push ( lazyConcat ( scenario ( ) ) . observeOn ( scheduler ) . subscribe ( ) ) ;
335+ scheduler . flush ( ) ;
336+
337+ // Both should complete successfully but through different paths
338+ expect ( textCompleted ) . toBe ( true ) ;
339+ expect ( voiceCompleted ) . toBe ( true ) ;
340+ expect ( textError ) . toBeUndefined ( ) ;
341+ expect ( voiceError ) . toBeUndefined ( ) ;
342+
343+ // Text activity should echo back, voice activity should not
344+ expect ( actual ) . toContainEqual ( textActivity ) ;
345+ expect ( actual ) . not . toContainEqual ( voiceActivity ) ;
346+ } ) ;
347+
348+ test ( 'InvalidVoiceActivityStructures' , ( ) => {
349+ const invalidStructures : DirectLineExport . Activity [ ] = [
350+ { type : 'event' , from : { id : 'user' } , value : null } as any ,
351+ { type : 'event' , from : { id : 'user' } , value : { voiceLiveEvent : null } } as any ,
352+ { type : 'event' , from : { id : 'user' } , value : { voiceLiveEvent : { } } } ,
353+ { type : 'event' , from : { id : 'user' } , value : { notVoice : { data : 'test' } } } as any
354+ ] ;
355+
356+ directline = new DirectLineExport . DirectLine ( { ...services , webSocket : true } ) ;
357+
358+ const actual : Array < DirectLineExport . Activity > = [ ] ;
359+ subscriptions . push ( directline . activity$ . subscribe ( a => actual . push ( a ) ) ) ;
360+
361+ let completedCount = 0 ;
362+ let errorCount = 0 ;
363+
364+ const scenario = function * ( ) : IterableIterator < Observable < unknown > > {
365+ yield Observable . timer ( 200 , scheduler ) ;
366+
367+ // Send each invalid structure - should all go through HTTP path
368+ for ( const invalidActivity of invalidStructures ) {
369+ yield directline . postActivity ( invalidActivity )
370+ . do ( ( ) => completedCount ++ )
371+ . catch ( error => {
372+ errorCount ++ ;
373+ return Observable . empty ( ) ;
374+ } ) ;
375+ yield Observable . timer ( 100 , scheduler ) ;
376+ }
377+
378+ yield Observable . timer ( 200 , scheduler ) ;
379+ } ;
380+
381+ subscriptions . push ( lazyConcat ( scenario ( ) ) . observeOn ( scheduler ) . subscribe ( ) ) ;
382+ scheduler . flush ( ) ;
383+
384+ // All invalid structures should complete successfully through HTTP path
385+ expect ( completedCount ) . toBe ( invalidStructures . length ) ;
386+ expect ( errorCount ) . toBe ( 0 ) ;
387+
388+ // All invalid structures should echo back (confirming they went through HTTP, not WebSocket)
389+ expect ( actual ) . toHaveLength ( invalidStructures . length ) ;
390+ invalidStructures . forEach ( invalidActivity => {
391+ expect ( actual ) . toContainEqual ( invalidActivity ) ;
392+ } ) ;
393+ } ) ;
246394} ) ;
0 commit comments