@@ -22,7 +22,7 @@ use matrix_sdk::{
2222 assert_let_timeout, attachment:: AttachmentConfig , test_utils:: mocks:: MatrixMockServer ,
2323} ;
2424use matrix_sdk_test:: { async_test, event_factory:: EventFactory , JoinedRoomBuilder , ALICE } ;
25- use matrix_sdk_ui:: timeline:: { EventSendState , RoomExt , TimelineItemContent } ;
25+ use matrix_sdk_ui:: timeline:: { AttachmentSource , EventSendState , RoomExt , TimelineItemContent } ;
2626use ruma:: {
2727 event_id,
2828 events:: room:: { message:: MessageType , MediaSource } ,
@@ -52,7 +52,7 @@ fn get_filename_and_caption(msg: &MessageType) -> (&str, Option<&str>) {
5252}
5353
5454#[ async_test]
55- async fn test_send_attachment ( ) {
55+ async fn test_send_attachment_from_file ( ) {
5656 let mock = MatrixMockServer :: new ( ) . await ;
5757 let client = mock. client_builder ( ) . build ( ) . await ;
5858
@@ -148,6 +148,105 @@ async fn test_send_attachment() {
148148 assert ! ( timeline_stream. next( ) . now_or_never( ) . is_none( ) ) ;
149149}
150150
151+ #[ async_test]
152+ async fn test_send_attachment_from_bytes ( ) {
153+ let mock = MatrixMockServer :: new ( ) . await ;
154+ let client = mock. client_builder ( ) . build ( ) . await ;
155+
156+ mock. mock_room_state_encryption ( ) . plain ( ) . mount ( ) . await ;
157+
158+ let room_id = room_id ! ( "!a98sd12bjh:example.org" ) ;
159+ let room = mock. sync_joined_room ( & client, room_id) . await ;
160+ let timeline = room. timeline ( ) . await . unwrap ( ) ;
161+
162+ let ( items, mut timeline_stream) =
163+ timeline. subscribe_filter_map ( |item| item. as_event ( ) . cloned ( ) ) . await ;
164+
165+ assert ! ( items. is_empty( ) ) ;
166+
167+ let f = EventFactory :: new ( ) ;
168+ mock. sync_room (
169+ & client,
170+ JoinedRoomBuilder :: new ( room_id) . add_timeline_event ( f. text_msg ( "hello" ) . sender ( & ALICE ) ) ,
171+ )
172+ . await ;
173+
174+ // Sanity check.
175+ assert_let_timeout ! ( Some ( VectorDiff :: PushBack { value: item } ) = timeline_stream. next( ) ) ;
176+ assert_let ! ( TimelineItemContent :: Message ( msg) = item. content( ) ) ;
177+ assert_eq ! ( msg. body( ) , "hello" ) ;
178+
179+ // No other updates.
180+ assert ! ( timeline_stream. next( ) . now_or_never( ) . is_none( ) ) ;
181+
182+ // The data of the file.
183+ let filename = "test.bin" ;
184+ let source =
185+ AttachmentSource :: Data { bytes : b"hello world" . to_vec ( ) , filename : filename. to_owned ( ) } ;
186+
187+ // Set up mocks for the file upload.
188+ mock. mock_upload ( )
189+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_delay ( Duration :: from_secs ( 2 ) ) . set_body_json (
190+ json ! ( {
191+ "content_uri" : "mxc://sdk.rs/media"
192+ } ) ,
193+ ) )
194+ . mock_once ( )
195+ . mount ( )
196+ . await ;
197+
198+ mock. mock_room_send ( ) . ok ( event_id ! ( "$media" ) ) . mock_once ( ) . mount ( ) . await ;
199+
200+ // Queue sending of an attachment.
201+ let config = AttachmentConfig :: new ( ) . caption ( Some ( "caption" . to_owned ( ) ) ) ;
202+ timeline. send_attachment ( source, mime:: TEXT_PLAIN , config) . use_send_queue ( ) . await . unwrap ( ) ;
203+
204+ {
205+ assert_let_timeout ! ( Some ( VectorDiff :: PushBack { value: item } ) = timeline_stream. next( ) ) ;
206+ assert_matches ! ( item. send_state( ) , Some ( EventSendState :: NotSentYet ) ) ;
207+ assert_let ! ( TimelineItemContent :: Message ( msg) = item. content( ) ) ;
208+
209+ // Body is the caption, because there's both a caption and filename.
210+ assert_eq ! ( msg. body( ) , "caption" ) ;
211+ assert_eq ! ( get_filename_and_caption( msg. msgtype( ) ) , ( filename, Some ( "caption" ) ) ) ;
212+
213+ // The URI refers to the local cache.
214+ assert_let ! ( MessageType :: File ( file) = msg. msgtype( ) ) ;
215+ assert_let ! ( MediaSource :: Plain ( uri) = & file. source) ;
216+ assert ! ( uri. to_string( ) . contains( "localhost" ) ) ;
217+ }
218+
219+ // Eventually, the media is updated with the final MXC IDs…
220+ sleep ( Duration :: from_secs ( 2 ) ) . await ;
221+
222+ {
223+ assert_let_timeout ! (
224+ Some ( VectorDiff :: Set { index: 1 , value: item } ) = timeline_stream. next( )
225+ ) ;
226+ assert_let ! ( TimelineItemContent :: Message ( msg) = item. content( ) ) ;
227+ assert_matches ! ( item. send_state( ) , Some ( EventSendState :: NotSentYet ) ) ;
228+ assert_eq ! ( get_filename_and_caption( msg. msgtype( ) ) , ( filename, Some ( "caption" ) ) ) ;
229+
230+ // The URI now refers to the final MXC URI.
231+ assert_let ! ( MessageType :: File ( file) = msg. msgtype( ) ) ;
232+ assert_let ! ( MediaSource :: Plain ( uri) = & file. source) ;
233+ assert_eq ! ( uri. to_string( ) , "mxc://sdk.rs/media" ) ;
234+ }
235+
236+ // And eventually the event itself is sent.
237+ {
238+ assert_let_timeout ! (
239+ Some ( VectorDiff :: Set { index: 1 , value: item } ) = timeline_stream. next( )
240+ ) ;
241+ assert_matches ! ( item. send_state( ) , Some ( EventSendState :: Sent { event_id } ) => {
242+ assert_eq!( event_id, event_id!( "$media" ) ) ;
243+ } ) ;
244+ }
245+
246+ // That's all, folks!
247+ assert ! ( timeline_stream. next( ) . now_or_never( ) . is_none( ) ) ;
248+ }
249+
151250#[ async_test]
152251async fn test_react_to_local_media ( ) {
153252 let mock = MatrixMockServer :: new ( ) . await ;
0 commit comments