fix(mobile): read an iOS screen title from the navigation bar identifier
Merged
SP
4d ago+1351+1351+0123456789012345678901234567890123456789-557-557-012345678901234567890123456789
What and why
Split out of #1105, for #842. This does not close #842: an untitled sheet still takes the navigation bar of the screen behind it.
screenTitlereturns an iOS navigation bar's identifier only when a text inside the bar shows that same string. UIKit and SwiftUI put the title in both. The bar's label is the back button on a pushed screen, and an untitled screen's identifier is a class name (_Tt…,Module.ClassName, or a bare ObjC name) with no matching text, so neither is a title.- An Android toolbar's resource id is not a title. The title is the toolbar's label, else the first text inside it.
- iOS recordings that start on a pushed screen were re-recorded on an iPhone 17 Pro running the iOS 26.5 runtime.
--strict-cachefails withREPLAY_STALEuntil that re-record.
Verified
iPhone 17 Pro, iOS 26.5 (23F73), the benchmark Release build.
pnpm run typecheckandpackages/mobileunit tests (308).- Committed iOS captures assert
screenTitle(modal-flow→Modal Flow,home→Benchmark Examples,bottom-tabs,stripe-paymentsheet, and the untitled SwiftUI screen → none). - SwiftUI fixture app (
tests-capture/swiftui-bar,NavigationStackplus a custom toolbar), raw snapshots committed: Inbox / Message / Compose take thenavigationTitle; the untitled screen's identifier is_TtGC7SwiftUI32NavigationStackHostingandscreenTitleis undefined. - Agent cache re-recorded on that simulator. Login Form, Modal Flow, Apple Pay, and Permission Prompt replay under
--strict-cacheatdev.e2e.benchmark / <screen title>. Bottom Tabs (no bar) replays atdev.e2e.benchmark. - WebView Accessibility, Debounced Search, and Choice Controls are main's recordings with only
startPathandendPathchanged to the screen title. WebView replays under--strict-cachewithCoupon appliedin its end check. - Permission Prompt resets the microphone on the device under test (
device.setPermission('microphone', 'reset'), iOS only) before the step, so the recorded Allow tap replays on a reused simulator. Replayed under--strict-cache. - Deleted: Apple Pay Billing Address (the sheet keyboard covers the remaining fields,
AUTOMATION_UNSUPPORTED; CI already skips it) and Stripe PaymentSheet (the recording taps New card, dismisses the keyboard, and taps Pay without entering a card, so it cannot replay to the end). - Main's Debounced Search and Choice Controls do not finish under
--strict-cacheon iOS 26.5 either, with or without this change: the recorded tap on theBenchmark Targetrow has no touch point of its own (action-failed), and the✓, Mushroomscheckbox is not on screen (target-not-found). iOS CI does not run--strict-cache, so both hand off to the model there as they do on main.
Checklist
- Changeset added (
pnpm changeset) for any change to a published package, or the change is testbed, docs, or CI only. - Breaking change: title carries
!, the changeset body names what breaks and what replaces it, and the deprecation warning landed a window earlier (stability policy in CONTRIBUTING.md). - Docs updated in the same PR: the
docs/**/*.mdxpage for the behavior, andskills/e2e/if the skill describes it. - Contract change: emitted
.d.tsreviewed,tests/types/sdk-types.tsupdated; wire change edits the schema and both fixtures. - Engine contract change (
e2e/engine): changesets fore2e,@e2e-dev/web, and@e2e-dev/mobile.