From 61e25060861826cfcfff1766245ab54e73ff68ff Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 28 Feb 2025 14:07:24 +0800 Subject: [PATCH 1/2] fix self DM track options don't disappears when moved to a report --- src/CONST.ts | 5 ++ src/pages/ReportDetailsPage.tsx | 20 ++--- tests/ui/ReportDetailsPageTest.tsx | 116 +++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 tests/ui/ReportDetailsPageTest.tsx diff --git a/src/CONST.ts b/src/CONST.ts index dcbba0c3205c..0641d87f6699 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3440,6 +3440,11 @@ const CONST = { UNAPPROVE: 'unapprove', DEBUG: 'debug', GO_TO_WORKSPACE: 'goToWorkspace', + TRACK: { + SUBMIT: 'submit', + CATEGORIZE: 'categorize', + SHARE: 'share', + }, }, EDIT_REQUEST_FIELD: { AMOUNT: 'amount', diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index f348445e3c91..d5d841063690 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -37,15 +37,7 @@ import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavig import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import {getConnectedIntegration, isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, isSubmitAndClose, shouldShowPolicy} from '@libs/PolicyUtils'; -import { - getOneTransactionThreadReportID, - getOriginalMessage, - getReportAction, - getTrackExpenseActionableWhisper, - isDeletedAction, - isMoneyRequestAction, - isTrackExpenseAction, -} from '@libs/ReportActionsUtils'; +import {getOneTransactionThreadReportID, getOriginalMessage, getTrackExpenseActionableWhisper, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; import { canDeleteTransaction, canEditReportDescription as canEditReportDescriptionUtil, @@ -169,6 +161,9 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta // The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here. /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || CONST.DEFAULT_NUMBER_ID}`); + const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, { + selector: (actions) => (report?.parentReportActionID ? actions?.[report.parentReportActionID] : undefined), + }); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID || CONST.DEFAULT_NUMBER_ID}`); const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.parentReportID || CONST.DEFAULT_NUMBER_ID}`); /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */ @@ -213,7 +208,6 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const isTaskReport = useMemo(() => isTaskReportUtil(report), [report]); const isSelfDM = useMemo(() => isSelfDMUtil(report), [report]); const isTrackExpenseReport = useMemo(() => isTrackExpenseReportUtil(report), [report]); - const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const isCanceledTaskReport = isCanceledTaskReportUtil(report, parentReportAction); const canEditReportDescription = useMemo(() => canEditReportDescriptionUtil(report, policy), [report, policy]); const shouldShowReportDescription = isChatRoom && (canEditReportDescription || report.description !== ''); @@ -460,7 +454,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta const whisperAction = getTrackExpenseActionableWhisper(iouTransactionID, moneyRequestReport?.reportID); const actionableWhisperReportActionID = whisperAction?.reportActionID; items.push({ - key: CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS, + key: CONST.REPORT_DETAILS_MENU_ITEM.TRACK.SUBMIT, translationKey: 'actionableMentionTrackExpense.submit', icon: Expensicons.Send, isAnonymousAction: false, @@ -470,7 +464,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta }, }); items.push({ - key: CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS, + key: CONST.REPORT_DETAILS_MENU_ITEM.TRACK.CATEGORIZE, translationKey: 'actionableMentionTrackExpense.categorize', icon: Expensicons.Folder, isAnonymousAction: false, @@ -480,7 +474,7 @@ function ReportDetailsPage({policies, report, route, reportMetadata}: ReportDeta }, }); items.push({ - key: CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS, + key: CONST.REPORT_DETAILS_MENU_ITEM.TRACK.SHARE, translationKey: 'actionableMentionTrackExpense.share', icon: Expensicons.UserPlus, isAnonymousAction: false, diff --git a/tests/ui/ReportDetailsPageTest.tsx b/tests/ui/ReportDetailsPageTest.tsx new file mode 100644 index 000000000000..42eb6e2e70fd --- /dev/null +++ b/tests/ui/ReportDetailsPageTest.tsx @@ -0,0 +1,116 @@ +import {act, render, screen} from '@testing-library/react-native'; +import React from 'react'; +import Onyx from 'react-native-onyx'; +import {LocaleContextProvider} from '@components/LocaleContextProvider'; +import OnyxProvider from '@components/OnyxProvider'; +import {translateLocal} from '@libs/Localize'; +import Navigation from '@libs/Navigation/Navigation'; +import {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; +import ReportDetailsPage from '@pages/ReportDetailsPage'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; +import {Report} from '@src/types/onyx'; +import createRandomReportAction from '../utils/collections/reportActions'; +import createRandomReport from '../utils/collections/reports'; + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useIsFocused: jest.fn(), + useRoute: jest.fn(), + UNSTABLE_usePreventRemove: jest.fn(), + }; +}); + +describe('ReportDetailsPage', () => { + beforeAll(() => { + Onyx.init({ + keys: ONYXKEYS, + safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + }); + }); + + afterEach(async () => { + await Onyx.clear(); + }); + + it('self DM track options should disappear when report moved to workspace', async () => { + const selfDMReportID = '1'; + const trackExpenseReportID = '2'; + const trackExpenseActionID = '123'; + const trackExpenseReport: Report = { + ...createRandomReport(Number(trackExpenseReportID)), + chatType: '' as Report['chatType'], + parentReportID: selfDMReportID, + parentReportActionID: trackExpenseActionID, + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`, { + ...createRandomReport(Number(selfDMReportID)), + chatType: CONST.REPORT.CHAT_TYPE.SELF_DM, + }); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${trackExpenseReportID}`, trackExpenseReport); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${selfDMReportID}`, { + [trackExpenseActionID]: { + ...createRandomReportAction(Number(trackExpenseActionID)), + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + originalMessage: { + type: CONST.IOU.REPORT_ACTION_TYPE.TRACK, + }, + }, + }); + + const {rerender} = render( + + + ['navigation']} + policies={{}} + report={trackExpenseReport} + reportMetadata={undefined} + route={{params: {reportID: trackExpenseReportID}} as PlatformStackScreenProps['route']} + /> + + , + ); + + const submitText = translateLocal('actionableMentionTrackExpense.submit'); + const categorizeText = translateLocal('actionableMentionTrackExpense.categorize'); + const shareText = translateLocal('actionableMentionTrackExpense.share'); + + await screen.findByText(submitText); + await screen.findByText(categorizeText); + await screen.findByText(shareText); + + const movedTrackExpenseReport = { + ...trackExpenseReport, + parentReportID: '3', + parentReportActionID: '234', + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${trackExpenseReportID}`, movedTrackExpenseReport); + + rerender( + + + ['navigation']} + policies={{}} + report={movedTrackExpenseReport} + reportMetadata={undefined} + route={{params: {reportID: trackExpenseReportID}} as PlatformStackScreenProps['route']} + /> + + , + ); + + expect(screen.queryByText(submitText)).not.toBeVisible(); + expect(screen.queryByText(categorizeText)).not.toBeVisible(); + expect(screen.queryByText(shareText)).not.toBeVisible(); + }); +}); From 9982a04befbc3aa43b2f295c458108e226f834a3 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Fri, 28 Feb 2025 14:44:50 +0800 Subject: [PATCH 2/2] lint --- tests/ui/ReportDetailsPageTest.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/ui/ReportDetailsPageTest.tsx b/tests/ui/ReportDetailsPageTest.tsx index 42eb6e2e70fd..9e115f0f98fc 100644 --- a/tests/ui/ReportDetailsPageTest.tsx +++ b/tests/ui/ReportDetailsPageTest.tsx @@ -1,17 +1,17 @@ -import {act, render, screen} from '@testing-library/react-native'; +import {render, screen} from '@testing-library/react-native'; import React from 'react'; import Onyx from 'react-native-onyx'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; import OnyxProvider from '@components/OnyxProvider'; import {translateLocal} from '@libs/Localize'; -import Navigation from '@libs/Navigation/Navigation'; -import {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; +import type Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import ReportDetailsPage from '@pages/ReportDetailsPage'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SCREENS from '@src/SCREENS'; -import {Report} from '@src/types/onyx'; +import type SCREENS from '@src/SCREENS'; +import type {Report} from '@src/types/onyx'; import createRandomReportAction from '../utils/collections/reportActions'; import createRandomReport from '../utils/collections/reports'; @@ -21,6 +21,7 @@ jest.mock('@react-navigation/native', () => { ...actualNav, useIsFocused: jest.fn(), useRoute: jest.fn(), + // eslint-disable-next-line @typescript-eslint/naming-convention UNSTABLE_usePreventRemove: jest.fn(), }; });