Compare commits

...

1 Commits

Author SHA1 Message Date
Ivan Vershigora
3ef9c56328
tst: fix flaky goBack in iOS e2e tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:34:37 +01:00
3 changed files with 46 additions and 18 deletions

View File

@ -194,9 +194,6 @@ jobs:
mkdir -p ios/build/Build/Products/Release-iphonesimulator
tar -xzf BlueWallet.app.tar.gz -C ios/build/Build/Products/Release-iphonesimulator
- name: Disable simulator animations
run: defaults write com.apple.iphonesimulator SlowMotionAnimation -bool NO
# Pre-boot simulator so first detox launchApp lands warm.
- name: Pre-boot iOS simulator
run: |
@ -210,6 +207,13 @@ jobs:
xcrun simctl bootstatus "$UDID" -b
xcrun simctl launch "$UDID" com.apple.springboard >/dev/null 2>&1 || true
# Cut animations so detox sync stays steady on slow CI VMs; Reduce Motion makes reanimated skip to final value.
- name: Disable simulator animations
run: |
defaults write com.apple.iphonesimulator SlowMotionAnimation -bool NO
xcrun simctl spawn booted defaults write com.apple.Accessibility ReduceMotionEnabled -bool true
xcrun simctl spawn booted notifyutil -p com.apple.Accessibility.ReduceMotionStatusDidChange
- name: Run detox tests
timeout-minutes: 360
run: |

View File

