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