-
Notifications
You must be signed in to change notification settings - Fork 154
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
fix: Bug on waitUntilVisible() and visible #2464
Conversation
This commit adds an Alignment parameter to the visibility related methods. The default setting by Flutter of Alignment.center fails, if hitting a SizedBox. This can be fixed by specifying an Alignment.
To view this pull requests documentation preview, visit the following URL: docs.page/leancodepl/patrol~2464 Documentation is deployed and generated using docs.page. |
this addresses the same issue leancodepl#2464 as the previous commit but with a different solution
the second commit offers an alternative solution to the one of the first commit. Here the API remains untouched but we check all possible Alignments for a possible hit test: /// Waits until [finder] finds at least one visible widget.
///
/// Throws a [WaitUntilVisibleTimeoutException] if more time than specified by
/// the timeout passed and no widgets were found.
Future<PatrolFinder> waitUntilVisible(
Finder finder, {
Duration? timeout,
bool enablePatrolLog = true,
}) {
return TestAsyncUtils.guard(
() => wrapWithPatrolLog(
action: 'waitUntilVisible',
finder: finder,
color: AnsiCodes.cyan,
enablePatrolLog: enablePatrolLog,
function: () async {
final duration = timeout ?? config.visibleTimeout;
final end = tester.binding.clock.now().add(duration);
final hitTestableFinders = alignments.map((alignment) => finder.hitTestable(at: alignment));
final hitTestableEvaluations = hitTestableFinders.map((finder) => finder.evaluate());
while (hitTestableEvaluations.map((result) => result.isNotEmpty).firstOrNull == null) {
final now = tester.binding.clock.now();
if (now.isAfter(end)) {
throw WaitUntilVisibleTimeoutException(
finder: finder,
duration: duration,
);
}
await tester.pump(const Duration(milliseconds: 100));
}
return PatrolFinder(
finder: hitTestableFinders.firstWhere((finder) => finder.evaluate().isNotEmpty),
tester: this,
);
},
),
);
} Looking forward to your feedback. |
Both solutions can be tested on this example: Patrol Example |
Hi @FritzMatthaeus! Welcome to the contributor lounge. 🎉 The idea of being "visible" is very hard to set in stone. We thought hit-testability is the closest we can get to visibility, since most of the time users want to interact with something if they wait for it to be visible. Thus, we check for In your case, the Your first commit provides a nice fix of just passing by the alignment parameter to the user, so that they're in full control of where the hit test happens. Your second commit tries to hit-test in 9 points which can lead to more unexpected issues for other users. Still, it won't cover all the cases because if the child is moved by a pixel (e.g. with a padding), then the hit-test fails. That said, we think your first commit is the best fix for this issue right now. I have generated a golden test image from your |
Hi @mateuszwojtczak, i fully agree with your decision. This PR has been the result of my learning curve on "visible" and "hitTestable". My final learning is, to be aware of Columns and Rows when testing "visibility". Maybe this should be added to the docs somewhere. I'll think about it and maybe i come up with a PR on the docs. Have good year 2025 and thank you for your work on this package! |
Hi @FritzMatthaeus, maybe it wasn't obvious from @mateuszwojtczak answer, but we would like to merge your first commit, as it extends user possibilities, so it would be nice to have it in Patrol. Thanks again for your contribution. |
update dart doc
avoid breaking change on visible add tests update dart doc
remove alignments
Hi @pdenert, sorry, i do not have much experience and routine on Pull Requests. As you requested, i have reverted to the Alignment parameter and added some dart doc and a test. I took me some merges, etc, etc. ;) Note that i thought it might be no good idea to create a breaking change by turning I hope that this time the PR is up to your needs. Best regards, |
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.
Very good job! Just few organizing things:
- Please format changed files. You can use
dart format ./packages/patrol_finders
- We try to stick with the standard flutter max 80 chars per line. Please change your comments to fit this requirement.
- Add new unreleased entry to patrol_finders changelog. See example here: https://github.com/leancodepl/patrol/pull/2425/files#diff-e17f3235f9e47a742b45c5a87b2ce49cb726b37d26fb9d478ce3880c6e61ba98
add changelog entry format files and comments
I did as requested. Thanks for guiding me through this process. Crash course in good package maintainance for me ;) |
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.
One last thing. There's one warning from analyzer:
warning - lib/src/custom_finders/patrol_tester.dart:5:8 - Unused import: 'package:patrol_finders/src/custom_finders/utils.dart'. Try removing the import directive. - unused_import
Please remove this import and then we can merge it
fixed :) |
After working a while with Patrol i am not quite sure anymore if merging this PR is recommended. It does enhance the methods this example fails with a Future<void> tap(
Finder finder, {
SettlePolicy? settlePolicy,
Duration? visibleTimeout,
Duration? settleTimeout,
bool enablePatrolLog = true,
}) {
return TestAsyncUtils.guard(
() => wrapWithPatrolLog(
action: 'tap',
finder: finder,
color: AnsiCodes.yellow,
enablePatrolLog: enablePatrolLog,
function: () async {
final resolvedFinder = await waitUntilVisible(
finder,
timeout: visibleTimeout,
enablePatrolLog: false,
);
await tester.tap(resolvedFinder.first);
await _performPump(
settlePolicy: settlePolicy,
settleTimeout: settleTimeout,
);
},
),
);
} this example fails also, though it is no longer a $(Widget).tap(at: Alignment.topCenter);
Future<void> tap(
Finder finder, {
SettlePolicy? settlePolicy,
Duration? visibleTimeout,
Duration? settleTimeout,
bool enablePatrolLog = true,
Alignment alignment = Alignment.center
}) {
return TestAsyncUtils.guard(
() => wrapWithPatrolLog(
action: 'tap',
finder: finder,
color: AnsiCodes.yellow,
enablePatrolLog: enablePatrolLog,
function: () async {
final resolvedFinder = await waitUntilVisible(
finder,
timeout: visibleTimeout,
enablePatrolLog: false,
alignment: alignment
);
await tester.tap(resolvedFinder.first); // <- the error occurs here
await _performPump(
settlePolicy: settlePolicy,
settleTimeout: settleTimeout,
);
},
),
);
} So passing an Alignment property to this method is not possible. This might end up in an inconsistency for the user as he might successfully test the visibility and hittest the Widget by calling await $(Widget).waitUntilVisible(at: Alignment.topCenter); but when he tries to tap it, the test will fail with a await $(Widget).tap(); I did not test I have come to the conclusion that it might be a better solution to stick with the current implementation of the visibility checks without Alignment parameters but to improve the documentation to clarify the visibility concept when it comes to Widgets including Rows and Columns. Or just take in the As the PR is now complete, i will leave this decision up to you. ;) Best regards, |
Good notice. Let us think about it. In meanwhile you can once again format files using: |
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.
LGTM.
I've added issue to enhance the tap method: #2484
One last request @FritzMatthaeus - please resolve changelog conflict and then I merge this |
oh my god, i feel like a total newbie (which i am)! hopefully my next PR is more smoothly ;) |
This fix is related to the issue #2463
The methods
waitUntilVisible()
and the gettervisible
default toAlignment.center
when callinghitTestable()
on the Flutter test framework. In case the tested Widget has an empty SizedBox in it's middle, both methods will fail. This can be fixed by allowing to pass an Alignment.Example:
Calling
$(Keys.elementB).waitUntilVisible()
on this widget will fail as the SizedBox will cover the middle of the Widget.Calling
$(Keys.elementB).waitUntilVisible(alignment: Alignment.topCenter)
on this widget will pass.The
visible
getter has to be refactored as a function to be able to pass an Alignment parameter:visible({Alignment alignment = Alignment.center})
An alternative Approach would be to evaluate the finder for all possible Alignments when calling
waitUntilVisible()
andvisible
. A separate Pull Request will be created for that solution. You are in a better position to decide which way would suit best for the package.