Skip to content

refactor(mfa): extract BiometricsTestToolRow from TestToolMenu#90967

Draft
dariusz-biela wants to merge 3 commits into
Expensify:mainfrom
software-mansion-labs:dariusz-biela/refactor/3ds/internal-navigation/biometrics-toolrow
Draft

refactor(mfa): extract BiometricsTestToolRow from TestToolMenu#90967
dariusz-biela wants to merge 3 commits into
Expensify:mainfrom
software-mansion-labs:dariusz-biela/refactor/3ds/internal-navigation/biometrics-toolrow

Conversation

@dariusz-biela
Copy link
Copy Markdown
Contributor

Explanation of Change

TestToolMenu was calling useBiometricRegistrationStatus, useSingleExecution, useWaitForNavigation and tracking isMFARevokeLoading unconditionally — even when the user is logged out and the biometrics row is not rendered (the row sits inside the existing isAuthenticated guard). The biometrics-specific concerns also bloated the menu and forced unrelated reads on every render.

Move the biometrics-related JSX, state, and hook calls into a dedicated BiometricsTestToolRow component. The new row keeps the existing trigger path (singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.MULTIFACTOR_AUTHENTICATION_BIOMETRICS_TEST)))) unchanged; only the rendering location changes. TestToolMenu now renders <BiometricsTestToolRow /> inside the same isAuthenticated block, so the biometrics hooks run only when the row is actually shown.

Stacked PR — part 3 of 4 splitting #89992. Built on top of #90966 (`deny-outcome-close`) which is built on top of #90964 (`context-split`). Cross-fork PRs target `main`, so the diff in this PR is cumulative against `main` and will shrink once the parents merge. To review only this PR's net work, look at the latest commit on the branch (`refactor(mfa): extract BiometricsTestToolRow from TestToolMenu`).

Fixed Issues

$ #81021
PROPOSAL:

Tests

  1. Sign in. Open Settings → Troubleshoot. Scroll to the biometrics row.
  2. Verify the title shows the correct status ("Never registered", "Registered on this device", "Registered on N other devices", or "Not registered").
  3. Tap Test — the existing biometrics test page opens (same behavior as before).
  4. If the device is registered, tap Revoke — the credentials are revoked.
  5. Sign out. Open the Test Tools modal (4-tap PanResponder on the SignIn page).
  6. Verify the menu renders without the biometrics row and without errors.
  7. Run `npx jest tests/ui/TestToolMenuBiometricsTest.tsx` — all 8 tests pass.
  • Verify that no errors appear in the JS console

Offline tests

The biometrics row is unchanged in offline behavior — both the Test button (via `waitForNavigate`) and the Revoke button work the same as today.

QA Steps

Same as Tests.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the `### Fixed Issues` section above
  • I wrote clear testing steps that cover the changes made in this PR
  • I tested with a High Traffic account against staging
  • I verified there are no console errors
  • I followed proper code patterns
  • I verified all code is DRY
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that the file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory

Screenshots/Videos

Android: Native

To be added.

Android: mWeb Chrome

To be added.

iOS: Native

To be added.

iOS: mWeb Safari

To be added.

MacOS: Chrome / Safari

To be added.

OutcomeScreenBase and AuthenticationMethodDescription pulled the full
Context module (Provider -> Main -> State -> stateReducer -> config ->
scenarios), forming an import cycle that resolved to
customConfig(undefined) at module-load and crashed the
sortTransactionsPending3DSReview test suite at load.

- Extract the two React Context objects + their consumer hooks into
  leaf files MultifactorAuthenticationStateContext.ts and
  MultifactorAuthenticationActionsContext.ts (no downstream imports).
- Merge State.tsx into Provider.tsx and rename to
  MultifactorAuthenticationComposedContextProviders.tsx; drop
  ComposeProviders for direct nesting.
- Rename Main.tsx to MultifactorAuthenticationMainContext.tsx.
- Point UI primitives at the leaf context files directly so their
  import graph stops at React + types.
The deny outcome (DeniedTransactionSuccessScreen / failure variants) is
rendered inline inside the RHP page, not inside its own navigator. The
outcome screen's default close handler calls Navigation.closeRHPFlow(),
which is correct for this surface, but useBeforeRemove was re-opening
the cancel-confirm modal because allowNavigatingAwayRef was still false
when the user tapped "Got it" / header back.

Add an optional onClose override to OutcomeScreenBase (forwarded through
Success/FailureScreenBase). AuthorizeTransactionPage passes a callback
that flips allowNavigatingAwayRef.current = true before closing the RHP,
so the unsaved-changes guard does not block the close.

The default onClose stays as Navigation.closeRHPFlow() — no behavior
change for non-RHP outcome callers.
TestToolMenu was calling useBiometricRegistrationStatus, useSingleExecution,
useWaitForNavigation and tracking isMFARevokeLoading unconditionally — even
when the user is logged out and the biometrics row is not rendered (it sits
inside the existing isAuthenticated guard). The biometrics-specific concerns
also bloated the menu and made unrelated reads on every render.

Move the biometrics-related JSX, state, and hook calls into a dedicated
BiometricsTestToolRow component. The new row keeps the existing trigger path
(singleExecution(waitForNavigate(() => Navigation.navigate(BIOMETRICS_TEST))))
unchanged; only the rendering location changes. TestToolMenu now renders
<BiometricsTestToolRow /> inside the same isAuthenticated block, so the
biometrics hooks run only when the row is actually shown.

Tests do not change: all mocks were module-level and continue to apply.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/components/BiometricsTestToolRow.tsx 100.00% <100.00%> (ø)
...Context/MultifactorAuthenticationActionsContext.ts 100.00% <100.00%> (ø)
...tifactorAuthenticationComposedContextProviders.tsx 100.00% <100.00%> (ø)
...n/Context/MultifactorAuthenticationMainContext.tsx 26.49% <ø> (ø)
...n/Context/MultifactorAuthenticationStateContext.ts 100.00% <100.00%> (ø)
...tifactorAuthentication/Context/usePromptContent.ts 0.00% <ø> (ø)
...ion/components/AuthenticationMethodDescription.tsx 28.57% <ø> (ø)
src/components/TestToolMenu.tsx 92.50% <ø> (+0.66%) ⬆️
.../OutcomeScreen/SuccessScreen/SuccessScreenBase.tsx 12.50% <0.00%> (ø)
.../OutcomeScreen/FailureScreen/FailureScreenBase.tsx 11.11% <0.00%> (-1.39%) ⬇️
... and 2 more
... and 8 files with indirect coverage changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant