This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
[web] Don't overwrite editing state with semantic updates #38271
Merged
auto-submit
merged 8 commits into
flutter:main
from
htoor3:remove-edit-state-overwrite-semantic
Dec 19, 2022
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
717fcc0
Remove editing state overwrite on semantic updates
htoor3 19d72e6
Add tests
htoor3 acabdc0
Fix test name
htoor3 85bb97e
Assert that framework messages will update semantic editing state
htoor3 3bf4a84
Merge remote-tracking branch 'upstream/main' into remove-edit-state-o…
htoor3 2e79071
Change test name
htoor3 df642d4
Merge branch 'main' into remove-edit-state-overwrite-semantic
htoor3 0040d98
Whitespace
htoor3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
|
||
@TestOn('chrome || safari || firefox') | ||
|
||
import 'dart:typed_data'; | ||
|
||
import 'package:test/bootstrap/browser.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
|
@@ -48,6 +50,11 @@ void testMain() { | |
testTextEditing.configuration = singlelineConfig; | ||
}); | ||
|
||
/// Emulates sending of a message by the framework to the engine. | ||
void sendFrameworkMessage(ByteData? message) { | ||
testTextEditing.channel.handleTextInput(message, (ByteData? data) {}); | ||
} | ||
|
||
test('renders a text field', () async { | ||
semantics() | ||
..debugOverrideTimestampFunction(() => _testTime) | ||
|
@@ -127,7 +134,7 @@ void testMain() { | |
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 | ||
skip: browserEngine != BrowserEngine.blink); | ||
|
||
test('Syncs editing state from framework', () async { | ||
test('Syncs semantic state from framework', () async { | ||
semantics() | ||
..debugOverrideTimestampFunction(() => _testTime) | ||
..semanticsEnabled = true; | ||
|
@@ -159,7 +166,6 @@ void testMain() { | |
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement); | ||
expect(appHostNode.activeElement, strategy.domElement); | ||
expect(textField.editableElement, strategy.domElement); | ||
expect((textField.editableElement as dynamic).value, 'hello'); | ||
expect(textField.editableElement.getAttribute('aria-label'), 'greeting'); | ||
expect(textField.editableElement.style.width, '10px'); | ||
expect(textField.editableElement.style.height, '15px'); | ||
|
@@ -174,7 +180,6 @@ void testMain() { | |
expect(domDocument.activeElement, domDocument.body); | ||
expect(appHostNode.activeElement, null); | ||
expect(strategy.domElement, null); | ||
expect((textField.editableElement as dynamic).value, 'bye'); | ||
expect(textField.editableElement.getAttribute('aria-label'), 'farewell'); | ||
expect(textField.editableElement.style.width, '12px'); | ||
expect(textField.editableElement.style.height, '17px'); | ||
|
@@ -188,6 +193,92 @@ void testMain() { | |
expect(actionCount, 0); | ||
}); | ||
|
||
test( | ||
'Does not overwrite text value and selection editing state on semantic updates', | ||
() async { | ||
semantics() | ||
..debugOverrideTimestampFunction(() => _testTime) | ||
..semanticsEnabled = true; | ||
|
||
strategy.enable( | ||
singlelineConfig, | ||
onChange: (_, __) {}, | ||
onAction: (_) {}, | ||
); | ||
|
||
final SemanticsObject textFieldSemantics = createTextFieldSemantics( | ||
value: 'hello', | ||
textSelectionBase: 1, | ||
textSelectionExtent: 3, | ||
isFocused: true, | ||
rect: const ui.Rect.fromLTWH(0, 0, 10, 15)); | ||
|
||
final TextField textField = | ||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField; | ||
final DomHTMLInputElement editableElement = | ||
textField.editableElement as DomHTMLInputElement; | ||
|
||
expect(editableElement, strategy.domElement); | ||
expect(editableElement.value, ''); | ||
expect(editableElement.selectionStart, 0); | ||
expect(editableElement.selectionEnd, 0); | ||
|
||
strategy.disable(); | ||
semantics().semanticsEnabled = false; | ||
}); | ||
|
||
test( | ||
'Updates editing state when receiving framework messages from the text input channel', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perfect! Thanks. |
||
() async { | ||
semantics() | ||
..debugOverrideTimestampFunction(() => _testTime) | ||
..semanticsEnabled = true; | ||
|
||
expect(domDocument.activeElement, domDocument.body); | ||
expect(appHostNode.activeElement, null); | ||
|
||
strategy.enable( | ||
singlelineConfig, | ||
onChange: (_, __) {}, | ||
onAction: (_) {}, | ||
); | ||
|
||
final SemanticsObject textFieldSemantics = createTextFieldSemantics( | ||
value: 'hello', | ||
textSelectionBase: 1, | ||
textSelectionExtent: 3, | ||
isFocused: true, | ||
rect: const ui.Rect.fromLTWH(0, 0, 10, 15)); | ||
|
||
final TextField textField = | ||
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField; | ||
final DomHTMLInputElement editableElement = | ||
textField.editableElement as DomHTMLInputElement; | ||
|
||
// No updates expected on semantic updates | ||
expect(editableElement, strategy.domElement); | ||
expect(editableElement.value, ''); | ||
expect(editableElement.selectionStart, 0); | ||
expect(editableElement.selectionEnd, 0); | ||
|
||
// Update from framework | ||
const MethodCall setEditingState = | ||
MethodCall('TextInput.setEditingState', <String, dynamic>{ | ||
'text': 'updated', | ||
'selectionBase': 2, | ||
'selectionExtent': 3, | ||
}); | ||
sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); | ||
|
||
// Editing state should now be updated | ||
expect(editableElement.value, 'updated'); | ||
expect(editableElement.selectionStart, 2); | ||
expect(editableElement.selectionEnd, 3); | ||
|
||
strategy.disable(); | ||
semantics().semanticsEnabled = false; | ||
}); | ||
|
||
test('Gives up focus after DOM blur', () async { | ||
semantics() | ||
..debugOverrideTimestampFunction(() => _testTime) | ||
|
@@ -446,6 +537,8 @@ SemanticsObject createTextFieldSemantics({ | |
bool isFocused = false, | ||
bool isMultiline = false, | ||
ui.Rect rect = const ui.Rect.fromLTRB(0, 0, 100, 50), | ||
int textSelectionBase = 0, | ||
int textSelectionExtent = 0, | ||
}) { | ||
final SemanticsTester tester = SemanticsTester(semantics()); | ||
tester.updateNode( | ||
|
@@ -458,6 +551,8 @@ SemanticsObject createTextFieldSemantics({ | |
hasTap: true, | ||
rect: rect, | ||
textDirection: ui.TextDirection.ltr, | ||
textSelectionBase: textSelectionBase, | ||
textSelectionExtent: textSelectionExtent | ||
); | ||
tester.apply(); | ||
return tester.getSemanticsObject(0); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This expectation used to verify that the semantics text value gets updated when the framework begins editing a text field. I think to cover that with the new approach we need a new test (or maybe just add it to this test) that sends the text editing state through the text input channel and checks that the editing state is applied to
textField.editableElement
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good point. I wrote it this way because we're delegating the responsibility of editing state updates to
text_editing.dart
, so it seems like the best place to test framework -> engine editing state updates would be intext_editing_test.dart
which has existing tests that verify that behavior.But is the idea here that we want to explicitly test whether that behavior applies specifically to semantic nodes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to test the behavior of
text_editing.dart
when using a semantics editing strategy to make sure that the element's value is still being updated correctly.