-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Add SemanticsFinder
API for searching the Semantics tree
#123634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
cc: @chunhtai @Hangyujin @dnfield @goderbauer I've talked about implementing a separate set of finders specifically for Semantics before, but have been trying to avoid it and work within the existing While working on #117410, @goderbauer and I reached a tipping point where working within the existing Any feedback you have on the above plan would be welcome! |
SemanticsFinder
API for interacting with the Semantics treeSemanticsFinder
API for searching the Semantics tree
I think the SemanticsFinder that target semantics tree can be useful for some use case. However, how would you use the API to test the use case like - On the other hand, is it possible to do the following logic? we look down the renderobject until we hit the leaf or multichildrenderingobject, if we find one with semantics node, we return that node. If not we start look up parent until we find one. This is kinda assuming how renderobject form semantics node explicitly or merge to parent |
I don't know that there really is a valid "I want to make sure Widget X has SemanticsNode Y" use case (with a specific exception below). The widget/element tree creates the render tree which composes the semantics tree, but there's not really a consistent way to find which SemanticsNode is related to which Widget. The current implementation of The
I thought of two other possibilities, one of which was your suggestion of "if we don't find anything going down, go back up". This would solve the specific case in the writeup, but still has the unbounded descendant search issue and introduces a new issue where it could return a parent's The last possibility I thought of was "closest semantics in either direction" where you'd search both descendants and ancestors and return the Long story short, I think anything we do to try to relate a The exception I mentioned above is the case where a widget directly makes its own
This is close to the current |
@pdblasi-google Thanks for the reply.
I think the idea sounds good to me. It has some overlaps with SemanticsTester/SemanticsTester.nodesWith though, but it is not too big of an issue since it is package private. We can consider merge them or deprecate SemanticsTester.
If we want, we can expose SemanticsFragment for test only. With this, we can tell
Just throwing out ideas, and I agree this is probably not something we should focus on this issue. We can continue discussion somewhere else. |
Personally, I'd like to deprecate and remove Thanks for the ideas, I wasn't aware of the We may be able to replace |
This finder seems odd? SemanticNodes don't have keys. Would these somehow use widget keys?
I would argue that is the wrong way to look at it. Your semantics tests should make sure that the correct information are exposed to the semantics tree (e.g. that there is a SemanticsNode marked button with label X that when tapped does something) - not necessarily what widget contributed those semantics.
I am hoping we can avoid that. Fragments are very complex, and it isn't a very user friendly API. The hope is that we can make testing for semantics easier with this.
Luckily, that's not a public API and just something used internally in framework tests. But I agree, it would be good if we wouldn't need it anymore and whatever we come up with here can replace it. |
The Probably not as useful as I thought it'd be when looking over the options with that additional context... Removed! |
* Pulled `FinderBase` out of `Finder` * `FinderBase` can be used for any object, not just elements * Terminology was updated to be more "find" related * Re-implemented `Finder` using `FinderBase<Element>` * Backwards compatibility maintained with `_LegacyFinderMixin` * Introduced base classes for SemanticsNode finders * Introduced basic SemanticsNode finders through `find.semantics` * Updated some relevant matchers to make use of the more generic `FinderBase` Closes #123634 Closes #115874
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
Uh oh!
There was an error while loading. Please reload this page.
Background
The
Finder
API is based off of navigating theElement
tree. For interacting with theSemantics
tree, we've been usingtester.semantics.find
(previouslytester.getSemantics
) which is based on theFinder
API and is implemented as follows:Finder
to get a singularElement
Element
, usefindRenderObject
to get the closest render objectfindRenderObject
method will return the element if it happens to also be a render object, or the nearest descendant of the element that is aRenderObject
RenderObject
, find the relatedSemanticsNode
to thatRenderObject
RenderObject
does not have a node, or that node is part of a merged node, we navigate up theRenderObject
tree until we find a standaloneSemanticsNode
The important parts here to note are:
RenderObject
tree to find aRenderObject
from anElement
RenderObject
is not guaranteed to have aSemanticsNode
RenderObject
tree to find aSemanticsNode
The problem
TLDR;
Translating between the
Element
,RenderObject
, andSemantics
trees isn't consistent enough to be able to use the existingFinder
API (which is based on the Element tree) to find specific SemanticsNodes in the Semantics tree.Deep dive
The current method of finding
SemanticsNode
s related to theElement
tree is a heuristic that doesn't consistently return theSemanticsNode
that one would expect. For example, take a look at the following test:This test is very straightforward. When we try to find the
SemanticsNode
for aTextField
theisTextField
flag should be true, but this test ends up failing with the current implementation offind
.What went wrong
Looking at the implementation of
find
we can see a potential issue there that might resolve the issue. ThefindRenderObject
method just looks for the closest descendentRenderObject
. It doesn't really care whether or not thatRenderObject
has any semantics attached to it.Turns out this is exactly the issue for the
TextField
case. TheRenderObject
returned is aRenderMouseRegion
with no semantics attached to it. Looking at theRenderObject
tree shows us that a couple steps further down the tree we can find a semantically relevant node:RenderSemanticsAnnotations#efb04
Which relates to the
SemanticsNode
we would expect:SemanticsNode#4
So, this gives us a potential solution! Lets adapt
find
to not just look for the closest descendantRenderObject
but the closest descendant that also has semantics. Here's our new test with afixedFind
method:Implementation of fixedFind
And tada! The test now passes and
find
returns theSemanticsNode
that we would expect from aTextField
!Success?
We solved the issue where we got the ancestor of the expected
SemanticsNode
! Perfect! Now let's make sure it works everywhere. Let's start with this test:Nothing particularly out of the ordinary here, we've got a list of items, we're finding one of those items by text and making sure it has the correct semantics label. Unfortunately, this test now fails with a
StateError
saying: "No semantics data found.". Looking at the implementation offixedFind
, we can see that this happens when we've walked down theRenderObject
tree and found noRenderObject
that has a relatedSemanticsNode
.If you dig really deep, the root cause for this specific case is that being in an indexed scrollable moves the semantics up the render tree from the
Element
that is found by thefind.text
finder. If the text is not in an indexed scrollable, the semantics are down the tree where the implementation offixedFind
would expect it.That just adds to the inconsistency of the
Element
->RenderObject
->Semantics
translation though. In the end, there isn't a heuristic for this translation that we could use that we could guarantee we're getting the "right"SemanticsNode
for a givenElement
.The solution
Create an API that is parallel to the
Element
basedFinder
API, but specifically for searching the Semantics tree.SemanticsFinder
will be based around theFinder
APIFinderBase<TEvaluation>
interface, though this isn't strictly necessaryflutter_test
will be exposed through aCommonSemanticsFinders
class, and be exposed through afind.semantics
property on the existing globalfind
propertysemantics
property toWidgetController
in 107866: Add support for verifying SemanticsNode ordering in widget tests #113133 while not requiring a secondary import, and not polluting the global namespace with a newsemantics
property.ancestor
SemanticsFinder
s and evaluates to all ancestors ofof
that meet the expectations ofmatching
, optionally inclusive withmatchesRoot
descendant
SemanticsFinder
s and evaluates to all descendants ofof
that meet the expectations ofmatching
, optionally inclusive withmatchesRoot
byLabel
SemanticsNodes
in the tree that has a label matching the givenPattern
.byValue
SemanticsNodes
in the tree that has a value matching the givenPattern
.byHint
SemanticsNodes
in the tree that has a hint matching the givenPattern
.byAction
SemanticsNodes
in the tree that has the givenSemanticsAction
.byAnyAction
SemanticsNodes
in the tree that has at least one of the givenSemanticsAction
s.byFlag
SemanticsNodes
in the tree that has the givenSemanticsFlag
.byAnyFlag
SemanticsNodes
in the tree that has at least one of the givenSemanticsFlag
s.The text was updated successfully, but these errors were encountered: