diff --git a/src/components/WorkspaceSwitcherButton.tsx b/src/components/WorkspaceSwitcherButton.tsx index d47f243af736..04349526aaea 100644 --- a/src/components/WorkspaceSwitcherButton.tsx +++ b/src/components/WorkspaceSwitcherButton.tsx @@ -52,6 +52,7 @@ function WorkspaceSwitcherButton({policy, onSwitchWorkspace}: WorkspaceSwitcherB accessibilityRole={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.workspaces')} accessible + testID="WorkspaceSwitcherButton" onPress={() => { onSwitchWorkspace?.(); pressableRef?.current?.blur(); diff --git a/src/pages/WorkspaceSwitcherPage/index.tsx b/src/pages/WorkspaceSwitcherPage/index.tsx index ce3875b93a4d..53b7fe5d6d7e 100644 --- a/src/pages/WorkspaceSwitcherPage/index.tsx +++ b/src/pages/WorkspaceSwitcherPage/index.tsx @@ -1,3 +1,4 @@ +import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useMemo} from 'react'; import {useOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -35,6 +36,7 @@ function WorkspaceSwitcherPage() { const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const {translate} = useLocalize(); const {activeWorkspaceID, setActiveWorkspaceID} = useActiveWorkspace(); + const isFocused = useIsFocused(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); @@ -77,6 +79,9 @@ function WorkspaceSwitcherPage() { const selectPolicy = useCallback( (policyID?: string) => { + if (!isFocused) { + return; + } const newPolicyID = policyID === activeWorkspaceID ? undefined : policyID; setActiveWorkspaceID(newPolicyID); @@ -85,7 +90,7 @@ function WorkspaceSwitcherPage() { Navigation.navigateWithSwitchPolicyID({policyID: newPolicyID}); } }, - [activeWorkspaceID, setActiveWorkspaceID], + [activeWorkspaceID, setActiveWorkspaceID, isFocused], ); const usersWorkspaces = useMemo(() => { diff --git a/tests/ui/WorkspaceSwitcherTest.tsx b/tests/ui/WorkspaceSwitcherTest.tsx new file mode 100644 index 000000000000..614ed4e5ab70 --- /dev/null +++ b/tests/ui/WorkspaceSwitcherTest.tsx @@ -0,0 +1,104 @@ +import * as NativeNavigation from '@react-navigation/native'; +import {act, fireEvent, render, screen} from '@testing-library/react-native'; +import React from 'react'; +import Onyx from 'react-native-onyx'; +import * as Localize from '@libs/Localize'; +import type Navigation from '@libs/Navigation/Navigation'; +import * as AppActions from '@userActions/App'; +import * as User from '@userActions/User'; +import App from '@src/App'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {NativeNavigationMock} from '../../__mocks__/@react-navigation/native'; +import * as LHNTestUtils from '../utils/LHNTestUtils'; +import PusherHelper from '../utils/PusherHelper'; +import * as TestHelper from '../utils/TestHelper'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; + +// We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App +jest.setTimeout(60000); + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useIsFocused: jest.fn(), + triggerTransitionEnd: jest.fn(), + }; +}); +TestHelper.setupApp(); + +async function signInAndGetApp(): Promise { + // Render the App and sign in as a test user. + render(); + await waitForBatchedUpdatesWithAct(); + + const hintText = Localize.translateLocal('loginForm.loginForm'); + const loginForm = await screen.findAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + + await act(async () => { + await TestHelper.signInWithTestUser(); + }); + await waitForBatchedUpdatesWithAct(); + + User.subscribeToUserEvents(); + await waitForBatchedUpdates(); + + AppActions.setSidebarLoaded(); + + await waitForBatchedUpdatesWithAct(); +} + +async function navigateToWorkspaceSwitcher(): Promise { + const workspaceSwitcherButton = await screen.findByTestId('WorkspaceSwitcherButton'); + fireEvent(workspaceSwitcherButton, 'press'); + await act(() => { + (NativeNavigation as NativeNavigationMock).triggerTransitionEnd(); + }); + await waitForBatchedUpdatesWithAct(); +} + +describe('WorkspaceSwitcherPage', () => { + beforeEach(() => { + jest.clearAllMocks(); + Onyx.clear(); + + // Unsubscribe to pusher channels + PusherHelper.teardown(); + }); + + it('navigates away when a workspace is selected and `isFocused` is true', async () => { + await signInAndGetApp(); + (NativeNavigation.useIsFocused as jest.Mock).mockReturnValue(true); + + await Onyx.mergeCollection(ONYXKEYS.COLLECTION.POLICY, { + [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: LHNTestUtils.getFakePolicy('1', 'Workspace A'), + [`${ONYXKEYS.COLLECTION.POLICY}2` as const]: LHNTestUtils.getFakePolicy('2', 'Workspace B'), + [`${ONYXKEYS.COLLECTION.POLICY}3` as const]: LHNTestUtils.getFakePolicy('3', 'Workspace C'), + }); + + await navigateToWorkspaceSwitcher(); + + const workspaceRowB = screen.getByLabelText('Workspace B'); + fireEvent.press(workspaceRowB); + expect(screen.queryByTestId('WorkspaceSwitcherPage')).toBeNull(); + }); + + it('does not navigate away when a workspace is selected and `isFocused` is false', async () => { + await signInAndGetApp(); + (NativeNavigation.useIsFocused as jest.Mock).mockReturnValue(false); + + await Onyx.mergeCollection(ONYXKEYS.COLLECTION.POLICY, { + [`${ONYXKEYS.COLLECTION.POLICY}1` as const]: LHNTestUtils.getFakePolicy('1', 'Workspace A'), + [`${ONYXKEYS.COLLECTION.POLICY}2` as const]: LHNTestUtils.getFakePolicy('2', 'Workspace B'), + [`${ONYXKEYS.COLLECTION.POLICY}3` as const]: LHNTestUtils.getFakePolicy('3', 'Workspace C'), + }); + + await navigateToWorkspaceSwitcher(); + + const workspaceRowB = screen.getByLabelText('Workspace B'); + fireEvent.press(workspaceRowB); + expect(screen.getByTestId('WorkspaceSwitcherPage')).toBeOnTheScreen(); + }); +});