1- // Copyright (c) 2018, the Zefyr project authors. Please see the AUTHORS file
2- // for details. All rights reserved. Use of this source code is governed by a
3- // BSD-style license that can be found in the LICENSE file.
4- import 'package:flutter/widgets.dart' ;
1+ import 'package:flutter/services.dart' ;
52import 'package:flutter_test/flutter_test.dart' ;
63
74import '../testing.dart' ;
85
96void main () {
107 group ('FleatherEditableText' , () {
11- testWidgets ('user input' , (tester) async {
8+ testWidgets ('user input inserts text ' , (tester) async {
129 final editor = EditorSandBox (tester: tester);
1310 await editor.pumpAndTap ();
1411 final currentValue = editor.document.toPlainText ();
15- await enterText (tester, 'Added $ currentValue ' );
12+ await insertText (tester, 'Added ' , inText : currentValue );
1613 expect (editor.document.toPlainText (), 'Added This House Is A Circus\n ' );
1714 });
1815
16+ testWidgets ('user input deletes text' , (tester) async {
17+ final editor = EditorSandBox (tester: tester);
18+ await editor.pumpAndTap ();
19+ final currentValue = editor.document.toPlainText ();
20+ await deleteText (tester, nbCharacters: 5 , inText: currentValue);
21+ expect (editor.document.toPlainText (), 'House Is A Circus\n ' );
22+ });
23+
24+ testWidgets ('user input replaced text' , (tester) async {
25+ final editor = EditorSandBox (tester: tester);
26+ await editor.pumpAndTap ();
27+ final currentValue = editor.document.toPlainText ();
28+ await replaceText (tester,
29+ inText: currentValue,
30+ range: const TextRange (start: 5 , end: 5 + 'House' .length),
31+ withText: 'Place' );
32+ expect (editor.document.toPlainText (), 'This Place Is A Circus\n ' );
33+ });
34+
1935 testWidgets ('autofocus' , (tester) async {
2036 final editor = EditorSandBox (tester: tester, autofocus: true );
2137 await editor.pump ();
@@ -30,14 +46,103 @@ void main() {
3046 });
3147}
3248
33- Future <void > enterText (WidgetTester tester, String text) async {
49+ Future <void > insertText (WidgetTester tester, String textInserted,
50+ {int atOffset = 0 , String inText = '' }) async {
3451 return TestAsyncUtils .guard (() async {
35- tester.testTextInput.updateEditingValue (
36- TextEditingValue (
37- text: text,
38- selection: const TextSelection .collapsed (offset: 6 ),
39- ),
40- );
52+ updateDeltaEditingValue (TextEditingDeltaInsertion (
53+ oldText: inText,
54+ textInserted: textInserted,
55+ insertionOffset: atOffset,
56+ selection: const TextSelection .collapsed (offset: 0 ),
57+ composing: TextRange .empty));
58+ await tester.idle ();
59+ });
60+ }
61+
62+ Future <void > deleteText (WidgetTester tester,
63+ {required int nbCharacters, int at = 0 , required String inText}) {
64+ return TestAsyncUtils .guard (() async {
65+ updateDeltaEditingValue (TextEditingDeltaDeletion (
66+ oldText: inText,
67+ deletedRange: TextRange (start: at, end: at + nbCharacters),
68+ selection: const TextSelection .collapsed (offset: 0 ),
69+ composing: TextRange .empty));
4170 await tester.idle ();
4271 });
4372}
73+
74+ Future <void > replaceText (WidgetTester tester,
75+ {required TextRange range,
76+ required String withText,
77+ required String inText}) {
78+ return TestAsyncUtils .guard (() async {
79+ updateDeltaEditingValue (TextEditingDeltaReplacement (
80+ oldText: inText,
81+ replacedRange: range,
82+ replacementText: withText,
83+ selection: const TextSelection .collapsed (offset: 0 ),
84+ composing: TextRange .empty));
85+ await tester.idle ();
86+ });
87+ }
88+
89+ void updateDeltaEditingValue (TextEditingDelta delta, {int ? client}) {
90+ TestDefaultBinaryMessengerBinding .instance! .defaultBinaryMessenger
91+ .handlePlatformMessage (
92+ SystemChannels .textInput.name,
93+ SystemChannels .textInput.codec.encodeMethodCall (
94+ MethodCall (
95+ 'TextInputClient.updateEditingStateWithDeltas' ,
96+ < dynamic > [
97+ client ?? - 1 ,
98+ {
99+ 'deltas' : [delta.toJSON ()]
100+ }
101+ ],
102+ ),
103+ ),
104+ (ByteData ? data) {
105+ /* ignored */
106+ },
107+ );
108+ }
109+
110+ extension DeltaJson on TextEditingDelta {
111+ Map <String , dynamic > toJSON () {
112+ final json = < String , dynamic > {};
113+ json['composingBase' ] = composing.start;
114+ json['composingExtent' ] = composing.end;
115+
116+ json['selectionBase' ] = selection.baseOffset;
117+ json['selectionExtent' ] = selection.extentOffset;
118+ json['selectionAffinity' ] = selection.affinity.name;
119+ json['selectionIsDirectional' ] = selection.isDirectional;
120+
121+ json['oldText' ] = oldText;
122+
123+ if (this is TextEditingDeltaInsertion ) {
124+ final insertion = this as TextEditingDeltaInsertion ;
125+ json['deltaStart' ] = insertion.insertionOffset;
126+ // Assumes no replacement, simply insertion here
127+ json['deltaEnd' ] = insertion.insertionOffset;
128+ json['deltaText' ] = insertion.textInserted;
129+ }
130+
131+ if (this is TextEditingDeltaDeletion ) {
132+ final deletion = this as TextEditingDeltaDeletion ;
133+ json['deltaStart' ] = deletion.deletedRange.start;
134+ // Assumes no replacement, simply insertion here
135+ json['deltaEnd' ] = deletion.deletedRange.end;
136+ json['deltaText' ] = '' ;
137+ }
138+
139+ if (this is TextEditingDeltaReplacement ) {
140+ final replacement = this as TextEditingDeltaReplacement ;
141+ json['deltaStart' ] = replacement.replacedRange.start;
142+ // Assumes no replacement, simply insertion here
143+ json['deltaEnd' ] = replacement.replacedRange.end;
144+ json['deltaText' ] = replacement.replacementText;
145+ }
146+ return json;
147+ }
148+ }
0 commit comments