From 756acaf8d2459fd06b2f96698f74e8ce232a7dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:11:40 +0100 Subject: [PATCH 1/7] add test for copilot held expenses RBR --- tests/unit/SidebarUtilsTest.ts | 121 ++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index ba613d10d933..71f5fa3cf3bf 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {act, renderHook} from '@testing-library/react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import DateUtils from '@libs/DateUtils'; @@ -8,7 +8,13 @@ import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import {formatReportLastMessageText, getAllReportErrors, getReportPreviewMessage} from '@libs/ReportUtils'; +import { + formatReportLastMessageText, + generateReportID, + getAllReportErrors, + getReasonAndReportActionThatRequiresAttention, + getReportPreviewMessage, +} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -22,11 +28,13 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; +import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; +import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ @@ -573,6 +581,115 @@ describe('SidebarUtils', () => { expect(result).toBe(true); }); + it('returns true when submitter has held expenses even if outstanding tasks trigger GBR', async () => { + const policyID = generateReportID(); + const expenseChatID = generateReportID(); + const expenseReportID = generateReportID(); + const holdReportActionID = generateReportID(); + const delegateEmail = 'copilot@example.com'; + + const policyExpenseChat: Report = { + reportID: expenseChatID, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + type: CONST.REPORT.TYPE.CHAT, + ownerAccountID: 12345, + policyID, + hasOutstandingChildRequest: true, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const expenseReport: Report = { + reportID: expenseReportID, + chatReportID: expenseChatID, + type: CONST.REPORT.TYPE.EXPENSE, + ownerAccountID: 12345, + managerID: 12345, + policyID, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const baseTransaction = createRandomTransaction(700); + const transactionID = generateTransactionID(); + const transaction: Transaction = { + ...baseTransaction, + transactionID, + reportID: expenseReport.reportID, + amount: 12345, + currency: CONST.CURRENCY.USD, + status: CONST.TRANSACTION.STATUS.POSTED, + comment: { + ...(baseTransaction.comment ?? {}), + hold: holdReportActionID, + }, + }; + + const transactionKey = `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}` as const; + const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as const; + const transactionViolations: OnyxCollection = { + [transactionViolationsKey]: [ + { + name: CONST.VIOLATIONS.HOLD, + type: CONST.VIOLATION_TYPES.VIOLATION, + showInReview: true, + }, + ], + }; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: { + accountID: 12345, + email: delegateEmail, + }, + [ONYXKEYS.ACCOUNT]: { + delegatedAccess: { + delegate: delegateEmail, + delegates: [{email: delegateEmail, role: CONST.DELEGATE_ROLE.ALL}], + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat.reportID}`]: policyExpenseChat, + [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport, + [transactionKey]: transaction, + [transactionViolationsKey]: transactionViolations[transactionViolationsKey], + } as unknown as OnyxMultiSetInput); + }); + + await waitForBatchedUpdatesWithAct(); + + const requiresAttention = getReasonAndReportActionThatRequiresAttention(policyExpenseChat); + expect(requiresAttention?.reason).toBe(CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION); + + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations, + false, + ) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); + + const {result: isReportArchived} = renderHook(() => useReportIsArchived(policyExpenseChat.reportID)); + const hasRedBrickRoad = SidebarUtils.shouldShowRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations as OnyxCollection, + isReportArchived.current, + ); + + expect(hasRedBrickRoad).toBe(true); + }); + it('returns true when report has errors', () => { const MOCK_REPORT: Report = { reportID: '1', From 964a61d833d60b9a865116379d23ffc8ab0e29a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:13:08 +0100 Subject: [PATCH 2/7] fix style --- tests/unit/SidebarUtilsTest.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 71f5fa3cf3bf..bc00e825e096 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -3,18 +3,13 @@ import {act, renderHook} from '@testing-library/react-native'; import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; +import {generateTransactionID} from '@libs/actions/Transaction'; import DateUtils from '@libs/DateUtils'; import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import { - formatReportLastMessageText, - generateReportID, - getAllReportErrors, - getReasonAndReportActionThatRequiresAttention, - getReportPreviewMessage, -} from '@libs/ReportUtils'; +import {formatReportLastMessageText, generateReportID, getAllReportErrors, getReasonAndReportActionThatRequiresAttention, getReportPreviewMessage} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -28,13 +23,12 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; -import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; +import createRandomTransaction from '../utils/collections/transaction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; -import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ From 30b586910c7756e1f1b308395000d01c7a557f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:14:11 +0100 Subject: [PATCH 3/7] add test for copilot held expenses RBR --- tests/unit/SidebarUtilsTest.ts | 121 ++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index ba613d10d933..71f5fa3cf3bf 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {act, renderHook} from '@testing-library/react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import DateUtils from '@libs/DateUtils'; @@ -8,7 +8,13 @@ import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import {formatReportLastMessageText, getAllReportErrors, getReportPreviewMessage} from '@libs/ReportUtils'; +import { + formatReportLastMessageText, + generateReportID, + getAllReportErrors, + getReasonAndReportActionThatRequiresAttention, + getReportPreviewMessage, +} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -22,11 +28,13 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; +import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; +import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ @@ -573,6 +581,115 @@ describe('SidebarUtils', () => { expect(result).toBe(true); }); + it('returns true when submitter has held expenses even if outstanding tasks trigger GBR', async () => { + const policyID = generateReportID(); + const expenseChatID = generateReportID(); + const expenseReportID = generateReportID(); + const holdReportActionID = generateReportID(); + const delegateEmail = 'copilot@example.com'; + + const policyExpenseChat: Report = { + reportID: expenseChatID, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + type: CONST.REPORT.TYPE.CHAT, + ownerAccountID: 12345, + policyID, + hasOutstandingChildRequest: true, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const expenseReport: Report = { + reportID: expenseReportID, + chatReportID: expenseChatID, + type: CONST.REPORT.TYPE.EXPENSE, + ownerAccountID: 12345, + managerID: 12345, + policyID, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const baseTransaction = createRandomTransaction(700); + const transactionID = generateTransactionID(); + const transaction: Transaction = { + ...baseTransaction, + transactionID, + reportID: expenseReport.reportID, + amount: 12345, + currency: CONST.CURRENCY.USD, + status: CONST.TRANSACTION.STATUS.POSTED, + comment: { + ...(baseTransaction.comment ?? {}), + hold: holdReportActionID, + }, + }; + + const transactionKey = `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}` as const; + const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as const; + const transactionViolations: OnyxCollection = { + [transactionViolationsKey]: [ + { + name: CONST.VIOLATIONS.HOLD, + type: CONST.VIOLATION_TYPES.VIOLATION, + showInReview: true, + }, + ], + }; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: { + accountID: 12345, + email: delegateEmail, + }, + [ONYXKEYS.ACCOUNT]: { + delegatedAccess: { + delegate: delegateEmail, + delegates: [{email: delegateEmail, role: CONST.DELEGATE_ROLE.ALL}], + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat.reportID}`]: policyExpenseChat, + [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport, + [transactionKey]: transaction, + [transactionViolationsKey]: transactionViolations[transactionViolationsKey], + } as unknown as OnyxMultiSetInput); + }); + + await waitForBatchedUpdatesWithAct(); + + const requiresAttention = getReasonAndReportActionThatRequiresAttention(policyExpenseChat); + expect(requiresAttention?.reason).toBe(CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION); + + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations, + false, + ) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); + + const {result: isReportArchived} = renderHook(() => useReportIsArchived(policyExpenseChat.reportID)); + const hasRedBrickRoad = SidebarUtils.shouldShowRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations as OnyxCollection, + isReportArchived.current, + ); + + expect(hasRedBrickRoad).toBe(true); + }); + it('returns true when report has errors', () => { const MOCK_REPORT: Report = { reportID: '1', From 622d061b27551c78d302e56d99685d9cc813c66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:14:58 +0100 Subject: [PATCH 4/7] fix style --- tests/unit/SidebarUtilsTest.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 71f5fa3cf3bf..bc00e825e096 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -3,18 +3,13 @@ import {act, renderHook} from '@testing-library/react-native'; import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; +import {generateTransactionID} from '@libs/actions/Transaction'; import DateUtils from '@libs/DateUtils'; import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import { - formatReportLastMessageText, - generateReportID, - getAllReportErrors, - getReasonAndReportActionThatRequiresAttention, - getReportPreviewMessage, -} from '@libs/ReportUtils'; +import {formatReportLastMessageText, generateReportID, getAllReportErrors, getReasonAndReportActionThatRequiresAttention, getReportPreviewMessage} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -28,13 +23,12 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; -import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; +import createRandomTransaction from '../utils/collections/transaction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; -import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ From 4aae2a7fc51f9546d410c5d43f0fd7e452cd9867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:14:11 +0100 Subject: [PATCH 5/7] add test for copilot held expenses RBR --- tests/unit/SidebarUtilsTest.ts | 121 ++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index ba613d10d933..71f5fa3cf3bf 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import {act, renderHook} from '@testing-library/react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; import DateUtils from '@libs/DateUtils'; @@ -8,7 +8,13 @@ import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import {formatReportLastMessageText, getAllReportErrors, getReportPreviewMessage} from '@libs/ReportUtils'; +import { + formatReportLastMessageText, + generateReportID, + getAllReportErrors, + getReasonAndReportActionThatRequiresAttention, + getReportPreviewMessage, +} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -22,11 +28,13 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; +import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; +import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ @@ -573,6 +581,115 @@ describe('SidebarUtils', () => { expect(result).toBe(true); }); + it('returns true when submitter has held expenses even if outstanding tasks trigger GBR', async () => { + const policyID = generateReportID(); + const expenseChatID = generateReportID(); + const expenseReportID = generateReportID(); + const holdReportActionID = generateReportID(); + const delegateEmail = 'copilot@example.com'; + + const policyExpenseChat: Report = { + reportID: expenseChatID, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + type: CONST.REPORT.TYPE.CHAT, + ownerAccountID: 12345, + policyID, + hasOutstandingChildRequest: true, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const expenseReport: Report = { + reportID: expenseReportID, + chatReportID: expenseChatID, + type: CONST.REPORT.TYPE.EXPENSE, + ownerAccountID: 12345, + managerID: 12345, + policyID, + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS_NUM.OPEN, + }; + + const baseTransaction = createRandomTransaction(700); + const transactionID = generateTransactionID(); + const transaction: Transaction = { + ...baseTransaction, + transactionID, + reportID: expenseReport.reportID, + amount: 12345, + currency: CONST.CURRENCY.USD, + status: CONST.TRANSACTION.STATUS.POSTED, + comment: { + ...(baseTransaction.comment ?? {}), + hold: holdReportActionID, + }, + }; + + const transactionKey = `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}` as const; + const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as const; + const transactionViolations: OnyxCollection = { + [transactionViolationsKey]: [ + { + name: CONST.VIOLATIONS.HOLD, + type: CONST.VIOLATION_TYPES.VIOLATION, + showInReview: true, + }, + ], + }; + + await act(async () => { + await Onyx.multiSet({ + [ONYXKEYS.SESSION]: { + accountID: 12345, + email: delegateEmail, + }, + [ONYXKEYS.ACCOUNT]: { + delegatedAccess: { + delegate: delegateEmail, + delegates: [{email: delegateEmail, role: CONST.DELEGATE_ROLE.ALL}], + }, + }, + [`${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat.reportID}`]: policyExpenseChat, + [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport, + [transactionKey]: transaction, + [transactionViolationsKey]: transactionViolations[transactionViolationsKey], + } as unknown as OnyxMultiSetInput); + }); + + await waitForBatchedUpdatesWithAct(); + + const requiresAttention = getReasonAndReportActionThatRequiresAttention(policyExpenseChat); + expect(requiresAttention?.reason).toBe(CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION); + + const {reason} = + SidebarUtils.getReasonAndReportActionThatHasRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations, + false, + ) ?? {}; + + expect(reason).toBe(CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS); + + const {result: isReportArchived} = renderHook(() => useReportIsArchived(policyExpenseChat.reportID)); + const hasRedBrickRoad = SidebarUtils.shouldShowRedBrickRoad( + policyExpenseChat, + policyExpenseChat, + {} as OnyxEntry, + true, + {}, + {[transactionKey]: transaction}, + transactionViolations as OnyxCollection, + isReportArchived.current, + ); + + expect(hasRedBrickRoad).toBe(true); + }); + it('returns true when report has errors', () => { const MOCK_REPORT: Report = { reportID: '1', From 8e873a25923ac8dffc09393b9dba3b3d852fe4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Fri, 21 Nov 2025 18:14:58 +0100 Subject: [PATCH 6/7] fix style --- tests/unit/SidebarUtilsTest.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 71f5fa3cf3bf..bc00e825e096 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -3,18 +3,13 @@ import {act, renderHook} from '@testing-library/react-native'; import type {OnyxCollection, OnyxEntry, OnyxMultiSetInput} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import useReportIsArchived from '@hooks/useReportIsArchived'; +import {generateTransactionID} from '@libs/actions/Transaction'; import DateUtils from '@libs/DateUtils'; import {getLastActorDisplayName} from '@libs/OptionsListUtils'; // eslint-disable-next-line no-restricted-syntax import type * as PolicyUtils from '@libs/PolicyUtils'; import {getOriginalMessage, getReportActionMessageText} from '@libs/ReportActionsUtils'; -import { - formatReportLastMessageText, - generateReportID, - getAllReportErrors, - getReasonAndReportActionThatRequiresAttention, - getReportPreviewMessage, -} from '@libs/ReportUtils'; +import {formatReportLastMessageText, generateReportID, getAllReportErrors, getReasonAndReportActionThatRequiresAttention, getReportPreviewMessage} from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; @@ -28,13 +23,12 @@ import {chatReportR14932, iouReportR14932} from '../../__mocks__/reportData/repo import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import {createRandomReport} from '../utils/collections/reports'; -import createRandomTransaction from '../utils/collections/transaction'; import {createSidebarReportsCollection, createSidebarTestData} from '../utils/collections/sidebarReports'; +import createRandomTransaction from '../utils/collections/transaction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import {localeCompare} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; -import { generateTransactionID } from '@libs/actions/Transaction'; // Mock PolicyUtils jest.mock('@libs/PolicyUtils', () => ({ From d562881e2a161847f24534cef53cdcc091a0b098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucien=20Akchot=C3=A9?= Date: Tue, 25 Nov 2025 14:47:30 +0100 Subject: [PATCH 7/7] remove unnecessary delegate onyx setup in test --- tests/unit/SidebarUtilsTest.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index bc00e825e096..685eb36697e0 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -580,7 +580,6 @@ describe('SidebarUtils', () => { const expenseChatID = generateReportID(); const expenseReportID = generateReportID(); const holdReportActionID = generateReportID(); - const delegateEmail = 'copilot@example.com'; const policyExpenseChat: Report = { reportID: expenseChatID, @@ -635,13 +634,6 @@ describe('SidebarUtils', () => { await Onyx.multiSet({ [ONYXKEYS.SESSION]: { accountID: 12345, - email: delegateEmail, - }, - [ONYXKEYS.ACCOUNT]: { - delegatedAccess: { - delegate: delegateEmail, - delegates: [{email: delegateEmail, role: CONST.DELEGATE_ROLE.ALL}], - }, }, [`${ONYXKEYS.COLLECTION.REPORT}${policyExpenseChat.reportID}`]: policyExpenseChat, [`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`]: expenseReport,