Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/pages/domain/BaseVerifyDomainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';

import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import {useMemoizedLazyAsset} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
Expand All @@ -26,6 +27,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import {isAdminSelector} from '@src/selectors/Domain';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';

import type {PropsWithChildren} from 'react';
Expand Down Expand Up @@ -57,11 +59,15 @@ function BaseVerifyDomainPage({domainAccountID, forwardTo}: BaseVerifyDomainPage
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();

const [domain, domainMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.DOMAIN}${domainAccountID}`);
const domainName = domain ? Str.extractEmailDomain(domain.email) : '';
const doesDomainExist = !!domain;

// A domain admin has nothing to verify once the domain is validated, so keep them out of the flow if they deep-link; non-admins still land here to re-verify
const isVerifiedDomainAdmin = !!domain?.validated && isAdminSelector(currentUserAccountID)(domain);

const {asset: Exclamation} = useMemoizedLazyAsset(() => loadExpensifyIcon('Exclamation'));

useEffect(() => {
Expand All @@ -72,18 +78,18 @@ function BaseVerifyDomainPage({domainAccountID, forwardTo}: BaseVerifyDomainPage
}, [domainAccountID, domain?.hasValidationSucceeded, forwardTo]);

useFocusEffect(() => {
if (!doesDomainExist || domain?.validated || domain?.validateCode || domain?.isValidateCodeLoading || domain?.validateCodeError) {
if (isVerifiedDomainAdmin || !doesDomainExist || domain?.validateCode || domain?.isValidateCodeLoading || domain?.validateCodeError) {
return;
}
getDomainValidationCode(domainAccountID, domainName);
});

useEffect(() => {
if (!doesDomainExist || domain?.validated) {
if (!doesDomainExist) {
return;
}
resetDomainValidationError(domainAccountID);
}, [domainAccountID, doesDomainExist, domain?.validated]);
}, [domainAccountID, doesDomainExist]);

const isLoadingDomain = isLoadingOnyxValue(domainMetadata);
if (isLoadingDomain) {
Expand All @@ -98,7 +104,7 @@ function BaseVerifyDomainPage({domainAccountID, forwardTo}: BaseVerifyDomainPage
return <NotFoundPage onLinkPress={() => Navigation.dismissModal()} />;
}

if (domain.validated) {
if (isVerifiedDomainAdmin) {
return (
<NotFoundPage
onLinkPress={() => Navigation.dismissModal()}
Expand Down
145 changes: 145 additions & 0 deletions tests/ui/BaseVerifyDomainPageTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import {act, render, screen} from '@testing-library/react-native';

import ComposeProviders from '@components/ComposeProviders';
import {CurrentUserPersonalDetailsProvider} from '@components/CurrentUserPersonalDetailsProvider';
import {LocaleContextProvider} from '@components/LocaleContextProvider';
import OnyxListItemProvider from '@components/OnyxListItemProvider';

import * as API from '@libs/API';
import {READ_COMMANDS} from '@libs/API/types';
import {navigationRef} from '@libs/Navigation/Navigation';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
import type {WorkspacesDomainModalNavigatorParamList} from '@libs/Navigation/types';

import WorkspacesVerifyDomainPage from '@pages/domain/WorkspacesVerifyDomainPage';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';

import {PortalProvider} from '@gorhom/portal';
import {NavigationContainer} from '@react-navigation/native';
import React from 'react';
import Onyx from 'react-native-onyx';

import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';

jest.mock('@components/RenderHTML', () => () => null);

const DOMAIN_ACCOUNT_ID = 123456;
const DOMAIN_EMAIL = 'user@test.com';
const DOMAIN_NAME = 'test.com';
const TEST_USER_ACCOUNT_ID = 1;

// Makes the signed-in test user an admin of the domain
const DOMAIN_ADMIN_ACCESS = {
[`${CONST.DOMAIN.EXPENSIFY_ADMIN_ACCESS_PREFIX}0`]: TEST_USER_ACCOUNT_ID,
};

const apiReadSpy = jest.spyOn(API, 'read').mockImplementation(() => {});

const Stack = createPlatformStackNavigator<WorkspacesDomainModalNavigatorParamList>();

function renderVerifyDomainPage() {
return render(
<ComposeProviders components={[OnyxListItemProvider, CurrentUserPersonalDetailsProvider, LocaleContextProvider]}>
<PortalProvider>
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName={SCREENS.WORKSPACES_VERIFY_DOMAIN}>
<Stack.Screen
name={SCREENS.WORKSPACES_VERIFY_DOMAIN}
component={WorkspacesVerifyDomainPage}
initialParams={{domainAccountID: DOMAIN_ACCOUNT_ID}}
/>
</Stack.Navigator>
</NavigationContainer>
</PortalProvider>
</ComposeProviders>,
);
}

describe('BaseVerifyDomainPage', () => {
beforeAll(async () => {
Onyx.init({keys: ONYXKEYS});
await act(async () => {
await Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.EN);
});
await waitForBatchedUpdatesWithAct();
});

afterEach(async () => {
apiReadSpy.mockClear();
await act(async () => {
await Onyx.clear();
});
await waitForBatchedUpdatesWithAct();
});

it('renders the DNS verification screen for a non-admin on an already-validated domain instead of NotFoundPage', async () => {
// Given an already-validated domain the current user is not an admin of
await TestHelper.signInWithTestUser();
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN}${DOMAIN_ACCOUNT_ID}`, {
accountID: DOMAIN_ACCOUNT_ID,
email: DOMAIN_EMAIL,
validated: true,
});
});
await waitForBatchedUpdatesWithAct();

