Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .github/actions/appleapp-road-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ runs:
echo "::endgroup::"
working-directory: apps/AppleApp
shell: bash
env:
# EXPERIMENT ONLY (never merge)
E2E_SIMULATE_LOST_TAP: ${{ inputs.variant == 'expo57' && 'all' || 'first' }}

- name: Upload Detox recordings on failure
if: failure() && inputs.run-e2e == 'true'
Expand Down
71 changes: 56 additions & 15 deletions apps/brownfield-example-shared-tests/e2e/appleAppDetoxUtils.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,30 +115,71 @@ async function tapSwiftUIControl(matcher) {
});
}

/** Push RNAppScreen from the native shell and wait until `isReady` resolves. */
async function openReactNative(isReady) {
const OPEN_REACT_NATIVE_ATTEMPTS = 3;

/**
* Push RNAppScreen from the native shell and wait until `isReady(timeoutMs)` resolves.
*
* The tap sometimes doesn't push on CI (#498): retry it up to 3 times, then fail fast.
*/
async function openReactNative(isReady, totalTimeoutMs) {
const attemptTimeoutMs = Math.round(
totalTimeoutMs / OPEN_REACT_NATIVE_ATTEMPTS
);
await device.disableSynchronization();
try {
await waitForVisible(OPEN_REACT_NATIVE_BUTTON, DETOX_TIMING.VISIBILITY_TIMEOUT_MS);
await tapSwiftUIControl(OPEN_REACT_NATIVE_BUTTON);
await isReady();
} finally {
await device.enableSynchronization();
await waitForVisible(
OPEN_REACT_NATIVE_BUTTON,
DETOX_TIMING.VISIBILITY_TIMEOUT_MS
);

for (let attempt = 1; ; attempt++) {
// Only re-tap while the shell is still showing.
const shouldTap =
attempt === 1 || (await isVisible(OPEN_REACT_NATIVE_BUTTON));
if (attempt > 1) {
console.warn(
`[AppleApp] React Native screen not visible after attempt ${attempt - 1}/${OPEN_REACT_NATIVE_ATTEMPTS}, ${shouldTap ? 're-tapping' : 'button gone, still waiting'}`
);
}
// EXPERIMENT ONLY (never merge): simulate lost taps.
const simulate = process.env.E2E_SIMULATE_LOST_TAP;
const skipTap = simulate === 'all' || (simulate === 'first' && attempt === 1);
if (shouldTap && skipTap) {
console.warn(`[AppleApp] EXPERIMENT: skipping tap on attempt ${attempt}`);
} else if (shouldTap) {
await tapSwiftUIControl(OPEN_REACT_NATIVE_BUTTON);
}
try {
await isReady(attemptTimeoutMs);
break;
} catch (error) {
if (attempt === OPEN_REACT_NATIVE_ATTEMPTS) {
throw error;
}
}
}
await device.enableSynchronization();
}

async function openReactNativeVanilla() {
await openReactNative(() =>
waitForVisible(by.id(ids.rnAppHome), DETOX_TIMING.VISIBILITY_TIMEOUT_MS)
await openReactNative(
(timeoutMs) => waitForVisible(by.id(ids.rnAppHome), timeoutMs),
DETOX_TIMING.VISIBILITY_TIMEOUT_MS
);
}

async function openReactNativeExpo() {
await openReactNative(() =>
waitForAnyVisible(
[EXPO_HOME_TAB_MATCHERS[0], EXPO_HOME_TAB_MATCHERS[1], EXPO_WELCOME_TITLE],
60_000
)
await openReactNative(
(timeoutMs) =>
waitForAnyVisible(
[
EXPO_HOME_TAB_MATCHERS[0],
EXPO_HOME_TAB_MATCHERS[1],
EXPO_WELCOME_TITLE,
],
timeoutMs
),
60_000
);
}

Expand Down
Loading