44
55@TestOn ('chrome || safari || firefox' )
66
7+ import 'dart:typed_data' ;
8+
79import 'package:test/bootstrap/browser.dart' ;
810import 'package:test/test.dart' ;
911
@@ -48,6 +50,11 @@ void testMain() {
4850 testTextEditing.configuration = singlelineConfig;
4951 });
5052
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+
5158 test ('renders a text field' , () async {
5259 semantics ()
5360 ..debugOverrideTimestampFunction (() => _testTime)
@@ -127,7 +134,7 @@ void testMain() {
127134 // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754
128135 skip: browserEngine != BrowserEngine .blink);
129136
130- test ('Syncs editing state from framework' , () async {
137+ test ('Syncs semantic state from framework' , () async {
131138 semantics ()
132139 ..debugOverrideTimestampFunction (() => _testTime)
133140 ..semanticsEnabled = true ;
@@ -159,7 +166,6 @@ void testMain() {
159166 expect (domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
160167 expect (appHostNode.activeElement, strategy.domElement);
161168 expect (textField.editableElement, strategy.domElement);
162- expect ((textField.editableElement as dynamic ).value, 'hello' );
163169 expect (textField.editableElement.getAttribute ('aria-label' ), 'greeting' );
164170 expect (textField.editableElement.style.width, '10px' );
165171 expect (textField.editableElement.style.height, '15px' );
@@ -174,7 +180,6 @@ void testMain() {
174180 expect (domDocument.activeElement, domDocument.body);
175181 expect (appHostNode.activeElement, null );
176182 expect (strategy.domElement, null );
177- expect ((textField.editableElement as dynamic ).value, 'bye' );
178183 expect (textField.editableElement.getAttribute ('aria-label' ), 'farewell' );
179184 expect (textField.editableElement.style.width, '12px' );
180185 expect (textField.editableElement.style.height, '17px' );
@@ -188,6 +193,92 @@ void testMain() {
188193 expect (actionCount, 0 );
189194 });
190195
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+
191282 test ('Gives up focus after DOM blur' , () async {
192283 semantics ()
193284 ..debugOverrideTimestampFunction (() => _testTime)
@@ -446,6 +537,8 @@ SemanticsObject createTextFieldSemantics({
446537 bool isFocused = false ,
447538 bool isMultiline = false ,
448539 ui.Rect rect = const ui.Rect .fromLTRB (0 , 0 , 100 , 50 ),
540+ int textSelectionBase = 0 ,
541+ int textSelectionExtent = 0 ,
449542}) {
450543 final SemanticsTester tester = SemanticsTester (semantics ());
451544 tester.updateNode (
@@ -458,6 +551,8 @@ SemanticsObject createTextFieldSemantics({
458551 hasTap: true ,
459552 rect: rect,
460553 textDirection: ui.TextDirection .ltr,
554+ textSelectionBase: textSelectionBase,
555+ textSelectionExtent: textSelectionExtent
461556 );
462557 tester.apply ();
463558 return tester.getSemanticsObject (0 );
0 commit comments