From cf0e2a762a719d3b013ebf77d0ca92b493475e08 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Thu, 9 Jan 2025 00:04:34 +0000 Subject: [PATCH 1/2] fix: missing RBR from the report in LHN when export to QBO fails --- src/libs/SidebarUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 626dc8d5ed68..18313d85e637 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -256,7 +256,8 @@ function getReasonAndReportActionThatHasRedBrickRoad( hasViolations: boolean, transactionViolations?: OnyxCollection, ): ReasonAndReportActionThatHasRedBrickRoad | null { - const {errors, reportAction} = ReportUtils.getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions); + const {reportAction} = ReportUtils.getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions); + const errors = ReportUtils.getAllReportErrors(report, reportActions); const hasErrors = Object.keys(errors).length !== 0; if (ReportUtils.shouldDisplayViolationsRBRInLHN(report, transactionViolations)) { From 8390a98635bbce64d18346657aea2910ba9c1e54 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Sun, 12 Jan 2025 16:31:45 +0000 Subject: [PATCH 2/2] test: add SidebarUtils test suite --- src/types/onyx/TransactionViolation.ts | 7 +- tests/unit/SidebarUtilsTest.ts | 337 +++++++++++++++++++++++++ 2 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 tests/unit/SidebarUtilsTest.ts diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index 49ed1545f37b..c6c920b7df7b 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -1,5 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +import type ONYXKEYS from '@src/ONYXKEYS'; +import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; /** * Names of violations. @@ -109,5 +111,8 @@ type TransactionViolation = { /** Collection of transaction violations */ type TransactionViolations = TransactionViolation[]; -export type {TransactionViolation, ViolationName, ViolationType, ViolationDataType, TransactionViolationData}; +/** Collection of mock transaction violations, indexed by transactionViolations_${transactionID} */ +type TransactionViolationsCollectionDataSet = CollectionDataSet; + +export type {TransactionViolation, ViolationName, ViolationType, ViolationDataType, TransactionViolationData, TransactionViolationsCollectionDataSet}; export default TransactionViolations; diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts new file mode 100644 index 000000000000..a666d7711f1f --- /dev/null +++ b/tests/unit/SidebarUtilsTest.ts @@ -0,0 +1,337 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import SidebarUtils from '@libs/SidebarUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report, ReportAction, ReportActions, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; +import type {TransactionViolationsCollectionDataSet} from '@src/types/onyx/TransactionViolation'; + +describe('SidebarUtils', () => { + beforeAll(() => + Onyx.init({ + keys: ONYXKEYS, + safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + }), + ); + + describe('getReasonAndReportActionThatHasRedBrickRoad', () => { + it('returns correct reason when report has transaction thread violations', async () => { + const MOCK_REPORT: Report = { + reportID: '1', + ownerAccountID: 12345, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + policyID: '6', + }; + + const MOCK_REPORTS: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT, + }; + + const MOCK_REPORT_ACTIONS: ReportActions = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '1': { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + actorAccountID: 12345, + created: '2024-08-08 18:20:44.171', + }, + }; + + const MOCK_TRANSACTION = { + transactionID: '1', + amount: 10, + modifiedAmount: 10, + reportID: MOCK_REPORT.reportID, + }; + + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ + { + type: CONST.VIOLATION_TYPES.VIOLATION, + name: CONST.VIOLATIONS.MISSING_CATEGORY, + }, + ], + }; + + await Onyx.multiSet({ + ...MOCK_REPORTS, + ...MOCK_TRANSACTION_VIOLATIONS, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT_ACTIONS, + [ONYXKEYS.SESSION]: { + accountID: 12345, + }, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + }); + + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); + }); + + it('returns correct reason when report has errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + errorFields: { + someField: { + error: 'Some error occurred', + }, + }, + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); + }); + + it('returns correct reason when report has violations', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_VIOLATIONS); + }); + + it('returns correct reason when report has report action errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '1': { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + actorAccountID: 12345, + created: '2024-08-08 18:20:44.171', + message: [ + { + type: '', + text: '', + }, + ], + errors: { + someError: 'Some error occurred', + }, + }, + }; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); + }); + + it('returns correct reason when report has export errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + errorFields: { + export: { + error: 'Some error occurred', + }, + }, + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const {reason} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_ERRORS); + }); + + it('returns correct report action when report has report action errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTION = { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + actorAccountID: 12345, + created: '2024-08-08 18:20:44.171', + message: [ + { + type: '', + text: '', + }, + ], + errors: { + someError: 'Some error occurred', + }, + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '1': MOCK_REPORT_ACTION, + }; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const {reportAction} = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS) ?? {}; + + expect(reportAction).toMatchObject(MOCK_REPORT_ACTION); + }); + + it('returns null when report has no errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBeNull(); + }); + }); + + describe('shouldShowRedBrickRoad', () => { + it('returns true when report has transaction thread violations', async () => { + const MOCK_REPORT: Report = { + reportID: '1', + ownerAccountID: 12345, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + policyID: '6', + }; + + const MOCK_REPORTS: ReportCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.REPORT}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT, + }; + + const MOCK_REPORT_ACTIONS: ReportActions = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '1': { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.IOU, + actorAccountID: 12345, + created: '2024-08-08 18:20:44.171', + }, + }; + + const MOCK_TRANSACTION = { + transactionID: '1', + amount: 10, + modifiedAmount: 10, + reportID: MOCK_REPORT.reportID, + }; + + const MOCK_TRANSACTION_VIOLATIONS: TransactionViolationsCollectionDataSet = { + [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${MOCK_TRANSACTION.transactionID}` as const]: [ + { + type: CONST.VIOLATION_TYPES.VIOLATION, + name: CONST.VIOLATIONS.MISSING_CATEGORY, + }, + ], + }; + + await Onyx.multiSet({ + ...MOCK_REPORTS, + ...MOCK_TRANSACTION_VIOLATIONS, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MOCK_REPORT.reportID}` as const]: MOCK_REPORT_ACTIONS, + [ONYXKEYS.SESSION]: { + accountID: 12345, + }, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${MOCK_TRANSACTION.transactionID}` as const]: MOCK_TRANSACTION, + }); + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS as OnyxCollection); + + expect(result).toBe(true); + }); + + it('returns true when report has errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + errorFields: { + someField: { + error: 'Some error occurred', + }, + }, + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBe(true); + }); + + it('returns true when report has violations', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, true, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBe(true); + }); + + it('returns true when report has report action errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '1': { + reportActionID: '1', + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + actorAccountID: 12345, + created: '2024-08-08 18:20:44.171', + message: [ + { + type: '', + text: '', + }, + ], + errors: { + someError: 'Some error occurred', + }, + }, + }; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBe(true); + }); + + it('returns true when report has export errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + errorFields: { + export: { + error: 'Some error occurred', + }, + }, + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBe(true); + }); + + it('returns false when report has no errors', () => { + const MOCK_REPORT: Report = { + reportID: '1', + }; + const MOCK_REPORT_ACTIONS: OnyxEntry = {}; + const MOCK_TRANSACTION_VIOLATIONS: OnyxCollection = {}; + + const result = SidebarUtils.shouldShowRedBrickRoad(MOCK_REPORT, MOCK_REPORT_ACTIONS, false, MOCK_TRANSACTION_VIOLATIONS); + + expect(result).toBe(false); + }); + }); +});