// When the verify-domain page is opened
renderVerifyDomainPage();
await waitForBatchedUpdatesWithAct();

// Then the verify screen renders (a validated domain must not dead-end on NotFoundPage)
expect(screen.getByTestId('BaseVerifyDomainPage')).toBeTruthy();

// And the validation code is fetched, so the DNS TXT field is not left empty
expect(apiReadSpy).toHaveBeenCalledWith(READ_COMMANDS.GET_DOMAIN_VALIDATE_CODE, {domainName: DOMAIN_NAME}, expect.anything());
});

it('renders NotFoundPage for an admin on an already-validated domain and skips the code fetch', async () => {
// Given an already-validated domain the current user is an admin of
await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID);
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN}${DOMAIN_ACCOUNT_ID}`, {
accountID: DOMAIN_ACCOUNT_ID,
email: DOMAIN_EMAIL,
validated: true,
...DOMAIN_ADMIN_ACCESS,
});
});
await waitForBatchedUpdatesWithAct();

// When the verify-domain page is deep-linked
renderVerifyDomainPage();
await waitForBatchedUpdatesWithAct();

// Then the verify screen is not shown (NotFoundPage), and no validation code is fetched
expect(screen.queryByTestId('BaseVerifyDomainPage')).toBeNull();
expect(apiReadSpy).not.toHaveBeenCalledWith(READ_COMMANDS.GET_DOMAIN_VALIDATE_CODE, expect.anything(), expect.anything());
});

it('renders the DNS verification screen for an admin on a not-yet-validated domain', async () => {
// Given a domain the current user is an admin of but that is NOT yet validated
await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID);
await act(async () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.DOMAIN}${DOMAIN_ACCOUNT_ID}`, {
accountID: DOMAIN_ACCOUNT_ID,
email: DOMAIN_EMAIL,
validated: false,
...DOMAIN_ADMIN_ACCESS,
});
});
await waitForBatchedUpdatesWithAct();

// When the verify-domain page is opened
renderVerifyDomainPage();
await waitForBatchedUpdatesWithAct();

// Then the verify screen renders
expect(screen.getByTestId('BaseVerifyDomainPage')).toBeTruthy();
expect(apiReadSpy).toHaveBeenCalledWith(READ_COMMANDS.GET_DOMAIN_VALIDATE_CODE, {domainName: DOMAIN_NAME}, expect.anything());
});
});
Loading