diff --git a/src/components/WorkspaceConfirmationForm.tsx b/src/components/WorkspaceConfirmationForm.tsx index 10d5194797b11..1a2a2f1260a88 100644 --- a/src/components/WorkspaceConfirmationForm.tsx +++ b/src/components/WorkspaceConfirmationForm.tsx @@ -10,7 +10,7 @@ import useOnyx from '@hooks/useOnyx'; import useThemeStyles from '@hooks/useThemeStyles'; import useWorkspaceConfirmationAvatar from '@hooks/useWorkspaceConfirmationAvatar'; import {clearDraftValues} from '@libs/actions/FormActions'; -import {generateDefaultWorkspaceName, generatePolicyID} from '@libs/actions/Policy/Policy'; +import {generatePolicyID, newGenerateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; import {addErrorMessage} from '@libs/ErrorUtils'; import getFirstAlphaNumericCharacter from '@libs/getFirstAlphaNumericCharacter'; @@ -21,6 +21,7 @@ import {isRequiredFulfilled} from '@libs/ValidationUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; +import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; import INPUT_IDS from '@src/types/form/WorkspaceConfirmationForm'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import AvatarWithImagePicker from './AvatarWithImagePicker'; @@ -114,7 +115,9 @@ function WorkspaceConfirmationForm({onSubmit, policyOwnerEmail = '', onBackButto const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [draftValues] = useOnyx(ONYXKEYS.FORMS.WORKSPACE_CONFIRMATION_FORM_DRAFT); - const defaultWorkspaceName = generateDefaultWorkspaceName(policyOwnerEmail || session?.email); + const email = policyOwnerEmail || (session?.email ?? ''); + const lastWorkspaceNumber = lastWorkspaceNumberSelector(policies, email); + const defaultWorkspaceName = newGenerateDefaultWorkspaceName(email, lastWorkspaceNumber); const [workspaceNameFirstCharacter, setWorkspaceNameFirstCharacter] = useState(defaultWorkspaceName ?? ''); const userCurrency = draftValues?.currency ?? currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD; diff --git a/src/hooks/useLastWorkspaceNumber.ts b/src/hooks/useLastWorkspaceNumber.ts new file mode 100644 index 0000000000000..30f0785cb3cc8 --- /dev/null +++ b/src/hooks/useLastWorkspaceNumber.ts @@ -0,0 +1,15 @@ +import type {OnyxCollection} from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; +import {emailSelector} from '@src/selectors/Session'; +import type {Policy} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +function useLastWorkspaceNumber() { + const [sessionEmail] = useOnyx(ONYXKEYS.SESSION, {selector: emailSelector}); + const lastWorkspaceNumberSelectorWithEmail = (policies: OnyxCollection) => lastWorkspaceNumberSelector(policies, sessionEmail ?? ''); + const [lastWorkspaceNumber] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: lastWorkspaceNumberSelectorWithEmail}); + return lastWorkspaceNumber; +} + +export default useLastWorkspaceNumber; diff --git a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx index 765c9f1ad66ed..cc37302ab89e4 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx @@ -58,7 +58,6 @@ function AuthScreensInitHandler() { const [initialLastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); - const [lastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); const lastUpdateIDAppliedToClientRef = useRef(lastUpdateIDAppliedToClient); diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 6114b063e0419..eb41ddb19dc6f 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -2135,6 +2135,53 @@ function clearDuplicateWorkspace() { Onyx.set(ONYXKEYS.DUPLICATE_WORKSPACE, {}); } +function getDisplayNameForWorkspace(email: string) { + const emailParts = email.split('@'); + const domain = emailParts.at(1) ?? ''; + const isSMSDomain = `@${domain}` === CONST.SMS.DOMAIN; + if (isSMSDomain) { + // eslint-disable-next-line @typescript-eslint/no-deprecated + return translateLocal('workspace.new.myGroupWorkspace', {}); + } + + if (!PUBLIC_DOMAINS_SET.has(domain.toLowerCase())) { + return Str.UCFirst(domain.split('.').at(0) ?? ''); + } + + const userDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email); + const displayName = userDetails?.displayName?.trim(); + if (displayName) { + return Str.UCFirst(displayName); + } + + const username = emailParts.at(0) ?? ''; + return Str.UCFirst(username); +} + +/** + * Generate a policy name based on an email and policy list. + * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead + * @param [lastWorkspaceNumber] the last workspace number + */ +function newGenerateDefaultWorkspaceName(email: string, lastWorkspaceNumber: number | undefined): string { + const emailParts = email ? email.split('@') : deprecatedSessionEmail.split('@'); + if (!emailParts || emailParts.length !== 2) { + return ''; + } + const domain = emailParts.at(1) ?? ''; + const isSMSDomain = `@${domain}` === CONST.SMS.DOMAIN; + + if (isSMSDomain) { + // eslint-disable-next-line @typescript-eslint/no-deprecated + return translateLocal('workspace.new.myGroupWorkspace', {workspaceNumber: lastWorkspaceNumber !== undefined ? lastWorkspaceNumber + 1 : undefined}); + } + + const displayNameForWorkspace = getDisplayNameForWorkspace(email || deprecatedSessionEmail); + + // eslint-disable-next-line @typescript-eslint/no-deprecated + return translateLocal('workspace.new.workspaceName', displayNameForWorkspace, lastWorkspaceNumber !== undefined ? lastWorkspaceNumber + 1 : undefined); +} + /** * Generate a policy name based on an email and policy list. * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead @@ -7081,6 +7128,8 @@ export { updateLastAccessedWorkspace, clearDeleteWorkspaceError, setWorkspaceDefaultSpendCategory, + getDisplayNameForWorkspace, + newGenerateDefaultWorkspaceName, generateDefaultWorkspaceName, updateGeneralSettings, deleteWorkspaceAvatar, diff --git a/src/pages/OnboardingWorkspaceConfirmation/BaseOnboardingWorkspaceConfirmation.tsx b/src/pages/OnboardingWorkspaceConfirmation/BaseOnboardingWorkspaceConfirmation.tsx index 6c7f875602b6b..542085a100d82 100644 --- a/src/pages/OnboardingWorkspaceConfirmation/BaseOnboardingWorkspaceConfirmation.tsx +++ b/src/pages/OnboardingWorkspaceConfirmation/BaseOnboardingWorkspaceConfirmation.tsx @@ -23,12 +23,13 @@ import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import {clearWorkspaceDetailsDraft} from '@userActions/Onboarding'; -import {createWorkspace, generateDefaultWorkspaceName, generatePolicyID} from '@userActions/Policy/Policy'; +import {createWorkspace, generatePolicyID, newGenerateDefaultWorkspaceName} from '@userActions/Policy/Policy'; import {setOnboardingAdminsChatReportID, setOnboardingErrorMessage, setOnboardingPolicyID} from '@userActions/Welcome'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; import INPUT_IDS from '@src/types/form/WorkspaceConfirmationForm'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import OnboardingCurrencyPicker from './OnboardingCurrencyPicker'; @@ -57,7 +58,9 @@ function BaseOnboardingWorkspaceConfirmation({shouldUseNativeStyles}: BaseOnboar const paidGroupPolicy = Object.values(allPolicies ?? {}).find((policy) => isPaidGroupPolicy(policy) && isPolicyAdmin(policy, session?.email)); - const defaultWorkspaceName = draftValues?.name ?? generateDefaultWorkspaceName(session?.email); + const email = session?.email ?? ''; + const lastWorkspaceNumber = lastWorkspaceNumberSelector(allPolicies, email); + const defaultWorkspaceName = draftValues?.name ?? newGenerateDefaultWorkspaceName(email, lastWorkspaceNumber); const defaultCurrency = draftValues?.currency ?? currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD; useEffect(() => { diff --git a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx index a9bc092db922d..cf415d09a8902 100644 --- a/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx +++ b/src/pages/OnboardingWorkspaceOptional/BaseOnboardingWorkspaceOptional.tsx @@ -26,7 +26,7 @@ import {navigateAfterOnboardingWithMicrotaskQueue} from '@libs/navigateAfterOnbo import Navigation from '@libs/Navigation/Navigation'; import {isPaidGroupPolicy, isPolicyAdmin} from '@libs/PolicyUtils'; import {getSubscriptionPrice} from '@libs/SubscriptionUtils'; -import {createWorkspace, generateDefaultWorkspaceName, generatePolicyID} from '@userActions/Policy/Policy'; +import {createWorkspace, generatePolicyID, newGenerateDefaultWorkspaceName} from '@userActions/Policy/Policy'; import {completeOnboarding as completeOnboardingReport} from '@userActions/Report'; import {setOnboardingAdminsChatReportID, setOnboardingErrorMessage, setOnboardingPolicyID} from '@userActions/Welcome'; import CONST from '@src/CONST'; @@ -34,6 +34,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; import type {OnboardingPurpose} from '@src/types/onyx'; import type IconAsset from '@src/types/utils/IconAsset'; import type {BaseOnboardingWorkspaceOptionalProps} from './types'; @@ -158,11 +159,13 @@ function BaseOnboardingWorkspaceOptional({shouldUseNativeStyles}: BaseOnboarding const paidGroupPolicy = Object.values(allPolicies ?? {}).find((policy) => isPaidGroupPolicy(policy) && isPolicyAdmin(policy, session?.email)); const shouldCreateWorkspace = !onboardingPolicyID && !paidGroupPolicy; + const email = session?.email ?? ''; + const lastWorkspaceNumber = lastWorkspaceNumberSelector(allPolicies, email); const {adminsChatReportID, policyID} = shouldCreateWorkspace ? createWorkspace({ policyOwnerEmail: undefined, makeMeAdmin: true, - policyName: generateDefaultWorkspaceName(session?.email), + policyName: newGenerateDefaultWorkspaceName(email, lastWorkspaceNumber), policyID: generatePolicyID(), engagementChoice: CONST.ONBOARDING_CHOICES.TRACK_WORKSPACE, currency: currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD, diff --git a/src/selectors/Policy.ts b/src/selectors/Policy.ts index aafe7542a4aa0..8fd11ff227532 100644 --- a/src/selectors/Policy.ts +++ b/src/selectors/Policy.ts @@ -1,4 +1,7 @@ +import escapeRegExp from 'lodash/escapeRegExp'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import {getDisplayNameForWorkspace} from '@libs/actions/Policy/Policy'; +import {translate} from '@libs/Localize'; import {areAllGroupPoliciesExpenseChatDisabled, getActiveAdminWorkspaces, getOwnedPaidPolicies, isPaidGroupPolicy, shouldShowPolicy} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import type {Policy, PolicyReportField} from '@src/types/onyx'; @@ -134,6 +137,32 @@ const hasPoliciesConnectedToSageIntacctSelector = (policies: OnyxCollection) => !!adminPoliciesConnectedToNetSuiteSelector(policies).length; +function lastWorkspaceNumberSelector(policies: OnyxCollection, email: string): number | undefined { + const emailParts = email.split('@'); + if (emailParts.length !== 2) { + return undefined; + } + + const displayNameForWorkspace = getDisplayNameForWorkspace(email); + // find default named workspaces and increment the last number + const escapedName = escapeRegExp(displayNameForWorkspace); + const workspaceTranslations = Object.values(CONST.LOCALES) + .map((lang) => translate(lang, 'workspace.common.workspace')) + .join('|'); + + const domain = emailParts.at(1) ?? ''; + const isSMSDomain = `@${domain}` === CONST.SMS.DOMAIN; + const workspaceRegex = isSMSDomain ? new RegExp(`^${escapedName}\\s*(\\d+)?$`, 'i') : new RegExp(`^(?=.*${escapedName})(?:.*(?:${workspaceTranslations})\\s*(\\d+)?)`, 'i'); + + const workspaceNumbers = Object.values(policies ?? {}) + .map((policy) => workspaceRegex.exec(policy?.name ?? '')) + .filter(Boolean) // Remove null matches + .map((match) => Number(match?.[1] ?? '0')); + const lastWorkspaceNumber = workspaceNumbers.length > 0 ? Math.max(...workspaceNumbers) : undefined; + + return lastWorkspaceNumber; +} + export { activePolicySelector, createAllPolicyReportFieldsSelector, @@ -150,4 +179,5 @@ export { adminPoliciesConnectedToNetSuiteSelector, hasPoliciesConnectedToSageIntacctSelector, hasPoliciesConnectedToNetSuiteSelector, + lastWorkspaceNumberSelector, }; diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts index 9a52f3aceabb6..2e676a5a18cf2 100644 --- a/tests/actions/PolicyTest.ts +++ b/tests/actions/PolicyTest.ts @@ -1786,7 +1786,7 @@ describe('actions/Policy', () => { await waitForBatchedUpdates(); const policyID = Policy.generatePolicyID(); - const expectedName = Policy.generateDefaultWorkspaceName(ESH_EMAIL); + const expectedName = Policy.newGenerateDefaultWorkspaceName(ESH_EMAIL, undefined); Policy.createDraftInitialWorkspace({choice: CONST.ONBOARDING_CHOICES.MANAGE_TEAM}, ESH_EMAIL, '', policyID, false); await waitForBatchedUpdates(); @@ -3001,7 +3001,7 @@ describe('actions/Policy', () => { accountID: TEST_ACCOUNT_ID, }); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_NON_PUBLIC_DOMAIN_EMAIL); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_NON_PUBLIC_DOMAIN_EMAIL, undefined); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.workspaceName', displayNameForWorkspace)); }); @@ -3014,7 +3014,7 @@ describe('actions/Policy', () => { accountID: TEST_ACCOUNT_ID, }); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_EMAIL); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_EMAIL, undefined); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.workspaceName', displayNameForWorkspace)); }); @@ -3029,7 +3029,7 @@ describe('actions/Policy', () => { accountID: TEST_ACCOUNT_ID, }); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_EMAIL_2); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_EMAIL_2, undefined); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.workspaceName', displayNameForWorkspace)); }); @@ -3047,7 +3047,7 @@ describe('actions/Policy', () => { await Onyx.set(ONYXKEYS.COLLECTION.POLICY, existingPolicies); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_EMAIL); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_EMAIL, 1); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.workspaceName', TEST_DISPLAY_NAME, 2)); }); @@ -3058,7 +3058,7 @@ describe('actions/Policy', () => { accountID: TEST_ACCOUNT_ID, }); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_SMS_DOMAIN_EMAIL); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_SMS_DOMAIN_EMAIL, undefined); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.myGroupWorkspace', {})); }); @@ -3080,7 +3080,7 @@ describe('actions/Policy', () => { await Onyx.set(ONYXKEYS.COLLECTION.POLICY, existingPolicies); - const workspaceName = Policy.generateDefaultWorkspaceName(TEST_EMAIL); + const workspaceName = Policy.newGenerateDefaultWorkspaceName(TEST_EMAIL, 1); expect(workspaceName).toBe(TestHelper.translateLocal('workspace.new.workspaceName', TEST_DISPLAY_NAME, 2)); }); }); diff --git a/tests/unit/PolicySelectorTest.ts b/tests/unit/PolicySelectorTest.ts new file mode 100644 index 0000000000000..8fd534e5cf778 --- /dev/null +++ b/tests/unit/PolicySelectorTest.ts @@ -0,0 +1,63 @@ +import CONST from '@src/CONST'; +import IntlStore from '@src/languages/IntlStore'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; +import type {Policy} from '@src/types/onyx'; + +describe('lastWorkspaceNumberSelector', () => { + const email = 'jdoe@expensify.com'; + const displayName = 'Expensify'; + const workspaceName = `${displayName} Workspace`; + + beforeAll(() => IntlStore.load(CONST.LOCALES.DEFAULT)); + + it('should return undefined when there are no policies', () => { + expect(lastWorkspaceNumberSelector({}, email)).toBeUndefined(); + }); + + it('should return undefined when email is invalid', () => { + expect(lastWorkspaceNumberSelector({}, 'invalid-email')).toBeUndefined(); + }); + + it('should return 0 when there is a matching workspace without a number', () => { + const policies = { + [`${ONYXKEYS.COLLECTION.POLICY}1`]: {name: workspaceName} as Policy, + }; + expect(lastWorkspaceNumberSelector(policies, email)).toBe(0); + }); + + it('should return the number when there is a matching workspace with a number', () => { + const policies = { + [`${ONYXKEYS.COLLECTION.POLICY}1`]: {name: `${workspaceName} 2`} as Policy, + }; + expect(lastWorkspaceNumberSelector(policies, email)).toBe(2); + }); + + it('should return the maximum number when there are multiple matching workspaces', () => { + const policies = { + [`${ONYXKEYS.COLLECTION.POLICY}1`]: {name: workspaceName} as Policy, + [`${ONYXKEYS.COLLECTION.POLICY}2`]: {name: `${workspaceName} 2`} as Policy, + [`${ONYXKEYS.COLLECTION.POLICY}3`]: {name: `${workspaceName} 5`} as Policy, + [`${ONYXKEYS.COLLECTION.POLICY}4`]: {name: 'Other Workspace'} as Policy, + }; + expect(lastWorkspaceNumberSelector(policies, email)).toBe(5); + }); + + it('should handle SMS domain correctly', () => { + const smsEmail = `+15555555555${CONST.SMS.DOMAIN}`; + const smsDisplayName = 'My Group Workspace'; + const policies = { + [`${ONYXKEYS.COLLECTION.POLICY}1`]: {name: smsDisplayName} as Policy, + [`${ONYXKEYS.COLLECTION.POLICY}2`]: {name: `${smsDisplayName} 3`} as Policy, + }; + expect(lastWorkspaceNumberSelector(policies, smsEmail)).toBe(3); + }); + + it('should ignore case when matching workspace names', () => { + const policies = { + [`${ONYXKEYS.COLLECTION.POLICY}1`]: {name: workspaceName.toLowerCase()} as Policy, + [`${ONYXKEYS.COLLECTION.POLICY}2`]: {name: `${workspaceName.toUpperCase()} 4`} as Policy, + }; + expect(lastWorkspaceNumberSelector(policies, email)).toBe(4); + }); +}); diff --git a/tests/unit/useLastWorkspaceNumberTest.ts b/tests/unit/useLastWorkspaceNumberTest.ts new file mode 100644 index 0000000000000..e6341a553199c --- /dev/null +++ b/tests/unit/useLastWorkspaceNumberTest.ts @@ -0,0 +1,58 @@ +import {renderHook} from '@testing-library/react-native'; +import Onyx from 'react-native-onyx'; +import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; +import CONST from '@src/CONST'; +import IntlStore from '@src/languages/IntlStore'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Policy} from '@src/types/onyx'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; + +describe('useLastWorkspaceNumber', () => { + const email = 'jdoe@expensify.com'; + const displayName = 'Expensify'; + const workspaceName = `${displayName} Workspace`; + + beforeAll(() => { + Onyx.init({keys: ONYXKEYS}); + return IntlStore.load(CONST.LOCALES.DEFAULT); + }); + + beforeEach(() => { + Onyx.clear(); + return waitForBatchedUpdates(); + }); + + it('should return the correct last workspace number from Onyx', async () => { + await Onyx.merge(ONYXKEYS.SESSION, {email}); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}1`, {name: `${workspaceName} 3`} as Policy); + await waitForBatchedUpdates(); + + const {result} = renderHook(() => useLastWorkspaceNumber()); + + expect(result.current).toBe(3); + }); + + it('should update when Onyx data changes', async () => { + await Onyx.merge(ONYXKEYS.SESSION, {email}); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}1`, {name: `${workspaceName} 3`} as Policy); + await waitForBatchedUpdates(); + + const {result} = renderHook(() => useLastWorkspaceNumber()); + expect(result.current).toBe(3); + + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}2`, {name: `${workspaceName} 5`} as Policy); + await waitForBatchedUpdates(); + + expect(result.current).toBe(5); + }); + + it('should return undefined if no matching workspaces exist', async () => { + await Onyx.merge(ONYXKEYS.SESSION, {email}); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}1`, {name: 'Other Workspace'} as Policy); + await waitForBatchedUpdates(); + + const {result} = renderHook(() => useLastWorkspaceNumber()); + + expect(result.current).toBeUndefined(); + }); +});