Skip to content

Commit 4dd2b73

Browse files
committed
test(mfa): drive the initial-screen layout through the rendered View
The INIT executor called handleInitialScreenLayout directly, bypassing the component. A testID on the initial screen lets the test fire the layout event on the real View, so the walk now exercises its onLayout wiring.
1 parent fc62112 commit 4dd2b73

2 files changed

Lines changed: 12 additions & 9 deletions

File tree

src/libs/Navigation/AppNavigator/Navigators/MultifactorAuthenticationModalNavigator.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const loadPromptPage = () => require<ReactComponentModule>('../../../../pages/Mu
4040
function TransparentScreen() {
4141
return (
4242
<View
43+
testID="MultifactorAuthenticationInitialScreen"
4344
style={StyleSheet.absoluteFill}
4445
onLayout={handleInitialScreenLayout}
4546
/>

tests/unit/components/MultifactorAuthentication/machine/graphTraversal/viewMatchesMachine.test.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import waitForBatchedUpdatesWithAct from 'tests/utils/waitForBatchedUpdatesWithA
99
import {matchesState} from 'xstate';
1010
import mfaMachine from '@components/MultifactorAuthentication/machine/mfaMachine';
1111
import type {MfaEvent} from '@components/MultifactorAuthentication/machine/types';
12-
import {handleInitialScreenLayout, mfaNavigationRef} from '@components/MultifactorAuthentication/mfaNavigation';
12+
import {mfaNavigationRef} from '@components/MultifactorAuthentication/mfaNavigation';
1313
import CONST from '@src/CONST';
1414
import SCREENS from '@src/SCREENS';
1515

@@ -30,11 +30,12 @@ jest.mock('@libs/XStateInspector', () => ({__esModule: true, default: {inspect:
3030
jest.mock('@components/MultifactorAuthentication/biometrics/useBiometrics', () => jest.requireActual<typeof MfaRealUiMocks>('tests/utils/mfa/realUi/mocks').biometricsHookMock());
3131
// Browser and Android history synchronization is outside the contract between the machine and UI.
3232
jest.mock('@components/MultifactorAuthentication/useSyncMfaModalNavigatorWithHistory', () => jest.requireActual<typeof MfaRealUiMocks>('tests/utils/mfa/realUi/mocks').syncHistoryMock());
33-
// jsdom runs no real navigation transitions, so the mock controls when the transition callbacks fire.
33+
// The test renderer runs no real navigation transitions, so the mock controls when the transition callbacks fire.
3434
jest.mock('@libs/Navigation/Navigation', () => jest.requireActual<typeof MfaRealUiMocks>('tests/utils/mfa/realUi/mocks').navigationMock());
3535

36-
// These UI markers distinguish the closed, closing, and outcome states. The backdrop exists only while
37-
// the MFA navigator is mounted.
36+
const INITIAL_SCREEN_TEST_ID = 'MultifactorAuthenticationInitialScreen';
37+
38+
// These UI markers distinguish the closed, closing, and outcome states. The backdrop exists only while the MFA navigator is mounted.
3839
const OUTCOME_SCREEN_TEST_ID = 'MultifactorAuthenticationOutcomeScreen';
3940
const MODAL_BACKDROP_TEST_ID = 'MultifactorAuthenticationModalBackdrop';
4041

@@ -63,9 +64,10 @@ function createMfaEventExecutors(executeScenario: ExecuteScenario) {
6364
await executeScenario(MFA_TEST_SCENARIO_NAME);
6465
});
6566
await waitForBatchedUpdatesWithAct();
66-
// The initial screen's `onLayout` does not fire in jsdom, so the test calls the same handler to flush the
67-
// buffered navigation.
68-
act(() => handleInitialScreenLayout());
67+
// The test renderer does not calculate layout, so dispatch the event through the rendered View to exercise its onLayout wiring.
68+
fireEvent(screen.getByTestId(INITIAL_SCREEN_TEST_ID), 'layout', {
69+
nativeEvent: {layout: {width: 1, height: 1, x: 0, y: 0}},
70+
});
6971
await waitForBatchedUpdatesWithAct();
7072
},
7173
CLOSE_MODAL: async () => {
@@ -95,8 +97,8 @@ const testConfig = {
9597
},
9698
[MFA_STATE.CLOSING]: () => {
9799
expect(screen.queryAllByTestId(MODAL_BACKDROP_TEST_ID)).toHaveLength(1);
98-
// The outcome stays mounted during the production close animation, while jsdom's goBack()
99-
// removes it synchronously. Its presence is therefore not part of the closing-state contract.
100+
// The outcome stays mounted during the production close animation, while the test renderer processes
101+
// goBack() synchronously. Its presence is therefore not part of the closing-state contract.
100102
},
101103
},
102104
};

0 commit comments

Comments
 (0)