Skip to content

Commit 5ee2b37

Browse files
Merge branch 'develop' into fix/ModalSectionSelects_crashing_app
2 parents 034c6ee + c925a27 commit 5ee2b37

File tree

87 files changed

+114031
-96018
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+114031
-96018
lines changed

.github/workflows/maestro-android.yml

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
android-test:
1212
name: 'Android Tests'
1313
runs-on: ubuntu-latest
14+
env:
15+
MAESTRO_VERSION: 2.2.0
1416

1517
steps:
1618
- name: Checkout Repository
@@ -19,8 +21,22 @@ jobs:
1921
- name: Setup Java
2022
uses: actions/setup-java@v4
2123
with:
22-
distribution: 'temurin'
23-
java-version: '17'
24+
distribution: temurin
25+
java-version: 17
26+
27+
- name: Cache Android AVD
28+
uses: actions/cache@v4
29+
with:
30+
path: |
31+
~/.android/avd/*
32+
~/.android/adb*
33+
key: avd-${{ runner.os }}-api34
34+
35+
- name: Cache Maestro
36+
uses: actions/cache@v4
37+
with:
38+
path: ~/.maestro
39+
key: maestro-${{ runner.os }}-${{ env.MAESTRO_VERSION }}
2440

2541
- name: Download APK
2642
uses: actions/download-artifact@v4
@@ -34,21 +50,26 @@ jobs:
3450

3551
- name: Install Maestro
3652
run: |
37-
curl -fsSL "https://get.maestro.mobile.dev" | bash
38-
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
39-
53+
curl -Ls "https://get.maestro.mobile.dev" | bash
54+
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
55+
4056
- name: Enable KVM group permissions
4157
run: |
4258
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
4359
sudo udevadm control --reload-rules
4460
sudo udevadm trigger --name-match=kvm
4561
62+
- name: Disable unnecessary services (improves emulator stability)
63+
run: |
64+
sudo systemctl stop docker || true
65+
sudo systemctl stop snapd || true
66+
4667
- name: Make Maestro script executable
4768
run: chmod +x .github/scripts/run-maestro.sh
4869

4970
- name: Start Android Emulator and Run Maestro Tests
5071
uses: reactivecircus/android-emulator-runner@v2
51-
timeout-minutes: 120
72+
timeout-minutes: 60
5273
with:
5374
api-level: 34
5475
disk-size: 4096M
@@ -58,8 +79,9 @@ jobs:
5879
cores: 4
5980
ram-size: 4096M
6081
force-avd-creation: false
61-
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
6282
disable-animations: true
83+
emulator-boot-timeout: 900
84+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -accel on
6385
script: ./.github/scripts/run-maestro.sh android ${{ inputs.shard }}
6486

6587
- name: Android Maestro Logs

.github/workflows/maestro-ios.yml

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ on:
1010
jobs:
1111
ios-test:
1212
runs-on: macos-14
13+
env:
14+
MAESTRO_VERSION: 2.2.0
1315

1416
steps:
1517
- name: Checkout Repo
@@ -18,8 +20,8 @@ jobs:
1820
- name: Setup Java
1921
uses: actions/setup-java@v4
2022
with:
21-
distribution: 'temurin'
22-
java-version: '17'
23+
distribution: temurin
24+
java-version: "17"
2325

2426
- name: Download App
2527
uses: actions/download-artifact@v4
@@ -32,70 +34,73 @@ jobs:
3234
with:
3335
E2E_ACCOUNT: ${{ secrets.E2E_ACCOUNT }}
3436

35-
- name: Install Maestro
37+
- name: Cache Maestro
38+
uses: actions/cache@v4
39+
with:
40+
path: ~/.maestro
41+
key: maestro-${{ runner.os }}-${{ env.MAESTRO_VERSION }}
42+
43+
- name: Install Maestro + idb
3644
run: |
3745
brew tap facebook/fb
3846
brew install facebook/fb/idb-companion
3947
curl -fsSL "https://get.maestro.mobile.dev" | bash
40-
echo "$HOME/.maestro/bin" >> $GITHUB_PATH
48+
echo "$HOME/.maestro/bin" >> "$GITHUB_PATH"
4149
4250
- name: Configure Simulator
4351
run: |
4452
defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool false
45-
defaults write com.apple.iphonesimulator ShowSingleTouches 1
4653
defaults write com.apple.iphonesimulator SlowAnimations -bool false
4754
defaults write com.apple.iphonesimulator ShowDeviceBezels -bool false
4855
defaults write com.apple.iphonesimulator DisableShadows -bool true
56+
defaults write com.apple.iphonesimulator AllowFullscreenMode -bool false
57+
defaults write com.apple.iphonesimulator PasteboardAutomaticSync -bool false
58+
defaults write com.apple.iphonesimulator ShowChrome -bool false
59+
defaults write com.apple.iphonesimulator DeviceFramebufferOnly -bool true
4960
5061
- name: Boot Simulator
51-
timeout-minutes: 30
62+
timeout-minutes: 15
5263
run: |
53-
SIM_NAME="iPhone 16 Pro Max"
54-
TIMEOUT=300 # 5 minutes in seconds
55-
INTERVAL=5 # Check every 5 seconds
56-
64+
SIM_NAME="iPhone 16 Pro"
65+
66+
echo "Booting simulator: $SIM_NAME"
67+
5768
xcrun simctl boot "$SIM_NAME" || true
58-
59-
ELAPSED=0
60-
while true; do
61-
BOOTED=$(xcrun simctl list devices | grep "$SIM_NAME (" | grep "(Booted)")
62-
if [ -n "$BOOTED" ]; then
63-
echo "$SIM_NAME is booted"
64-
break
65-
fi
66-
67-
if [ $ELAPSED -ge $TIMEOUT ]; then
68-
echo "Simulator did not boot within 5 minutes. Retrying..."
69-
xcrun simctl shutdown "$SIM_NAME"
70-
xcrun simctl boot "$SIM_NAME"
71-
ELAPSED=0
72-
fi
73-
74-
sleep $INTERVAL
75-
ELAPSED=$((ELAPSED + INTERVAL))
76-
done
77-
69+
xcrun simctl bootstatus "$SIM_NAME" -b
70+
71+
echo "Disabling animations"
72+
xcrun simctl spawn booted defaults write -g UIAnimationDragCoefficient -float 10
73+
74+
echo "Warming SpringBoard"
75+
xcrun simctl launch booted com.apple.springboard
76+
sleep 15
77+
78+
echo "Booted devices:"
7879
xcrun simctl list devices | grep Booted
7980
80-
- name: Get Simulator UDID
81+
- name: Get Booted Simulator UDID
8182
id: booted-sim
8283
run: |
83-
UDID=$(xcrun simctl list devices | grep "iPhone 16 Pro Max (" | grep "(Booted)" | grep -oE '[A-F0-9-]{36}' | head -n1)
84+
UDID=$(xcrun simctl list devices booted | grep -oE '[A-F0-9-]{36}' | head -n1)
8485
echo "UDID=$UDID"
8586
echo "UDID=$UDID" >> $GITHUB_ENV
8687
87-
- name: Make Maestro script executable
88-
run: chmod +x .github/scripts/run-maestro.sh
89-
9088
- name: Install App
9189
run: |
92-
xcrun simctl install $UDID "ios-simulator-app"
90+
xcrun simctl install booted ios-simulator-app
9391
94-
- name: Run Tests
95-
timeout-minutes: 120
96-
run: ./.github/scripts/run-maestro.sh ios ${{ inputs.shard }}
92+
- name: Make Maestro Script Executable
93+
run: chmod +x .github/scripts/run-maestro.sh
94+
95+
- name: Run Maestro Tests
96+
uses: nick-fields/retry@v3
97+
with:
98+
timeout_minutes: 30
99+
max_attempts: 2
100+
retry_on: timeout
101+
command: ./.github/scripts/run-maestro.sh ios ${{ inputs.shard }}
97102

98-
- name: iOS Maestro Logs
103+
- name: Upload Maestro Logs
99104
if: always()
100105
uses: actions/upload-artifact@v4
101106
with:

.maestro/helpers/create-account.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ tags:
2424
- tapOn:
2525
id: register-view-confirm-password
2626
- inputText: ${output.user.password}
27-
- hideKeyboard
27+
- runFlow: './hide-keyboard.yaml'
2828
- scrollUntilVisible:
2929
element:
3030
id: register-view-submit

.maestro/helpers/search-room.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ tags:
55
---
66
- assertVisible:
77
id: 'rooms-list-view'
8+
- waitForAnimationToEnd:
9+
timeout: 5000
810
- tapOn:
911
id: 'rooms-list-view-search'
1012
- tapOn:

.maestro/helpers/send-message.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ tags:
2828
id: message-composer-send
2929
- extendedWaitUntil:
3030
visible:
31-
text: '.*${message}.*'
31+
id: 'message-content-${message}'
3232
timeout: 60000

.maestro/scripts/data-setup.js

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const getDeepLink = (method, server, ...params) => {
2222

2323

2424
const login = (username, password) => {
25-
const response = http.post(`${data.server}/api/v1/login`, {
25+
const response = postWithRetry(`${data.server}/api/v1/login`, {
2626
headers: {
2727
'Content-Type': 'application/json'
2828
},
@@ -44,7 +44,7 @@ const createUser = (customProps) => {
4444

4545
login(output.account.adminUser, output.account.adminPassword);
4646

47-
http.post(`${data.server}/api/v1/users.create`, {
47+
postWithRetry(`${data.server}/api/v1/users.create`, {
4848
headers: {
4949
'Content-Type': 'application/json',
5050
...headers
@@ -74,19 +74,20 @@ const deleteCreatedUser = async ({ username: usernameToDelete }) => {
7474
try {
7575
login(output.account.adminUser, output.account.adminPassword);
7676

77-
const result = http.get(`${data.server}/api/v1/users.info?username=${usernameToDelete}`, {
77+
const result = getWithRetry(`${data.server}/api/v1/users.info?username=${usernameToDelete}`, {
7878
headers: {
7979
'Content-Type': 'application/json',
8080
...headers
8181
}
8282
});
8383

8484
const userId = json(result.body)?.data?.user?._id;
85-
http.post(`${data.server}/api/v1/users.delete`, { userId, confirmRelinquish: true }, {
85+
postWithRetry(`${data.server}/api/v1/users.delete`, {
8686
headers: {
8787
'Content-Type': 'application/json',
8888
...headers
89-
}
89+
},
90+
body: JSON.stringify({ userId, confirmRelinquish: true })
9091
});
9192
} catch (error) {
9293
console.log(JSON.stringify(error));
@@ -98,7 +99,7 @@ const createRandomTeam = (username, password) => {
9899

99100
const teamName = output.randomTeamName();
100101

101-
http.post(`${data.server}/api/v1/teams.create`, {
102+
postWithRetry(`${data.server}/api/v1/teams.create`, {
102103
headers: {
103104
'Content-Type': 'application/json',
104105
...headers
@@ -113,7 +114,7 @@ const createRandomRoom = (username, password, type = 'c') => {
113114
login(username, password);
114115
const room = `room${output.random()}`;
115116

116-
const response = http.post(`${data.server}/api/v1/${type === 'c' ? 'channels.create' : 'groups.create'}`, {
117+
const response = postWithRetry(`${data.server}/api/v1/${type === 'c' ? 'channels.create' : 'groups.create'}`, {
117118
headers: {
118119
'Content-Type': 'application/json',
119120
...headers
@@ -133,7 +134,7 @@ const sendMessage = (username, password, channel, msg, tmid) => {
133134
login(username, password);
134135
const channelParam = tmid ? { roomId: channel } : { channel };
135136

136-
const response = http.post(`${data.server}/api/v1/chat.postMessage`, {
137+
const response = postWithRetry(`${data.server}/api/v1/chat.postMessage`, {
137138
headers: {
138139
'Content-Type': 'application/json',
139140
...headers
@@ -153,7 +154,7 @@ const sendMessage = (username, password, channel, msg, tmid) => {
153154
const getProfileInfo = (userId) => {
154155
login(output.account.adminUser, output.account.adminPassword);
155156

156-
const result = http.get(`${data.server}/api/v1/users.info?userId=${userId}`, {
157+
const result = getWithRetry(`${data.server}/api/v1/users.info?userId=${userId}`, {
157158
headers: {
158159
'Content-Type': 'application/json',
159160
...headers
@@ -168,7 +169,7 @@ const getProfileInfo = (userId) => {
168169
const post = (endpoint, username, password, body) => {
169170
login(username, password);
170171

171-
const response = http.post(`${data.server}/api/v1/${endpoint}`, {
172+
const response = postWithRetry(`${data.server}/api/v1/${endpoint}`, {
172173
headers: {
173174
'Content-Type': 'application/json',
174175
...headers
@@ -182,7 +183,7 @@ const post = (endpoint, username, password, body) => {
182183
const createDM = (username, password, otherUsername) => {
183184
login(username, password);
184185

185-
const result = http.post(`${data.server}/api/v1/im.create`, {
186+
const result = postWithRetry(`${data.server}/api/v1/im.create`, {
186187
headers: {
187188
'Content-Type': 'application/json',
188189
...headers
@@ -208,6 +209,48 @@ function logAccounts() {
208209
console.log(JSON.stringify(data.accounts));
209210
}
210211

212+
const sleep = (ms) => {
213+
const start = Date.now();
214+
while (Date.now() - start < ms) { }
215+
}
216+
217+
const retryRequest = (fn, {
218+
retries = 3,
219+
delay = 1000,
220+
factor = 2
221+
} = {}) => {
222+
let lastError;
223+
for (let attempt = 1; attempt <= retries; attempt++) {
224+
try {
225+
const response = fn();
226+
227+
if (response && response.status >= 200 && response.status < 300) {
228+
return response;
229+
}
230+
231+
if (response && response.status >= 400 && response.status < 500) {
232+
throw new Error(`Non-retryable error ${response.status}`);
233+
}
234+
235+
lastError = new Error(`HTTP ${response ? response.status : 'unknown'}`);
236+
} catch (err) {
237+
lastError = err;
238+
}
239+
240+
if (attempt < retries) {
241+
const wait = delay * Math.pow(factor, attempt - 1);
242+
console.log(`Retry ${attempt}/${retries} after ${wait}ms`);
243+
sleep(wait);
244+
}
245+
}
246+
247+
throw lastError;
248+
};
249+
250+
const postWithRetry = (url, options) => retryRequest(() => http.post(url, options));
251+
252+
const getWithRetry = (url, options) => retryRequest(() => http.get(url, options));
253+
211254
output.utils = {
212255
createUser,
213256
createUserWithPasswordChange,

.maestro/tests/assorted/broadcast.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ tags:
6565
- tapOn:
6666
id: 'create-channel-name'
6767
- inputText: ${output.room}
68-
- hideKeyboard
68+
- runFlow: '../../helpers/hide-keyboard.yaml'
6969
- assertVisible:
7070
id: 'create-channel-broadcast'
7171
- tapOn:

.maestro/tests/assorted/change-avatar.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ tags:
173173
timeout: 60000
174174
- tapOn:
175175
text: 'Fetch image from URL'
176-
- hideKeyboard
176+
- runFlow: '../../helpers/hide-keyboard.yaml'
177177
- extendedWaitUntil:
178178
visible:
179179
id: 'change-avatar-view-submit'

0 commit comments

Comments
 (0)