Skip to content

chore(test): group e2e utils by concern and deduplicate spec helpers - #4441

Draft
LKuchno wants to merge 20 commits into
mainfrom
@lkuchno/e2e-utils-restructure
Draft

chore(test): group e2e utils by concern and deduplicate spec helpers#4441
LKuchno wants to merge 20 commits into
mainfrom
@lkuchno/e2e-utils-restructure

Conversation

@LKuchno

@LKuchno LKuchno commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Description

Test-infrastructure only PR — no library code is touched.

FabricExample/e2e/e2e-utils.ts had grown into a flat pile of helpers with no
grouping, 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 was
also 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
ElementMatcher object with plain Detox NativeMatchers, and pulls the
helpers that specs had been duplicating up into the shared module.

Changes

e2e-utils.ts — grouped into sections (platform & suite gating, element
attributes, interactions, settings controls, stacked screens, test screen
navigation) with the rationale for each non-obvious helper documented inline.

  • Attributes API reworked. getElementAttributes({ by, value, index }) and
    its resolveMatcher switch are gone; helpers now take a Detox
    NativeMatcher directly. New/renamed: getSingleMatch (throws on an
    ambiguous matcher instead of silently taking the first hit), getFrame,
    and the exported ElementAttributes type. countMatches and
    getTopmostMatch were folded into their only call sites.
  • Scrolling. scrollUntilVisible takes a pixelsPerStep override; new
    rewindAndScrollUntilVisible scrolls to top first so a target above the
    current offset is still reachable (whileElement only scrolls one way).
  • selectPickerOption + pickerOptionId. SettingsPicker driving is now
    one call: open → tap option → close → assert the settled value. Option ids
    are derived from the picker's label via the same expression the component
    uses, so a label rename no longer fails at runtime with no compile error.
  • Stacked-screen helpers. readTopmostText, tapTopmost,
    tapTopmostButton, waitForRoute — extracted from
    test-stack-simple-nav.e2e.ts, with the Android/iOS caveat (covered screens
    stay attached on Android, so a matcher can resolve once per stacked screen)
    documented on the helpers rather than rediscovered per spec.
  • Navigation deduplicated. selectComponentIntegrationTestsScreen and
    selectSingleFeatureTestsScreen are now thin wrappers over one
    selectDrilldownTestScreen(section, ...). selectIssueTestScreen stays
    separate — that list is searched, not drilled.
  • elements/back-button.ts uses isIOSVersionAtLeast / getMatches
    instead of its own getIOSVersionNumber require and hand-rolled
    getAttributes() unwrapping.
  • elements/toolbar-menu-android.ts (new) — overflow-menu label, popup
    matcher, and open/close helpers shared by the Android toolbar-menu specs.
  • 18 spec files updated to the new API, dropping their local copies of the
    above. test-stack-simple-nav.e2e.ts's Android-only poller is renamed
    waitForTopmostRoute to avoid shadowing the shared waitForRoute.
  • apps/src/tests/issue-tests/Test4361.tsx — formatting only (JSX
    indentation; the Array.from callback was also missing its parens-wrapped
    return, so the list items rendered as undefined).

LKuchno added 7 commits July 29, 2026 11:54
…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
…mansion/react-native-screens into @lkuchno/e2e-utils-restructure

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts into 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.

LKuchno and others added 6 commits August 3, 2026 13:09
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>
@LKuchno LKuchno added type:e2e Actions related to e2e maintenance and development, especially automation. platform:js Issue related to the JS part of the library labels Aug 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • waitForRoute currently uses waitFor(...).toBeVisible(), which throws on Android when the by.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; reusing expectTopmostVisible here 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);
}

@LKuchno
LKuchno marked this pull request as ready for review August 12, 2026 08:21
@LKuchno
LKuchno marked this pull request as draft August 12, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform:js Issue related to the JS part of the library type:e2e Actions related to e2e maintenance and development, especially automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants