@@ -3,6 +3,7 @@ import 'dart:convert';
3
3
4
4
import 'package:checks/checks.dart' ;
5
5
import 'package:file_picker/file_picker.dart' ;
6
+ import 'package:flutter/services.dart' ;
6
7
import 'package:flutter_checks/flutter_checks.dart' ;
7
8
import 'package:http/http.dart' as http;
8
9
import 'package:flutter/material.dart' ;
@@ -575,6 +576,106 @@ void main() {
575
576
576
577
// TODO test what happens when capturing/uploading fails
577
578
});
579
+
580
+ group ('attach from keyboard' , () {
581
+ // This is adapted from:
582
+ // https://github.com/flutter/flutter/blob/0ffc4ce00ea7bb912e379adf39354644eab2c17e/packages/flutter/test/widgets/editable_text_test.dart#L724-L740
583
+ Future <void > insertContentFromKeyboard (WidgetTester tester, {
584
+ required List <int >? data,
585
+ required String attachedFileUrl,
586
+ required String mimeType,
587
+ }) async {
588
+ await tester.showKeyboard (contentInputFinder);
589
+ // This invokes [EditableText.performAction] on the content [TextField],
590
+ // which did not expose an API for testing.
591
+ // TODO(upstream): support a better API for testing this
592
+ await tester.binding.defaultBinaryMessenger.handlePlatformMessage (
593
+ SystemChannels .textInput.name,
594
+ SystemChannels .textInput.codec.encodeMethodCall (
595
+ MethodCall ('TextInputClient.performAction' , < dynamic > [
596
+ - 1 ,
597
+ 'TextInputAction.commitContent' ,
598
+ // This fakes data originally provided by the Flutter engine:
599
+ // https://github.com/flutter/flutter/blob/0ffc4ce00ea7bb912e379adf39354644eab2c17e/engine/src/flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java#L497-L548
600
+ {
601
+ "mimeType" : mimeType,
602
+ "data" : data,
603
+ "uri" : attachedFileUrl,
604
+ },
605
+ ])),
606
+ (ByteData ? data) {});
607
+ }
608
+
609
+ testWidgets ('success' , (tester) async {
610
+ const fileContent = [1 , 0 , 1 , 0 , 0 ];
611
+ await prepare (tester);
612
+ const uploadUrl = '/user_uploads/1/4e/m2A3MSqFnWRLUf9SaPzQ0Up_/test.gif' ;
613
+ connection.prepare (json: UploadFileResult (uri: uploadUrl).toJson ());
614
+ await insertContentFromKeyboard (tester,
615
+ data: fileContent,
616
+ attachedFileUrl:
617
+ 'content://com.zulip.android.zulipboard.provider'
618
+ '/root/com.zulip.android.zulipboard/candidate_temp/test.gif' ,
619
+ mimeType: 'image/gif' );
620
+
621
+ await tester.pump ();
622
+ check (controller! .content.text)
623
+ .equals ('see image: [Uploading test.gif…]()\n\n ' );
624
+ // (the request is checked more thoroughly in API tests)
625
+ check (connection.lastRequest! ).isA< http.MultipartRequest > ()
626
+ ..method.equals ('POST' )
627
+ ..files.single.which ((it) => it
628
+ ..field.equals ('file' )
629
+ ..length.equals (fileContent.length)
630
+ ..filename.equals ('test.gif' )
631
+ ..contentType.asString.equals ('image/gif' )
632
+ ..has <Future <List <int >>>((f) => f.finalize ().toBytes (), 'contents' )
633
+ .completes ((it) => it.deepEquals (fileContent))
634
+ );
635
+ checkAppearsLoading (tester, true );
636
+
637
+ await tester.pump (Duration .zero);
638
+ check (controller! .content.text)
639
+ .equals ('see image: [test.gif]($uploadUrl )\n\n ' );
640
+ checkAppearsLoading (tester, false );
641
+ });
642
+
643
+ testWidgets ('data is null' , (tester) async {
644
+ await prepare (tester);
645
+ await insertContentFromKeyboard (tester,
646
+ data: null ,
647
+ attachedFileUrl:
648
+ 'content://com.zulip.android.zulipboard.provider'
649
+ '/root/com.zulip.android.zulipboard/candidate_temp/test.gif' ,
650
+ mimeType: 'image/jpeg' );
651
+
652
+ await tester.pump ();
653
+ check (controller! .content.text).equals ('see image: ' );
654
+ check (connection.takeRequests ()).isEmpty ();
655
+ checkErrorDialog (tester,
656
+ expectedTitle: 'Content not inserted' ,
657
+ expectedMessage: 'The file to be inserted is empty or cannot be accessed.' );
658
+ checkAppearsLoading (tester, false );
659
+ });
660
+
661
+ testWidgets ('data is empty' , (tester) async {
662
+ await prepare (tester);
663
+ await insertContentFromKeyboard (tester,
664
+ data: [],
665
+ attachedFileUrl:
666
+ 'content://com.zulip.android.zulipboard.provider'
667
+ '/root/com.zulip.android.zulipboard/candidate_temp/test.gif' ,
668
+ mimeType: 'image/jpeg' );
669
+
670
+ await tester.pump ();
671
+ check (controller! .content.text).equals ('see image: ' );
672
+ check (connection.takeRequests ()).isEmpty ();
673
+ checkErrorDialog (tester,
674
+ expectedTitle: 'Content not inserted' ,
675
+ expectedMessage: 'The file to be inserted is empty or cannot be accessed.' );
676
+ checkAppearsLoading (tester, false );
677
+ });
678
+ });
578
679
});
579
680
580
681
group ('error banner' , () {
0 commit comments