-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Add support for verifying SemanticsNode ordering in widget tests #107866
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
I think we want to avoid mandatory full semantics matching because taken to even a mild extreme, that becomes just a different way to encode a golden test. The advantage of the isBeforeX and isAfterX matchers is that they intentionally limited and very, uh, for like of better term semantic. Though I could see cases where you'd want to verify the complete order - for example, the old time picker where each number was on a clock face. We had to check that it went 1 -> 12 instead of jumping around the clock. |
It also depends on the use cases, what are some examples outside of the repo where users are checking ordering? |
@jonahwilliams Agreed about the full semantics concern – I'm leaning towards an option that just verifies a11y labels or something similarly minimal. Re: use cases, I think any page with multiple elements would benefit from an element ordering test. Say I'm building a contact info form. I could write: expect(find.text('Name'), isOrderedBefore(find.text('Address 1')));
expect(find.text('Address 1'), isOrderedBefore(find.text('Address 2')));
expect(find.text('Address 2'), isOrderedBefore(find.text('City')));
expect(find.text('City'), isOrderedBefore(find.text('State')));
expect(find.text('State'), isOrderedBefore(find.text('ZIP'))); or, I could write expect(
find.byKey(ValueKey('myForm')),
hasSemanticsElements([
find.text('Name'),
find.text('Address 1'),
find.text('Address 2'),
find.text('City'),
find.text('State'),
find.text('ZIP'),
]),
); The second of those is a lot clearer and was much more enjoyable to write. I think this applies anywhere element ordering matters, which is everywhere IMO. |
One problem folks frequently encounter, is that testing the order here sort of requires knowledge about how data is combined into semantic labels. For example, suppose they had two different widgets for the name field like And they wrote:
In this case both the |
I guess this technically applies to isBefore and isAfter too, but in that case it seems more obvious that you would explicitly fail since they are the same node |
one thing that may make this hard is that there may be internal semantics node wrapping each other, for example, the text widget may be wrapped with other semantics node before it is attached to the semantics node of the list view. We would have to figure out how to handle this. |
API wise, I would lean towards the list parameter myself. Two reasons for it from my perspective:
For developer experience sake, I'd take two passes. One to confirm that all of the expected elements are there at all, then another pass to validate the order. That way it's simple to distinguish between a missing element, or unordered elements. |
@chunhtai could you expand on the wrapping case you're talking about? I think I know what you're saying, but I want to be sure. |
I think I would fail in that case since
We should pick a name that implies that this matcher is about ordering. |
I agree that this would be a failure case. We're checking semantic elements, not necessarily individual widgets. So if they're combined semantically, but the user is trying to check via the widgets that were created, then that's an error in the writing of the test.
I agree with this, explicit is always better. I was thinking something along the lines of |
One more note: With semantics nodes, "order" is a little ambiguous because there is hit test order and traversal order. I don't think we have to clear this up in the matcher's name, but the accompanying doc should be very specific about what order we're talking about. (And I think this issue is only talking about traversal order, @bryanoltman?) |
Yep! |
So, I'm going to try to give a summary of the known requirements as I see them at the moment, just to make sure everyone's on the same page (or at least in the same book). I'll summarize open questions in a followup comment.
|
Here are the open questions that I still see:
|
Regarding 1): I would throw Regarding 2): Since I also can't think of a great use case for unordered assertions, I would not provide such a matcher for now - until we have a valid use case. |
Oooh. I like |
I like
expect(
find.byKey(ValueKey('myForm')),
hasOrderedSemantics([
find.text('First Name'),
find.text('Address 2'),
find.text('ZIP'),
]),
); Would that pass? I don't know that I feel strongly either way, but I suppose I'd lean towards this failing as I'm not super convinced there's a use case for a partial list.
|
I dont think figure out the complete traversal list is possible. There is the internal semantics node that convey information but a11y unfocusable for example,
We also cannot tell whether a semantics node is a11y focusable from the framework side. The other issue we may run into is the additional semantics node in a listview due to cache extent. |
I was thinking that this would pass. Specifically for making it easy to split up tests. For example you could want a test that all of the labels for your form were in order, then you may want a separate test for validating that after you type something in for "First Name", it reads directly after the "First Name" label.
My assumption here was that the first argument was the top level that you'd be checking against, so you would expect all semantics elements being checked against to be children of the first argument of the That said, I was also confused when I originally looked at the proposed syntax. I don't have any better ideas in mind though. It may be worth revisiting the syntax such that the finder should gather all of the relevant elements, and the matcher only validates the order of the gathered elements. That could be useful as a more generic tool as well, instead of just being a tool for semantics. |
@chunhtai @goderbauer @jonahwilliams @bryanoltman I've got some updates for this ticket! Apologies, I need to get better at providing in progress updates. As @chunhtai mentioned, the platforms have slightly different implementations saying what is important for semantics and when. Because of this, I went with a I ended up going with a more general I think the end result ended up pretty readable and clean, see this example:
|
In the example given above I was slightly surprised to see Does this setup mean, I always have to assert order globally on the entire app under test? Or can I somehow express that all I care about is that button X is traversed directly before button Y? Without asserting that button X must come before text Foo? |
Well, the usual order of the Because of those limitations, I went with the new That said, the The |
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 |
Use case
It is important for app accessibility that semantic elements are ordered in a logical way1. At present, there does not seem to be a way to verify SemanticsNode ordering.
matchesSemantics
has achildren
argument which seems like it might solve this problem, but I can find no documentation on this parameter and have not been able to use it in any meaningful way on the button counter app generated byflutter create
.Proposal
There are multiple ways this might be accomplished. One (originally proposed by @goderbauer) is to add an
isOrderedBefore
matcher that could be used like:Another option would allow the developer to specify multiple matchers. If we wanted to verify the order of the semantic elements in the default
flutter create
app, it might look something like:There are some open questions here, including:
isOrderedBefore
/hasSemanticsElements
expect? I'm intentionally being somewhat handwavy about theSemanticsElement
type. I'm not sure if it makes sense to have a full semantics matcher here (see the next point).matchesSemantics
to only match certain properties #107859 might make this less of an issue.Footnotes
https://accessibility.huit.harvard.edu/encode-elements-logical-order ↩
The text was updated successfully, but these errors were encountered: