@@ -325,6 +325,39 @@ describe('AttachmentManager', () => {
325
325
// Should have max slots minus the number of attachments in the message
326
326
expect ( manager . availableUploadSlots ) . toBe ( API_MAX_FILES_ALLOWED_PER_MESSAGE - 2 ) ;
327
327
} ) ;
328
+
329
+ it ( 'should take into consideration uploads in progress' , ( ) => {
330
+ const { attachmentManager } = setup ( ) ;
331
+
332
+ // Set up state with successful uploads and uploads in progress
333
+ attachmentManager . state . next ( {
334
+ attachments : [
335
+ {
336
+ type : 'image' ,
337
+ image_url : 'test-image-url' ,
338
+ localMetadata : {
339
+ id : 'test-uuid-1' ,
340
+ uploadState : 'finished' ,
341
+ file : new File ( [ '' ] , 'test1.jpg' , { type : 'image/jpeg' } ) ,
342
+ } ,
343
+ } ,
344
+ {
345
+ type : 'image' ,
346
+ image_url : 'test-image-url' ,
347
+ localMetadata : {
348
+ id : 'test-uuid-2' ,
349
+ uploadState : 'uploading' ,
350
+ file : new File ( [ '' ] , 'test2.jpg' , { type : 'image/jpeg' } ) ,
351
+ } ,
352
+ } ,
353
+ ] ,
354
+ } ) ;
355
+
356
+ // Should have max slots minus successful uploads (1) minus uploads in progress (1)
357
+ expect ( attachmentManager . availableUploadSlots ) . toBe (
358
+ API_MAX_FILES_ALLOWED_PER_MESSAGE - 2 ,
359
+ ) ;
360
+ } ) ;
328
361
} ) ;
329
362
330
363
describe ( 'initState' , ( ) => {
@@ -691,7 +724,23 @@ describe('AttachmentManager', () => {
691
724
mockChannel . sendImage . mockRejectedValueOnce ( new Error ( 'Upload failed' ) ) ;
692
725
const file = new File ( [ '' ] , 'test.jpg' , { type : 'image/jpeg' } ) ;
693
726
694
- await expect ( attachmentManager . uploadFiles ( [ file ] ) ) . rejects . toThrowError ( ) ;
727
+ await expect ( attachmentManager . uploadFiles ( [ file ] ) ) . resolves . toEqual ( [
728
+ {
729
+ fallback : 'test.jpg' ,
730
+ file_size : 0 ,
731
+ localMetadata : {
732
+ id : 'test-uuid' ,
733
+ file,
734
+ uploadState : 'failed' ,
735
+ previewUri : expect . any ( String ) ,
736
+ uploadPermissionCheck : {
737
+ uploadBlocked : false ,
738
+ } ,
739
+ } ,
740
+ mime_type : 'image/jpeg' ,
741
+ type : 'image' ,
742
+ } ,
743
+ ] ) ;
695
744
696
745
expect ( attachmentManager . failedUploadsCount ) . toBe ( 1 ) ;
697
746
expect ( mockClient . notifications . addError ) . toHaveBeenCalledWith ( {
@@ -703,6 +752,70 @@ describe('AttachmentManager', () => {
703
752
} ) ;
704
753
} ) ;
705
754
755
+ it ( 'should register notification for blocked file' , async ( ) => {
756
+ const { attachmentManager, mockClient } = setup ( ) ;
757
+
758
+ // Create a blocked attachment
759
+ const blockedAttachment = {
760
+ type : 'image' ,
761
+ localMetadata : {
762
+ id : 'test-id' ,
763
+ file : new File ( [ '' ] , 'test.jpg' , { type : 'image/jpeg' } ) ,
764
+ } ,
765
+ } ;
766
+
767
+ // Mock getUploadConfigCheck to return blocked
768
+ vi . spyOn ( attachmentManager , 'getUploadConfigCheck' ) . mockResolvedValue ( {
769
+ uploadBlocked : true ,
770
+ reason : 'size_limit' ,
771
+ } ) ;
772
+
773
+ await attachmentManager . uploadAttachment ( blockedAttachment ) ;
774
+
775
+ // Verify notification was added
776
+ expect ( mockClient . notifications . addError ) . toHaveBeenCalledWith ( {
777
+ message : 'Error uploading attachment' ,
778
+ origin : {
779
+ emitter : 'AttachmentManager' ,
780
+ context : { attachment : blockedAttachment } ,
781
+ } ,
782
+ } ) ;
783
+ } ) ;
784
+
785
+ it ( 'should use custom upload function when provided' , async ( ) => {
786
+ const { attachmentManager, mockChannel } = setup ( ) ;
787
+
788
+ // Create a custom upload function
789
+ const customUploadFn = vi . fn ( ) . mockResolvedValue ( { file : 'custom-upload-url' } ) ;
790
+
791
+ // Set the custom upload function
792
+ attachmentManager . setCustomUploadFn ( customUploadFn ) ;
793
+
794
+ // Create a file to upload
795
+ const file = new File ( [ '' ] , 'test.jpg' , { type : 'image/jpeg' } ) ;
796
+
797
+ // Mock fileToLocalUploadAttachment to return a valid attachment
798
+ const attachment = {
799
+ type : 'image' ,
800
+ localMetadata : {
801
+ id : 'test-id' ,
802
+ file,
803
+ uploadState : 'pending' ,
804
+ } ,
805
+ } ;
806
+
807
+ vi . spyOn ( attachmentManager , 'ensureLocalUploadAttachment' ) . mockResolvedValue (
808
+ attachment ,
809
+ ) ;
810
+
811
+ // Upload the attachment
812
+ await attachmentManager . uploadAttachment ( attachment ) ;
813
+
814
+ // Verify the custom upload function was called
815
+ expect ( customUploadFn ) . toHaveBeenCalledWith ( file ) ;
816
+ expect ( mockChannel . sendImage ) . not . toHaveBeenCalled ( ) ;
817
+ } ) ;
818
+
706
819
it ( 'should respect maxNumberOfFilesPerMessage' , async ( ) => {
707
820
const { attachmentManager } = setup ( ) ;
708
821
const files = Array ( API_MAX_FILES_ALLOWED_PER_MESSAGE + 1 )
@@ -848,5 +961,44 @@ describe('AttachmentManager', () => {
848
961
849
962
expect ( result ) . toEqual ( expectedAttachment ) ;
850
963
} ) ;
964
+
965
+ it ( 'should preserve original ID if it exists' , async ( ) => {
966
+ const { attachmentManager } = setup ( ) ;
967
+ const ensureLocalUploadAttachment = ( attachmentManager as any )
968
+ . ensureLocalUploadAttachment ;
969
+
970
+ // Set a fileUploadFilter that allows all files
971
+ attachmentManager . fileUploadFilter = ( ) => true ;
972
+
973
+ // Create an attachment with an ID
974
+ const originalId = 'original-test-id' ;
975
+ const file = new File ( [ '' ] , 'test.jpg' , { type : 'image/jpeg' } ) ;
976
+
977
+ // Mock fileToLocalUploadAttachment to return a new attachment
978
+ const newAttachment = {
979
+ type : 'image' ,
980
+ image_url : 'test-url' ,
981
+ localMetadata : {
982
+ id : 'new-test-id' , // Different ID
983
+ file : new File ( [ '' ] , 'test.jpg' , { type : 'image/jpeg' } ) ,
984
+ uploadState : 'finished' ,
985
+ } ,
986
+ } ;
987
+
988
+ vi . spyOn ( attachmentManager , 'fileToLocalUploadAttachment' ) . mockResolvedValue (
989
+ newAttachment ,
990
+ ) ;
991
+
992
+ // Call with original ID
993
+ const result = await ensureLocalUploadAttachment ( {
994
+ localMetadata : {
995
+ id : originalId ,
996
+ file,
997
+ } ,
998
+ } ) ;
999
+
1000
+ // Verify the original ID was preserved
1001
+ expect ( result . localMetadata . id ) . toBe ( originalId ) ;
1002
+ } ) ;
851
1003
} ) ;
852
1004
} ) ;
0 commit comments