@@ -432,6 +432,27 @@ describe('StreamerService', () => {
432432 await timeoutPromise ;
433433 assert . calledWith ( fakeStore . highlightAnnotations , [ ] ) ;
434434 } ) ;
435+
436+ it ( 'handles "past" notifications' , ( ) => {
437+ const pastNotification = {
438+ type : 'annotation-notification' ,
439+ options : { action : 'past' } ,
440+ payload : [ { id : 'past-id' } ] ,
441+ } ;
442+ fakeWebSocket . notify ( pastNotification ) ;
443+ assert . calledWith ( fakeStore . receiveRealTimeUpdates , {
444+ updatedAnnotations : pastNotification . payload ,
445+ } ) ;
446+ } ) ;
447+
448+ it ( 'ignores unknown actions' , ( ) => {
449+ fakeWebSocket . notify ( {
450+ type : 'annotation-notification' ,
451+ options : { action : 'unknown' } ,
452+ payload : [ ] ,
453+ } ) ;
454+ assert . notCalled ( fakeStore . receiveRealTimeUpdates ) ;
455+ } ) ;
435456 } ) ;
436457
437458 context ( 'when the app is the sidebar' , ( ) => {
@@ -494,6 +515,44 @@ describe('StreamerService', () => {
494515 activeStreamer . applyPendingUpdates ( ) ;
495516 assert . calledWith ( fakeStore . clearPendingUpdates ) ;
496517 } ) ;
518+
519+ it ( 'focuses the oldest updated annotation and selects the correct tab' , ( ) => {
520+ const updates = {
521+ newest : {
522+ id : 'newest' ,
523+ updated : '2024-01-02T00:00:00Z' ,
524+ target : [ { selector : [ { type : 'TextQuoteSelector' } ] } ] ,
525+ } ,
526+ oldest : {
527+ id : 'oldest' ,
528+ updated : '2024-01-01T00:00:00Z' ,
529+ target : [ ] ,
530+ } ,
531+ } ;
532+ fakeStore . pendingUpdates . returns ( updates ) ;
533+
534+ activeStreamer . applyPendingUpdates ( ) ;
535+
536+ assert . calledWith ( fakeStore . selectTab , 'note' ) ;
537+ assert . calledWith ( fakeStore . setAnnotationFocusRequest , 'oldest' ) ;
538+ } ) ;
539+
540+ it ( 'does not focus if no updates have IDs' , ( ) => {
541+ fakeStore . pendingUpdates . returns ( {
542+ 'no-id' : { updated : '2024-01-01T00:00:00Z' } ,
543+ } ) ;
544+ activeStreamer . applyPendingUpdates ( ) ;
545+ assert . notCalled ( fakeStore . setAnnotationFocusRequest ) ;
546+ } ) ;
547+
548+ it ( 'does nothing if there are no pending updates or deletions' , ( ) => {
549+ fakeStore . pendingUpdates . returns ( { } ) ;
550+ fakeStore . pendingDeletions . returns ( { } ) ;
551+ activeStreamer . applyPendingUpdates ( ) ;
552+ assert . notCalled ( fakeStore . addAnnotations ) ;
553+ assert . notCalled ( fakeStore . removeAnnotations ) ;
554+ assert . called ( fakeStore . clearPendingUpdates ) ;
555+ } ) ;
497556 } ) ;
498557
499558 describe ( 'session change notifications' , ( ) => {
@@ -577,6 +636,30 @@ describe('StreamerService', () => {
577636 } ) ;
578637 } ) ;
579638
639+ describe ( '#setConfig' , ( ) => {
640+ it ( 'sends configuration messages immediately if connected' , async ( ) => {
641+ createDefaultStreamer ( ) ;
642+ await activeStreamer . connect ( ) ;
643+ fakeWebSocket . messages = [ ] ;
644+
645+ activeStreamer . setConfig ( 'test' , { foo : 'bar' } ) ;
646+ assert . deepEqual ( fakeWebSocket . messages , [ { foo : 'bar' } ] ) ;
647+ } ) ;
648+
649+ it ( 'does not send null configuration messages on reconnect' , async ( ) => {
650+ createDefaultStreamer ( ) ;
651+ activeStreamer . setConfig ( 'test' , null ) ;
652+ await activeStreamer . connect ( ) ;
653+
654+ // Reconnect to trigger _sendClientConfig
655+ fakeWebSocket . messages = [ ] ;
656+ fakeWebSocket . emit ( 'open' ) ;
657+
658+ const testMsg = fakeWebSocket . messages . find ( msg => msg === null ) ;
659+ assert . isUndefined ( testMsg ) ;
660+ } ) ;
661+ } ) ;
662+
580663 describe ( 'reconnections' , ( ) => {
581664 it ( 'resends configuration messages when a reconnection occurs' , ( ) => {
582665 createDefaultStreamer ( ) ;
0 commit comments