fix(drivers): scrollUntilVisible accepts malformed (negative-area) rects that tapOn rejects#103
Conversation
…reen scrollUntilVisible's success predicate (isElementOnScreen) only rejected zero-area bounds (Width == 0 || Height == 0), not negative ones. A clipped below-the-fold ScrollView child is reported by findElement with top > bottom — a negative-height rect (raw [270,2300][1080,2274] => h=-26, centre (675,2287)). isElementOnScreen accepted it as "on screen", so scrollUntilVisible short-circuited to success without ever scrolling the element into view. tapOn then correctly rejected the same rect via boundsTappable's devicelab-dev#94 guard (Width <= 0 || Height <= 0) and re-polled to the deadline: ✓ scrollUntilVisible id=…/extintoresInstalacionParte (729ms) ✗ tapOn id=…/extintoresInstalacionParte (12.0s) element rect not tappable (w=810 h=-26 center=(675,2287) screen=1080x2340) Align isElementOnScreen with boundsTappable: treat non-positive width/height as off-screen so the scroll loop keeps going until the element has a real, on-screen rect. This is independent of the usable-vs-physical height fix in PR devicelab-dev#101 — the inverted rect comes from clipping, not from the screen-size value, so it reproduces even against the physical height. Fixed in both the devicelab and uiautomator2 drivers (shared, duplicated predicate; only the devicelab path was observed failing in the field, but the predicate is identical and must reject the same degenerate rects). Regression cases added to both TestIsElementOnScreen tables, including the field repro (w=810 h=-26). Verified end-to-end on a local API 33 arm64 emulator: a previously-failing extinguisher-list flow now passes 21/21 (tapOn 1.3s vs the prior 12s deadline).
|
Thanks @MarioRial22 — clean follow-up, and this closes exactly the predicate gap I had in mind. I traced the repro and it checks out: isElementOnScreen accepted the clipped h=-26 rect (not exactly zero, and it overlaps the viewport numerically), so The fix is the right one: making isElementOnScreen reject non-positive width/height so the scroll-stop predicate agrees with boundsTappable. A negative-area rect is never actually visible, so there's nothing legitimate this newly excludes — worst case A few things I appreciated:
Looks good to me — merging. Thanks again for the thorough write-up and the e2e numbers. |
Follow-up to the discussion in #101.
Problem
scrollUntilVisibledeclares success and stops scrolling, then atapOnon the same element fails with a deterministic signature:Note
h=-26: the element's bounds are a malformed rect with top > bottom (negative height). Reproduces on thedevicelabdriver against a button at the bottom of aScrollViewthat is still below the fold when the scroll loop stops.uiautomator2taps the same button fine.Root cause
The scroll-stop predicate and the tap predicate disagree on what counts as a valid on-screen rect:
boundsTappable(used bytapOn) rejects non-positive dimensions — the [BUG] Android DeviceLab: FindAndClick taps off-screen centre from a malformed negative-height rect (bottom-sheet first frame) #94 malformed-rect guard:isElementOnScreen(used byscrollUntilVisible) only rejected exactly zero:A clipped below-the-fold
ScrollViewchild is reported with top > bottom (raw[270,2300][1080,2274]→h=-26, centre(675,2287)).isElementOnScreenaccepts it — it isn't exactly zero, and it numerically "overlaps" the viewport — soscrollUntilVisibleshort-circuits to success without ever scrolling the button into view.tapOnthen correctly refuses the same degenerate rect via the #94 guard and re-polls to the deadline.This is independent of the usable-vs-physical height fix in #101: the inverted rect comes from clipping, not from the screen-size value, so it reproduces even when
isElementOnScreenis given the physical height (as it now is post-#101 viatappableScreenSize()).Fix
Make
isElementOnScreenreject non-positive width/height, matchingboundsTappable's #94 guard. The scroll loop then keeps going until the element has a real, on-screen rect, and the followingtapOnsucceeds. This stays a defensive Go-side guard in the same spirit as #101 — no magic tolerance — so the #94 malformed-rect rejection is now consistent on both the tap side and the scroll side.Applied to both the
devicelabanduiautomator2drivers: the predicate is duplicated and identical in each. Only thedevicelabpath was observed failing in the field; theuiautomator2edit has no independent repro but keeps the two predicates consistent and is unambiguously correct (a negative-area rect is never visible).Tests
TestIsElementOnScreentables (pkg/driver/devicelab,pkg/driver/uiautomator2):negative widthandnegative height (clipped …), the latter the exact field repro{X:270, Y:2300, Width:810, Height:-26}. Each must returnfalse; against the old== 0predicate they returntrue, so they fail without the fix.go build ./...,go vet ./pkg/driver/devicelab/ ./pkg/driver/uiautomator2/, andgo test ./pkg/driver/devicelab/ ./pkg/driver/uiautomator2/all pass.ScrollViewbutton and taps it now passes 21/21 (tapOn1.3s vs the prior 12s deadline).