Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3466192
chore: pin Maestro 2.5.1, drop Android warmup, widen iOS retry trigger
diegolmello May 7, 2026
5ee7fad
chore: bump Android emulator headroom and add AVD snapshot caching
diegolmello May 7, 2026
c9c5025
fix: restore sidebar-view testID visibility for Maestro
diegolmello May 11, 2026
54a72ea
chore(maestro): batch profile edits into a single submit
diegolmello May 11, 2026
9dd754c
ci(maestro): address CodeRabbit review on #7311
diegolmello May 12, 2026
fecac89
test(maestro): restructure shard 12 thread-50 scroll for reliability
diegolmello May 13, 2026
c14d8d0
fix(maestro): revert shard 12 restructure, just bump scroll timeout
diegolmello May 13, 2026
9ce694c
revert(maestro): drop inert timeout bump on shard 12 thread-50 test
diegolmello May 13, 2026
88bc91e
fix(maestro): scroll focused FormContainer inputs above keyboard
diegolmello May 13, 2026
5b553e6
test(maestro): make discussion back-tap robust on Android shard 10
diegolmello May 14, 2026
cb0be44
fix(maestro): tap room-header by testID in create-team flow
diegolmello May 15, 2026
e4e0f67
ci: upload maestro junit reports and full test artifacts
diegolmello May 18, 2026
3939d39
ci(maestro-ios): run on macos-26 with Xcode 26.2.0
diegolmello May 18, 2026
1fdc848
ci(maestro-ios): pick highest-numbered iPhone Pro from sim catalog
diegolmello May 18, 2026
b04b5ab
fix(maestro): dismiss iOS 26 system Save Password prompt after login
diegolmello May 19, 2026
7823f08
chore: trigger CI re-run for shard 9 fix
diegolmello May 19, 2026
0dee25c
chore: re-trigger CI after Actions hiccup
diegolmello May 19, 2026
a7d1f5d
Merge branch 'develop' into chore/maestro-ci-bundle
diegolmello May 19, 2026
42cc143
ci(maestro): drop nick-fields/retry wrapper, cap reruns at 2
diegolmello May 19, 2026
9eaeca8
fix(maestro): robust iOS 26 Save Password dismiss across change-passw…
diegolmello May 19, 2026
345db57
fix(maestro): suppress iOS Save Password prompt at FormTextInput level
diegolmello May 19, 2026
b3bf6c6
fix(maestro): also suppress textContentType/autoComplete under E2E
diegolmello May 20, 2026
2066fbf
ci(maestro): fix shard 1 Open dialog regex and raise tight timeouts
diegolmello May 20, 2026
63db0eb
ci(maestro): seed Android AVD cache once before the matrix
diegolmello May 20, 2026
cdc3c62
Merge remote-tracking branch 'origin/develop' into chore/maestro-ci-b…
diegolmello May 20, 2026
e43e4e9
ci(e2e-android): single-quote keystore passwords so echo -e leaves th…
diegolmello May 20, 2026
1ef98fd
Merge branch 'develop' into chore/maestro-ci-bundle
diegolmello May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/scripts/run-maestro.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ PLATFORM="${1:-${PLATFORM:-android}}"
SHARD="${2:-${SHARD:-default}}"
FLOWS_DIR=".maestro/tests"
MAIN_REPORT="maestro-report.xml"
MAX_RERUN_ROUNDS="${MAX_RERUN_ROUNDS:-3}"
MAX_RERUN_ROUNDS="${MAX_RERUN_ROUNDS:-2}"
RERUN_REPORT_PREFIX="maestro-rerun"
export MAESTRO_DRIVER_STARTUP_TIMEOUT="${MAESTRO_DRIVER_STARTUP_TIMEOUT:-120000}"

Expand Down Expand Up @@ -72,9 +72,6 @@ printf ' %s\n' "${FLOW_FILES[@]}"
if [ "$PLATFORM" = "android" ]; then
adb shell settings put system show_touches 1 || true
adb install -r "app-release.apk" || true
adb shell monkey -p "$APP_ID" -c android.intent.category.LAUNCHER 1 || true
sleep 6
adb shell am force-stop "$APP_ID" || true

maestro test "${FLOW_FILES[@]}" \
-e APP_ID="$APP_ID" \
Expand Down
57 changes: 56 additions & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,66 @@ jobs:
needs: [e2e-hold]
secrets: inherit

# Pre-populates the AVD snapshot cache before the matrix fans out. Without
# this, all 14 shards check actions/cache at the same instant — nobody has
# written yet, so every shard misses and runs its own snapshot-generation
# step. With this seed job, the shards' cache restore is guaranteed to hit.
e2e-seed-android-avd:
name: E2E Seed Android AVD Cache
if: ${{ github.repository == 'RocketChat/Rocket.Chat.ReactNative' }}
runs-on: ubuntu-latest
needs: [e2e-hold]
env:
ANDROID_AVD_HOME: /home/runner/.android/avd
steps:
- name: Checkout Repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Setup Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
with:
distribution: temurin
java-version: 17

