Skip to content

fix(mobile): keep the sign-in submit button clear of the iOS keyboard - #4820

Merged
iscekic merged 1 commit into
mainfrom
e2e-login-fix-f906
Jul 28, 2026
Merged

fix(mobile): keep the sign-in submit button clear of the iOS keyboard#4820
iscekic merged 1 commit into
mainfrom
e2e-login-fix-f906

Conversation

@iscekic

@iscekic iscekic commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What was broken

apps/mobile/e2e/login.sh stopped being able to sign in: every attempt died at
Assert that "Verify code" is visible... FAILED after the 15s wait, behind Maestro's
generic "This could be a real regression that needs to be addressed" — which sent an
agent hunting a product bug for four rounds.

It is not a backend problem and not a typing problem. The email lands in the field
correctly; the submit tap is swallowed by the keyboard.

Root cause

The login form is vertically centred in a ScrollView, so the "Send code" button sits a
few points from the keyboard's top edge. On an iPhone 17 Pro:

element bounds (pt) centre
email field 506–538 522
Send code 549–588 568
keyboard window 566–874

Maestro taps an element's centre, and iOS delivers any touch inside
UIRemoteKeyboardWindow to the keyboard. From the failing run's own device log:

Tapping 201.0, 568.0
Sending UIEvent type: 0; subtype: 0; to window: <UIRemoteKeyboardWindow: 0x159a5b200>

The app never saw the touch. requestEmailCode never ran, no POST /api/auth/native/otp
was sent (the outbox directory did not even exist), and Maestro reported the tap
COMPLETED because it gets no hit-test result back. The garbled field content seen in
later rounds (-cli-69f6@example.com@ee2e-mobile-…) is a downstream artefact of retries
typing into a field that was never cleared, not the cause.

automaticallyAdjustKeyboardInsets did not save it: it only makes the ScrollView
scrollable, it never scrolls, and iOS auto-reveals only the focused field — which was
already visible.

On the regression hunt

Not caused by 304b31e3d (Mobile Audit W1-B). I checked out that commit's parent
versions of login-screen.tsx + idle-auth.tsx on the device and measured again:
identical geometry — field 506–538, submit 549–588, more-options 598–637, submit still
under the keyboard. Screenshot of the pre-commit build shows "Send code" cut off by the
keyboard exactly as HEAD does. That commit only mitigated the Android half of this
same defect (returnKeyType="go" + onSubmitEditing).

The defect is older than the sign-in screen's last few changes: the margin at the
submit button's centre was ~2pt, and it is a function of screen height minus keyboard
height, so the same flow passes on a taller simulator and fails on a shorter one. The
simulator pool here spans iPhone SE through 17 Pro Max, and dev:mobile:simulator claim
takes whichever iPhone is free — that is the "this used to work". I did not re-run
history on the other models, so which model the earlier green runs used is unproven;
the geometry above is measured, the model-dependence is arithmetic from it.

The fix

Product (login-screen.tsx, the real user-visible half): the root
KeyboardAvoidingView now runs on iOS too, with behavior="padding". It shrinks the
ScrollView so the whole form re-centres above the keyboard. The now-redundant
automaticallyAdjustKeyboardInsets is removed — stacking both pushes the form off the
top of the screen (observed, then fixed). After the change, submit centre 414 vs
keyboard top 566: clear by 152pt, with the logo, field, submit and "More sign-in
options" all visible.

No unit test: the change is a platform-conditional prop, there is no extractable logic
to assert. It is covered by the device runs below.

Harness:

  • login-request-code.yaml erases the field before typing (it is uncontrolled, so a
    login page left on screen still holds its address and inputText inserts at the caret
    the tap dropped mid-string) and asserts the typed address before submitting — a
    mismatch now fails instantly and legibly instead of 15s later on Verify code.
  • login.sh cold-relaunches through open-app.yaml before its single retry, and if that
    fails too it names which half broke: no new outbox email means the request never
    reached the backend; a new email means the request worked and only the code screen was
    never reached.
  • e2e/AGENTS.md records both constraints; the swallowed-tap diagnosis (including the
    two log greps that prove which window got the touch) is in
    .kilo_workflow/learnings/maestro-tap-swallowed-by-ios-keyboard.md.