@ -1,5 +1,5 @@
PODS:
- BugsnagReactNative (8.8.1):
- BugsnagReactNative (8.9.0):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@ -29,7 +29,7 @@ PODS:
- hermes-engine/Pre-built (= 250829098.0.10)
- hermes-engine/Pre-built (250829098.0.10)
- lottie-ios (4.6.0)
- lottie-react-native (7.3.7):
- lottie-react-native (7.3.8):
- hermes-engine
- lottie-ios (= 4.6.0)
- RCTRequired
@ -2018,6 +2018,8 @@ PODS:
- ReactNativeDependencies (0.85.3)
- RealmJS (20.2.0):
- React
- RNBackgroundFetch (4.2.9):
- React-Core
- RNCAsyncStorage (2.2.0):
- hermes-engine
- RCTRequired
@ -2543,6 +2545,7 @@ DEPENDENCIES:
- ReactNativeCameraKit (from `../node_modules/react-native-camera-kit-no-google`)
- ReactNativeDependencies (from `../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec`)
- RealmJS (from `../node_modules/realm`)
- RNBackgroundFetch (from `../node_modules/react-native-background-fetch`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- RNDefaultPreference (from `../node_modules/react-native-default-preference`)
@ -2762,6 +2765,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/ReactNativeDependencies.podspec"
RealmJS:
:path: "../node_modules/realm"
RNBackgroundFetch:
:path: "../node_modules/react-native-background-fetch"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNCClipboard:
@ -2802,13 +2807,13 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
BugsnagReactNative: bee770e3f497a8571feb1579bdc083a070bee1f3
BugsnagReactNative: 73ce58aac04585e7cba3081c0abba06d848d62fc
BVLinearGradient: cb006ba232a1f3e4f341bb62c42d1098c284da70
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
FBLazyVector: 24e62c765683b8d89006a88a2c8f5cf019f0074d
hermes-engine: 86cdbf283775c54dc008895c3eacd24a1f2a40b4
hermes-engine: 4ed74710a31e8e31f20356c641eab1d8f7d54595
lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3
lottie-react-native: 26b365c3d5615e87f4db048dcb151de3eb9a8e76
lottie-react-native: ee142214581f3bb68fbda7efcf07b835a189eeda
RCTDeprecation: a4c521821fab57cbb125b36effe84d897d0dfa12
RCTRequired: 9f3a7e5645d4bc3f551593de7550bb66ab6e42bc
RCTSwiftUI: 239ed2eb9e73de5a6f518810630f0c95e01c8702
@ -2817,7 +2822,7 @@ SPEC CHECKSUMS:
React: e2dc35338068bbd299c66f043ae0d7f25de8499e
React-callinvoker: 28b25d21b124c26cebaea713ba7d801b9351dc48
React-Core: 02ed7d2ffb70437bdf2aba074a13078a7b0b9ff0
React-Core-prebuilt: 9e875134f667c471ab68bf9edf1661fa11b86540
React-Core-prebuilt: 3445f1028d9b206cd45c8bbb7e2427ee891f810e
React-CoreModules: b3a5a42dadcde3b5d47b325bd912eb2ced89e146
React-cxxreact: fe8f88dda044e5905e99a00f41b7a874c3908716
React-debug: 92944dc4d89f56d640e75498266cbde557a48189
@ -2898,8 +2903,9 @@ SPEC CHECKSUMS:
ReactCodegen: 1bd7f2174582b0e142f8671735b5c906c08b72ea
ReactCommon: 7dfc3250793bf36cf221096ff59e1179e13eef7f
ReactNativeCameraKit: 5974256fc608631c1c812710cd98abe95dae0f88
ReactNativeDependencies: 0a5c93845772e4b1c5ad065c59a859518b13a6b7
ReactNativeDependencies: 75299c281f422106c723e79dc1f6ce7ef03241be
RealmJS: 1c37c6bdfe060f4caa0f9175aa0eedb962622ee1
RNBackgroundFetch: 64b1215fbb8ec58afba877ca0ce177e009ce12b7
RNCAsyncStorage: 2ad919e88b8bc2cd80e8697ce66d04d006743283
RNCClipboard: 715fa7c6c8366f17d00f05a439ee7488f390fa5f
RNDefaultPreference: 8a089ee8ce829a66c5453e3c5434f0785499d1c3

View File

@ -173,7 +173,7 @@ export async function helperDeleteWallet(label, remainingBalanceSat = false) {
await waitForId('WalletDetails');
await element(by.id('WalletDetails')).tap();
await element(by.id('WalletDetailsScroll')).swipe('up', 'fast', 1);
await sleep(200);
await sleep(1000);
await element(by.id('DeleteWallet')).tap();
await waitForText('Yes, delete');
await element(by.text('Yes, delete')).tap();
@ -368,19 +368,37 @@ export async function goBack() {
// Try each back/close affordance in order; retry the full set up to 10 times.
const candidates = [by.id('BackButton'), by.id('NavigationCloseButton'), by.label('Back'), by.text('Close')];
// A matcher can hit several elements across stacked screens: each nav back
// button exists twice (_UIButtonBarButton wrapper + UIAccessibilityBackButtonElement),
// and when a modal covers a stack that also has a back button, the covered
// one can precede the visible one in match order (seen with Reduce Motion on).
// Probe attributes and only tap an element detox reports as visible & hittable.
let lastErr;
for (let attempt = 0; attempt < 10; attempt++) {
for (const matcher of candidates) {
try {
await element(matcher).atIndex(0).tap();
return;
} catch (_) {
/* try next */
for (let idx = 0; idx < 6; idx++) {
let attrs;
try {
attrs = await element(matcher).atIndex(idx).getAttributes();
} catch (err) {
lastErr = err;
break; // no element at this index — try next candidate
}
if (!attrs.visible || attrs.hittable === false) continue;
try {
await element(matcher).atIndex(idx).tap();
return;
} catch (err) {
lastErr = err;
}
}
}
await sleep(500);
}
rethrowWithCallsite(new Error('goBack: no back/close affordance tappable after 10 attempts.'), callsite);
const wrapped = new Error('goBack: no back/close affordance tappable after 10 attempts.');
if (lastErr) wrapped.cause = lastErr;
rethrowWithCallsite(wrapped, callsite);
}
export async function typeTextIntoAlertInput(text) {
@ -405,7 +423,7 @@ export async function scrollUpOnHomeScreen() {
// if no wallets there will be just one scroll
await element(by.type('RCTEnhancedScrollView')).swipe('down', 'slow', 0.5);
}
await sleep(200); // bounce animation
await sleep(1000); // bounce animation
}
// We really only need this function when running tests locally.