@@ -34,6 +34,9 @@ vi.mock('../../lib/integrationService', () => ({
34
34
revokeShare : vi . fn ( ) ,
35
35
grantResourceAccess : vi . fn ( ) ,
36
36
isApiError : vi . fn ( ) ,
37
+ getSelectedChannels : vi . fn ( ) ,
38
+ selectChannelsForAnalysis : vi . fn ( ) ,
39
+ analyzeChannel : vi . fn ( ) ,
37
40
} ,
38
41
IntegrationType : {
39
42
SLACK : 'slack' ,
@@ -100,6 +103,32 @@ const mockResource = {
100
103
updated_at : '2023-01-01T00:00:00Z' ,
101
104
}
102
105
106
+ const mockSelectedChannels = [
107
+ {
108
+ id : 'res-1' ,
109
+ integration_id : 'test-int-1' ,
110
+ resource_type : ResourceType . SLACK_CHANNEL ,
111
+ external_id : 'C12345' ,
112
+ name : 'general' ,
113
+ created_at : '2023-01-01T00:00:00Z' ,
114
+ updated_at : '2023-01-01T00:00:00Z' ,
115
+ } ,
116
+ {
117
+ id : 'res-2' ,
118
+ integration_id : 'test-int-1' ,
119
+ resource_type : ResourceType . SLACK_CHANNEL ,
120
+ external_id : 'C67890' ,
121
+ name : 'random' ,
122
+ created_at : '2023-01-01T00:00:00Z' ,
123
+ updated_at : '2023-01-01T00:00:00Z' ,
124
+ } ,
125
+ ]
126
+
127
+ const mockAnalysisResult = {
128
+ status : 'success' ,
129
+ analysis_id : 'analysis-123' ,
130
+ }
131
+
103
132
// Helper component for testing hooks
104
133
const TestHookComponent = ( { callback } : { callback : ( ) => void } ) => {
105
134
callback ( )
@@ -184,6 +213,18 @@ describe('IntegrationContext', () => {
184
213
message : 'Resources synced' ,
185
214
} )
186
215
216
+ // Setup channel selection mocks
217
+ vi . mocked ( integrationService . getSelectedChannels ) . mockResolvedValue (
218
+ mockSelectedChannels
219
+ )
220
+ vi . mocked ( integrationService . selectChannelsForAnalysis ) . mockResolvedValue ( {
221
+ status : 'success' ,
222
+ message : 'Channels selected for analysis' ,
223
+ } )
224
+ vi . mocked ( integrationService . analyzeChannel ) . mockResolvedValue (
225
+ mockAnalysisResult
226
+ )
227
+
187
228
// Mock share response
188
229
const mockShare = {
189
230
id : 'share-1' ,
@@ -468,6 +509,290 @@ describe('IntegrationContext', () => {
468
509
expect ( hookResult ?. error ) . toBeNull ( )
469
510
} )
470
511
512
+ it ( 'should fetch selected channels when requested' , async ( ) => {
513
+ let hookResult : ReturnType < typeof useIntegration > | undefined
514
+
515
+ await act ( async ( ) => {
516
+ render (
517
+ < MockAuthProvider >
518
+ < IntegrationProvider >
519
+ < TestHookComponent
520
+ callback = { ( ) => {
521
+ hookResult = useIntegration ( )
522
+ } }
523
+ />
524
+ </ IntegrationProvider >
525
+ </ MockAuthProvider >
526
+ )
527
+ } )
528
+
529
+ // Verify we have the function
530
+ expect ( hookResult ) . toBeDefined ( )
531
+ expect ( hookResult ?. fetchSelectedChannels ) . toBeDefined ( )
532
+
533
+ // Call the function
534
+ await act ( async ( ) => {
535
+ await hookResult ?. fetchSelectedChannels ( 'test-int-1' )
536
+ } )
537
+
538
+ // Check that the service was called correctly
539
+ expect ( integrationService . getSelectedChannels ) . toHaveBeenCalledWith (
540
+ 'test-int-1'
541
+ )
542
+
543
+ // Verify the state was updated
544
+ expect ( hookResult ?. selectedChannels . length ) . toBe ( 2 )
545
+ expect ( hookResult ?. selectedChannels [ 0 ] . id ) . toBe ( 'res-1' )
546
+ expect ( hookResult ?. selectedChannels [ 1 ] . id ) . toBe ( 'res-2' )
547
+ expect ( hookResult ?. loadingChannelSelection ) . toBe ( false )
548
+ } )
549
+
550
+ it ( 'should select channels for analysis' , async ( ) => {
551
+ let hookResult : ReturnType < typeof useIntegration > | undefined
552
+
553
+ await act ( async ( ) => {
554
+ render (
555
+ < MockAuthProvider >
556
+ < IntegrationProvider >
557
+ < TestHookComponent
558
+ callback = { ( ) => {
559
+ hookResult = useIntegration ( )
560
+ } }
561
+ />
562
+ </ IntegrationProvider >
563
+ </ MockAuthProvider >
564
+ )
565
+ } )
566
+
567
+ // Verify we have the function
568
+ expect ( hookResult ) . toBeDefined ( )
569
+ expect ( hookResult ?. selectChannelsForAnalysis ) . toBeDefined ( )
570
+
571
+ // Call the function
572
+ let result : boolean | undefined
573
+ await act ( async ( ) => {
574
+ result = await hookResult ?. selectChannelsForAnalysis ( 'test-int-1' , [
575
+ 'res-1' ,
576
+ 'res-2' ,
577
+ ] )
578
+ } )
579
+
580
+ // Check that the service was called correctly
581
+ expect ( integrationService . selectChannelsForAnalysis ) . toHaveBeenCalledWith (
582
+ 'test-int-1' ,
583
+ { channel_ids : [ 'res-1' , 'res-2' ] , for_analysis : true }
584
+ )
585
+
586
+ // Should also refresh the selected channels
587
+ expect ( integrationService . getSelectedChannels ) . toHaveBeenCalledWith (
588
+ 'test-int-1'
589
+ )
590
+
591
+ // Verify the state was updated and the result is correct
592
+ expect ( result ) . toBe ( true )
593
+ expect ( hookResult ?. loadingChannelSelection ) . toBe ( false )
594
+ } )
595
+
596
+ it ( 'should deselect channels for analysis' , async ( ) => {
597
+ let hookResult : ReturnType < typeof useIntegration > | undefined
598
+
599
+ await act ( async ( ) => {
600
+ render (
601
+ < MockAuthProvider >
602
+ < IntegrationProvider >
603
+ < TestHookComponent
604
+ callback = { ( ) => {
605
+ hookResult = useIntegration ( )
606
+ } }
607
+ />
608
+ </ IntegrationProvider >
609
+ </ MockAuthProvider >
610
+ )
611
+ } )
612
+
613
+ // Verify we have the function
614
+ expect ( hookResult ) . toBeDefined ( )
615
+ expect ( hookResult ?. deselectChannelsForAnalysis ) . toBeDefined ( )
616
+
617
+ // Call the function
618
+ let result : boolean | undefined
619
+ await act ( async ( ) => {
620
+ result = await hookResult ?. deselectChannelsForAnalysis ( 'test-int-1' , [
621
+ 'res-2' ,
622
+ ] )
623
+ } )
624
+
625
+ // Check that the service was called correctly
626
+ expect ( integrationService . selectChannelsForAnalysis ) . toHaveBeenCalledWith (
627
+ 'test-int-1' ,
628
+ { channel_ids : [ 'res-2' ] , for_analysis : false }
629
+ )
630
+
631
+ // Should also refresh the selected channels
632
+ expect ( integrationService . getSelectedChannels ) . toHaveBeenCalledWith (
633
+ 'test-int-1'
634
+ )
635
+
636
+ // Verify the result is correct
637
+ expect ( result ) . toBe ( true )
638
+ } )
639
+
640
+ it ( 'should check if a channel is selected for analysis' , async ( ) => {
641
+ let hookResult : ReturnType < typeof useIntegration > | undefined
642
+
643
+ await act ( async ( ) => {
644
+ render (
645
+ < MockAuthProvider >
646
+ < IntegrationProvider >
647
+ < TestHookComponent
648
+ callback = { ( ) => {
649
+ hookResult = useIntegration ( )
650
+ } }
651
+ />
652
+ </ IntegrationProvider >
653
+ </ MockAuthProvider >
654
+ )
655
+ } )
656
+
657
+ // First fetch selected channels to populate state
658
+ await act ( async ( ) => {
659
+ await hookResult ?. fetchSelectedChannels ( 'test-int-1' )
660
+ } )
661
+
662
+ // Verify we have the function
663
+ expect ( hookResult ) . toBeDefined ( )
664
+ expect ( hookResult ?. isChannelSelectedForAnalysis ) . toBeDefined ( )
665
+
666
+ // Check for known selected channel
667
+ expect ( hookResult ?. isChannelSelectedForAnalysis ( 'res-1' ) ) . toBe ( true )
668
+ expect ( hookResult ?. isChannelSelectedForAnalysis ( 'C12345' ) ) . toBe ( true ) // Should work with external_id too
669
+
670
+ // Check for non-selected channel
671
+ expect ( hookResult ?. isChannelSelectedForAnalysis ( 'non-existent' ) ) . toBe (
672
+ false
673
+ )
674
+ } )
675
+
676
+ it ( 'should run analysis on a channel' , async ( ) => {
677
+ let hookResult : ReturnType < typeof useIntegration > | undefined
678
+
679
+ await act ( async ( ) => {
680
+ render (
681
+ < MockAuthProvider >
682
+ < IntegrationProvider >
683
+ < TestHookComponent
684
+ callback = { ( ) => {
685
+ hookResult = useIntegration ( )
686
+ } }
687
+ />
688
+ </ IntegrationProvider >
689
+ </ MockAuthProvider >
690
+ )
691
+ } )
692
+
693
+ // Verify we have the function
694
+ expect ( hookResult ) . toBeDefined ( )
695
+ expect ( hookResult ?. analyzeChannel ) . toBeDefined ( )
696
+
697
+ // Call the function with analysis options
698
+ let result : { status : string ; analysis_id : string } | null | undefined
699
+ const options = {
700
+ start_date : '2023-01-01' ,
701
+ end_date : '2023-01-31' ,
702
+ include_threads : true ,
703
+ include_reactions : false ,
704
+ }
705
+
706
+ await act ( async ( ) => {
707
+ result = await hookResult ?. analyzeChannel (
708
+ 'test-int-1' ,
709
+ 'res-1' ,
710
+ options
711
+ )
712
+ } )
713
+
714
+ // Check that the service was called correctly
715
+ expect ( integrationService . analyzeChannel ) . toHaveBeenCalledWith (
716
+ 'test-int-1' ,
717
+ 'res-1' ,
718
+ options
719
+ )
720
+
721
+ // Verify the result contains the expected data
722
+ expect ( result ) . toBeDefined ( )
723
+ expect ( result ?. status ) . toBe ( 'success' )
724
+ expect ( result ?. analysis_id ) . toBe ( 'analysis-123' )
725
+ } )
726
+
727
+ it ( 'should handle errors when selecting channels' , async ( ) => {
728
+ // Save original implementations to restore after the test
729
+ const originalSelectChannelsForAnalysis = vi
730
+ . mocked ( integrationService . selectChannelsForAnalysis )
731
+ . getMockImplementation ( )
732
+ const originalIsApiError = vi
733
+ . mocked ( integrationService . isApiError )
734
+ . getMockImplementation ( )
735
+
736
+ // Mock the service to return an error
737
+ vi . mocked (
738
+ integrationService . selectChannelsForAnalysis
739
+ ) . mockResolvedValueOnce ( {
740
+ status : 400 ,
741
+ message : 'Failed to select channels' ,
742
+ } )
743
+
744
+ // Mock isApiError to identify this as an error
745
+ vi . mocked ( integrationService . isApiError ) . mockImplementation ( ( ) => true )
746
+
747
+ let hookResult : ReturnType < typeof useIntegration > | undefined
748
+
749
+ await act ( async ( ) => {
750
+ render (
751
+ < MockAuthProvider >
752
+ < IntegrationProvider >
753
+ < TestHookComponent
754
+ callback = { ( ) => {
755
+ hookResult = useIntegration ( )
756
+ } }
757
+ />
758
+ </ IntegrationProvider >
759
+ </ MockAuthProvider >
760
+ )
761
+ } )
762
+
763
+ // Call the function that should fail
764
+ let result : boolean | undefined
765
+ await act ( async ( ) => {
766
+ result = await hookResult ?. selectChannelsForAnalysis ( 'test-int-1' , [
767
+ 'res-1' ,
768
+ ] )
769
+ } )
770
+
771
+ // Verify the result and error state
772
+ expect ( result ) . toBe ( false )
773
+ expect ( hookResult ?. channelSelectionError ) . toBeDefined ( )
774
+ expect ( hookResult ?. loadingChannelSelection ) . toBe ( false )
775
+
776
+ // Test clearing the error
777
+ await act ( async ( ) => {
778
+ hookResult ?. clearChannelSelectionError ( )
779
+ } )
780
+
781
+ expect ( hookResult ?. channelSelectionError ) . toBeNull ( )
782
+
783
+ // Restore original implementations
784
+ if ( originalSelectChannelsForAnalysis ) {
785
+ vi . mocked (
786
+ integrationService . selectChannelsForAnalysis
787
+ ) . mockImplementation ( originalSelectChannelsForAnalysis )
788
+ }
789
+ if ( originalIsApiError ) {
790
+ vi . mocked ( integrationService . isApiError ) . mockImplementation (
791
+ originalIsApiError
792
+ )
793
+ }
794
+ } )
795
+
471
796
// Add timeout to the full test suite to prevent hanging
472
797
vi . setConfig ( {
473
798
testTimeout : 5000 , // 5 second timeout
0 commit comments