Verification

iPhone 17 Pro simulator (iOS 26.5), this worktree's own stack and Metro.

Reproduced at HEAD first: login.sh exit 1, both attempts dying on Verify code, live
maestro hierarchy confirming submit centre 568 inside the keyboard window at 566.

With the fix, apps/mobile/e2e/login.sh <udid> reached Home from all three starting
states, first attempt each time, no retry needed:

start state result
signed out with a full-length address already in the field exit 0 — Erase 100 charactersAssert that "${EMAIL}" is visible... COMPLETED → Home
already signed in exit 0 — signed out, re-requested, verified, Home
cold app (process terminated) exit 0 — deep link relaunch, signed out, verified, Home

The OTP screen shares the same container and was re-checked with the number pad up:
Verify code 238–276, Resend code 287–325, Back 336–374, all above the keyboard
window at 566.

Checks from apps/mobile: pnpm format, pnpm typecheck, pnpm lint (0 errors),
pnpm check:unused, pnpm test (2337 passed). git diff --check clean,
bash -n login.sh and maestro check-syntax OK.

What remains unexplained

Which simulator model the historical green runs used — the fix makes it moot (there is
now 152pt of clearance instead of 2), but the "used to work" timeline is inferred from
geometry, not from a recovered passing run. The OTP screen now has both this
KeyboardAvoidingView and its own keyboard-padding listener; both were verified working
together, and collapsing that duplication was out of scope here.

The login form is centred in a ScrollView, so on shorter iPhones the "Send
code" button's centre fell inside UIRemoteKeyboardWindow (iPhone 17 Pro: centre
568pt vs keyboard window top 566pt). iOS hands such a touch to the keyboard, so
Maestro's centre tap was a silent no-op: e2e/login.sh logged the tap COMPLETED,
the OTP request never went out, and the flow timed out 15s later on a missing
"Verify code" behind Maestro's generic "could be a real regression" advice.

automaticallyAdjustKeyboardInsets only made the ScrollView scrollable — it
never scrolls, and iOS auto-reveals only the focused field — so the root
KeyboardAvoidingView now runs on iOS too with behavior="padding", and the inset
it replaces is removed (stacking both pushes the form off the top).

Harness side: login-request-code.yaml erases the email field before typing (it
is uncontrolled, so inputText appended at a caret the tap dropped mid-string
and interleaved two attempts into one malformed address) and asserts the typed
address before submitting; login.sh cold-relaunches before its single retry and
names which half failed instead of leaving Maestro's advice as the only signal.

Verified on an iPhone 17 Pro simulator from three starting states: already
signed in, signed out with a dirty email field, and a cold app launch.
@iscekic iscekic self-assigned this Jul 28, 2026
@kilo-code-bot

kilo-code-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed the iOS keyboard-avoidance fix, e2e harness hardening, and documentation updates; no security, correctness, or reliability issues found in the changed lines.

Files Reviewed (5 files)
  • apps/mobile/src/components/login-screen.tsx
  • apps/mobile/e2e/login.sh
  • apps/mobile/e2e/flows/login-request-code.yaml
  • apps/mobile/e2e/AGENTS.md
  • .kilo_workflow/learnings/maestro-tap-swallowed-by-ios-keyboard.md

Reviewed by claude-sonnet-5 · Input: 18 · Output: 3.8K · Cached: 406.6K

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic merged commit a9f3eb8 into main Jul 28, 2026
21 checks passed
@iscekic
iscekic deleted the e2e-login-fix-f906 branch July 28, 2026 14:03
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