chore: stabilize Maestro CI#7311
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR upgrades Maestro to 2.5.1 (Android/iOS), changes Android AVD caching and emulator RAM/options, replaces the Android monkey pre-test step with ChangesMaestro Test Infrastructure Upgrade
Sidebar View Layout Change
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/maestro-android.yml (1)
41-48:⚠️ Potential issue | 🟠 Major | ⚡ Quick winShard 14 can poison the shared AVD cache, defeating the snapshot optimization for all other shards
The
Cache Android AVDstep has noifcondition, so shard 14 participates in both cache restore and save under the same key (avd-${{ runner.os }}-api34).On a cold run, all 14 shards race to save to that key (GitHub Actions is first-writer-wins). If shard 14 wins, the cache stores only
Pixel_API_34_Keyboard.avd+Pixel_API_34_Keyboard.ini. On subsequent runs:
- Non-keyboard shards see
steps.avd-cache.outputs.cache-hit == 'true'- The "Generate AVD snapshot" step is skipped (its condition checks
cache-hit != 'true')- The test step runs with
force-avd-creation: false— but the expected default-profile AVD is absent from the restored cacheThe behavior of
reactivecircus/android-emulator-runnerwhenforce-avd-creation: falseand the named AVD doesn't exist is unspecified; it may silently cold-boot (losing the optimization) or fail outright. Either way, the caching strategy is silently broken.🐛 Proposed fix — exclude shard 14 from AVD caching
- name: Cache Android AVD + if: ${{ inputs.shard != '14' }} id: avd-cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.android/avd/* ~/.android/adb* key: avd-${{ runner.os }}-api34🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/maestro-android.yml around lines 41 - 48, The "Cache Android AVD" step (id: avd-cache, name: Cache Android AVD, key: avd-${{ runner.os }}-api34) must be excluded from shard 14 so it doesn't poison the shared AVD cache; update the step to run only when the shard is not 14 (e.g. add an if condition referencing your matrix/shard variable such as if: matrix.shard != 14) so shard 14 neither restores nor saves to the common key, leaving other shards to generate and cache the full AVD snapshot correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/maestro-android.yml:
- Around line 24-33: The workflow currently uses the jlumbroso/free-disk-space
action with swap-storage: true while configuring the emulator's ram-size: 6144M,
which disables swap and leaves under ~800MB host memory — change either the
swap-storage or ram-size to avoid OOMs: set swap-storage: false to retain the
runner swapfile, or reduce ram-size to 5120M or 4608M so host+emulator fit in
physical RAM; update the keys swap-storage and ram-size in the action usage
(uses: jlumbroso/free-disk-space@...) and apply the same change to the other
occurrences noted (the other blocks referenced) so all workflow entries are
consistent.
In @.github/workflows/maestro-ios.yml:
- Around line 14-15: The comment incorrectly mentions the Android-specific
"dadb"/ADB change; update the comment text that currently references "dadb" and
ADB to an iOS-appropriate note explaining that iOS Maestro uses
idb/libimobiledevice (not ADB) and that the Android-specific CLI 2.5.0 change
does not apply to the iOS workflow; either remove the ADB-specific sentence or
replace it with a short iOS-focused explanation and keep or adjust the release
link as needed so the comment accurately reflects iOS tooling.
---
Outside diff comments:
In @.github/workflows/maestro-android.yml:
- Around line 41-48: The "Cache Android AVD" step (id: avd-cache, name: Cache
Android AVD, key: avd-${{ runner.os }}-api34) must be excluded from shard 14 so
it doesn't poison the shared AVD cache; update the step to run only when the
shard is not 14 (e.g. add an if condition referencing your matrix/shard variable
such as if: matrix.shard != 14) so shard 14 neither restores nor saves to the
common key, leaving other shards to generate and cache the full AVD snapshot
correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ee299d9c-ce31-45fd-89b1-673983985558
📒 Files selected for processing (3)
.github/scripts/run-maestro.sh.github/workflows/maestro-android.yml.github/workflows/maestro-ios.yml
💤 Files with no reviewable changes (1)
- .github/scripts/run-maestro.sh
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Build Android / android-build
- GitHub Check: ESLint and Test / run-eslint-and-test
|
Android Build Available Rocket.Chat 4.72.0.108814 |
|
Android Build Available Rocket.Chat 4.72.0.108814 Internal App Sharing: https://play.google.com/apps/test/RQQ8k09hlnQ/ahAO29uNRwjAsJvaiquGjKRmucVItIwkqzqmtF75VX3MLDxOhuznxMA2GxetWxp_dAfRbh33wL0-dHZMTeQVHqchi7 |
|
Android Build Available Rocket.Chat Experimental 4.72.0.108813 |
|
Android Build Available Rocket.Chat Experimental 4.72.0.108813 Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNTowuRjjHlDYUgXqroMl8epJWmBJOAe-7N_6W5JjAcJyBHu-MfbZDIilMfFnPJkwzs7kKMzme1NVFTUUxnE |
|
iOS Build Available Rocket.Chat Experimental 4.72.0.108816 |
|
Android Build Available Rocket.Chat Experimental 4.72.0.108821 Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNQLTMEQOTK7Yu4n98ufM0ImDLGPoEI7tdqqt4_8PqA3XXsk_0JhIS1tJIiRPRZJU0Eh3mtvcPjDUgnnZ0rq |
|
Android Build Available Rocket.Chat 4.72.0.108820 Internal App Sharing: https://play.google.com/apps/test/RQQ8k09hlnQ/ahAO29uNQrTqib34jYoZve6A-78lxC3zI5hjF5NuHRtFdGLOxp7tcQ3xwQyvjeTqlI5Lq2c3nCQqQiv5zaR2Rc0VBd |
|
iOS Build Available Rocket.Chat Experimental 4.72.0.108822 |
The iOS / Android Maestro workflows only uploaded *.png under ~/.maestro/tests. JUnit XML (maestro-report.xml, maestro-rerun-round-*.xml) and Maestro's per-flow command logs were discarded, so a CI failure surfaced only as a screenshot of the final state, not the failing step or step trace. Expand the upload-artifact globs on both workflows to include the JUnit reports (in CWD) and the full ~/.maestro/tests tree.
Align Maestro iOS shards with e2e-build-ios so the artifact built against the iOS 26 SDK is executed on an iOS 26 simulator runtime instead of the macos-14 default (iOS 18.2). Cross-SDK execution surfaced an app crash on the rocketchat://room?path= deeplink that does not reproduce on the build-matched runtime.
The macos-26 image does not ship an "iPhone 16 Pro" device; Boot Simulator failed with "Invalid device: iPhone 16 Pro" and aborted the shard before maestro could run. Enumerate the available simulator catalog and select the highest-numbered iPhone N Pro present so the workflow survives image rotation.
iOS 26 surfaces a system "Save Password?" prompt after successful native form submission. It is presented as a modal sheet on top of the rooms list, which blocks XCUITest hit-testing for elements underneath. Maestro's `extendedWaitUntil id: rooms-list-view-sidebar, enabled: true` therefore never resolves and shard 9 (e2e-encryption) times out on the macos-26 / iOS 26 runners (reliably green on macos-14 / iOS 18.2). Dismiss the prompt with "Not Now" if visible, gated on iOS, in both `helpers/login.yaml` (catches the modal immediately after submit) and `tests/e2ee/utils/navigate-to-e2ee-security.yaml` (catches it if the e2ee saga delays the modal past the login wait). On platforms or runs where the prompt does not appear, the `runFlow when` block is a no-op.
iOS Maestro tests were running each failing flow up to 8 times: nick-fields/retry max_attempts=2 multiplied by run-maestro.sh's internal 1 main + 3 rerun rounds. Deterministic failures wasted ~20 min/job; the wrapper also re-ran every flow in the shard, not just the failed one, since it had no flow-level state. Drop the wrapper on iOS (Android never had it) and lower MAX_RERUN_ROUNDS from 3 to 2 so the script-internal loop alone gives at most 3 runs per failing flow.
…ord flow The "Save Password?" system sheet that iOS 26 shows after a native password form submission is the root cause of the change-password.yaml shard-1 flake (8/8 failure screenshots in run 26117774450 show the same sheet overlaying rooms-list-view). Two gaps in b04b5ab covered only login.yaml: 1. The change-password submit (`change-password-view-set-new-password-button`) also surfaces the sheet. Nothing dismissed it there, so the subsequent `assertVisible: rooms-list-view` was blocked by the modal. 2. The sheet has variable latency on iOS 26 (~0.5s–7s observed). The pre-existing one-shot `runFlow when: visible:` in login.yaml fires immediately after rooms-list-view becomes visible and frequently misses a late-rendering sheet, so the next interactive assertion times out. Consolidate the dismiss into a shared `helpers/dismiss-save-password.yaml` that waits up to 8s for the sheet with `optional: true` (no-op on Android / iOS 18), then taps Not Now with `optional: true`. Call it from login.yaml, change-password.yaml (both after new-password submit and before the final re-login assertion), and the existing e2ee navigate helper.
iOS 26 surfaces a system "Save Password?" sheet asynchronously (0.5s–7s variable latency) after any secureTextEntry field submit. It overlays the app and blocks XCUITest hit-testing, causing Maestro change-password shard-1 to fail at rooms-list-view assertions 100% on macos-26 runners (8/8 failures in run 26117774450 show the same sheet over rooms-list-view). Research summary: iOS sim has no documented kill switch (confirmed by Detox #3761, Selenium #11426, Maestro #2089). Maestro has no auto-dismiss and no workspace-level onFlowStart hook (#2063). iOS only engages Password AutoFill on `secureTextEntry={true}` fields, regardless of `textContentType` value, except `oneTimeCode`. Setting `secureTextEntry={false}` declassifies the field and suppresses the offer entirely — the same trick `RegisterView` already uses at `register-view-password`. Apply this gating once at `FormTextInput.tsx` so every password field that uses the shared input (LoginView, ChangePasswordView, E2EE views, RoomInfoEditView, etc.) is covered. Visual mask + show/hide eye icon stay driven by the original `secureTextEntry` prop; only the native prop passed to the underlying TextInput is suppressed. Plaintext password rendering under E2E mirrors what RegisterView already does and is acceptable for test environments that use throwaway credentials. Revert b04b5ab + the dismiss-save-password helper added earlier on this branch — no longer needed since the prompt never surfaces in E2E builds. Note: TwoFactor, JoinCode, ProfileView ActionSheets and the generic ActionSheetContentWithInputAndSubmit use raw TextInput (not FormTextInput) and are not covered by this fix. They are not currently exercised by any failing iOS Maestro shard; same pattern can be applied locally there if a flow lands that exercises them on iOS 26.
iOS classifies a field as a credential for Password AutoFill / Save
Password via any of three signals: secureTextEntry, textContentType in
{password, newPassword, ...}, or autoComplete in {password, password-new,
...}. The previous patch flipped only secureTextEntry, so iOS still
classified LoginView and E2EEnterYourPasswordView (both hardcode
textContentType='password' + autoComplete='password' on iOS) as
credential fields and surfaced the "Save Password?" sheet on iPhone 17
Pro / iOS 26 fresh sims in CI, blocking shard 9 (e2e-encryption.yaml).
Extend the FormTextInput-level suppression to override all three under
RUNNING_E2E_TESTS on iOS so the underlying TextInput presents as a
neutral text field to UIKit. Visual masking and the show/hide eye icon
still follow the original secureTextEntry prop.
iOS shard 1's open-deeplink helper used `text: Open` which is a regex match. On iOS 26 cold-launch the alert title "Open in 'Rocket.Chat Experimental'?" also matches `Open`, and Maestro's index:0 hits the title bounds (~275, 485 pt) instead of the button (~277, 573 pt), so the dialog stays up and every flow in shard 1 chain-fails. Other shards launch the app via UI first so the dialog either does not appear or its hierarchy looks different — same regex happens to work there. Anchor the selector to `^Open$`. iOS Run Maestro Tests step was capped at 30 min after we dropped the nick-fields/retry wrapper (which gave 2x30=60 min). Shard 12 ran ~35 min and was killed mid-flight; raise to 45 to match the longest passing shard plus rerun budget. Android AVD snapshot generation step was capped at 30 min. A cache miss after the 4G→6G RAM bump pushed cold-boot + recreate past that. Raise to 45 so a one-time cache repopulate succeeds and future runs hit cache.
All 14 Android shards start in parallel and check actions/cache at the same instant. Nothing has been written yet, so every shard misses and runs its own "Generate AVD snapshot for cache" step — burning ~90s per shard on a 6 GB emulator boot the matrix could have shared. The repo is also at ~12.5 GB of caches (over the 10 GB cap), so the AVD cache is the LRU victim and gets evicted before the next run anyway. Add a single serial `e2e-seed-android-avd` job between `e2e-hold` and `e2e-run-android`. It restores or generates the AVD snapshot once, post-uploads, and then the matrix kicks off — so every shard's `Cache Android AVD` step now hits. The existing per-shard fallback stays as defense in depth for the (rare) eviction-between-jobs case.
…ttern
Bumps `rooms-list-view` (was assertVisible w/ no timeout) and
`rooms-list-view-item-${ROOM}` (was 10s) to the 60s extendedWaitUntil
pattern used elsewhere. Targets the Android 5 / Accessibility flake
observed in run 26244170118 where the search-result visibility check
timed out twice consecutively before finally passing.
…t.Chat.ReactNative into chore/maestro-ci-bundle
…calls sdk.methodCall appended `this.code || ''` (the TOTP code) as a trailing positional argument to every DDP method call. That empty string was a harmless ignored extra until the server's loadSurroundingMessages method grew a typed `showThreadMessages: boolean` 3rd param (RocketChat/Rocket.Chat #39092). The '' then fails the server's `check(showThreadMessages, Boolean)`, returning a 500 that rejects loadSurroundingMessages and aborts the whole jump-to-message flow (loading shows, no navigation). Append the TOTP code only when a 2FA flow is in progress, so non-2FA DDP calls send exactly their declared params — matching the REST path and the server default. The 2FA flow is preserved: the codeless first call still gets totp-required, then the retry appends the resolved code. Covered by the existing .maestro/tests/room/jump-to-message.yaml flow.
RoomView.jumpToMessage races the list scroll against a fixed wall-clock cap. The scroll grows the rendered take(count) window 50 rows at a time (600ms/round) until the target enters it, so its duration is O(rows already loaded), not constant. On the slow CI release emulator the loop exceeds 5s, the race cancels, the target never centers, and the jump-to-message shard flakes (message-content-29 never visible). Bump the cap 5s→20s to cover the slow-device convergence; the Loading overlay already exposes a cancel button, so a longer ceiling is not a UX trap. Deterministic re-anchoring of the window is the proper fix and is deferred to a follow-up.
…hard 8) On CI, opening the keyboard on ChangePasswordView shrinks the KeyboardView (behavior='padding') scroll viewport until only the title + current-password fit, clipping new-password/confirm below the fold (they drop out of the view hierarchy). The bare assertVisible does not scroll, so it never sees them and shard 8 fails on every attempt that reaches this sub-flow. Dismiss the keyboard via the existing hide-keyboard helper before each assert, mirroring how the ProfileView edits already behave.
|
Android Build Available Rocket.Chat 4.73.0.108998 Internal App Sharing: https://play.google.com/apps/test/RQQ8k09hlnQ/ahAO29uNSsPbZ7wO2zN0HQPLAhY8fvDmlFQy-JUoyO6NwsSJMQ7FF3zZGWHUsSfWu6Xf4rrflHMZFR-3YF_ulHdWJn |
A long background marks the DDP socket stale, so foregrounding triggers checkAndReopen -> forceReopen, which drops all SDK subscriptions and reopens the socket without going through connect(). The module-level roomsSubscription guard therefore stayed set, so subscribeRooms() skipped re-subscribing stream-notify-user and the rooms list silently stopped reflecting subscriptions/favorites/reads until a manual reconnect. Reset the guard from the socket 'close' listener (via unsubscribeRooms), the same teardown connect() already performs, so the resume-login that follows the reopen re-subscribes stream-notify-user. Other streams (permissions, presence, settings, roles) subscribe unconditionally and were never affected.
…bber)
Auth-deeplink login dispatched loginRequest before the socket reached 'connected', so the SDK login-guard opened a second (orphan) socket. When the server later idle-timed-out the abandoned socket, its close flipped Redux to disconnected and the app showed "Waiting for network" while a live socket still existed.
- SDK (patch): onClose ignores close events from a non-current socket; open() tears down the previous connection before replacing it.
- deepLinking saga: wait for METEOR.SUCCESS ('connected') before dispatching the resume loginRequest (auth-deeplink and call-push paths); update saga test accordingly.
…undle # Conflicts: # app/sagas/deepLinking.js
A quoted reply renders its text via a nested <Markdown> inside the message's single accessible Touchable. On iOS that subtree is merged into the parent accessibility element, so the quoted text is never exposed on its own (on Android the raw text node is still enumerated). Add a useQuoteDescriptionLabel hook, mirroring useImageDescriptionLabel, and fold the quoted text into useMessageAccessibilityLabel. VoiceOver now announces quoted content, and the iOS jump-to-message Maestro assertion that matches the quoted text can resolve.
… shard - maestro-ios.yml: start the simulator cold boot asynchronously before installing Maestro + idb so the ~3.5min cold boot overlaps the ~30s install, then block on bootstatus afterwards. Trim post-boot settle 15s -> 5s. - Fold the lone shard-9 flow (e2e-encryption) into shard 3 and drop shard 9 from both iOS and Android matrices, removing one cold boot per platform per run.
The android-emulator-runner action's internal 'Install Android SDK' step re-downloads the emulator binary and the api-34 system image (~1 GB) from dl.google.com on every one of the 13 parallel Maestro shards. Under that concurrent load the download intermittently arrives corrupt/truncated and sdkmanager dies with 'Error on ZipFile unknown archive', failing a random shard before any test runs. The existing AVD cache only persists the AVD instance (~/.android/avd), not the SDK packages. Promote the e2e-seed-android-avd seed job to also seed the SDK packages: - a separate read-write cache (key android-sdk-emu-<os>-api34) over the emulator + system-image dirs, and - a single serialized, retried sdkmanager install (3x, clearing partial downloads between tries) that runs before the snapshot step, so a corrupt archive self-heals instead of failing the matrix. Each Maestro shard restores that cache read-only (seed job is the single writer), so android-emulator-runner's install becomes a no-op and the per-shard dl.google.com download is eliminated.
#7311 carried the original cherry-pick base of the deeplink/orphan-socket fix. The standalone PR #7380 refined it before merging to develop: - guard the login gate against the connect-before-select race (METEOR.SUCCESS may fire before SERVER.SELECT_SUCCESS; the unconditional take would hang) - added regression tests for that ordering + the call-push path - clarified the SDK patch comments (FIX A primary/defense) Bring app/sagas/deepLinking.js, its test, and the SDK patch in line with develop's merged #7380 version so #7311 stops carrying a divergent copy.
The Android test shards start with ~89 GB free (/dev/root 145G, 39% used) before anything runs; the emulator image + snapshot + downloaded APK use a few GB against that. jlumbroso/free-disk-space cost ~62s on every shard to free space nothing was competing for. The real emulator constraint is RAM, which this action doesn't address (and swap-storage was already kept). Removes ~1 min from the matrix critical path and ~13 runner-minutes/run.
Android shards each persisted a ~3 GB AVD snapshot under a shared key. With multiple open PRs this pushed the repo past GitHub's 10 GB Actions cache cap, LRU-evicting the SDK system-image cache before the shards restored it. Evicted shards then cold-booted and re-downloaded the system image in a single unretried sdkmanager call, intermittently hitting "Error on ZipFile unknown archive" and failing the shard. - AVD cache -> actions/cache/restore (restore-only). The seed job is the single writer, so shards no longer duplicate the 3 GB snapshot per PR; the footprint stays under the 10 GB cap and the SDK cache survives. - Add a 3x-retried sdkmanager install (mirrors the seed job), gated on an SDK-cache miss and run before any emulator boot, so a corrupt download self-heals on both the default cold-boot path and shard 14.
The Unstar step swiped the action sheet up once and asserted 'Unstar' was visible, while the symmetric Star step uses scrollUntilVisible. When a single swipe didn't fully expand the sheet, 'Unstar' stayed below the fold and the shard failed with "Assertion is false: 'Unstar' is visible". Mirror the Star step so the sheet is scrolled until the item is on screen.
Flows are driven by REST calls (login, users.create, …) that data-setup.js makes to the test server. When the server is down or blips, a flow fails deep inside an evalScript as an opaque "[Failed] <flow>" with no reason in the job log — diagnosing it meant downloading the Maestro artifact and reading maestro.log. Two shell-level signals (run-maestro.sh stdout is the job console, so these render as GitHub annotations without opening logs): - Preflight: probe <server>/api/info before running flows; on non-200 emit a red annotation and exit. A full outage makes every shard fail with the same clear reason instead of 13 mystery flow failures. - Mid-run scan: on failure, grep the local Maestro logs for retryRequest's "Non-retryable error <code>" / connection-error fingerprints and annotate that the failure is likely a server/environment flake.
Proposed changes
CI observability: surface E2E server outages (commit 8d9b14f)
login,users.create, …) that.maestro/scripts/data-setup.jsmakes to the test server (mobile.qa.rocket.chat). When that server is down or blips, the flow fails deep inside anevalScriptas an opaque[Failed] <flow>with no reason in the job console — diagnosing it meant downloading the Maestro artifact and readingmaestro.log(that is how shard 12'sroom.yamlfailure on run 26904017219 turned out to be a transientNon-retryable error 404onusers.create, not an app/test bug).run-maestro.sh's stdout is the job console, so two shell-level signals now render as GitHub annotations with no log/artifact spelunking: (1) a preflight that probes<server>/api/infobefore running flows and, on non-200, emits a red::error::annotation and exits — a full outage then makes every shard fail with the same clear reason instead of 13 mystery failures; (2) a mid-run scan that, on failure, greps the local Maestro logs forretryRequest'sNon-retryable error <code>/ connection-error fingerprints and annotates that the failure is likely a server/environment flake. Verified locally: the scan flags the real shard-12404log, and the preflight passes (HTTP 200) against the healthy server.Test flow: Room Actions Unstar scroll (commit 11cfa17, shard 11)
Room Actionsflow's Unstar step swiped the action sheet up once and then assertedtext: 'Unstar'was visible, while the symmetric Star step a few lines above usesscrollUntilVisible. When a single swipe didn't fully expand the sheet,Unstarsat below the fold and the shard failed withAssertion is false: "Unstar" is visible(e.g. run 26904017219 shard 11). It is a flake, not a regression — the same unchanged flow passed on the prior run (26897814998) — and this PR's app code touches neither the starred feature nor the action sheet. Fix: mirror the Star step so the sheet is scrolled until the item is on screen instead of asserting after one swipe.CI infra follow-up #6 (commit 71ef5b4)
avd-<os>-api34key. With three open PRs that is ~8.5 GB of AVD caches alone — well past GitHub's 10 GB per-repo cap — so the eviction policy (oldest last-access first) purged the smaller restore-only SDK cache. Evicted shards then fell through to the cold-boot path and re-ranandroid-emulator-runner's internal unretriedsdkmanagerinstall, hitting the exactError on ZipFile unknown archiveflake Sync subscriptions #4 set out to kill — just on the shard cold-boot path instead of the test-run path (e.g. run 26897814998 shard 5). Two changes: (1) the shards' AVD cache becomes restore-only (actions/cache/restore) — the seed job is the single writer, so shards stop duplicating the 3 GB snapshot per PR, the footprint drops back under the 10 GB cap, and the SDK cache survives to be restored; (2) the seed job's serialized 3×-retriedsdkmanagerinstall (clears partial downloads between tries) is added to the shards too, gated on an SDK-cache miss and run before any emulator boot, so a corrupt download self-heals on both the default cold-boot path and shard 14 — not only inside the seed. Durable next step (separate change, once this validates): move the seed tobuild-develop.ymlso the AVD + SDK caches live on the default branch and are restored by every PR (GitHub lets any branch restore default-branch caches), collapsing N × ~4.7 GB of per-PR caches into one shared copy.CI infra follow-up #5 (commits 037b120, 96525f6)
jlumbroso/free-disk-spacestep from the Android Maestro shards. Each shard starts with ~89 GB free (/dev/root145 G, 39 % used) before anything runs; the emulator image, snapshot, and downloaded APK use a few GB against that — so the step spent ~62 s on every shard freeing space nothing was competing for. The real emulator constraint is RAM (addressed by the 6 GB bump;swap-storagewas deliberately kept), which this action does not touch. Removes ~1 min from the matrix critical path and ~13 runner-minutes/run. Supersedes the "Pre-launch disk cleanup" bullet below.develop— guarding the deeplink login gate against the connect-before-select race (METEOR.SUCCESScan fire beforeSERVER.SELECT_SUCCESS, where the unconditional take would hang), adding regression tests, and clarifying the SDK-patch comments.app/sagas/deepLinking.js, its test, andpatches/@rocket.chat+sdk+1.3.3-mobile.patchare now identical todevelop, so the branch stops carrying a divergent copy and won't conflict on the eventualdevelopmerge.CI infra follow-up #4 (commit a3c4788)
reactivecircus/android-emulator-runner's internal "Install Android SDK" step (sdkmanager … --channel=0→Error on ZipFile unknown archive). Each of the 13 parallel shards re-downloaded the emulator binary + the api-34 system image (~1 GB) from dl.google.com; under that concurrent load the archive intermittently arrived corrupt/truncated and killed a random shard before any test ran. The failing shard roamed (shard 12 emulator pkg, shard 7 system-image pkg in earlier runs), confirming it is download contention, not a shard-specific bug — the existing AVD cache only persists the AVD instance (~/.android/avd), not the SDK packages. Thee2e-seed-android-avdseed job now also seeds the SDK packages: a separate read-write cache (keyandroid-sdk-emu-<os>-api34) over the emulator + system-image dirs, plus a single serialized, retriedsdkmanagerinstall (3×, clearing partial downloads between tries) that self-heals a corrupt archive. Each Maestro shard (incl. shard 14) restores that cache read-only — the seed job is the single writer — soandroid-emulator-runner's install becomes a no-op and the per-shard dl.google.com download (and its flake) disappears. Net: 13 concurrent unguarded downloads → 1 serialized retried download.CI infra follow-up #3 (commit 026a3d3)
maestro-ios.ymlnow startssimctl bootin a dedicatedStart Simulator Bootstep beforeInstall Maestro + idb, so the ~3.5 min cold boot runs in the background while Maestro + idb install (~30 s); a laterWait for Simulator Readystep blocks onsimctl bootstatus -b. The SpringBoard settle after boot is trimmed 15 s → 5 s (bootstatus -balready guarantees a completed boot). Shaves ~40–55 s off every iOS shard's critical path.e2ee/e2e-encryption.yaml,test-9→test-3) into shard 3 and drop shard 9 from both the iOS and Android matrices inbuild-pr.yml. Shard 9 ran one flow, so its ~3.5 min cold boot was almost pure overhead — this removes one boot per platform per run. Shard 3 (onboarding/workspace validation, the lightest 3-flow shard) goes 3 → 4 flows.CI infra follow-up #2 (commit 63db0eb)
e2e-seed-android-avdjob inbuild-pr.ymlbetweene2e-holdande2e-run-android. Every shard in the 14-job matrix was previously hittingactions/cachesimultaneously and missing — nothing had been written yet — so every shard ran its own "Generate AVD snapshot for cache" step. The seed job restores or generates the AVD snapshot once, post-uploads, and then the matrix fans out — every shard's cache restore now hits. The repo is also at ~12.5 GB of caches (over the 10 GB cap), so the AVD entry was the LRU victim across runs; the seed job re-populates on each PR run regardless. The per-shard fallback step stays as defense in depth.CI infra follow-up (commit 2066fbf)
Open in App?dialog tap selector in.maestro/helpers/open-deeplink.yamltotext: '^Open$'. UnanchoredOpenmatched both the iOS 26 alert title ("Open in 'Rocket.Chat Experimental'?") and the button; on cold-launch (shard 1) Maestro'sindex: 0picked the title at(275, 485) ptinstead of the button at~(277, 573) pt, the dialog persisted, and all 4 flows chain-failed. Other shards launch the app via UI first so the dialog either doesn't appear or its hierarchy looks different — same regex happened to work. Anchoring kills the ambiguity.Run Maestro Testssteptimeout-minutes30 → 45. Droppingnick-fields/retryremoved the previous 2×30=60 min ceiling; shard 12 ran 35 min on run 26168373950 and was killed mid-flight. 45 min matches the slowest passing shard (~27 min) plus rerun headroom.timeout-minutes30 → 45. A cache miss after the 4 GB → 6 GB RAM bump pushed cold-boot + AVD recreate past the cap on shard 3. Once the cache repopulates, future runs hit cache and skip this step.CI infra (Android & iOS)
Broken pipeinstall failures that were the dominant Android shard flake source.monkeywarmup inrun-maestro.sh(+ trailingsleep 6/am force-stop) — it stressedsystem_serverright before Maestro's driver install.-no-snapshot-saveto prevent corruption. Keyboard AVD (shard 14) untouched.android-emulator-runnersteps.jlumbroso/free-disk-space@v1.3.1(Android SDK preserved; ~6 GB freed).~~ Removed in follow-up Show user's list #5 — the shards already start with ~89 GB free.nick-fields/retrywrapper on iOS Maestro shards and lowerMAX_RERUN_ROUNDSinrun-maestro.shfrom 3 → 2. The compounded retry produced up to 8 reruns of each failing flow (outermax_attempts: 2× inner 1 main + 3 rerun rounds), wasting ~20 min/job on deterministic failures and re-running every flow in the shard on outer retry. New ceiling per failing flow: 3 runs (1 main + 2 rerun rounds). Android already called the script directly and inherits the new default.maestro-report.xml,maestro-rerun-round-*.xml) and the full~/.maestro/teststree — previously only screenshots were captured.macos-26+ Xcode 26.2.0 to matche2e-build-ios.yml. The artifact is built against the iOS 26 SDK; the priormacos-14runner booted iOS 18.2, which surfaced arocketchat://room?path=deeplink crash that does not reproduce on the build-matched runtime.App: sidebar testID fix (unblocks shard 8 + ~12 other flows)
SidebarViewwraps theScrollViewin a plainViewcarryingtestID='sidebar-view'. On Android,ScrollViewdoes not propagatetestIDto the native accessibility tree; a regularViewdoes. Folded in from #7319.App: profile rate-limit fix (shard 8, Android)
profile.yamlwas making three consecutiveusers.updateOwnBasicInfocalls; the endpoint is rate-limited per user. Collapsed into one combined submit (name, username, nickname, bio, email) before tapping submit. The password-confirm sheet andChangePasswordViewpath are preserved.App: form keyboard scroll fix (shard 6, iOS)
FormContainerswapsKeyboardAvoidingView + ScrollViewforKeyboardAwareScrollViewfromreact-native-keyboard-controller(already a dep). KAS scrolls the focusedTextInputabove the keyboard on focus change, preventing Maestro 2.5.1'stapOnfrom landing on the QWERTY row instead of the target field.App: iOS Save Password suppression at FormTextInput (shards 1 & 9, iOS)
iOS 26 surfaces a system
Save Password?sheet asynchronously (0.5s–7s variable latency) after anysecureTextEntry={true}field submit. It overlays the app and blocks XCUITest hit-testing, breaking shard 1 (change-password.yaml, 100% deterministic — 8/8 failures in run 26117774450 all show the sheet overrooms-list-view) and shard 9 (e2e-encryption.yaml). The earlier YAML-level dismiss (a one-shotrunFlow when: visible: 'Securely store your password') missed the late-arriving variant of the sheet and required per-test discipline.Researched alternatives (no clean sim/CLI/Maestro-config kill switch exists — confirmed by Detox #3761, Selenium #11426, Maestro #2089). iOS only engages Password AutoFill on
secureTextEntry={true}fields; setting it tofalsedeclassifies the field at the OS level and suppresses the offering entirely.RegisterViewalready uses this same pattern atregister-view-password.Fix: in
app/containers/TextInput/FormTextInput.tsx, whenRUNNING_E2E_TESTS === 'true'on iOS, suppress the nativesecureTextEntryprop passed to the underlyingTextInput. Visual masking and the show/hide eye icon stay driven by the original prop. One seam covers every password field that uses the shared input (LoginView, ChangePasswordView, E2EE views, RoomInfoEditView, etc.). Plaintext password rendering under E2E mirrors what RegisterView already does and is acceptable for test environments using throwaway credentials. The YAML dismiss block added in b04b5ab is reverted.App: jump to message over DDP (fixes NATIVE-1126, unblocks
jump-to-message.yaml)sdk.methodCallappended the 2FA code asthis.code || ''— a trailing positional argument on every DDP method call. That empty string was a harmless ignored extra until the server'sloadSurroundingMessagesgrew a typedshowThreadMessages: boolean3rd param (RocketChat/Rocket.Chat#39092, 2026-03-10); the''then fails the server'scheck(showThreadMessages, Boolean), returning a 500 that rejectsloadSurroundingMessagesand aborts the quoted-message "Jump to message" flow (loading spinner shows, never navigates). Fix inapp/lib/services/sdk.ts: append the TOTP code only when a 2FA flow is in progress, so non-2FA DDP calls send exactly their declared params (matching the REST path and the server default); the 2FA retry still appends the resolved code. Verified against mobile.qa (server 8.4): the quoted-message jump now loads surroundings and scrolls tomessage-content-1.App: jump-to-message scroll race timeout (shard 11,
jump-to-message.yaml"search old message and load surroundings" block)RoomView.jumpToMessageraces the list scroll against a fixed 5s wall-clock cap. Reaching an off-screen target grows the renderedtake(count)window 50 rows per round (600ms each, viascrollToEnd → onEndReached) until the target row enters it — so the scroll's duration is O(rows already loaded in the DB), not constant. On the slow CI release emulator that loop exceeds 5s, the race cancels, the target never centers, and the assertion onmessage-content-29times out. Raised the cap 5s→20s inapp/views/RoomView/index.tsx; the Loading overlay already exposes a cancel button, so a longer ceiling is not a UX trap. The proper fix — re-anchoring the rendered window onto the target so the jump is deterministic and O(1) regardless of distance (the approach Slack/Telegram/Matrix use) — is deferred to a follow-up.Deferred (tracked separately)
room-last-message-thread-50-plus.yaml) — Maestro gesture injection exits RoomView mid-scroll; tracked as NATIVE-1128.jump-to-message.yaml) — quoted-message jump (NATIVE-1126) fixed here; composer auto-focus (NATIVE-1125) tracked separately.Issue(s)
NATIVE-1123 · NATIVE-1126
Related (deferred, not fixed here): NATIVE-1125 · NATIVE-1128
How to test or reproduce
Broken pipe,Monkey aborted, andFailed to install apkin shard logs.sidebar-viewstep and complete profile edits + password change in a single submit.helpers/create-account.yamlshould complete with a clean email field (no leadingt/tmobilecharacter corruption).change-password.yamlande2e-encryption.yamlshould complete on iOS 26 — noSave Password?sheet appears because everyFormTextInput-backed password field is declassified underRUNNING_E2E_TESTS=true.message-content-1(NATIVE-1126); any remaining red is the separately-tracked composer auto-focus (NATIVE-1125). TheRoom Actionsflow's Unstar step now scrolls the action sheet to theUnstaritem instead of asserting after a single swipe, so it no longer flakes onAssertion is false: "Unstar" is visible.Boot Simulatorstep is split intoStart Simulator Boot(async, before Maestro install) +Wait for Simulator Ready(blocks onbootstatus -b); per-shard boot+setup wall time should drop ~40–55 s vs the previous serial boot. Shard 3 now also runse2e-encryption.yamland should stay green without becoming the slowest iOS shard.Pre-install Android SDK packagesstep downloads the emulator + api-34 system image once (and retries on a corrupt archive). Each shard'sRestore Android SDK packagesstep should report a cache hit,android-emulator-runner's "Install Android SDK" step should be a no-op (no dl.google.com pull), and no shard should die withError on ZipFile unknown archive.Screenshots
n/a
Types of changes
Checklist
Further comments
The sidebar testID fix was originally opened as #7319 and folded here so CI stabilization and its dependency ship together (#7319 closed). Acceptance is measured over a 10-run window on
developafter this lands; tracked separately.Summary by CodeRabbit
Chores
Tests
Style