chore(test): group e2e utils by concern and deduplicate spec helpers - #4441
chore(test): group e2e utils by concern and deduplicate spec helpers#4441LKuchno wants to merge 20 commits into
Conversation
…ns into @lkuchno/e2e-utils-restructure
…structure # Conflicts: # FabricExample/e2e/e2e-utils.ts # FabricExample/e2e/single-feature-tests/stack-v5/test-stack-simple-nav.e2e.ts # FabricExample/e2e/single-feature-tests/tabs/test-tabs-lifecycle-events.e2e.ts # FabricExample/e2e/single-feature-tests/tabs/test-tabs-stale-update-rejection.e2e.ts
…ns into @lkuchno/e2e-utils-restructure
…mansion/react-native-screens into @lkuchno/e2e-utils-restructure
…ns into @lkuchno/e2e-utils-restructure
There was a problem hiding this comment.
Pull request overview
Refactors the FabricExample Detox E2E test infrastructure by reorganizing shared utilities, replacing ad-hoc matcher objects with Detox NativeMatchers, and deduplicating common spec helpers (picker driving, attribute/frame reads, stacked-screen interactions, and drilldown navigation). This is primarily test-infra work, with one small fix in an example “issue test” screen.
Changes:
- Reworked
FabricExample/e2e/e2e-utils.tsinto concern-based sections and introduced matcher-first helpers (getMatches/getSingleMatch/getFrame,selectPickerOption, stacked-screen helpers, and drilldown navigation). - Updated multiple E2E specs to use the shared helpers and remove duplicated local implementations.
- Added shared Android toolbar overflow-menu helpers and updated the back-button helper to use the new matcher-based attribute helpers.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| FabricExample/e2e/e2e-utils.ts | Major re-org and new shared matcher-based helpers (attributes/frames, scrolling, picker driving, stacked-screen utilities, drilldown navigation). |
| FabricExample/e2e/elements/back-button.ts | Refactored to use isIOSVersionAtLeast/getMatches instead of local iOS version parsing + manual attribute unwrapping. |
| FabricExample/e2e/elements/toolbar-menu-android.ts | New shared helpers/matchers for Android toolbar overflow menu open/close + popup matcher. |
| FabricExample/e2e/issue-tests/Test758.e2e.ts | Switched to shared isIOSVersionAtLeast helper for iOS-version gating. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-tab-bar-layout-direction.e2e.ts | Uses shared scrolling + picker helper; uses getSingleMatch for frame-based layout assertions. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-tab-bar-controller-mode-ios.e2e.ts | Migrated picker interaction to selectPickerOption. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-system-item-ios.e2e.ts | Uses shared iOS version helper + shared match/attribute reads. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-stale-update-rejection.e2e.ts | Import cleanup; keeps toast/navigation assertions while relying on shared utils. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-override-scroll-view-content-inset-ios.e2e.ts | Replaced local attribute/frame helpers with shared getSingleMatch/getFrame/getMatches. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-lifecycle-events.e2e.ts | Import reordering only (shared utils import). |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-item-badge.e2e.ts | Uses shared iOS version gating helper. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-ime-insets-android.e2e.ts | Replaced ad-hoc getAttributes() frame reads with shared getFrame. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-general-appearance-android.e2e.ts | Migrated picker interaction to selectPickerOption. |
| FabricExample/e2e/single-feature-tests/tabs/test-tabs-bottom-accessory-layout-ios.e2e.ts | Migrated attribute reads to getMatches/getSingleMatch. |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-toolbar-menu-commands-android.e2e.ts | Migrated picker interactions + scrolling to shared helpers. |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-simple-nav.e2e.ts | Uses shared stacked-screen helpers (readTopmostText, tapTopmostButton) and shared waitForRoute on iOS; renames Android poller to avoid name collision. |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-lifecycle-events.e2e.ts | Uses shared tapTopmostButton and shared waitForRoute (removed local copies). |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-header-selective-updates-ios.e2e.ts | Migrated picker interaction to selectPickerOption. |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-header-menu-options-ios.e2e.ts | Migrated attribute/frame reads to getFrame/getSingleMatch. |
| FabricExample/e2e/single-feature-tests/stack-v5/test-stack-back-button-android.e2e.ts | Reuses shared picker option-id derivation and getMatches; adds tapTopmostButton where appropriate. |
| apps/src/tests/issue-tests/Test4361.tsx | Fixes JSX mapping to actually return list items (and formats indentation). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Detox exposes `waitFor` as a global at runtime (behavior.init.exposeGlobals defaults to true) and declares it in `detox/globals.d.ts`, which is pulled in by the existing `from 'detox'` import. The explicit imports were unnecessary. Reverts 299f9a4 and a5fbfd6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ns into @lkuchno/e2e-utils-restructure
…ns into @lkuchno/e2e-utils-restructure
…ns into @lkuchno/e2e-utils-restructure
…ns into @lkuchno/e2e-utils-restructure
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (1)
FabricExample/e2e/e2e-utils.ts:424
waitForRoutecurrently useswaitFor(...).toBeVisible(), which throws on Android when theby.text('Name: ...')matcher resolves to multiple stacked-screen instances (covered screens stay attached). Since this util is now exported and used from Android specs, it should either be Android-safe or explicitly iOS-only; reusingexpectTopmostVisiblehere makes it robust and keeps iOS on Detox-synced waits.
export async function waitForRoute(
routeName: string,
timeout = 3000,
): Promise<void> {
await waitFor(element(by.text(`Name: ${routeName}`)))
.toBeVisible()
.withTimeout(timeout);
}
…ns into @lkuchno/e2e-utils-restructure
Description
Test-infrastructure only PR — no library code is touched.
FabricExample/e2e/e2e-utils.tshad grown into a flat pile of helpers with nogrouping, while individual specs kept re-implementing the same primitives
locally: reading element attributes, waiting for a route, tapping the topmost
stacked screen, driving a
SettingsPicker. The same navigation drill-down wasalso written out twice (component-integration vs single-feature sections),
differing only in an id prefix.
This PR groups the shared utils by concern, replaces the ad-hoc
ElementMatcherobject with plain DetoxNativeMatchers, and pulls thehelpers that specs had been duplicating up into the shared module.
Changes
e2e-utils.ts— grouped into sections (platform & suite gating, elementattributes, interactions, settings controls, stacked screens, test screen
navigation) with the rationale for each non-obvious helper documented inline.
getElementAttributes({ by, value, index })andits
resolveMatcherswitch are gone; helpers now take a DetoxNativeMatcherdirectly. New/renamed:getSingleMatch(throws on anambiguous matcher instead of silently taking the first hit),
getFrame,and the exported
ElementAttributestype.countMatchesandgetTopmostMatchwere folded into their only call sites.scrollUntilVisibletakes apixelsPerStepoverride; newrewindAndScrollUntilVisiblescrolls to top first so a target above thecurrent offset is still reachable (
whileElementonly scrolls one way).selectPickerOption+pickerOptionId.SettingsPickerdriving is nowone call: open → tap option → close → assert the settled value. Option ids
are derived from the picker's
labelvia the same expression the componentuses, so a label rename no longer fails at runtime with no compile error.
readTopmostText,tapTopmost,tapTopmostButton,waitForRoute— extracted fromtest-stack-simple-nav.e2e.ts, with the Android/iOS caveat (covered screensstay attached on Android, so a matcher can resolve once per stacked screen)
documented on the helpers rather than rediscovered per spec.
selectComponentIntegrationTestsScreenandselectSingleFeatureTestsScreenare now thin wrappers over oneselectDrilldownTestScreen(section, ...).selectIssueTestScreenstaysseparate — that list is searched, not drilled.
elements/back-button.tsusesisIOSVersionAtLeast/getMatchesinstead of its own
getIOSVersionNumberrequire and hand-rolledgetAttributes()unwrapping.elements/toolbar-menu-android.ts(new) — overflow-menu label, popupmatcher, and open/close helpers shared by the Android toolbar-menu specs.
above.
test-stack-simple-nav.e2e.ts's Android-only poller is renamedwaitForTopmostRouteto avoid shadowing the sharedwaitForRoute.apps/src/tests/issue-tests/Test4361.tsx— formatting only (JSXindentation; the
Array.fromcallback was also missing its parens-wrappedreturn, so the list items rendered as
undefined).