From 7152cdfa97cabd9d0e578dff452cbde449bd4ba8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 8 May 2025 15:26:16 +0200 Subject: [PATCH 01/20] expose value from a derived value --- src/libs/actions/OnyxDerived/configs/reportAttributes.ts | 3 ++- src/types/onyx/DerivedValues.ts | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 06ff411fb7ac2..6f78913b1e75a 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -88,7 +88,7 @@ export default createOnyxDerivedValueConfig({ } const reportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]; - const {hasAnyViolations, requiresAttention, isReportArchived} = generateReportAttributes({ + const {hasAnyViolations, requiresAttention, isReportArchived, reportErrors} = generateReportAttributes({ report, reportActions, transactionViolations, @@ -108,6 +108,7 @@ export default createOnyxDerivedValueConfig({ acc[report.reportID] = { reportName: generateReportName(report), brickRoadStatus, + reportErrors, }; return acc; diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts index fd380284d5c92..10058ba77f57b 100644 --- a/src/types/onyx/DerivedValues.ts +++ b/src/types/onyx/DerivedValues.ts @@ -1,5 +1,6 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +import type {Errors} from './OnyxCommon'; /** * The attributes of a report. @@ -13,6 +14,10 @@ type ReportAttributes = { * The status of the brick road. */ brickRoadStatus: ValueOf | undefined; + /** + * The errors of the report. + */ + reportErrors: Errors; }; /** From fabc6e1fafe30c2f6628c29ba4aa042319fef1d8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 8 May 2025 15:26:47 +0200 Subject: [PATCH 02/20] replace in SidebarUtils --- src/libs/SidebarUtils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 7cd66ff50561c..a4a22d7750872 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -414,7 +414,6 @@ function getOptionData({ reportAttributes, oneTransactionThreadReport, reportNameValuePairs, - reportActions, personalDetails, preferredLocale, policy, @@ -425,7 +424,6 @@ function getOptionData({ report: OnyxEntry; oneTransactionThreadReport: OnyxEntry; reportNameValuePairs: OnyxEntry; - reportActions: OnyxEntry; personalDetails: OnyxEntry; preferredLocale: DeepValueOf; policy: OnyxEntry | undefined; @@ -444,7 +442,7 @@ function getOptionData({ const result: OptionData = { text: '', alternateText: undefined, - allReportErrors: getAllReportErrors(report, reportActions), + allReportErrors: reportAttributes?.reportErrors, brickRoadIndicator: null, tooltipText: null, subtitle: undefined, From ceccba1e8e680425ac7289add01b8185d930da99 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 8 May 2025 15:27:03 +0200 Subject: [PATCH 03/20] replace in OptionListUtils --- src/libs/OptionsListUtils.ts | 75 ++++++++++++------------------------ src/libs/ReportUtils.ts | 12 ++++++ 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 251a0db581b95..13342d7a5a280 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -28,6 +28,7 @@ import type { ReportNameValuePairs, TransactionViolation, } from '@src/types/onyx'; +import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -98,6 +99,7 @@ import { getReimbursementDeQueuedOrCanceledActionMessage, getReimbursementQueuedActionMessage, getRejectedReportMessage, + getReportAttributes, getReportAutomaticallyApprovedMessage, getReportAutomaticallyForwardedMessage, getReportAutomaticallySubmittedMessage, @@ -845,7 +847,7 @@ function createOption( accountIDs: number[], personalDetails: OnyxInputOrEntry, report: OnyxInputOrEntry, - reportActions: ReportActions, + reportAttributes: OnyxEntry, config?: PreviewConfig, ): OptionData { const {showChatPreviewLine = false, forcePolicyNamePreview = false, showPersonalDetails = false, selected, isSelected, isDisabled} = config ?? {}; @@ -907,8 +909,8 @@ function createOption( result.shouldShowSubscript = shouldReportShowSubscript(report); result.isPolicyExpenseChat = reportUtilsIsPolicyExpenseChat(report); result.isOwnPolicyExpenseChat = report.isOwnPolicyExpenseChat ?? false; - result.allReportErrors = getAllReportErrors(report, reportActions); - result.brickRoadIndicator = hasReportErrors(report, reportActions) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + result.allReportErrors = reportAttributes?.reportErrors ?? {}; + result.brickRoadIndicator = !isEmptyObject(reportAttributes?.reportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom ?? report.pendingFields.createChat : undefined; result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; @@ -987,16 +989,10 @@ function getReportOption(participant: Participant): OptionData { const report = getReportOrDraftReport(participant.reportID); const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); - const option = createOption( - visibleParticipantAccountIDs, - allPersonalDetails ?? {}, - !isEmptyObject(report) ? report : undefined, - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ); + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, getReportAttributes(report?.reportID), { + showChatPreviewLine: false, + forcePolicyNamePreview: false, + }); // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { @@ -1031,16 +1027,10 @@ function getReportOption(participant: Participant): OptionData { function getReportDisplayOption(report: OnyxEntry, unknownUserDetails: OnyxEntry): OptionData { const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); - const option = createOption( - visibleParticipantAccountIDs, - allPersonalDetails ?? {}, - !isEmptyObject(report) ? report : undefined, - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ); + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, getReportAttributes(report?.reportID), { + showChatPreviewLine: false, + forcePolicyNamePreview: false, + }); // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { @@ -1072,16 +1062,10 @@ function getPolicyExpenseReportOption(participant: Participant | OptionData): Op .filter(([, reportParticipant]) => reportParticipant && !isHiddenForCurrentUser(reportParticipant.notificationPreference)) .map(([accountID]) => Number(accountID)); - const option = createOption( - visibleParticipantAccountIDs, - allPersonalDetails ?? {}, - !isEmptyObject(expenseReport) ? expenseReport : null, - {}, - { - showChatPreviewLine: false, - forcePolicyNamePreview: false, - }, - ); + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(expenseReport) ? expenseReport : null, undefined, { + showChatPreviewLine: false, + forcePolicyNamePreview: false, + }); // Update text & alternateText because createOption returns workspace name only if report is owned by the user option.text = getPolicyName({report: expenseReport}); @@ -1173,7 +1157,7 @@ function processReport( reportMapEntry, reportOption: { item: report, - ...createOption(accountIDs, personalDetails, report, {}), + ...createOption(accountIDs, personalDetails, report, undefined), }, }; } @@ -1199,13 +1183,9 @@ function createOptionList(personalDetails: OnyxEntry, repor const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => ({ item: personalDetail, - ...createOption( - [personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], - personalDetails, - reportMapForAccountIDs[personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], - {}, - {showPersonalDetails: true}, - ), + ...createOption([personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], undefined, { + showPersonalDetails: true, + }), })); return { @@ -1219,7 +1199,7 @@ function createOptionFromReport(report: Report, personalDetails: OnyxEntry Date: Thu, 8 May 2025 17:03:29 +0200 Subject: [PATCH 04/20] replace occurencies of getAllReportErrors --- .../LHNOptionsList/LHNOptionsList.tsx | 9 ++++---- src/hooks/useSidebarOrderedReportIDs.tsx | 4 +++- src/libs/ReportUtils.ts | 21 ++++++++++++------- src/libs/SidebarUtils.ts | 14 ++++++++----- .../OnyxDerived/configs/reportAttributes.ts | 2 +- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index b5eba4588064e..c7e4b2b4549ea 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -21,7 +21,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isValidDraftComment} from '@libs/DraftCommentUtils'; import getPlatform from '@libs/getPlatform'; import Log from '@libs/Log'; -import {getIOUReportIDOfLastAction, getLastMessageTextForReport, hasReportErrors} from '@libs/OptionsListUtils'; +import {getIOUReportIDOfLastAction, getLastMessageTextForReport} from '@libs/OptionsListUtils'; import {getOneTransactionThreadReportID, getOriginalMessage, getSortedReportActionsForDisplay, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {canUserPerformWriteAction, requiresAttentionFromCurrentUser} from '@libs/ReportUtils'; import isProductTrainingElementDismissed from '@libs/TooltipUtils'; @@ -29,6 +29,7 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; import OptionRowLHNData from './OptionRowLHNData'; import OptionRowRendererComponent from './OptionRowRendererComponent'; @@ -70,11 +71,11 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio } return data.find((reportID) => { const itemFullReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - const itemReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]; + const itemReportErrors = reportAttributes?.[reportID]?.reportErrors; if (!itemFullReport) { return false; } - if (hasReportErrors(itemFullReport, itemReportActions)) { + if (!isEmptyObject(itemReportErrors)) { return true; } const itemParentReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${itemFullReport?.parentReportID}`]; @@ -82,7 +83,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const hasGBR = requiresAttentionFromCurrentUser(itemFullReport, itemParentReportAction); return hasGBR; }); - }, [isGBRorRBRTooltipDismissed, data, reportActions, reports]); + }, [isGBRorRBRTooltipDismissed, data, reportActions, reports, reportAttributes]); // When the first item renders we want to call the onFirstItemRendered callback. // At this point in time we know that the list is actually displaying items. diff --git a/src/hooks/useSidebarOrderedReportIDs.tsx b/src/hooks/useSidebarOrderedReportIDs.tsx index 8f9e7b85305bd..32b8de795f6ca 100644 --- a/src/hooks/useSidebarOrderedReportIDs.tsx +++ b/src/hooks/useSidebarOrderedReportIDs.tsx @@ -59,6 +59,7 @@ function SidebarOrderedReportIDsContextProvider({ const [reportsDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {initialValue: {}}); const draftAmount = Object.keys(reportsDrafts ?? {}).length; const [betas] = useOnyx(ONYXKEYS.BETAS); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports}); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {accountID} = useCurrentUserPersonalDetails(); @@ -80,10 +81,11 @@ function SidebarOrderedReportIDsContextProvider({ activeWorkspaceID, policyMemberAccountIDs, reportNameValuePairs, + reportAttributes, ), // we need reports draft in deps array to reload the list when a draft is added or removed // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - [chatReports, betas, policies, priorityMode, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, draftAmount, reportNameValuePairs], + [chatReports, betas, policies, priorityMode, transactionViolations, activeWorkspaceID, policyMemberAccountIDs, draftAmount, reportNameValuePairs, reportAttributes], ); const orderedReportIDs = useMemo(() => getOrderedReportIDs(), [getOrderedReportIDs]); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 24fc65b31631d..8f79bd52ed0cf 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -54,6 +54,7 @@ import type { TransactionViolation, UserWallet, } from '@src/types/onyx'; +import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {SelectedParticipant} from '@src/types/onyx/NewGroupChatDraft'; import type {OriginalMessageExportedToIntegration} from '@src/types/onyx/OldDotAction'; @@ -1048,14 +1049,14 @@ Onyx.connect({ callback: (value) => (activePolicyID = value), }); -let reportAttributes: ReportAttributesDerivedValue['reports']; +let reportAttributesDerivedValue: ReportAttributesDerivedValue['reports']; Onyx.connect({ key: ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, callback: (value) => { if (!value) { return; } - reportAttributes = value.reports; + reportAttributesDerivedValue = value.reports; }, }); @@ -4636,7 +4637,7 @@ function getReportName( ): string { // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined; - const attributes = reportAttributesParam ?? reportAttributes; + const attributes = reportAttributesParam ?? reportAttributesDerivedValue; const derivedNameExists = report && !!attributes?.[report.reportID]?.reportName; if (canUseDerivedValue && derivedNameExists) { return attributes[report.reportID].reportName; @@ -7648,9 +7649,13 @@ function getAllReportErrors(report: OnyxEntry, reportActions: OnyxEntry< return allReportErrors; } -function hasReportErrorsOtherThanFailedReceipt(report: Report, doesReportHaveViolations: boolean, transactionViolations: OnyxCollection) { - const reportActions = allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`] ?? {}; - const allReportErrors = getAllReportErrors(report, reportActions) ?? {}; +function hasReportErrorsOtherThanFailedReceipt( + report: Report, + doesReportHaveViolations: boolean, + transactionViolations: OnyxCollection, + reportAttributes?: ReportAttributes, +) { + const allReportErrors = reportAttributes?.reportErrors ?? {}; const transactionReportActions = getAllReportActions(report.reportID); const oneTransactionThreadReportID = getOneTransactionThreadReportID(report.reportID, transactionReportActions, undefined); let doesTransactionThreadReportHasViolations = false; @@ -10673,14 +10678,14 @@ function getReportPersonalDetailsParticipants(report: Report, personalDetailsPar } function getReportAttributes(reportID: string | undefined) { - if (!reportID || !reportAttributes?.[reportID]) { + if (!reportID || !reportAttributesDerivedValue?.[reportID]) { return { reportName: '', brickRoadStatus: undefined, reportErrors: {}, }; } - return reportAttributes[reportID]; + return reportAttributesDerivedValue[reportID]; } export { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index a4a22d7750872..ec1fac2309695 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -5,9 +5,10 @@ import type {ValueOf} from 'type-fest'; import type {PartialPolicyForSidebar} from '@hooks/useSidebarOrderedReportIDs'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportAttributesDerivedValue, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; import type Beta from '@src/types/onyx/Beta'; import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; import type Policy from '@src/types/onyx/Policy'; import type PriorityMode from '@src/types/onyx/PriorityMode'; import type Report from '@src/types/onyx/Report'; @@ -68,7 +69,6 @@ import { doesReportBelongToWorkspace, formatReportLastMessageText, getAllReportActionsErrorsAndReportActionThatRequiresAttention, - getAllReportErrors, getChatRoomSubtitle, getDisplayNameForParticipant, getDisplayNamesWithTooltips, @@ -201,6 +201,7 @@ function getOrderedReportIDs( currentPolicyID = '', policyMemberAccountIDs: number[] = [], reportNameValuePairs?: OnyxCollection, + reportAttributes?: ReportAttributesDerivedValue['reports'], ): string[] { Performance.markStart(CONST.TIMING.GET_ORDERED_REPORT_IDS); const isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD; @@ -218,11 +219,12 @@ function getOrderedReportIDs( if ((Object.values(CONST.REPORT.UNSUPPORTED_TYPE) as string[]).includes(report?.type ?? '')) { return; } + const singleReportAttributes = reportAttributes?.[report?.reportID]; const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(report, transactionViolations); const isHidden = isHiddenForCurrentUser(report); const isFocused = report.reportID === currentReportId; - const hasErrorsOtherThanFailedReceipt = hasReportErrorsOtherThanFailedReceipt(report, doesReportHaveViolations, transactionViolations); + const hasErrorsOtherThanFailedReceipt = hasReportErrorsOtherThanFailedReceipt(report, doesReportHaveViolations, transactionViolations, singleReportAttributes); const isReportInAccessible = report?.errorFields?.notFound; if (isOneTransactionThread(report.reportID, report.parentReportID, parentReportAction)) { return; @@ -345,11 +347,12 @@ function getReasonAndReportActionThatHasRedBrickRoad( report: Report, reportActions: OnyxEntry, hasViolations: boolean, + reportErrors: Errors, transactionViolations?: OnyxCollection, isReportArchived = false, ): ReasonAndReportActionThatHasRedBrickRoad | null { const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions); - const errors = getAllReportErrors(report, reportActions); + const errors = reportErrors; const hasErrors = Object.keys(errors).length !== 0; if (isReportArchived) { @@ -400,10 +403,11 @@ function shouldShowRedBrickRoad( report: Report, reportActions: OnyxEntry, hasViolations: boolean, + reportErrors: Errors, transactionViolations?: OnyxCollection, isReportArchived = false, ) { - return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isReportArchived); + return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isReportArchived); } /** diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index 6f78913b1e75a..5d884c0db5926 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -97,7 +97,7 @@ export default createOnyxDerivedValueConfig({ let brickRoadStatus; // if report has errors or violations, show red dot - if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, transactionViolations, !!isReportArchived)) { + if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactionViolations, !!isReportArchived)) { brickRoadStatus = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } // if report does not have error, check if it should show green dot From 2667e8660bae45531cb6a0c81884d033b9773633 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 9 May 2025 12:38:08 +0200 Subject: [PATCH 05/20] fix CI jobs --- .../LHNOptionsList/OptionRowLHNData.tsx | 1 - src/libs/DebugUtils.ts | 5 +- src/pages/Debug/Report/DebugReportPage.tsx | 4 +- tests/perf-test/SidebarUtils.perf-test.ts | 7 -- tests/unit/DebugUtilsTest.ts | 64 +++++++++++-------- tests/unit/SidebarUtilsTest.ts | 56 ++++++++-------- 6 files changed, 71 insertions(+), 66 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx index 94f691529fdb7..d01b69a7ad86b 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.tsx +++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx @@ -45,7 +45,6 @@ function OptionRowLHNData({ reportAttributes, oneTransactionThreadReport, reportNameValuePairs, - reportActions, personalDetails, preferredLocale: preferredLocale ?? CONST.LOCALES.DEFAULT, policy, diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index ab725d1937871..2c13a546db603 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -9,6 +9,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Beta, Policy, Report, ReportAction, ReportActions, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {Comment} from '@src/types/onyx/Transaction'; import {getLinkedTransactionID} from './ReportActionsUtils'; import {getReasonAndReportActionThatRequiresAttention, getReportNameValuePairs, isArchivedReport, reasonForReportToBeInOptionList, shouldDisplayViolationsRBRInLHN} from './ReportUtils'; @@ -1368,13 +1369,13 @@ type RBRReasonAndReportAction = { /** * Gets the report action that is causing the RBR to show up in LHN */ -function getReasonAndReportActionForRBRInLHNRow(report: Report, reportActions: OnyxEntry, hasViolations: boolean): RBRReasonAndReportAction | null { +function getReasonAndReportActionForRBRInLHNRow(report: Report, reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors): RBRReasonAndReportAction | null { // This will get removed as part of https://github.com/Expensify/App/issues/59961 // eslint-disable-next-line deprecation/deprecation const reportNameValuePairs = getReportNameValuePairs(report?.reportID); const {reason, reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, transactionViolations, isArchivedReport(reportNameValuePairs)) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isArchivedReport(reportNameValuePairs)) ?? {}; if (reason) { return {reason: `debug.reasonRBR.${reason}`, reportAction}; diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index d706dbe97610f..95f5057abda31 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -53,6 +53,7 @@ function DebugReportPage({ const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (attributes) => attributes?.reports?.[reportID]}); const transactionID = DebugUtils.getTransactionID(report, reportActions); const metadata = useMemo(() => { @@ -64,7 +65,8 @@ function DebugReportPage({ const shouldDisplayReportViolations = ReportUtils.isReportOwner(report) && ReportUtils.hasReportViolations(reportID); const hasViolations = !!shouldDisplayViolations || shouldDisplayReportViolations; const {reason: reasonGBR, reportAction: reportActionGBR} = DebugUtils.getReasonAndReportActionForGBRInLHNRow(report) ?? {}; - const {reason: reasonRBR, reportAction: reportActionRBR} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations) ?? {}; + const {reason: reasonRBR, reportAction: reportActionRBR} = + DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations, reportAttributes?.reportErrors ?? {}) ?? {}; const hasRBR = !!reasonRBR; const hasGBR = !hasRBR && !!reasonGBR; const reasonLHN = DebugUtils.getReasonForShowingRowInLHN(report, hasRBR); diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index d844185cd4bc9..a4691a84e4381 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -8,7 +8,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, TransactionViolation} from '@src/types/onyx'; import type Policy from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; -import type ReportAction from '@src/types/onyx/ReportAction'; import createCollection from '../utils/collections/createCollection'; import createPersonalDetails from '../utils/collections/personalDetails'; import createRandomPolicy from '../utils/collections/policies'; @@ -34,11 +33,6 @@ const allReports = createCollection( REPORTS_COUNT, ); -const reportActions = createCollection( - (item) => `${item.reportActionID}`, - (index) => createRandomReportAction(index), -); - const personalDetails = createCollection( (item) => item.accountID, (index) => createPersonalDetails(index), @@ -85,7 +79,6 @@ describe('SidebarUtils', () => { report, reportAttributes: undefined, reportNameValuePairs, - reportActions, personalDetails, preferredLocale, policy, diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 7dfde9d4cab6f..5e39be5cc6992 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -2,6 +2,7 @@ import Onyx from 'react-native-onyx'; import DateUtils from '@libs/DateUtils'; import type {ObjectType} from '@libs/DebugUtils'; import DebugUtils from '@libs/DebugUtils'; +import {getAllReportErrors} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report, ReportAction, ReportActions, Transaction} from '@src/types/onyx'; @@ -1108,6 +1109,7 @@ describe('DebugUtils', () => { }, undefined, false, + {}, ) ?? {}; expect(reportAction).toBeUndefined(); }); @@ -1159,6 +1161,7 @@ describe('DebugUtils', () => { MOCK_REPORTS[`${ONYXKEYS.COLLECTION.REPORT}1`] as Report, undefined, false, + {}, ) ?? {}; expect(reportAction).toBe(undefined); }); @@ -1217,7 +1220,8 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1275,7 +1279,8 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1334,7 +1339,8 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false) ?? {}; + const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS); + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']); }); it('returns correct report action which is a split bill and has an error', async () => { @@ -1396,7 +1402,8 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS); + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); it("returns undefined if there's no report action is a report preview or a split bill", async () => { @@ -1452,7 +1459,8 @@ describe('DebugUtils', () => { accountID: 12345, }, }); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false) ?? {}; + const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1498,6 +1506,7 @@ describe('DebugUtils', () => { ], }, }; + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow( { @@ -1505,35 +1514,35 @@ describe('DebugUtils', () => { }, MOCK_REPORT_ACTIONS, false, + reportErrors, ) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['1']); }); }); describe('reason', () => { it('returns correct reason when there are errors', () => { - const {reason} = - DebugUtils.getReasonAndReportActionForRBRInLHNRow( - { - reportID: '1', - }, - { - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { - reportActionID: '1', - actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, - created: '2024-09-20 13:11:11.122', - message: [ - { - type: 'TEXT', - text: 'Hello world!', - }, - ], - errors: { - randomError: 'Something went wrong', - }, + const mockedReport = { + reportID: '1', + }; + const mockedReportActions = { + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}1`]: { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + created: '2024-09-20 13:11:11.122', + message: [ + { + type: 'TEXT', + text: 'Hello world!', }, + ], + errors: { + randomError: 'Something went wrong', }, - false, - ) ?? {}; + }, + }; + + const reportErrors = getAllReportErrors(mockedReport, mockedReportActions); + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, mockedReportActions, false, reportErrors) ?? {}; expect(reason).toBe('debug.reasonRBR.hasErrors'); }); it('returns correct reason when there are violations', () => { @@ -1544,6 +1553,7 @@ describe('DebugUtils', () => { }, undefined, true, + {}, ) ?? {}; expect(reason).toBe('debug.reasonRBR.hasViolations'); }); @@ -1581,7 +1591,7 @@ describe('DebugUtils', () => { }, ], }); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false, {}) ?? {}; expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations'); }); }); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 3df324f59dd30..0b56520876315 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -5,6 +5,7 @@ import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import DateUtils from '@libs/DateUtils'; import {getReportActionMessageText} from '@libs/ReportActionsUtils'; +import {getAllReportErrors} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -94,6 +95,7 @@ describe('SidebarUtils', () => { MOCK_REPORT, MOCK_REPORT_ACTIONS, false, + {}, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ) ?? {}; @@ -114,7 +116,9 @@ describe('SidebarUtils', () => { const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -128,7 +132,8 @@ describe('SidebarUtils', () => { // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS); }); @@ -159,7 +164,9 @@ describe('SidebarUtils', () => { // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -175,10 +182,11 @@ describe('SidebarUtils', () => { }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; - + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -207,11 +215,11 @@ describe('SidebarUtils', () => { '1': MOCK_REPORT_ACTION, }; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; - + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTION); }); @@ -225,7 +233,7 @@ describe('SidebarUtils', () => { // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBeNull(); }); @@ -244,7 +252,6 @@ describe('SidebarUtils', () => { report: MOCK_REPORT_PINNED, reportAttributes: undefined, reportNameValuePairs: {}, - reportActions: {}, personalDetails: {}, preferredLocale: CONST.LOCALES.DEFAULT, policy: undefined, @@ -255,7 +262,6 @@ describe('SidebarUtils', () => { report: MOCK_REPORT_UNPINNED, reportAttributes: undefined, reportNameValuePairs: {}, - reportActions: {}, personalDetails: {}, preferredLocale: CONST.LOCALES.DEFAULT, policy: undefined, @@ -303,7 +309,7 @@ describe('SidebarUtils', () => { // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBeNull(); }); @@ -367,6 +373,7 @@ describe('SidebarUtils', () => { MOCK_REPORT, MOCK_REPORT_ACTIONS, false, + {}, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ); @@ -430,6 +437,7 @@ describe('SidebarUtils', () => { MOCK_REPORT, MOCK_REPORT_ACTIONS, false, + {}, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ); @@ -448,9 +456,9 @@ describe('SidebarUtils', () => { }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; - + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -463,7 +471,7 @@ describe('SidebarUtils', () => { const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -491,9 +499,9 @@ describe('SidebarUtils', () => { }, }; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; - + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -509,9 +517,9 @@ describe('SidebarUtils', () => { }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; - + const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -524,7 +532,7 @@ describe('SidebarUtils', () => { const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); @@ -545,7 +553,7 @@ describe('SidebarUtils', () => { // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); @@ -617,7 +625,6 @@ describe('SidebarUtils', () => { const result = SidebarUtils.getOptionData({ report, - reportActions, reportAttributes: undefined, reportNameValuePairs: {}, personalDetails: {}, @@ -676,7 +683,6 @@ describe('SidebarUtils', () => { const result = SidebarUtils.getOptionData({ report, - reportActions, reportAttributes: undefined, reportNameValuePairs: {}, personalDetails: {}, @@ -720,7 +726,6 @@ describe('SidebarUtils', () => { report, reportAttributes: undefined, reportNameValuePairs, - reportActions: {}, personalDetails: {}, preferredLocale, policy, @@ -756,7 +761,6 @@ describe('SidebarUtils', () => { report, reportAttributes: undefined, reportNameValuePairs, - reportActions: {}, personalDetails: LHNTestUtils.fakePersonalDetails, preferredLocale, policy, @@ -789,7 +793,6 @@ describe('SidebarUtils', () => { report, reportAttributes: undefined, reportNameValuePairs, - reportActions: {}, personalDetails: {}, preferredLocale, policy, @@ -826,7 +829,6 @@ describe('SidebarUtils', () => { report, reportAttributes: undefined, reportNameValuePairs, - reportActions: {}, personalDetails: {}, preferredLocale, policy, @@ -892,7 +894,6 @@ describe('SidebarUtils', () => { const result = SidebarUtils.getOptionData({ report, reportAttributes: undefined, - reportActions, reportNameValuePairs: {}, personalDetails: {}, policy: undefined, @@ -935,7 +936,6 @@ describe('SidebarUtils', () => { const result = SidebarUtils.getOptionData({ report, reportAttributes: undefined, - reportActions, reportNameValuePairs: {}, personalDetails: {}, policy: undefined, From 6f69d6e3e9212bd8b3e36bb59ec2823709a6aa35 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 9 May 2025 13:25:15 +0200 Subject: [PATCH 06/20] fix lint job --- src/pages/Debug/Report/DebugReportPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index 95f5057abda31..ff1c3ec5f6261 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -114,7 +114,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, reportActions, reportID, transactionViolations, translate]); + }, [report, reportActions, reportAttributes?.reportErrors, reportID, transactionViolations, translate]); const DebugDetailsTab = useCallback( () => ( From 5f7c4274cbfebd7b41460dc78438c46933a297b2 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 9 May 2025 13:30:45 +0200 Subject: [PATCH 07/20] fix lint changed in DebugReportPage --- src/pages/Debug/Report/DebugReportPage.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index ff1c3ec5f6261..a18be78d301a2 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -12,13 +12,13 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {navigateToConciergeChatAndDeleteReport} from '@libs/actions/Report'; import DebugUtils from '@libs/DebugUtils'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import type {DebugTabNavigatorRoutes} from '@libs/Navigation/DebugTabNavigator'; import DebugTabNavigator from '@libs/Navigation/DebugTabNavigator'; import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {DebugParamList} from '@libs/Navigation/types'; -import * as ReportUtils from '@libs/ReportUtils'; +import {hasReportViolations, isReportOwner, shouldDisplayViolationsRBRInLHN} from '@libs/ReportUtils'; import DebugDetails from '@pages/Debug/DebugDetails'; import DebugJSON from '@pages/Debug/DebugJSON'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -50,10 +50,13 @@ function DebugReportPage({ const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const theme = useTheme(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); - const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); - const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (attributes) => attributes?.reports?.[reportID]}); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); + const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { + selector: (attributes) => attributes?.reports?.[reportID], + canBeMissing: true, + }); const transactionID = DebugUtils.getTransactionID(report, reportActions); const metadata = useMemo(() => { @@ -61,8 +64,8 @@ function DebugReportPage({ return []; } - const shouldDisplayViolations = ReportUtils.shouldDisplayViolationsRBRInLHN(report, transactionViolations); - const shouldDisplayReportViolations = ReportUtils.isReportOwner(report) && ReportUtils.hasReportViolations(reportID); + const shouldDisplayViolations = shouldDisplayViolationsRBRInLHN(report, transactionViolations); + const shouldDisplayReportViolations = isReportOwner(report) && hasReportViolations(reportID); const hasViolations = !!shouldDisplayViolations || shouldDisplayReportViolations; const {reason: reasonGBR, reportAction: reportActionGBR} = DebugUtils.getReasonAndReportActionForGBRInLHNRow(report) ?? {}; const {reason: reasonRBR, reportAction: reportActionRBR} = @@ -215,7 +218,7 @@ function DebugReportPage({ {({safeAreaPaddingBottomStyle}) => ( From d8722a8f9707b43a0a8f3f578c783ef7e290ba23 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 12 May 2025 09:57:03 +0200 Subject: [PATCH 08/20] reduce diff --- Mobile-Expensify | 2 +- src/libs/OptionsListUtils.ts | 25 +++++++++---------------- src/libs/ReportUtils.ts | 22 ++++++++++------------ src/libs/SidebarUtils.ts | 3 +-- tests/unit/DebugUtilsTest.ts | 1 + 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 2992f02e5f3c3..6666c8792a1bb 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 2992f02e5f3c31ddca5edd51bcebe4e54041c8a4 +Subproject commit 6666c8792a1bb88d473b2d12cc152d5671207ef5 diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 13342d7a5a280..0232c357f109c 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -28,7 +28,6 @@ import type { ReportNameValuePairs, TransactionViolation, } from '@src/types/onyx'; -import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type {Attendee, Participant} from '@src/types/onyx/IOU'; import type {Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -843,13 +842,7 @@ function hasReportErrors(report: Report, reportActions: OnyxEntry /** * Creates a report list option */ -function createOption( - accountIDs: number[], - personalDetails: OnyxInputOrEntry, - report: OnyxInputOrEntry, - reportAttributes: OnyxEntry, - config?: PreviewConfig, -): OptionData { +function createOption(accountIDs: number[], personalDetails: OnyxInputOrEntry, report: OnyxInputOrEntry, config?: PreviewConfig): OptionData { const {showChatPreviewLine = false, forcePolicyNamePreview = false, showPersonalDetails = false, selected, isSelected, isDisabled} = config ?? {}; const result: OptionData = { text: undefined, @@ -909,8 +902,8 @@ function createOption( result.shouldShowSubscript = shouldReportShowSubscript(report); result.isPolicyExpenseChat = reportUtilsIsPolicyExpenseChat(report); result.isOwnPolicyExpenseChat = report.isOwnPolicyExpenseChat ?? false; - result.allReportErrors = reportAttributes?.reportErrors ?? {}; - result.brickRoadIndicator = !isEmptyObject(reportAttributes?.reportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + result.allReportErrors = getReportAttributes(report.reportID).reportErrors; + result.brickRoadIndicator = !isEmptyObject(result.allReportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom ?? report.pendingFields.createChat : undefined; result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; @@ -989,7 +982,7 @@ function getReportOption(participant: Participant): OptionData { const report = getReportOrDraftReport(participant.reportID); const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); - const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, getReportAttributes(report?.reportID), { + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, { showChatPreviewLine: false, forcePolicyNamePreview: false, }); @@ -1027,7 +1020,7 @@ function getReportOption(participant: Participant): OptionData { function getReportDisplayOption(report: OnyxEntry, unknownUserDetails: OnyxEntry): OptionData { const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); - const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, getReportAttributes(report?.reportID), { + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(report) ? report : undefined, { showChatPreviewLine: false, forcePolicyNamePreview: false, }); @@ -1062,7 +1055,7 @@ function getPolicyExpenseReportOption(participant: Participant | OptionData): Op .filter(([, reportParticipant]) => reportParticipant && !isHiddenForCurrentUser(reportParticipant.notificationPreference)) .map(([accountID]) => Number(accountID)); - const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(expenseReport) ? expenseReport : null, undefined, { + const option = createOption(visibleParticipantAccountIDs, allPersonalDetails ?? {}, !isEmptyObject(expenseReport) ? expenseReport : null, { showChatPreviewLine: false, forcePolicyNamePreview: false, }); @@ -1183,7 +1176,7 @@ function createOptionList(personalDetails: OnyxEntry, repor const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => ({ item: personalDetail, - ...createOption([personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], undefined, { + ...createOption([personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], personalDetails, reportMapForAccountIDs[personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID], { showPersonalDetails: true, }), })); @@ -1199,7 +1192,7 @@ function createOptionFromReport(report: Report, personalDetails: OnyxEntry, personalDetails?: Partial, invoiceReceiverPolicy?: OnyxEntry, - reportAttributesParam?: ReportAttributesDerivedValue['reports'], + reportAttributes?: ReportAttributesDerivedValue['reports'], ): string { // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined; - const attributes = reportAttributesParam ?? reportAttributesDerivedValue; + const attributes = reportAttributes ?? reportAttributesDerivedValue; const derivedNameExists = report && !!attributes?.[report.reportID]?.reportName; if (canUseDerivedValue && derivedNameExists) { return attributes[report.reportID].reportName; @@ -7677,9 +7677,9 @@ function hasReportErrorsOtherThanFailedReceipt( report: Report, doesReportHaveViolations: boolean, transactionViolations: OnyxCollection, - reportAttributes?: ReportAttributes, + reportAttributes?: ReportAttributesDerivedValue['reports'], ) { - const allReportErrors = reportAttributes?.reportErrors ?? {}; + const allReportErrors = getReportAttributes(report.reportID, reportAttributes).reportErrors; const transactionReportActions = getAllReportActions(report.reportID); const oneTransactionThreadReportID = getOneTransactionThreadReportID(report.reportID, transactionReportActions, undefined); let doesTransactionThreadReportHasViolations = false; @@ -10706,15 +10706,13 @@ function getReportPersonalDetailsParticipants(report: Report, personalDetailsPar }; } -function getReportAttributes(reportID: string | undefined) { - if (!reportID || !reportAttributesDerivedValue?.[reportID]) { - return { - reportName: '', - brickRoadStatus: undefined, - reportErrors: {}, - }; +function getReportAttributes(reportID: string | undefined, reportAttributes?: ReportAttributesDerivedValue['reports']) { + const attributes = reportAttributes ?? reportAttributesDerivedValue; + + if (!reportID || !attributes?.[reportID]) { + return {} as ReportAttributes; } - return reportAttributesDerivedValue[reportID]; + return attributes[reportID]; } export { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ec1fac2309695..ca1c30c6e5c88 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -219,12 +219,11 @@ function getOrderedReportIDs( if ((Object.values(CONST.REPORT.UNSUPPORTED_TYPE) as string[]).includes(report?.type ?? '')) { return; } - const singleReportAttributes = reportAttributes?.[report?.reportID]; const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID); const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(report, transactionViolations); const isHidden = isHiddenForCurrentUser(report); const isFocused = report.reportID === currentReportId; - const hasErrorsOtherThanFailedReceipt = hasReportErrorsOtherThanFailedReceipt(report, doesReportHaveViolations, transactionViolations, singleReportAttributes); + const hasErrorsOtherThanFailedReceipt = hasReportErrorsOtherThanFailedReceipt(report, doesReportHaveViolations, transactionViolations, reportAttributes); const isReportInAccessible = report?.errorFields?.notFound; if (isOneTransactionThread(report.reportID, report.parentReportID, parentReportAction)) { return; diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 883311fe10ab2..becfc73d6d2dd 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1565,6 +1565,7 @@ describe('DebugUtils', () => { }, undefined, true, + {}, true, ) ?? {}; expect(reason).toBe(undefined); From ada63055d9a630dafde73e6d6aa76eaf04b8ccbc Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 12 May 2025 11:26:49 +0200 Subject: [PATCH 09/20] update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 6666c8792a1bb..2992f02e5f3c3 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 6666c8792a1bb88d473b2d12cc152d5671207ef5 +Subproject commit 2992f02e5f3c31ddca5edd51bcebe4e54041c8a4 From 2d64a4e57037b8f44a6e17c9b831540cccadbcfe Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 12 May 2025 12:59:27 +0200 Subject: [PATCH 10/20] fix changed files --- src/hooks/useSidebarOrderedReportIDs.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hooks/useSidebarOrderedReportIDs.tsx b/src/hooks/useSidebarOrderedReportIDs.tsx index 32b8de795f6ca..c974b7f515efc 100644 --- a/src/hooks/useSidebarOrderedReportIDs.tsx +++ b/src/hooks/useSidebarOrderedReportIDs.tsx @@ -51,15 +51,15 @@ function SidebarOrderedReportIDsContextProvider({ */ currentReportIDForTests, }: SidebarOrderedReportIDsContextProviderProps) { - const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE, {initialValue: CONST.PRIORITY_MODE.DEFAULT}); - const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (c) => mapOnyxCollectionItems(c, policySelector)}); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); - const [reportsDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {initialValue: {}}); + const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE, {initialValue: CONST.PRIORITY_MODE.DEFAULT, canBeMissing: true}); + const [chatReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); + const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: (c) => mapOnyxCollectionItems(c, policySelector), canBeMissing: true}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); + const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, {canBeMissing: true}); + const [reportsDrafts] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, {initialValue: {}, canBeMissing: true}); const draftAmount = Object.keys(reportsDrafts ?? {}).length; - const [betas] = useOnyx(ONYXKEYS.BETAS); - const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports}); + const [betas] = useOnyx(ONYXKEYS.BETAS, {canBeMissing: true}); + const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: (value) => value?.reports, canBeMissing: true}); const {shouldUseNarrowLayout} = useResponsiveLayout(); const {accountID} = useCurrentUserPersonalDetails(); From 99033c4a338248a52a76e30cf330ad327564544f Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 12 May 2025 13:22:10 +0200 Subject: [PATCH 11/20] fix CI jobs --- src/libs/OptionsListUtils.ts | 2 +- src/libs/ReportUtils.ts | 5 ++--- tests/unit/SidebarTest.ts | 9 ++++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 0232c357f109c..5ef5f68baad26 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -902,7 +902,7 @@ function createOption(accountIDs: number[], personalDetails: OnyxInputOrEntry, reportAttributes?: ReportAttributesDerivedValue['reports'], ) { - const allReportErrors = getReportAttributes(report.reportID, reportAttributes).reportErrors; + const allReportErrors = getReportAttributes(report.reportID, reportAttributes)?.reportErrors ?? {}; const transactionReportActions = getAllReportActions(report.reportID); const oneTransactionThreadReportID = getOneTransactionThreadReportID(report.reportID, transactionReportActions, undefined); let doesTransactionThreadReportHasViolations = false; @@ -10710,7 +10709,7 @@ function getReportAttributes(reportID: string | undefined, reportAttributes?: Re const attributes = reportAttributes ?? reportAttributesDerivedValue; if (!reportID || !attributes?.[reportID]) { - return {} as ReportAttributes; + return; } return attributes[reportID]; } diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 0b9611e9a9006..b81d86309ffca 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -1,6 +1,7 @@ import {screen} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; import DateUtils from '@libs/DateUtils'; +import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,12 +23,14 @@ const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'email1@test.com'; describe('Sidebar', () => { - beforeAll(() => + beforeAll(() => { Onyx.init({ keys: ONYXKEYS, evictableKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], - }), - ); + }); + + initOnyxDerivedValues(); + }); beforeEach(() => { // Wrap Onyx each onyx action with waitForBatchedUpdates From c18b10d239b6b955f160867122f5da6106df0254 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 21 May 2025 11:25:46 +0200 Subject: [PATCH 12/20] use reportAttributes from param --- Mobile-Expensify | 2 +- src/components/LHNOptionsList/LHNOptionsList.tsx | 2 +- src/libs/OptionsListUtils.ts | 10 ++++++++-- src/libs/ReportUtils.ts | 12 +----------- src/libs/SidebarUtils.ts | 5 ++--- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 734e5db55dd21..8304d07aa8ccc 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 734e5db55dd21aaabf687a7ad2ac1679d94d72c0 +Subproject commit 8304d07aa8ccc31ca91f17b513eafa8eb48350bf diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 4ad641fa70c39..0a92e711133ed 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -23,7 +23,7 @@ import getPlatform from '@libs/getPlatform'; import Log from '@libs/Log'; import {getIOUReportIDOfLastAction, getLastMessageTextForReport} from '@libs/OptionsListUtils'; import {getOneTransactionThreadReportID, getOriginalMessage, getSortedReportActionsForDisplay, isMoneyRequestAction} from '@libs/ReportActionsUtils'; -import {canUserPerformWriteAction, getReportAttributes} from '@libs/ReportUtils'; +import {canUserPerformWriteAction} from '@libs/ReportUtils'; import isProductTrainingElementDismissed from '@libs/TooltipUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index fb5587a2b3603..da90d165246fe 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -27,6 +27,7 @@ import type { Report, ReportAction, ReportActions, + ReportAttributesDerivedValue, ReportNameValuePairs, TransactionViolation, } from '@src/types/onyx'; @@ -105,7 +106,6 @@ import { getReimbursementDeQueuedOrCanceledActionMessage, getReimbursementQueuedActionMessage, getRejectedReportMessage, - getReportAttributes, getReportAutomaticallyApprovedMessage, getReportAutomaticallyForwardedMessage, getReportAutomaticallySubmittedMessage, @@ -451,6 +451,12 @@ Onyx.connect({ callback: (value) => (nvpDismissedProductTraining = value), }); +let reportAttributesDerivedValue: ReportAttributesDerivedValue['reports'] | undefined; +Onyx.connect({ + key: ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, + callback: (value) => (reportAttributesDerivedValue = value?.reports), +}); + /** * @param defaultValues {login: accountID} In workspace invite page, when new user is added we pass available data to opt in * @returns Returns avatar data for a list of user accountIDs @@ -915,7 +921,7 @@ function createOption(accountIDs: number[], personalDetails: OnyxInputOrEntry, reportAttributes?: ReportAttributesDerivedValue['reports'], ) { - const allReportErrors = getReportAttributes(report.reportID, reportAttributes)?.reportErrors ?? {}; + const allReportErrors = reportAttributes?.[report?.reportID]?.reportErrors ?? {}; const transactionReportActions = getAllReportActions(report.reportID); const oneTransactionThreadReportID = getOneTransactionThreadReportID(report.reportID, transactionReportActions, undefined); let doesTransactionThreadReportHasViolations = false; @@ -10773,15 +10773,6 @@ function getReportPersonalDetailsParticipants(report: Report, personalDetailsPar }; } -function getReportAttributes(reportID: string | undefined, reportAttributes?: ReportAttributesDerivedValue['reports']) { - const attributes = reportAttributes ?? reportAttributesDerivedValue; - - if (!reportID || !attributes?.[reportID]) { - return; - } - return attributes[reportID]; -} - export { addDomainToShortMention, completeShortMention, @@ -11159,7 +11150,6 @@ export { getReportPersonalDetailsParticipants, isAllowedToSubmitDraftExpenseReport, isWorkspaceEligibleForReportChange, - getReportAttributes, }; export type { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ef37b3211847b..1a66a26ed1fc0 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -76,7 +76,6 @@ import { getIcons, getParticipantsAccountIDsForDisplay, getPolicyName, - getReportAttributes, getReportDescription, getReportName, getReportNameValuePairs, @@ -245,7 +244,7 @@ function getOrderedReportIDs( // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing report.isPinned || (!isInFocusMode && isArchivedReport(reportNameValuePairs)) || - getReportAttributes(report.reportID, reportAttributes)?.requiresAttention; + reportAttributes?.[report?.reportID]?.requiresAttention; if (isHidden && !shouldOverrideHidden) { return; } @@ -294,7 +293,7 @@ function getOrderedReportIDs( const isPinned = report?.isPinned ?? false; const rNVPs = reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; - if (isPinned || getReportAttributes(report?.reportID, reportAttributes)?.requiresAttention) { + if (isPinned || reportAttributes?.[report?.reportID]?.requiresAttention) { pinnedAndGBRReports.push(miniReport); } else if (report?.hasErrorsOtherThanFailedReceipt) { errorReports.push(miniReport); From 631692fde3c63b88c4427718b6bf5b32a7a0bfa3 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 21 May 2025 11:27:42 +0200 Subject: [PATCH 13/20] update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 8304d07aa8ccc..734e5db55dd21 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 8304d07aa8ccc31ca91f17b513eafa8eb48350bf +Subproject commit 734e5db55dd21aaabf687a7ad2ac1679d94d72c0 From 6d8d8332ba3bf350d7a21bea159f91e5db71513f Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 21 May 2025 12:14:44 +0200 Subject: [PATCH 14/20] remove deprecated getTransaction from SidebarUtils --- src/libs/SidebarUtils.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 1a66a26ed1fc0..5796abd7540a1 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -5,7 +5,7 @@ import type {ValueOf} from 'type-fest'; import type {PartialPolicyForSidebar} from '@hooks/useSidebarOrderedReports'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportAttributesDerivedValue, ReportNameValuePairs, TransactionViolation} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, ReportActions, ReportAttributesDerivedValue, ReportNameValuePairs, Transaction, TransactionViolation} from '@src/types/onyx'; import type Beta from '@src/types/onyx/Beta'; import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type {Errors} from '@src/types/onyx/OnyxCommon'; @@ -116,7 +116,7 @@ import { shouldReportShowSubscript, } from './ReportUtils'; import {getTaskReportActionMessage} from './TaskUtils'; -import {getTransaction, getTransactionID} from './TransactionUtils'; +import {getTransactionID} from './TransactionUtils'; type WelcomeMessage = {showReportName: boolean; phrase1?: string; phrase2?: string; phrase3?: string; phrase4?: string; messageText?: string; messageHtml?: string}; @@ -164,6 +164,18 @@ Onyx.connect({ }, }); +let allTransactions: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.TRANSACTION, + waitForCollectionCallback: true, + callback: (value) => { + if (!value) { + return; + } + allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => !!transaction)); + }, +}); + function compareStringDates(a: string, b: string): 0 | 1 | -1 { if (a < b) { return -1; @@ -379,7 +391,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( const transactionThreadReportID = getOneTransactionThreadReportID(report.reportID, reportActions ?? []); if (transactionThreadReportID) { const transactionID = getTransactionID(transactionThreadReportID); - const transaction = getTransaction(transactionID); + const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; if (hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, @@ -387,7 +399,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( } } const transactionID = getTransactionID(report.reportID); - const transaction = getTransaction(transactionID); + const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; if (isTransactionThread(parentReportAction) && hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, From 00760da1190ac6fe6a76224d200e57d0e9640318 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 08:38:49 +0200 Subject: [PATCH 15/20] remove hasReportErrors --- Mobile-Expensify | 2 +- src/libs/OptionsListUtils.ts | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 727a0d6c95a54..734e5db55dd21 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 727a0d6c95a54632b8673861ceac57687c9e41b5 +Subproject commit 734e5db55dd21aaabf687a7ad2ac1679d94d72c0 diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 679dfa541a30c..f6ff23f329cc7 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -89,7 +89,6 @@ import { import { canUserPerformWriteAction, formatReportLastMessageText, - getAllReportErrors, getChatByParticipants, getChatRoomSubtitle, getDeletedParentActionMessageForChatReport, @@ -849,10 +848,6 @@ function getLastMessageTextForReport( return lastMessageTextFromReport || (report?.lastMessageText ?? ''); } -function hasReportErrors(report: Report, reportActions: OnyxEntry) { - return !isEmptyObject(getAllReportErrors(report, reportActions)); -} - /** * Creates a report list option */ @@ -1201,7 +1196,7 @@ function processReport( reportMapEntry, reportOption: { item: report, - ...createOption(accountIDs, personalDetails, report, undefined), + ...createOption(accountIDs, personalDetails, report), }, }; } @@ -2444,7 +2439,6 @@ export { getAttendeeOptions, getAlternateText, getReportDisplayOption, - hasReportErrors, combineOrderingOfReportsAndPersonalDetails, filterWorkspaceChats, orderWorkspaceOptions, From 29445ff7eba3a5ffc15c864c4b37a20557da2fba Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 09:09:44 +0200 Subject: [PATCH 16/20] migrate from deprecated getTransaction --- src/libs/DebugUtils.ts | 3 +- src/libs/SidebarUtils.ts | 20 +-- .../OnyxDerived/configs/reportAttributes.ts | 6 +- src/pages/Debug/Report/DebugReportPage.tsx | 3 +- tests/unit/DebugUtilsTest.ts | 19 ++- tests/unit/SidebarUtilsTest.ts | 136 ++++++++++++++++-- 6 files changed, 146 insertions(+), 41 deletions(-) diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index ff70a40772d87..73fbb700ebe8c 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1381,12 +1381,13 @@ type RBRReasonAndReportAction = { function getReasonAndReportActionForRBRInLHNRow( report: Report, reportActions: OnyxEntry, + transactions: OnyxCollection, hasViolations: boolean, reportErrors: Errors, isArchivedReport = false, ): RBRReasonAndReportAction | null { const {reason, reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isArchivedReport) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isArchivedReport) ?? {}; if (reason) { return {reason: `debug.reasonRBR.${reason}`, reportAction}; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 5796abd7540a1..95d2360b836ee 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -164,18 +164,6 @@ Onyx.connect({ }, }); -let allTransactions: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.TRANSACTION, - waitForCollectionCallback: true, - callback: (value) => { - if (!value) { - return; - } - allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => !!transaction)); - }, -}); - function compareStringDates(a: string, b: string): 0 | 1 | -1 { if (a < b) { return -1; @@ -358,6 +346,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, + transactions: OnyxCollection, transactionViolations?: OnyxCollection, isReportArchived = false, ): ReasonAndReportActionThatHasRedBrickRoad | null { @@ -391,7 +380,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( const transactionThreadReportID = getOneTransactionThreadReportID(report.reportID, reportActions ?? []); if (transactionThreadReportID) { const transactionID = getTransactionID(transactionThreadReportID); - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const transaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; if (hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, @@ -399,7 +388,7 @@ function getReasonAndReportActionThatHasRedBrickRoad( } } const transactionID = getTransactionID(report.reportID); - const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const transaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; if (isTransactionThread(parentReportAction) && hasReceiptError(transaction)) { return { reason: CONST.RBR_REASONS.HAS_ERRORS, @@ -414,10 +403,11 @@ function shouldShowRedBrickRoad( reportActions: OnyxEntry, hasViolations: boolean, reportErrors: Errors, + transactions: OnyxCollection, transactionViolations?: OnyxCollection, isReportArchived = false, ) { - return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactionViolations, isReportArchived); + return !!getReasonAndReportActionThatHasRedBrickRoad(report, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isReportArchived); } /** diff --git a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts index d0a80241a23bd..4a63376d167f7 100644 --- a/src/libs/actions/OnyxDerived/configs/reportAttributes.ts +++ b/src/libs/actions/OnyxDerived/configs/reportAttributes.ts @@ -34,12 +34,12 @@ export default createOnyxDerivedValueConfig({ ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, ONYXKEYS.COLLECTION.REPORT_ACTIONS, ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, - ONYXKEYS.PERSONAL_DETAILS_LIST, ONYXKEYS.COLLECTION.TRANSACTION, + ONYXKEYS.PERSONAL_DETAILS_LIST, ONYXKEYS.COLLECTION.POLICY, ONYXKEYS.COLLECTION.REPORT_METADATA, ], - compute: ([reports, preferredLocale, transactionViolations, reportActions, reportNameValuePairs], {currentValue, sourceValues, areAllConnectionsSet}) => { + compute: ([reports, preferredLocale, transactionViolations, reportActions, reportNameValuePairs, transactions], {currentValue, sourceValues, areAllConnectionsSet}) => { if (!areAllConnectionsSet) { return { reports: {}, @@ -119,7 +119,7 @@ export default createOnyxDerivedValueConfig({ let brickRoadStatus; // if report has errors or violations, show red dot - if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactionViolations, !!isReportArchived)) { + if (SidebarUtils.shouldShowRedBrickRoad(report, reportActionsList, hasAnyViolations, reportErrors, transactions, transactionViolations, !!isReportArchived)) { brickRoadStatus = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } // if report does not have error, check if it should show green dot diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index c930029507bfd..775898614839b 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -53,6 +53,7 @@ function DebugReportPage({ const theme = useTheme(); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {canBeMissing: true}); + const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: true}); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {canBeMissing: true}); const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, { selector: (attributes) => attributes?.reports?.[reportID], @@ -71,7 +72,7 @@ function DebugReportPage({ const hasViolations = !!shouldDisplayViolations || shouldDisplayReportViolations; const {reason: reasonGBR, reportAction: reportActionGBR} = DebugUtils.getReasonAndReportActionForGBRInLHNRow(report) ?? {}; const {reason: reasonRBR, reportAction: reportActionRBR} = - DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, hasViolations, reportAttributes?.reportErrors ?? {}, isReportArchived) ?? {}; + DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, reportActions, transactions, hasViolations, reportAttributes?.reportErrors ?? {}, isReportArchived) ?? {}; const hasRBR = !!reasonRBR; const hasGBR = !hasRBR && !!reasonGBR; const reasonLHN = DebugUtils.getReasonForShowingRowInLHN(report, hasRBR); diff --git a/tests/unit/DebugUtilsTest.ts b/tests/unit/DebugUtilsTest.ts index 21a2120dfb279..245195ef5cc74 100644 --- a/tests/unit/DebugUtilsTest.ts +++ b/tests/unit/DebugUtilsTest.ts @@ -1108,6 +1108,7 @@ describe('DebugUtils', () => { reportID: '1', }, undefined, + {}, false, {}, ) ?? {}; @@ -1160,6 +1161,7 @@ describe('DebugUtils', () => { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style MOCK_REPORTS[`${ONYXKEYS.COLLECTION.REPORT}1`] as Report, undefined, + {}, false, {}, ) ?? {}; @@ -1221,7 +1223,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1280,7 +1282,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1340,7 +1342,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, false, reportErrors) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_CHAT_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_CHAT_REPORT_ACTIONS['1']); }); it('returns correct report action which is a split bill and has an error', async () => { @@ -1403,7 +1405,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_CHAT_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); it("returns undefined if there's no report action is a report preview or a split bill", async () => { @@ -1460,7 +1462,7 @@ describe('DebugUtils', () => { }, }); const reportErrors = getAllReportErrors(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS); - const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors) ?? {}; + const {reportAction} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(MOCK_IOU_REPORT, MOCK_REPORT_ACTIONS, {}, false, reportErrors) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTIONS['3']); }); }); @@ -1513,6 +1515,7 @@ describe('DebugUtils', () => { reportID: '1', }, MOCK_REPORT_ACTIONS, + {}, false, reportErrors, ) ?? {}; @@ -1542,7 +1545,7 @@ describe('DebugUtils', () => { }; const reportErrors = getAllReportErrors(mockedReport, mockedReportActions); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, mockedReportActions, false, reportErrors) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(mockedReport, mockedReportActions, {}, false, reportErrors) ?? {}; expect(reason).toBe('debug.reasonRBR.hasErrors'); }); it('returns correct reason when there are violations', () => { @@ -1552,6 +1555,7 @@ describe('DebugUtils', () => { reportID: '1', }, undefined, + {}, true, {}, ) ?? {}; @@ -1564,6 +1568,7 @@ describe('DebugUtils', () => { reportID: '1', }, undefined, + {}, true, {}, true, @@ -1604,7 +1609,7 @@ describe('DebugUtils', () => { }, ], }); - const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, false, {}) ?? {}; + const {reason} = DebugUtils.getReasonAndReportActionForRBRInLHNRow(report, {}, {}, false, {}) ?? {}; expect(reason).toBe('debug.reasonRBR.hasTransactionThreadViolations'); }); }); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 0b56520876315..6b75676712c0b 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -10,7 +10,7 @@ import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report, ReportAction, ReportActions, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {Policy, Report, ReportAction, ReportActions, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; import type {TransactionViolationsCollectionDataSet} from '@src/types/onyx/TransactionViolation'; import createRandomPolicy from '../utils/collections/policies'; @@ -67,6 +67,10 @@ describe('SidebarUtils', () => { reportID: MOCK_REPORT.reportID, }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -96,6 +100,7 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ) ?? {}; @@ -113,12 +118,21 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -128,12 +142,21 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + true, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS); }); @@ -160,13 +183,22 @@ describe('SidebarUtils', () => { }, }, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -181,12 +213,21 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reason} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); }); @@ -214,12 +255,21 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); const {reportAction} = - SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current) ?? {}; + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ) ?? {}; expect(reportAction).toMatchObject(MOCK_REPORT_ACTION); }); @@ -229,11 +279,20 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBeNull(); }); @@ -305,11 +364,20 @@ describe('SidebarUtils', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '1': MOCK_REPORT_ACTION, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + {}, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBeNull(); }); @@ -347,6 +415,10 @@ describe('SidebarUtils', () => { reportID: MOCK_REPORT.reportID, }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -374,6 +446,7 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ); @@ -412,6 +485,10 @@ describe('SidebarUtils', () => { reportID: MOCK_REPORT.reportID, }; + const MOCK_TRANSACTIONS = { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + } as OnyxCollection; + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ { @@ -438,6 +515,7 @@ describe('SidebarUtils', () => { MOCK_REPORT_ACTIONS, false, {}, + MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection, isReportArchived.current, ); @@ -455,10 +533,19 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -468,10 +555,11 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(true); }); @@ -498,10 +586,19 @@ describe('SidebarUtils', () => { }, }, }; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -516,10 +613,19 @@ describe('SidebarUtils', () => { }, }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const reportErrors = getAllReportErrors(MOCK_REPORT, MOCK_REPORT_ACTIONS); const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, reportErrors, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad( + MOCK_REPORT, + MOCK_REPORT_ACTIONS, + false, + reportErrors, + MOCK_TRANSACTIONS, + MOCK_TRANSACTION_VIOLATIONS, + isReportArchived.current, + ); expect(result).toBe(true); }); @@ -529,10 +635,11 @@ describe('SidebarUtils', () => { reportID: '1', }; const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); @@ -549,11 +656,12 @@ describe('SidebarUtils', () => { // This report with reportID 5 is already archived from previous tests // where we set reportNameValuePairs with private_isArchived const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTIONS = {}; const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; // Simulate how components determind if a report is archived by using this hook const {result: isReportArchived} = renderHook(() => useReportIsArchived(MOCK_REPORT?.reportID)); - const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, {}, MOCK_TRANSACTIONS, MOCK_TRANSACTION_VIOLATIONS, isReportArchived.current); expect(result).toBe(false); }); From 11d6ee4a7183486ac48d0ff6cad227fd717fa443 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 09:19:03 +0200 Subject: [PATCH 17/20] update deps in DebugReportPage --- src/pages/Debug/Report/DebugReportPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index 775898614839b..e1ea9d98bfb04 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -120,7 +120,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, reportActions, reportAttributes?.reportErrors, reportID, transactionViolations, translate, isReportArchived]); + }, [report, transactionViolations, reportID, reportActions, transactions, reportAttributes?.reportErrors, isReportArchived, translate]); const DebugDetailsTab = useCallback( () => ( From bd560a23800102e733671f5699446f90c727a9cd Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 12:20:52 +0200 Subject: [PATCH 18/20] update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 734e5db55dd21..d2f4f38ad8066 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 734e5db55dd21aaabf687a7ad2ac1679d94d72c0 +Subproject commit d2f4f38ad8066d40aa17bd559fd983300ee9da5c From 5f7386c28c7de416b0d8788a4c3306c100ceb2e5 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 15:21:36 +0200 Subject: [PATCH 19/20] add getAllReportErrors tests --- tests/unit/OnyxDerivedTest.ts | 147 ++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/unit/OnyxDerivedTest.ts b/tests/unit/OnyxDerivedTest.ts index aabaafa0916a6..3fe04acd0e8a7 100644 --- a/tests/unit/OnyxDerivedTest.ts +++ b/tests/unit/OnyxDerivedTest.ts @@ -1,8 +1,11 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import Onyx from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReportActions} from '@src/types/onyx/ReportAction'; +import createRandomReport from '../utils/collections/reports'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; @@ -84,5 +87,149 @@ describe('OnyxDerived', () => { locale: 'es', }); }); + + describe('reportErrors', () => { + it('returns empty errors when no errors exist', async () => { + const report = createRandomReport(1); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await waitForBatchedUpdates(); + + const derivedReportAttributes = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES); + expect(derivedReportAttributes?.reports[report.reportID].reportErrors).toEqual({}); + }); + + it('combines report error fields with report action errors', async () => { + const report = { + ...createRandomReport(1), + errorFields: { + field1: { + '1234567890': 'Error message 1', + }, + }, + }; + + const reportActions: ReportActions = { + '1': { + reportActionID: '1', + actionName: 'ADDCOMMENT', + created: '2024-01-01', + message: [{html: 'some content', text: 'some content', type: 'text'}], + errors: { + field2: { + '1234567891': 'Error message 2', + }, + }, + }, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, reportActions); + + await waitForBatchedUpdates(); + + const derivedReportAttributes = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES); + + await waitForBatchedUpdates(); + + expect(derivedReportAttributes?.reports[report.reportID].reportErrors).toEqual({ + '1234567890': 'Error message 1', + '1234567891': 'Error message 2', + }); + }); + + it('handles multiple error sources', async () => { + const report = { + ...createRandomReport(1), + errorFields: { + field1: { + '1234567890': 'Error message 1', + }, + field2: { + '1234567891': 'Error message 2', + }, + }, + }; + + const reportActions: ReportActions = { + '1': { + reportActionID: '1', + actionName: 'ADDCOMMENT', + created: '2024-01-01', + message: [{html: 'some content', text: 'some content', type: 'text'}], + errors: { + field3: { + '1234567892': 'Error message 3', + }, + }, + }, + '2': { + reportActionID: '2', + actionName: 'ADDCOMMENT', + created: '2024-01-01', + message: [{html: 'some content', text: 'some content', type: 'text'}], + errors: { + field4: { + '1234567893': 'Error message 4', + }, + }, + }, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, reportActions); + await waitForBatchedUpdates(); + + const derivedReportAttributes = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES); + expect(derivedReportAttributes?.reports[report.reportID].reportErrors).toEqual({ + '1234567890': 'Error message 1', + '1234567891': 'Error message 2', + '1234567892': 'Error message 3', + '1234567893': 'Error message 4', + }); + }); + + it('handles empty error objects in sources', async () => { + const report = { + ...createRandomReport(1), + errorFields: { + field1: {}, + field2: { + '1234567890': 'Error message 1', + }, + }, + }; + + const reportActions: ReportActions = { + '1': { + reportActionID: '1', + actionName: 'ADDCOMMENT', + created: '2024-01-01', + message: [{html: 'some content', text: 'some content', type: 'text'}], + errors: {}, + }, + '2': { + reportActionID: '2', + actionName: 'ADDCOMMENT', + created: '2024-01-01', + message: [{html: 'some content', text: 'some content', type: 'text'}], + errors: { + field3: { + '1234567891': 'Error message 2', + }, + }, + }, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, reportActions); + await waitForBatchedUpdates(); + + const derivedReportAttributes = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES); + expect(derivedReportAttributes?.reports[report.reportID].reportErrors).toEqual({ + '1234567890': 'Error message 1', + '1234567891': 'Error message 2', + }); + }); + }); }); }); From 629804eb3d498b5ae9d3b6d0b6cd57f2b227e9ae Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 22 May 2025 15:22:03 +0200 Subject: [PATCH 20/20] update submodule --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index d2f4f38ad8066..6b668f1fa1e01 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit d2f4f38ad8066d40aa17bd559fd983300ee9da5c +Subproject commit 6b668f1fa1e016a278280d48de8f65d5d07d0871