diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c55bfb86f95d..b6df1d384727 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -235,6 +235,14 @@ const DYNAMIC_ROUTES = { ], getRoute: (cardID: string) => `missing-personal-details/${cardID}/confirm-magic-code` as const, }, + MONEY_REQUEST_STEP_TIME: { + path: 'per-diem-time', + entryScreens: [SCREENS.MONEY_REQUEST.STEP_DESTINATION, SCREENS.MONEY_REQUEST.CREATE], + }, + MONEY_REQUEST_STEP_TIME_EDIT: { + path: 'per-diem-time-edit', + entryScreens: [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION], + }, PROFILE: { path: 'a/:accountID', entryScreens: ['*'], @@ -1943,11 +1951,6 @@ const ROUTES = { getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') => getUrlWithBackToParam(`${action as string}/${iouType as string}/destination/${transactionID}/${reportID}${backToReport ? `/${backToReport}` : ''}`, backTo), }, - MONEY_REQUEST_STEP_TIME: { - route: ':action/:iouType/time/:transactionID/:reportID/:backToReport?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/time/${transactionID}/${reportID}${backToReport ? `/${backToReport}` : ''}`, backTo), - }, MONEY_REQUEST_STEP_SUBRATE: { route: ':action/:iouType/subrate/:transactionID/:reportID/:backToReport?/:pageIndex', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string, backTo = '') => @@ -1958,11 +1961,6 @@ const ROUTES = { getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`${action as string}/${iouType as string}/destination/${transactionID}/${reportID}/edit`, backTo), }, - MONEY_REQUEST_STEP_TIME_EDIT: { - route: ':action/:iouType/time/:transactionID/:reportID/edit', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') => - getUrlWithBackToParam(`${action as string}/${iouType as string}/time/${transactionID}/${reportID}/edit`, backTo), - }, MONEY_REQUEST_STEP_SUBRATE_EDIT: { route: ':action/:iouType/subrate/:transactionID/:reportID/edit/:pageIndex', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = 0, backTo = '') => diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 8f58e99213a8..f0856e8ca7db 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -356,10 +356,10 @@ const SCREENS = { STEP_ATTENDEES: 'Money_Request_Attendee', STEP_ACCOUNTANT: 'Money_Request_Accountant', STEP_DESTINATION: 'Money_Request_Destination', - STEP_TIME: 'Money_Request_Time', + DYNAMIC_STEP_TIME: 'Dynamic_Money_Request_Time', STEP_SUBRATE: 'Money_Request_SubRate', STEP_DESTINATION_EDIT: 'Money_Request_Destination_Edit', - STEP_TIME_EDIT: 'Money_Request_Time_Edit', + DYNAMIC_STEP_TIME_EDIT: 'Dynamic_Money_Request_Time_Edit', STEP_SUBRATE_EDIT: 'Money_Request_SubRate_Edit', STEP_REPORT: 'Money_Request_Report', EDIT_REPORT: 'Money_Request_Edit_Report', diff --git a/src/components/MoneyRequestConfirmationList/sections/PerDiemFields.tsx b/src/components/MoneyRequestConfirmationList/sections/PerDiemFields.tsx index 116831d45e35..a082a04fa59d 100644 --- a/src/components/MoneyRequestConfirmationList/sections/PerDiemFields.tsx +++ b/src/components/MoneyRequestConfirmationList/sections/PerDiemFields.tsx @@ -5,12 +5,13 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getDestinationForDisplay, getSubratesFields, getSubratesForDisplay, getTimeDifferenceIntervals, getTimeForDisplay} from '@libs/PerDiemRequestUtils'; import CONST from '@src/CONST'; import type {IOUAction, IOUType} from '@src/CONST'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {CustomUnit} from '@src/types/onyx/Policy'; @@ -125,7 +126,7 @@ function PerDiemFields({perDiemCustomUnit, transaction, isReadOnly, didConfirm, if (!transactionID) { return; } - Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.getRoute(action, iouType, transactionID, reportID)); + Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path)); }} disabled={didConfirm} interactive={!isReadOnly} diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index e8574be05242..40b00652fb65 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -208,10 +208,10 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../../pages/iou/request/step/IOURequestStepAccountant').default, [SCREENS.MONEY_REQUEST.STEP_UPGRADE]: () => require('../../../../pages/iou/request/step/IOURequestStepUpgrade').default, [SCREENS.MONEY_REQUEST.STEP_DESTINATION]: () => require('../../../../pages/iou/request/step/IOURequestStepDestination').default, - [SCREENS.MONEY_REQUEST.STEP_TIME]: () => require('../../../../pages/iou/request/step/IOURequestStepTime').default, + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: () => require('../../../../pages/iou/request/step/DynamicIOURequestStepTime').default, [SCREENS.MONEY_REQUEST.STEP_SUBRATE]: () => require('../../../../pages/iou/request/step/IOURequestStepSubrate').default, [SCREENS.MONEY_REQUEST.STEP_DESTINATION_EDIT]: () => require('../../../../pages/iou/request/step/IOURequestStepDestination').default, - [SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: () => require('../../../../pages/iou/request/step/IOURequestStepTime').default, + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: () => require('../../../../pages/iou/request/step/DynamicIOURequestStepTime').default, [SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: () => require('../../../../pages/iou/request/step/IOURequestStepSubrate').default, [SCREENS.MONEY_REQUEST.RECEIPT_VIEW]: () => require('../../../../pages/iou/request/step/IOURequestStepScan/ReceiptView').default, [SCREENS.MONEY_REQUEST.SPLIT_EXPENSE]: () => require('../../../../pages/iou/SplitExpensePage').default, diff --git a/src/libs/Navigation/linkingConfig/OldRoutes.ts b/src/libs/Navigation/linkingConfig/OldRoutes.ts index 27ed312e628c..d555ffbc10b5 100644 --- a/src/libs/Navigation/linkingConfig/OldRoutes.ts +++ b/src/libs/Navigation/linkingConfig/OldRoutes.ts @@ -1,5 +1,11 @@ const oldRoutes: Record = { /* eslint-disable @typescript-eslint/naming-convention */ + // Per diem time step migrated to dynamic routes (#83850). Dynamic suffixes are namespaced `per-diem-*` because + // dynamic route paths must be globally unique across DYNAMIC_ROUTES (react-navigation flattens them). Edit variant + // redirects to the confirmation-based suffix; the wizard variant to the destination-based suffix. Query is not + // preserved here (the trailing wildcard would swallow it) - acceptable because callers no longer append `?backTo=`. + '/*/*/time/*/*/edit': '/$1/$2/confirmation/$3/$4/per-diem-time-edit', + '/*/*/time/*/*': '/$1/$2/destination/$3/$4/per-diem-time', '/settings/*/category/*/edit': '/settings/$1/categories/category-settings/$2/category-edit', '/settings/*/category/*': '/settings/$1/categories/category-settings/$2', '/settings/*/tags/*/edit': '/settings/$1/tags/settings-tags-edit/$2', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f4839edce363..eabf7466f9b9 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1785,10 +1785,10 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_ACCOUNTANT]: ROUTES.MONEY_REQUEST_ACCOUNTANT.route, [SCREENS.MONEY_REQUEST.STEP_UPGRADE]: ROUTES.MONEY_REQUEST_UPGRADE.route, [SCREENS.MONEY_REQUEST.STEP_DESTINATION]: ROUTES.MONEY_REQUEST_STEP_DESTINATION.route, - [SCREENS.MONEY_REQUEST.STEP_TIME]: ROUTES.MONEY_REQUEST_STEP_TIME.route, + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME.path, [SCREENS.MONEY_REQUEST.STEP_SUBRATE]: ROUTES.MONEY_REQUEST_STEP_SUBRATE.route, [SCREENS.MONEY_REQUEST.STEP_DESTINATION_EDIT]: ROUTES.MONEY_REQUEST_STEP_DESTINATION_EDIT.route, - [SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.route, + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path, [SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: ROUTES.MONEY_REQUEST_STEP_SUBRATE_EDIT.route, [SCREENS.MONEY_REQUEST.STEP_TIME_RATE]: ROUTES.MONEY_REQUEST_STEP_TIME_RATE.route, [SCREENS.MONEY_REQUEST.STEP_HOURS]: ROUTES.MONEY_REQUEST_STEP_HOURS.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8685eba094e9..a0649c2eeca2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2277,14 +2277,12 @@ type MoneyRequestNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; - [SCREENS.MONEY_REQUEST.STEP_TIME]: { + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME]: { action: IOUAction; iouType: Exclude; transactionID: string; reportID: string; backToReport?: string; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_SUBRATE]: { iouType: Exclude; @@ -2304,14 +2302,12 @@ type MoneyRequestNavigatorParamList = { // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md backTo: Routes | undefined; }; - [SCREENS.MONEY_REQUEST.STEP_TIME_EDIT]: { + [SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT]: { action: IOUAction; iouType: Exclude; transactionID: string; reportID: string; backToReport?: string; - // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md - backTo: Routes | undefined; }; [SCREENS.MONEY_REQUEST.STEP_SUBRATE_EDIT]: { iouType: Exclude; diff --git a/src/pages/iou/request/step/IOURequestStepTime.tsx b/src/pages/iou/request/step/DynamicIOURequestStepTime.tsx similarity index 73% rename from src/pages/iou/request/step/IOURequestStepTime.tsx rename to src/pages/iou/request/step/DynamicIOURequestStepTime.tsx index fcdbb35bdc43..cf07f657e9b9 100644 --- a/src/pages/iou/request/step/IOURequestStepTime.tsx +++ b/src/pages/iou/request/step/DynamicIOURequestStepTime.tsx @@ -5,9 +5,8 @@ import type {FormOnyxValues} from '@components/Form/types'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import TimeModalPicker from '@components/TimeModalPicker'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; import usePolicyForTransaction from '@hooks/usePolicyForTransaction'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -15,14 +14,13 @@ import DateUtils from '@libs/DateUtils'; import {addErrorMessage} from '@libs/ErrorUtils'; import {isValidMoneyRequestType} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates} from '@libs/PolicyUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getIOURequestPolicyID, setMoneyRequestDateAttribute} from '@userActions/IOU/MoneyRequest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestTimeForm'; import type * as OnyxTypes from '@src/types/onyx'; @@ -30,7 +28,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {OnyxEntry} from 'react-native-onyx'; -import React, {useMemo} from 'react'; +import React from 'react'; import {View} from 'react-native'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; @@ -39,7 +37,7 @@ import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -type IOURequestStepTimeProps = WithWritableReportOrNotFoundProps & { +type DynamicIOURequestStepTimeProps = WithWritableReportOrNotFoundProps & { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ transaction: OnyxEntry; @@ -50,17 +48,16 @@ type IOURequestStepTimeProps = WithWritableReportOrNotFoundProps; }; -function IOURequestStepTime({ +function DynamicIOURequestStepTime({ route: { - params: {action, iouType, reportID, transactionID, backTo, backToReport}, + params: {action, iouType, reportID, transactionID, backToReport}, name, }, transaction, isLoadingTransaction, report, -}: IOURequestStepTimeProps) { +}: DynamicIOURequestStepTimeProps) { const styles = useThemeStyles(); - const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const iouPolicyID = getIOURequestPolicyID(transaction, report); const {policy} = usePolicyForTransaction({ transaction, @@ -74,37 +71,17 @@ function IOURequestStepTime({ const currentDateAttributes = transaction?.comment?.customUnit?.attributes?.dates; const currentStartDate = currentDateAttributes?.start ? DateUtils.extractDate(currentDateAttributes.start) : undefined; const currentEndDate = currentDateAttributes?.end ? DateUtils.extractDate(currentDateAttributes.end) : undefined; - const isEditPage = name === SCREENS.MONEY_REQUEST.STEP_TIME_EDIT; + const isEditPage = name === SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT; const shouldShowNotFound = !isValidMoneyRequestType(iouType) || isEmptyObject(policy) || (isEditPage && isEmptyObject(transaction?.comment?.customUnit)); - const {login: currentUserLogin} = useCurrentUserPersonalDetails(); - const policiesWithPerDiemEnabled = useMemo(() => getActivePoliciesWithExpenseChatAndPerDiemEnabledAndHasRates(allPolicies, currentUserLogin), [allPolicies, currentUserLogin]); - const hasMoreThanOnePolicyWithPerDiemEnabled = policiesWithPerDiemEnabled.length > 1; - const navigateBack = () => { - if (isEditPage) { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, iouType, transactionID, reportID)); - return; - } - - if (backTo) { - Navigation.goBack(backTo); - return; - } + // Back removes this step's dynamic suffix from the current URL. In edit mode the suffix (`time-edit`) sits on the + // confirmation base, so back returns to confirmation. In the wizard flow the suffix (`time`) sits on the destination + // base (multi-policy) or the start base (single-policy), so back returns to whichever preceding step was appended to. + const backPath = useDynamicBackPath(isEditPage ? DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME_EDIT.path : DYNAMIC_ROUTES.MONEY_REQUEST_STEP_TIME.path); - if (transaction?.isFromGlobalCreate || iouType === CONST.IOU.TYPE.TRACK) { - // We want to navigate to destination step only when the first step was the workspace selector. - // If there is only one policy with per diem enabled, we want to navigate back to the start step because there is no separate destination step in that flow. - if (hasMoreThanOnePolicyWithPerDiemEnabled) { - Navigation.goBack(ROUTES.MONEY_REQUEST_STEP_DESTINATION.getRoute(action, iouType, transactionID, reportID, backToReport)); - return; - } - - // If there is only one per diem policy, we can't override the reportID that is already on the stack to make sure we go back to the right screen. - Navigation.goBack(); - } - - Navigation.goBack(ROUTES.MONEY_REQUEST_CREATE_TAB_PER_DIEM.getRoute(action, iouType, transactionID, reportID)); + const navigateBack = () => { + Navigation.goBack(backPath); }; const validate = (value: FormOnyxValues) => { @@ -161,11 +138,11 @@ function IOURequestStepTime({ return ( ): Partial> => { diff --git a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx index c092db1a6e6f..4758425c8ad7 100644 --- a/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx +++ b/src/pages/iou/request/step/withFullTransactionOrNotFound.tsx @@ -51,8 +51,8 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_REPORT | typeof SCREENS.MONEY_REQUEST.STEP_COMPANY_INFO | typeof SCREENS.MONEY_REQUEST.STEP_DESTINATION - | typeof SCREENS.MONEY_REQUEST.STEP_TIME - | typeof SCREENS.MONEY_REQUEST.STEP_TIME_EDIT + | typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME + | typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT | typeof SCREENS.MONEY_REQUEST.STEP_SUBRATE | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE_MAP | typeof SCREENS.MONEY_REQUEST.STEP_DISTANCE_GPS diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx index 71ddd975c102..c22a82780909 100644 --- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx +++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx @@ -57,8 +57,8 @@ type MoneyRequestRouteName = | typeof SCREENS.MONEY_REQUEST.STEP_ACCOUNTANT | typeof SCREENS.MONEY_REQUEST.STEP_UPGRADE | typeof SCREENS.MONEY_REQUEST.STEP_DESTINATION - | typeof SCREENS.MONEY_REQUEST.STEP_TIME - | typeof SCREENS.MONEY_REQUEST.STEP_TIME_EDIT + | typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME + | typeof SCREENS.MONEY_REQUEST.DYNAMIC_STEP_TIME_EDIT | typeof SCREENS.MONEY_REQUEST.STEP_SUBRATE | typeof SCREENS.MONEY_REQUEST.EDIT_REPORT | typeof SCREENS.MONEY_REQUEST.DISTANCE_CREATE diff --git a/tests/navigation/dynamicRoutesUniquePathTest.ts b/tests/navigation/dynamicRoutesUniquePathTest.ts new file mode 100644 index 000000000000..21da01220452 --- /dev/null +++ b/tests/navigation/dynamicRoutesUniquePathTest.ts @@ -0,0 +1,16 @@ +import {DYNAMIC_ROUTES} from '@src/ROUTES'; + +/** + * Dynamic route paths are flattened into the react-navigation linking config, which runs + * `checkForDuplicatedConfigs` at NavigationContainer mount. Two DYNAMIC_ROUTES that emit the + * same `path` pattern (regardless of their `entryScreens`) crash the whole app at boot. This + * guard fails fast in CI instead of at runtime. Regression guard for #83850, where a per diem + * `destination` suffix collided with Sage Intacct's existing `destination` suffix. + */ +describe('DYNAMIC_ROUTES', () => { + it('has globally unique path patterns', () => { + const paths = Object.values(DYNAMIC_ROUTES).map((route) => route.path); + const duplicates = paths.filter((path, index) => paths.indexOf(path) !== index); + expect(duplicates).toEqual([]); + }); +}); diff --git a/tests/navigation/getMatchingNewRouteTest.ts b/tests/navigation/getMatchingNewRouteTest.ts index 673d40e1f942..e6cb28ba06ff 100644 --- a/tests/navigation/getMatchingNewRouteTest.ts +++ b/tests/navigation/getMatchingNewRouteTest.ts @@ -403,4 +403,22 @@ describe('getBestMatchingPath', () => { expect(getMatchingNewRoute('/workspaces/p123/expensify-card/card-details/456')).toBe('/workspaces/p123/expensify-card/card-details/456'); expect(getMatchingNewRoute('/workspaces/p123/expensify-card/card-details/456/edit/limit')).toBe('/workspaces/p123/expensify-card/card-details/456/edit/limit'); }); + + it('redirects legacy per diem time step to the new destination-based dynamic route (#83850)', () => { + expect(getMatchingNewRoute('/create/submit/time/123/456')).toBe('/create/submit/destination/123/456/per-diem-time'); + }); + + // NOTE: the redirect appends the dynamic suffix (`/time`) so a deep-link restores the actual wizard step. Query + // preservation is intentionally not asserted: the OldRoutes matcher captures the trailing segment with a greedy + // `(.*)` that also swallows the query string, so a suffix cannot be appended after it while keeping `?backTo=…`. + // This is acceptable because no call site produces these legacy paths with a query anymore (all navigations moved to + // `createDynamicRoute` without `backTo`). + it('redirects legacy per diem time edit step to the new confirmation-based dynamic route (#83850)', () => { + expect(getMatchingNewRoute('/create/submit/time/123/456/edit')).toBe('/create/submit/confirmation/123/456/per-diem-time-edit'); + }); + + it('does not redirect the already-migrated per diem time dynamic routes (#83850)', () => { + expect(getMatchingNewRoute('/create/submit/destination/123/456/per-diem-time')).toBe(undefined); + expect(getMatchingNewRoute('/create/submit/confirmation/123/456/per-diem-time-edit')).toBe(undefined); + }); });