- name: Cache Android AVD
id: avd-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ runner.os }}-api34

- name: Enable KVM group permissions
if: steps.avd-cache.outputs.cache-hit != 'true'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Generate AVD snapshot (cache miss only)
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0
timeout-minutes: 45
with:
api-level: 34
disk-size: 4096M
arch: x86_64
target: google_apis
profile: pixel_7_pro
cores: 4
ram-size: 6144M
force-avd-creation: true
disable-animations: true
emulator-boot-timeout: 900
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
script: echo "AVD snapshot generated for cache"

e2e-run-android:
name: E2E Run Android
if: ${{ github.repository == 'RocketChat/Rocket.Chat.ReactNative' }}
uses: ./.github/workflows/maestro-android.yml
needs: [e2e-build-android]
needs: [e2e-build-android, e2e-seed-android-avd]
secrets: inherit
strategy:
matrix:
Expand Down
60 changes: 55 additions & 5 deletions .github/workflows/maestro-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,42 @@ jobs:
name: 'Android Tests'
runs-on: ubuntu-latest
env:
MAESTRO_VERSION: 2.2.0
# 2.5.0 switched gRPC to dadb direct ADB socket — eliminates flaky TCP forwarding (Broken pipe at install).
# https://github.com/mobile-dev-inc/maestro/releases/tag/cli-2.5.0
MAESTRO_VERSION: 2.5.1
ANDROID_AVD_HOME: /home/runner/.android/avd

steps:
- name: Checkout Repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
with:
tool-cache: false
android: false
dotnet: true
haskell: true
large-packages: true
docker-images: true
# ubuntu-latest has ~6.8 GiB RAM; emulator guest is ram-size: 6144M.
# Keep the ~4 GiB swapfile so host OS, ADB, Gradle, JVM, and the CI
# process have an OOM buffer instead of being squeezed into ~800 MB.
swap-storage: false

- name: Setup Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
with:
distribution: temurin
java-version: 17

# Shard 14 builds a custom keyboard-enabled AVD (Pixel_API_34_Keyboard)
# under the same path. Skip cache here so it can't race-save its AVD
# under the shared default-profile key and starve the other shards of
# the pixel_7_pro snapshot.
- name: Cache Android AVD
if: ${{ inputs.shard != '14' }}
id: avd-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
Expand Down Expand Up @@ -134,13 +156,38 @@ jobs:
target: google_apis
avd-name: Pixel_API_34_Keyboard
cores: 4
ram-size: 4096M
ram-size: 6144M
force-avd-creation: false
disable-animations: true
emulator-boot-timeout: 900
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
script: ./.github/scripts/run-maestro.sh android ${{ inputs.shard }}

# Cache miss → boot the default AVD once with no test payload so reactivecircus saves a snapshot to ~/.android/avd
# which the AVD cache step then persists. Subsequent runs hit cache and the test step below replays the snapshot
# instead of cold-booting (cuts ~60–90s and avoids the system_server hiccup window).
- name: Generate AVD snapshot for cache (default profile, cache miss only)
if: ${{ inputs.shard != '14' && steps.avd-cache.outputs.cache-hit != 'true' }}
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0
# Cold-boot + AVD recreate after the RAM 4G→6G bump can push past the
# tight 30-min cap (emulator-boot-timeout is 15 min and the runner may
# retry the boot). Give it a real margin so a one-time cache repopulate
# succeeds and lets every subsequent run hit the cache.
timeout-minutes: 45
with:
api-level: 34
disk-size: 4096M
arch: x86_64
target: google_apis
profile: pixel_7_pro
cores: 4
ram-size: 6144M
force-avd-creation: true
disable-animations: true
emulator-boot-timeout: 900
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
script: echo "AVD snapshot generated for cache"

- name: Start Android Emulator and Run Maestro Tests (default)
if: ${{ inputs.shard != '14' }}
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0
Expand All @@ -152,17 +199,20 @@ jobs:
target: google_apis
profile: pixel_7_pro
cores: 4
ram-size: 4096M
ram-size: 6144M
force-avd-creation: false
disable-animations: true
emulator-boot-timeout: 900
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
script: ./.github/scripts/run-maestro.sh android ${{ inputs.shard }}

- name: Android Maestro Logs
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: Android Maestro Logs - Shard ${{ inputs.shard }}
path: ~/.maestro/tests/**/*.png
path: |
~/.maestro/tests/**/*
maestro-report.xml
maestro-rerun-round-*.xml
retention-days: 7
45 changes: 32 additions & 13 deletions .github/workflows/maestro-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ on:

jobs:
ios-test:
runs-on: macos-14
runs-on: macos-26
env:
MAESTRO_VERSION: 2.2.0
# Pinned alongside the Android workflow so both platforms run the same
# Maestro CLI. See cli-2.5.1 release notes for driver improvements.
# https://github.com/mobile-dev-inc/maestro/releases/tag/cli-2.5.1
MAESTRO_VERSION: 2.5.1

steps:
- name: Checkout Repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Xcode
# Match e2e-build-ios.yml so the artifact built against the iOS 26 SDK
# is exercised on an iOS 26 simulator runtime (avoids SDK/runtime drift).
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
with:
xcode-version: '26.2.0'

