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' ;
5
2
import 'package:flutter_test/flutter_test.dart' ;
6
3
7
4
import '../testing.dart' ;
8
5
9
6
void main () {
10
7
group ('FleatherEditableText' , () {
11
- testWidgets ('user input' , (tester) async {
8
+ testWidgets ('user input inserts text ' , (tester) async {
12
9
final editor = EditorSandBox (tester: tester);
13
10
await editor.pumpAndTap ();
14
11
final currentValue = editor.document.toPlainText ();
15
- await enterText (tester, 'Added $ currentValue ' );
12
+ await insertText (tester, 'Added ' , inText : currentValue );
16
13
expect (editor.document.toPlainText (), 'Added This House Is A Circus\n ' );
17
14
});
18
15
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
+
19
35
testWidgets ('autofocus' , (tester) async {
20
36
final editor = EditorSandBox (tester: tester, autofocus: true );
21
37
await editor.pump ();
@@ -30,14 +46,103 @@ void main() {
30
46
});
31
47
}
32
48
33
- Future <void > enterText (WidgetTester tester, String text) async {
49
+ Future <void > insertText (WidgetTester tester, String textInserted,
50
+ {int atOffset = 0 , String inText = '' }) async {
34
51
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));
41
70
await tester.idle ();
42
71
});
43
72
}
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