diff --git a/src/hooks/useOnboardingFlow.ts b/src/hooks/useOnboardingFlow.ts index 84bb1a4d6257..e63c23428586 100644 --- a/src/hooks/useOnboardingFlow.ts +++ b/src/hooks/useOnboardingFlow.ts @@ -102,6 +102,7 @@ function useOnboardingFlowRouter() { currentOnboardingPurposeSelected: onboardingPurposeSelected, onboardingInitialPath, onboardingValues, + isAccountValidated: !!account?.validated, }); }); } @@ -125,6 +126,7 @@ function useOnboardingFlowRouter() { onboardingValues, account?.isFromPublicDomain, account?.hasAccessibleDomainPolicies, + account?.validated, onboardingCompanySize, onboardingPurposeSelected, onboardingInitialPath, diff --git a/src/libs/Navigation/guards/OnboardingGuard.ts b/src/libs/Navigation/guards/OnboardingGuard.ts index 2318b6eea998..1783195aed48 100644 --- a/src/libs/Navigation/guards/OnboardingGuard.ts +++ b/src/libs/Navigation/guards/OnboardingGuard.ts @@ -110,6 +110,7 @@ function getOnboardingRoute(): Route { currentOnboardingPurposeSelected: onboardingPurposeSelected, onboardingInitialPath, onboardingValues: onboarding, + isAccountValidated: !!account?.validated, }) as Route; } diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index 67c20470020a..bb4d383021c5 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -29,6 +29,7 @@ type GetOnboardingInitialPathParamsType = { currentOnboardingCompanySize: OnyxEntry; onboardingInitialPath: OnyxEntry; onboardingValues: OnyxEntry; + isAccountValidated?: boolean; }; type OnboardingTaskLinks = Partial<{ @@ -107,6 +108,7 @@ function getOnboardingInitialPath(getOnboardingInitialPathParams: GetOnboardingI currentOnboardingCompanySize, onboardingInitialPath = '', onboardingValues, + isAccountValidated, } = getOnboardingInitialPathParams; const state = getStateFromPath(onboardingInitialPath, linkingConfig.config); const currentOnboardingValues = onboardingValuesParam ?? onboardingValues; @@ -126,10 +128,23 @@ function getOnboardingInitialPath(getOnboardingInitialPathParams: GetOnboardingI if (isIndividual) { Onyx.set(ONYXKEYS.ONBOARDING_CUSTOM_CHOICES, [CONST.ONBOARDING_CHOICES.EMPLOYER, CONST.ONBOARDING_CHOICES.TRACK_BUSINESS, CONST.ONBOARDING_CHOICES.TRACK_PERSONAL]); } - if (isUserFromPublicDomain && !onboardingValuesParam?.isMergeAccountStepCompleted) { + // A validated account has no reason to be on the onboarding "add work email" screen. + if (isUserFromPublicDomain && !onboardingValuesParam?.isMergeAccountStepCompleted && !isAccountValidated) { return `/${ROUTES.ONBOARDING_WORK_EMAIL.route}`; } + // PRIVATE_DOMAIN ("People you may know are already here") only makes sense for users on a private domain. Only redirect + // validated accounts; unvalidated users mid-AddWorkEmail can legitimately land here while isFromPublicDomain is stale. + if (isUserFromPublicDomain && isAccountValidated && onboardingInitialPath.includes(ROUTES.ONBOARDING_PRIVATE_DOMAIN.route)) { + if (isVsb) { + return `/${ROUTES.ONBOARDING_ACCOUNTING.route}`; + } + if (isSmb) { + return `/${ROUTES.ONBOARDING_EMPLOYEES.route}`; + } + return `/${ROUTES.ONBOARDING_PURPOSE.route}`; + } + if (!isUserFromPublicDomain && hasAccessiblePolicies) { if (onboardingInitialPath) { return onboardingInitialPath; diff --git a/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx b/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx index 40bf0bdeba79..003b25d0dad5 100644 --- a/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx +++ b/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx @@ -29,6 +29,12 @@ function BaseOnboardingPrivateDomain({shouldUseNativeStyles, route}: BaseOnboard const {translate} = useLocalize(); const [loginList] = useOnyx(ONYXKEYS.LOGIN_LIST); const [session] = useOnyx(ONYXKEYS.SESSION); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, { + selector: (acc) => ({ + validated: acc?.validated, + isFromPublicDomain: acc?.isFromPublicDomain, + }), + }); const {isBetaEnabled} = usePermissions(); const canUseSubmit2026 = isBetaEnabled(CONST.BETAS.SUBMIT_2026); @@ -65,14 +71,41 @@ function BaseOnboardingPrivateDomain({shouldUseNativeStyles, route}: BaseOnboard Navigation.goBack(routeToNavigate); }, [route.params?.backTo, onboardingValues]); + const navigateToNextOnboardingStep = useCallback( + (backTo: string | undefined, options?: {forceReplace?: boolean}) => { + if (isVsb) { + Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(backTo), options); + return; + } + if (isSmb) { + Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(backTo), options); + return; + } + Navigation.navigate(ROUTES.ONBOARDING_PURPOSE.getRoute(backTo), options); + }, + [isVsb, isSmb], + ); + + // Only validated public-domain users are blocked from this screen — for them the "people on YOUR domain" copy would reference gmail.com. + // An unvalidated public-domain user who just submitted a work email may land here before isFromPublicDomain updates; that's the staging happy path. + const shouldBlockPublicDomain = !!account?.validated && !!account?.isFromPublicDomain; + useEffect(() => { + if (shouldBlockPublicDomain) { + return; + } if (isValidated) { return; } sendValidateCode(); - }, [sendValidateCode, isValidated]); + }, [sendValidateCode, isValidated, shouldBlockPublicDomain]); useEffect(() => { + if (shouldBlockPublicDomain) { + navigateToNextOnboardingStep(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute(), {forceReplace: true}); + return; + } + if (!isValidated) { return; } @@ -85,17 +118,13 @@ function BaseOnboardingPrivateDomain({shouldUseNativeStyles, route}: BaseOnboard // When validation succeeded but there are no joinable workspaces and the API call has completed, // navigate to the next onboarding step (same as the skip button behavior). if (getAccessiblePoliciesAction?.loading === false) { - if (isVsb) { - Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); - return; - } - if (isSmb) { - Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); - return; - } - Navigation.navigate(ROUTES.ONBOARDING_PURPOSE.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); + navigateToNextOnboardingStep(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute(), {forceReplace: true}); } - }, [isValidated, joinablePoliciesLength, getAccessiblePoliciesAction?.loading, isVsb, isSmb]); + }, [isValidated, joinablePoliciesLength, getAccessiblePoliciesAction?.loading, shouldBlockPublicDomain, navigateToNextOnboardingStep]); + + if (shouldBlockPublicDomain) { + return null; + } return ( { - if (isVsb) { - Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(route.params?.backTo)); - return; - } - - if (isSmb) { - Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(route.params?.backTo)); - return; - } - Navigation.navigate(ROUTES.ONBOARDING_PURPOSE.getRoute(route.params?.backTo)); - }} + handleSkipButtonPress={() => navigateToNextOnboardingStep(route.params?.backTo)} buttonStyles={[styles.flex2, styles.justifyContentEnd]} isLoading={getAccessiblePoliciesAction?.loading} /> diff --git a/src/pages/OnboardingWorkEmail/BaseOnboardingWorkEmail.tsx b/src/pages/OnboardingWorkEmail/BaseOnboardingWorkEmail.tsx index 836e0c98d987..455ec5872e39 100644 --- a/src/pages/OnboardingWorkEmail/BaseOnboardingWorkEmail.tsx +++ b/src/pages/OnboardingWorkEmail/BaseOnboardingWorkEmail.tsx @@ -50,6 +50,12 @@ function BaseOnboardingWorkEmail({shouldUseNativeStyles}: BaseOnboardingWorkEmai const illustrations = useMemoizedLazyIllustrations(['EnvelopeReceipt', 'Gears', 'Profile']); const [onboardingValues] = useOnyx(ONYXKEYS.NVP_ONBOARDING); const [session] = useOnyx(ONYXKEYS.SESSION); + const [account] = useOnyx(ONYXKEYS.ACCOUNT, { + selector: (acc) => ({ + validated: acc?.validated, + isFromPublicDomain: acc?.isFromPublicDomain, + }), + }); const [formValue] = useOnyx(ONYXKEYS.FORMS.ONBOARDING_WORK_EMAIL_FORM); const workEmail = formValue?.[INPUT_IDS.ONBOARDING_WORK_EMAIL]; const [onboardingErrorMessage] = useOnyx(ONYXKEYS.ONBOARDING_ERROR_MESSAGE_TRANSLATION_KEY); @@ -69,34 +75,50 @@ function BaseOnboardingWorkEmail({shouldUseNativeStyles}: BaseOnboardingWorkEmai }, []); useEffect(() => { - if (onboardingValues?.shouldValidate === undefined && onboardingValues?.isMergeAccountStepCompleted === undefined) { - return; - } - setOnboardingErrorMessage(null); + const navigateToNextStep = (shouldSkipPrivateDomain = false) => { + if (isVsb) { + Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(), {forceReplace: true}); + return; + } + if (isSmb) { + Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(), {forceReplace: true}); + return; + } + if (!shouldSkipPrivateDomain && !onboardingValues?.isMergeAccountStepSkipped) { + Navigation.navigate(ROUTES.ONBOARDING_PRIVATE_DOMAIN.getRoute(), {forceReplace: true}); + return; + } + Navigation.navigate(ROUTES.ONBOARDING_PURPOSE.getRoute(), {forceReplace: true}); + }; - if (onboardingValues?.shouldValidate) { - Navigation.navigate(ROUTES.ONBOARDING_WORK_EMAIL_VALIDATION.getRoute()); - return; - } - // Once we verify that shouldValidate is false, we need to force replace the screen - // so that we don't navigate back on back button press - if (isVsb) { - Navigation.navigate(ROUTES.ONBOARDING_ACCOUNTING.getRoute(), {forceReplace: true}); + // A validated account has no reason to be on the onboarding "add work email" screen. For a public-domain primary the + // PRIVATE_DOMAIN screen would reference gmail.com (etc.) so skip it. + if (account?.validated) { + navigateToNextStep(account?.isFromPublicDomain); return; } - if (isSmb) { - Navigation.navigate(ROUTES.ONBOARDING_EMPLOYEES.getRoute(), {forceReplace: true}); + if (onboardingValues?.shouldValidate === undefined && onboardingValues?.isMergeAccountStepCompleted === undefined) { return; } + setOnboardingErrorMessage(null); - if (!onboardingValues?.isMergeAccountStepSkipped) { - Navigation.navigate(ROUTES.ONBOARDING_PRIVATE_DOMAIN.getRoute(), {forceReplace: true}); + if (onboardingValues?.shouldValidate) { + Navigation.navigate(ROUTES.ONBOARDING_WORK_EMAIL_VALIDATION.getRoute()); return; } - Navigation.navigate(ROUTES.ONBOARDING_PURPOSE.getRoute(), {forceReplace: true}); - }, [onboardingValues?.shouldValidate, isVsb, isSmb, isFocused, onboardingValues?.isMergeAccountStepCompleted, onboardingValues?.isMergeAccountStepSkipped]); + navigateToNextStep(); + }, [ + account?.validated, + account?.isFromPublicDomain, + onboardingValues?.shouldValidate, + isVsb, + isSmb, + isFocused, + onboardingValues?.isMergeAccountStepCompleted, + onboardingValues?.isMergeAccountStepSkipped, + ]); const submitWorkEmail = useCallback((values: FormOnyxValues) => { AddWorkEmail(values[INPUT_IDS.ONBOARDING_WORK_EMAIL].trim()); diff --git a/tests/ui/WorkEmailOnboarding.tsx b/tests/ui/WorkEmailOnboarding.tsx index 7493f8e877fa..e05e28a47fc0 100644 --- a/tests/ui/WorkEmailOnboarding.tsx +++ b/tests/ui/WorkEmailOnboarding.tsx @@ -16,6 +16,7 @@ import HttpUtils from '@libs/HttpUtils'; import Navigation from '@libs/Navigation/Navigation'; import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator'; import type {OnboardingModalNavigatorParamList} from '@navigation/types'; +import OnboardingPrivateDomain from '@pages/OnboardingPrivateDomain'; import OnboardingWorkEmail from '@pages/OnboardingWorkEmail'; import OnboardingWorkEmailValidation from '@pages/OnboardingWorkEmailValidation'; import CONST from '@src/CONST'; @@ -92,6 +93,28 @@ const renderOnboardingWorkEmailValidationPage = ( ); }; +const renderOnboardingPrivateDomainPage = ( + initialRouteName: typeof SCREENS.ONBOARDING.PRIVATE_DOMAIN, + initialParams: OnboardingModalNavigatorParamList[typeof SCREENS.ONBOARDING.PRIVATE_DOMAIN], +) => { + return render( + + + + + + + + + , + {wrapper: HTMLProviderWrapper}, + ); +}; + const navigate = jest.spyOn(Navigation, 'navigate'); function MergeIntoAccountAndLoginBlockMerge() { @@ -325,6 +348,8 @@ describe('OnboardingWorkEmail Page', () => { await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { hasCompletedGuidedSetupFlow: false, }); + // AddWorkEmail is gated on an unvalidated caller; signInWithTestUser sets validated:true by default. + await Onyx.merge(ONYXKEYS.ACCOUNT, {validated: false}); }); const {unmount} = renderOnboardingWorkEmailPage(SCREENS.ONBOARDING.WORK_EMAIL, {backTo: ''}); @@ -375,6 +400,83 @@ describe('OnboardingWorkEmail Page', () => { unmount(); await waitForBatchedUpdatesWithAct(); }); + + it('should still navigate to Onboarding work email validation page when caller is on a public domain', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + }); + // Public-domain users with a merge target must still reach WORK_EMAIL_VALIDATION so MergeIntoAccountAndLogIn can run. + await Onyx.merge(ONYXKEYS.ACCOUNT, {validated: false, isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingWorkEmailPage(SCREENS.ONBOARDING.WORK_EMAIL, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + AddWorkEmailShouldValidate(); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_WORK_EMAIL_VALIDATION.getRoute()); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); + + it('should navigate to Onboarding private domain when an unvalidated public-domain user adds a work email with no existing account', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + }); + // Match staging: an unvalidated public-domain user submitting a non-merge work email still sees PRIVATE_DOMAIN after the primary swap. + await Onyx.merge(ONYXKEYS.ACCOUNT, {validated: false, isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingWorkEmailPage(SCREENS.ONBOARDING.WORK_EMAIL, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + AddWorkEmailShouldValidateFailure(); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_PRIVATE_DOMAIN.getRoute(), {forceReplace: true}); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); + + it('should skip Onboarding private domain for a validated public-domain user', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + }); + // Validated public-domain user (original bug case): PRIVATE_DOMAIN would say "people on gmail.com" — skip to PURPOSE. + await Onyx.merge(ONYXKEYS.ACCOUNT, {validated: true, isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingWorkEmailPage(SCREENS.ONBOARDING.WORK_EMAIL, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_PURPOSE.getRoute(), {forceReplace: true}); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); }); describe('OnboardingWorkEmailValidation Page', () => { @@ -631,3 +733,94 @@ describe('OnboardingWorkEmailValidation Page', () => { await waitForBatchedUpdatesWithAct(); }); }); + +describe('OnboardingPrivateDomain Page', () => { + beforeAll(() => { + Onyx.init({ + keys: ONYXKEYS, + }); + }); + + beforeEach(() => { + jest.spyOn(useResponsiveLayoutModule, 'default').mockReturnValue({ + isSmallScreenWidth: false, + shouldUseNarrowLayout: false, + } as ResponsiveLayoutResult); + }); + + afterEach(async () => { + await act(async () => { + await Onyx.clear(); + }); + + jest.clearAllMocks(); + }); + + it('should redirect a public-domain user away to the purpose step', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + }); + await Onyx.merge(ONYXKEYS.ACCOUNT, {isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingPrivateDomainPage(SCREENS.ONBOARDING.PRIVATE_DOMAIN, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_PURPOSE.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); + + it('should redirect a public-domain SMB user away to the employees step', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.SMB, + }); + await Onyx.merge(ONYXKEYS.ACCOUNT, {isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingPrivateDomainPage(SCREENS.ONBOARDING.PRIVATE_DOMAIN, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_EMPLOYEES.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); + + it('should redirect a public-domain VSB user away to the accounting step', async () => { + await TestHelper.signInWithTestUser(); + + await act(async () => { + await Onyx.merge(ONYXKEYS.NVP_ONBOARDING, { + hasCompletedGuidedSetupFlow: false, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.VSB, + }); + await Onyx.merge(ONYXKEYS.ACCOUNT, {isFromPublicDomain: true}); + }); + + const {unmount} = renderOnboardingPrivateDomainPage(SCREENS.ONBOARDING.PRIVATE_DOMAIN, {backTo: ''}); + + await waitForBatchedUpdatesWithAct(); + + await waitFor(() => { + expect(navigate).toHaveBeenCalledWith(ROUTES.ONBOARDING_ACCOUNTING.getRoute(ROUTES.ONBOARDING_PERSONAL_DETAILS.getRoute()), {forceReplace: true}); + }); + + unmount(); + await waitForBatchedUpdatesWithAct(); + }); +}); diff --git a/tests/unit/OnboardingFlowTest.ts b/tests/unit/OnboardingFlowTest.ts index e6fba9282507..18be0fc5a84b 100644 --- a/tests/unit/OnboardingFlowTest.ts +++ b/tests/unit/OnboardingFlowTest.ts @@ -87,5 +87,118 @@ describe('OnboardingFlow', () => { const path = getOnboardingInitialPath(params); expect(path).toBe('/onboarding/employees'); }); + + it('should skip the work email step when the account is already validated', () => { + const params: GetOnboardingInitialPathParamsType = { + isUserFromPublicDomain: true, + hasAccessiblePolicies: true, + onboardingValuesParam: { + hasCompletedGuidedSetupFlow: false, + shouldRedirectToClassicAfterMerge: false, + shouldValidate: false, + isMergingAccountBlocked: false, + isMergeAccountStepCompleted: false, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.SMB, + }, + currentOnboardingPurposeSelected: CONST.ONBOARDING_CHOICES.EMPLOYER, + currentOnboardingCompanySize: CONST.ONBOARDING_COMPANY_SIZE.SMALL, + onboardingInitialPath: '/', + onboardingValues: undefined, + isAccountValidated: true, + }; + const path = getOnboardingInitialPath(params); + expect(path).not.toBe('/onboarding/work-email'); + expect(path).toBe('/onboarding/employees'); + }); + + it('should still route to the work email step when the account is not validated', () => { + const params: GetOnboardingInitialPathParamsType = { + isUserFromPublicDomain: true, + hasAccessiblePolicies: true, + onboardingValuesParam: { + hasCompletedGuidedSetupFlow: false, + shouldRedirectToClassicAfterMerge: false, + shouldValidate: false, + isMergingAccountBlocked: false, + isMergeAccountStepCompleted: false, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.SMB, + }, + currentOnboardingPurposeSelected: CONST.ONBOARDING_CHOICES.EMPLOYER, + currentOnboardingCompanySize: CONST.ONBOARDING_COMPANY_SIZE.SMALL, + onboardingInitialPath: '/', + onboardingValues: undefined, + isAccountValidated: false, + }; + const path = getOnboardingInitialPath(params); + expect(path).toBe('/onboarding/work-email'); + }); + + it('should skip a private-domain URL for a public-domain validated user', () => { + const params: GetOnboardingInitialPathParamsType = { + isUserFromPublicDomain: true, + hasAccessiblePolicies: true, + onboardingValuesParam: { + hasCompletedGuidedSetupFlow: false, + shouldRedirectToClassicAfterMerge: false, + shouldValidate: false, + isMergingAccountBlocked: false, + isMergeAccountStepCompleted: true, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.INDIVIDUAL, + }, + currentOnboardingPurposeSelected: CONST.ONBOARDING_CHOICES.PERSONAL_SPEND, + currentOnboardingCompanySize: CONST.ONBOARDING_COMPANY_SIZE.SMALL, + onboardingInitialPath: '/onboarding/private-domain', + onboardingValues: undefined, + isAccountValidated: true, + }; + const path = getOnboardingInitialPath(params); + expect(path).toBe('/onboarding/purpose'); + }); + + it('should not redirect away from a private-domain URL for a public-domain unvalidated user', () => { + // Mirrors the BaseOnboardingPrivateDomain screen-level guard: an unvalidated public-domain user who just + // submitted a work email may land here while isFromPublicDomain is stale. They must keep the private-domain step. + const params: GetOnboardingInitialPathParamsType = { + isUserFromPublicDomain: true, + hasAccessiblePolicies: true, + onboardingValuesParam: { + hasCompletedGuidedSetupFlow: false, + shouldRedirectToClassicAfterMerge: false, + shouldValidate: false, + isMergingAccountBlocked: false, + isMergeAccountStepCompleted: true, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.INDIVIDUAL, + }, + currentOnboardingPurposeSelected: CONST.ONBOARDING_CHOICES.PERSONAL_SPEND, + currentOnboardingCompanySize: CONST.ONBOARDING_COMPANY_SIZE.SMALL, + onboardingInitialPath: '/onboarding/private-domain', + onboardingValues: undefined, + isAccountValidated: false, + }; + const path = getOnboardingInitialPath(params); + expect(path).not.toBe('/onboarding/purpose'); + }); + + it('should not redirect away from a work-email-validation URL for a public-domain user', () => { + const params: GetOnboardingInitialPathParamsType = { + isUserFromPublicDomain: true, + hasAccessiblePolicies: true, + onboardingValuesParam: { + hasCompletedGuidedSetupFlow: false, + shouldRedirectToClassicAfterMerge: false, + shouldValidate: true, + isMergingAccountBlocked: false, + isMergeAccountStepCompleted: true, + signupQualifier: CONST.ONBOARDING_SIGNUP_QUALIFIERS.INDIVIDUAL, + }, + currentOnboardingPurposeSelected: CONST.ONBOARDING_CHOICES.PERSONAL_SPEND, + currentOnboardingCompanySize: CONST.ONBOARDING_COMPANY_SIZE.SMALL, + onboardingInitialPath: '/onboarding/work-email/validation', + onboardingValues: undefined, + isAccountValidated: true, + }; + const path = getOnboardingInitialPath(params); + expect(path).not.toBe('/onboarding/purpose'); + }); }); });