4
4
5
5
@TestOn ('chrome || safari || firefox' )
6
6
7
+ import 'dart:typed_data' ;
8
+
7
9
import 'package:test/bootstrap/browser.dart' ;
8
10
import 'package:test/test.dart' ;
9
11
@@ -48,6 +50,11 @@ void testMain() {
48
50
testTextEditing.configuration = singlelineConfig;
49
51
});
50
52
53
+ /// Emulates sending of a message by the framework to the engine.
54
+ void sendFrameworkMessage (ByteData ? message) {
55
+ testTextEditing.channel.handleTextInput (message, (ByteData ? data) {});
56
+ }
57
+
51
58
test ('renders a text field' , () async {
52
59
semantics ()
53
60
..debugOverrideTimestampFunction (() => _testTime)
@@ -127,7 +134,7 @@ void testMain() {
127
134
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50754
128
135
skip: browserEngine != BrowserEngine .blink);
129
136
130
- test ('Syncs editing state from framework' , () async {
137
+ test ('Syncs semantic state from framework' , () async {
131
138
semantics ()
132
139
..debugOverrideTimestampFunction (() => _testTime)
133
140
..semanticsEnabled = true ;
@@ -159,7 +166,6 @@ void testMain() {
159
166
expect (domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
160
167
expect (appHostNode.activeElement, strategy.domElement);
161
168
expect (textField.editableElement, strategy.domElement);
162
- expect ((textField.editableElement as dynamic ).value, 'hello' );
163
169
expect (textField.editableElement.getAttribute ('aria-label' ), 'greeting' );
164
170
expect (textField.editableElement.style.width, '10px' );
165
171
expect (textField.editableElement.style.height, '15px' );
@@ -174,7 +180,6 @@ void testMain() {
174
180
expect (domDocument.activeElement, domDocument.body);
175
181
expect (appHostNode.activeElement, null );
176
182
expect (strategy.domElement, null );
177
- expect ((textField.editableElement as dynamic ).value, 'bye' );
178
183
expect (textField.editableElement.getAttribute ('aria-label' ), 'farewell' );
179
184
expect (textField.editableElement.style.width, '12px' );
180
185
expect (textField.editableElement.style.height, '17px' );
@@ -188,6 +193,92 @@ void testMain() {
188
193
expect (actionCount, 0 );
189
194
});
190
195
196
+ test (
197
+ 'Does not overwrite text value and selection editing state on semantic updates' ,
198
+ () async {
199
+ semantics ()
200
+ ..debugOverrideTimestampFunction (() => _testTime)
201
+ ..semanticsEnabled = true ;
202
+
203
+ strategy.enable (
204
+ singlelineConfig,
205
+ onChange: (_, __) {},
206
+ onAction: (_) {},
207
+ );
208
+
209
+ final SemanticsObject textFieldSemantics = createTextFieldSemantics (
210
+ value: 'hello' ,
211
+ textSelectionBase: 1 ,
212
+ textSelectionExtent: 3 ,
213
+ isFocused: true ,
214
+ rect: const ui.Rect .fromLTWH (0 , 0 , 10 , 15 ));
215
+
216
+ final TextField textField =
217
+ textFieldSemantics.debugRoleManagerFor (Role .textField)! as TextField ;
218
+ final DomHTMLInputElement editableElement =
219
+ textField.editableElement as DomHTMLInputElement ;
220
+
221
+ expect (editableElement, strategy.domElement);
222
+ expect (editableElement.value, '' );
223
+ expect (editableElement.selectionStart, 0 );
224
+ expect (editableElement.selectionEnd, 0 );
225
+
226
+ strategy.disable ();
227
+ semantics ().semanticsEnabled = false ;
228
+ });
229
+
230
+ test (
231
+ 'Updates editing state when receiving framework messages from the text input channel' ,
232
+ () async {
233
+ semantics ()
234
+ ..debugOverrideTimestampFunction (() => _testTime)
235
+ ..semanticsEnabled = true ;
236
+
237
+ expect (domDocument.activeElement, domDocument.body);
238
+ expect (appHostNode.activeElement, null );
239
+
240
+ strategy.enable (
241
+ singlelineConfig,
242
+ onChange: (_, __) {},
243
+ onAction: (_) {},
244
+ );
245
+
246
+ final SemanticsObject textFieldSemantics = createTextFieldSemantics (
247
+ value: 'hello' ,
248
+ textSelectionBase: 1 ,
249
+ textSelectionExtent: 3 ,
250
+ isFocused: true ,
251
+ rect: const ui.Rect .fromLTWH (0 , 0 , 10 , 15 ));
252
+
253
+ final TextField textField =
254
+ textFieldSemantics.debugRoleManagerFor (Role .textField)! as TextField ;
255
+ final DomHTMLInputElement editableElement =
256
+ textField.editableElement as DomHTMLInputElement ;
257
+
258
+ // No updates expected on semantic updates
259
+ expect (editableElement, strategy.domElement);
260
+ expect (editableElement.value, '' );
261
+ expect (editableElement.selectionStart, 0 );
262
+ expect (editableElement.selectionEnd, 0 );
263
+
264
+ // Update from framework
265
+ const MethodCall setEditingState =
266
+ MethodCall ('TextInput.setEditingState' , < String , dynamic > {
267
+ 'text' : 'updated' ,
268
+ 'selectionBase' : 2 ,
269
+ 'selectionExtent' : 3 ,
270
+ });
271
+ sendFrameworkMessage (codec.encodeMethodCall (setEditingState));
272
+
273
+ // Editing state should now be updated
274
+ expect (editableElement.value, 'updated' );
275
+ expect (editableElement.selectionStart, 2 );
276
+ expect (editableElement.selectionEnd, 3 );
277
+
278
+ strategy.disable ();
279
+ semantics ().semanticsEnabled = false ;
280
+ });
281
+
191
282
test ('Gives up focus after DOM blur' , () async {
192
283
semantics ()
193
284
..debugOverrideTimestampFunction (() => _testTime)
@@ -446,6 +537,8 @@ SemanticsObject createTextFieldSemantics({
446
537
bool isFocused = false ,
447
538
bool isMultiline = false ,
448
539
ui.Rect rect = const ui.Rect .fromLTRB (0 , 0 , 100 , 50 ),
540
+ int textSelectionBase = 0 ,
541
+ int textSelectionExtent = 0 ,
449
542
}) {
450
543
final SemanticsTester tester = SemanticsTester (semantics ());
451
544
tester.updateNode (
@@ -458,6 +551,8 @@ SemanticsObject createTextFieldSemantics({
458
551
hasTap: true ,
459
552
rect: rect,
460
553
textDirection: ui.TextDirection .ltr,
554
+ textSelectionBase: textSelectionBase,
555
+ textSelectionExtent: textSelectionExtent
461
556
);
462
557
tester.apply ();
463
558
return tester.getSemanticsObject (0 );
0 commit comments