- name: Setup Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
with:
Expand Down Expand Up @@ -61,13 +71,20 @@ jobs:
- name: Boot Simulator
timeout-minutes: 15
run: |
if [ "${{ inputs.shard }}" = "14" ]; then
SIM_NAME="iPhone SE (3rd generation)"
else
SIM_NAME="iPhone 16 Pro"
# macos-26 catalogs different iPhone names per Xcode release; pick the
# highest-numbered "iPhone N Pro" present in the installed runtimes
# so we are not pinned to a name that disappears on image rotation.
SIM_NAME=$(xcrun simctl list devices available --json \
| jq -r '[.devices | to_entries[] | .value[] | select(.name | test("^iPhone [0-9]+ Pro$"))] | sort_by(.name | capture("(?<n>[0-9]+)").n | tonumber) | last | .name')

if [[ -z "$SIM_NAME" || "$SIM_NAME" == "null" ]]; then
echo "::error::No iPhone N Pro simulator available"
xcrun simctl list devices available
exit 1
fi

echo "Booting simulator: $SIM_NAME"
echo "SIM_NAME=$SIM_NAME" >> "$GITHUB_ENV"

xcrun simctl boot "$SIM_NAME" || true
xcrun simctl bootstatus "$SIM_NAME" -b
Expand Down Expand Up @@ -97,17 +114,19 @@ jobs:
run: chmod +x .github/scripts/run-maestro.sh

- name: Run Maestro Tests
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3.0.2
with:
timeout_minutes: 30
max_attempts: 2
retry_on: timeout
command: ./.github/scripts/run-maestro.sh ios ${{ inputs.shard }}
# Slowest passing shard is ~27 min; with up to 2 internal rerun rounds
# we need headroom above that. nick-fields/retry previously gave 60 min
# (2 × 30); 45 min is a closer single-step equivalent without overshoot.
timeout-minutes: 45
run: ./.github/scripts/run-maestro.sh ios ${{ inputs.shard }}

- name: Upload Maestro Logs
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: iOS Maestro Logs - Shard ${{ inputs.shard }}
path: ~/.maestro/tests/**/*.png
path: |
~/.maestro/tests/**/*
maestro-report.xml
maestro-rerun-round-*.xml
retention-days: 7
11 changes: 5 additions & 6 deletions .maestro/helpers/open-deeplink.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ tags:
visible: '.*Open in.*'
platform: iOS
commands:
# Anchored regex: the dialog title "Open in 'Rocket.Chat'?" also matches
# `Open` as a substring, so an unanchored selector picks the title
# (cold-launch on iOS 26) instead of the button and leaves the dialog
# up. ^Open$ targets only the button label.
- tapOn:
text: Open
index: 0
optional: true
- tapOn:
text: Open
index: 1
text: '^Open$'
optional: true
- runFlow:
when:
Expand Down
22 changes: 3 additions & 19 deletions .maestro/tests/assorted/profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ tags:
id: 'profile-view-submit'
direction: UP

# should change name and username
# edit name, username, nickname, bio, and email then submit once.
# users.updateOwnBasicInfo is rate-limited per-user, so we batch all changes
# into a single request instead of three separate submits.
- assertVisible:
id: 'profile-view-name'
- runFlow:
Expand All @@ -98,15 +100,6 @@ tags:
- inputText: ${output.user.username + 'username'}
- runFlow:
file: '../../helpers/hide-keyboard.yaml'
- scrollUntilVisible:
element:
id: 'profile-view-submit'
timeout: 60000
centerElement: true
- tapOn:
id: 'profile-view-submit'

# should change nickname and bio
- assertVisible:
id: 'profile-view-nickname'
- tapOn:
Expand All @@ -122,15 +115,6 @@ tags:
- tapOn:
text: '.*Bio.*'
index: 0
- scrollUntilVisible:
element:
id: 'profile-view-submit'
timeout: 60000
centerElement: true
- tapOn:
id: 'profile-view-submit'

# should change email
- scrollUntilVisible:
element:
id: 'profile-view-email'
Expand Down
9 changes: 8 additions & 1 deletion .maestro/tests/room/discussion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ tags:
visible:
id: 'room-view-title-${output.discussionFromNewMessage}'
timeout: 60000
- tapOn: 'Back'
- runFlow: '../../helpers/hide-keyboard.yaml'
- runFlow:
when:
visible:
id: 'room-view'
commands:
- tapOn:
id: 'header-back'
- extendedWaitUntil:
visible:
id: 'rooms-list-view-item-${output.discussionFromNewMessage}'
Expand Down
3 changes: 2 additions & 1 deletion .maestro/tests/teams/create-team.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ tags:
id: 'room-view-messages'
- assertVisible:
id: 'room-view-title-${output.teamname}'
- tapOn: 'private team ${output.teamname} .'
- tapOn:
id: 'room-header'
- assertVisible:
id: 'room-actions-info'
- tapOn:
Expand Down
Loading
Loading