Skip to content

fix(devicelab): re-sample the keyboard-blocking check until IME-resize geometry settles#127

Open
MarioRial22 wants to merge 2 commits into
devicelab-dev:mainfrom
seguridadea1:fix/keyboard-blocking-repoll
Open

fix(devicelab): re-sample the keyboard-blocking check until IME-resize geometry settles#127
MarioRial22 wants to merge 2 commits into
devicelab-dev:mainfrom
seguridadea1:fix/keyboard-blocking-repoll

Conversation

@MarioRial22

Copy link
Copy Markdown
Contributor

Summary

Since v1.1.20, the new keyboard-blocking guard in the tap paths (checkKeyboardBlocking) samples element and keyboard geometry exactly once, immediately after an input step, and fails with:

Element found but keyboard is covering it (keyboard top: 1560, element center Y: 1627) — add a `- hideKeyboard` step before this step

Windows with SOFT_INPUT_ADJUST_RESIZE (any stock AlertDialog with a scrollable body) relayout a few frames after the IME appears or after typing: on that first frame the target still reports covered bounds, then the window shrinks and the target rises above the keyboard (center Y 1627 → ~1400 in our trace). The single-shot check reads the stale first frame and rejects an element that is tappable an instant later. Our flows submitting a search dialog via tapOn: android:id/button1 right after inputText pass on v1.1.19 and fail 3/6 CI shards on v1.1.20; hideKeyboard is not a usable workaround on Android (falls back to BACK, which apps may intercept).

Same family as #100/#101/#103: a geometric guard rejecting a legitimately tappable element.

This PR keeps the guard's purpose — fail with the actionable hint when a tap would genuinely land on the IME — but re-samples element vs keyboard geometry every 50ms for up to 2s, returning the error only when the overlap persists through the whole window. It bails out early (proceeds with the tap) as soon as the keyboard is dismissed or the element clears it. True positives still fail fast (2s, well under the tap deadline) with the exact same message.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • pkg/driver/devicelab/keyboard.go: checkKeyboardBlocking re-samples until keyboardSettleWindow (2s, package var so tests can shrink it) at a 50ms cadence; per-sample verdict extracted to keyboardStillCovering.
  • pkg/driver/devicelab/keyboard_test.go (new): table-driven tests for the per-sample verdict with the real-world repro geometry (covered at first frame, clear after relayout, keyboard dismissed mid-settle, both edges of the 50px suggestion-strip margin).

Related Issues

Related: #100, #101, #103 (same subsystem; no open issue filed for this one — happy to open one if you prefer).

Testing

  • Tests pass locally (go test ./pkg/driver/devicelab/ -count=1 green — the touched package. Full go test ./... in my environment shows 3 failing packages — pkg/device (TestFindFreePort_InvalidRange), pkg/report and pkg/driver/browser/cdp — all of which fail identically on unmodified main here: environmental (sandboxed network / no real browser), unrelated to this change)
  • Linting passes (golangci-lint run ./pkg/driver/devicelab/... — 0 issues in the touched files; the pre-existing errcheck findings in webview.go are identical on main)
  • Added tests for new functionality
  • Tested manually with sample flows (real-world repro: search-dialog flows tapping android:id/button1 with the IME open — fail on v1.1.20, pass with this settle behavior, matching v1.1.19)

Checklist

  • Code follows project style guidelines (gofmt/go vet clean)
  • Self-reviewed the code
  • Added/updated documentation as needed (godoc on the settle behavior and the two tunables)
  • No breaking changes (or documented if breaking) — true positives keep the exact same error message and failure semantics
  • CHANGELOG.md updated — it looks maintainer-curated per release (docs(changelog): add 1.1.x release notes); happy to add a line if you'd like it in the PR

Additional Notes

No latency is added to the happy path: the first sample that finds the element clear (or the keyboard gone) returns immediately, so only genuinely-covered taps wait out the 2s window before failing.

…e geometry settles

The keyboard-blocking guard added to the tap paths reads element and keyboard
bounds exactly once, right after an input step. Windows with
SOFT_INPUT_ADJUST_RESIZE (e.g. a plain AlertDialog whose body is a ScrollView)
relayout a few frames after the IME appears or after typing: on that first
frame the target still reports covered bounds, then the window shrinks and the
target rises above the keyboard. The single-shot check reads the stale first
frame and rejects a perfectly tappable element with "Element found but
keyboard is covering it", suggesting a hideKeyboard step that is not needed
(and on Android falls back to BACK, which apps may intercept).

Real-world hit: tapOn android:id/button1 (dialog positive button) right after
inputText — reported covered at centerY=1627 vs keyboard top 1560 on the first
frame, tappable at ~1400 a few frames later. Worked on v1.1.19, regressed by
the new guard.

Fix: keep the guard's purpose (fail with the actionable hint when a tap would
genuinely land on the IME) but re-sample element vs keyboard geometry every
50ms for up to 2s, returning the error only when the overlap PERSISTS through
the whole window. Bail out early (proceed with the tap) as soon as the
keyboard is dismissed or the element clears it. The verdict per sample is
extracted to keyboardStillCovering and covered by tests with the real-world
geometry, including both edges of the 50px suggestion-strip margin.
@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/driver/devicelab/keyboard.go 96.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Extract the re-sampling loop into settleKeyboardBlocking with injected
samplers so the settle behavior is testable without a live driver, and
cover it end to end: element missing, clear on first frame, relayout
lifting the element mid-settle, keyboard dismissed mid-settle, and a
persistent overlap failing with the geometry hint, plus a pass through
checkKeyboardBlocking with the mock client. No behavior change.

Addresses the Codecov patch-coverage warning.
@omnarayan

Copy link
Copy Markdown
Contributor

Thanks @MarioRial22 — this is an excellent fix, and an important one: the guard it's correcting is a regression we introduced in v1.1.20, so this is squarely on us.

The write-up is exemplary — precise root cause (SOFT_INPUT_ADJUST_RESIZE relayout lifting the target a few frames after the IME appears), the real center-Y trace, the CI-shard repro, and the honest note that the env-test failures reproduce identically on main.

The implementation is clean, too:

  • Re-sampling until the settle window with an early exit means no latency on the happy path — the first clear sample returns immediately.
  • True positives still fail fast with the exact same message.
  • Extracting settleKeyboardBlocking with injected samplers makes the settle logic unit-testable without a live driver.
  • The table-driven tests using the real repro geometry are exactly what I'd want here.

I built and ran it locally — green.

One thing before we merge — the same bug lives in the UIAutomator2 driver - I ll do it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants