Skip to content

Commit 3ee125b

Browse files
committed
add tests & docs
1 parent 6b5fb76 commit 3ee125b

2 files changed

Lines changed: 195 additions & 0 deletions

File tree

src/components/MoneyReportTransactionThreadContext.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import ONYXKEYS from '@src/ONYXKEYS';
99
import type * as OnyxTypes from '@src/types/onyx';
1010

1111
type MoneyReportTransactionThreadContextValue = {
12+
/** The transaction ID from the parent IOU report action */
1213
iouTransactionID: string | undefined;
14+
/** The parent IOU report action for the transaction thread */
1315
requestParentReportAction: OnyxTypes.ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.IOU> | null;
16+
/** The transaction thread report ID */
1417
transactionThreadReportID: string | undefined;
18+
/** Filtered report actions for the transaction thread */
1519
reportActions: OnyxTypes.ReportAction[];
1620
};
1721

@@ -25,7 +29,9 @@ const defaultValue: MoneyReportTransactionThreadContextValue = {
2529
const MoneyReportTransactionThreadContext = createContext<MoneyReportTransactionThreadContextValue>(defaultValue);
2630

2731
type MoneyReportTransactionThreadProviderProps = {
32+
/** The money request report ID */
2833
reportID: string | undefined;
34+
/** The children */
2935
children: ReactNode;
3036
};
3137

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import {renderHook} from '@testing-library/react-native';
2+
import React from 'react';
3+
import type {PropsWithChildren} from 'react';
4+
import type {OnyxKey, ResultMetadata, UseOnyxResult} from 'react-native-onyx';
5+
import {MoneyReportTransactionThreadProvider, useMoneyReportTransactionThread} from '@components/MoneyReportTransactionThreadContext';
6+
import useOnyx from '@hooks/useOnyx';
7+
import useTransactionThreadReport from '@hooks/useTransactionThreadReport';
8+
import CONST from '@src/CONST';
9+
import ONYXKEYS from '@src/ONYXKEYS';
10+
import type {Report, ReportAction, ReportActions} from '@src/types/onyx';
11+
12+
jest.mock('@hooks/useOnyx', () => ({
13+
__esModule: true,
14+
default: jest.fn(),
15+
}));
16+
17+
jest.mock('@hooks/useTransactionThreadReport', () => ({
18+
__esModule: true,
19+
default: jest.fn(),
20+
}));
21+
22+
const mockUseOnyx = jest.mocked(useOnyx);
23+
const mockUseTransactionThreadReport = jest.mocked(useTransactionThreadReport);
24+
25+
const MONEY_REPORT_ID = '10001';
26+
const THREAD_REPORT_ID = '10002';
27+
const PARENT_ACTION_ID = '9001';
28+
const IOU_TRANSACTION_ID = '3106135972713435169';
29+
30+
const loadedMetadata: ResultMetadata<ReportActions> = {status: 'loaded'};
31+
32+
function asReportActionsOnyxResult(reportActions: ReportActions | undefined): UseOnyxResult<ReportActions> {
33+
return [reportActions, loadedMetadata];
34+
}
35+
36+
function createIOUReportAction(reportActionID: string, transactionID: string = IOU_TRANSACTION_ID): ReportAction {
37+
return {
38+
reportActionID,
39+
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
40+
originalMessage: {
41+
IOUTransactionID: transactionID,
42+
type: CONST.IOU.REPORT_ACTION_TYPE.CREATE,
43+
},
44+
created: '2025-02-14 08:12:05.165',
45+
actorAccountID: 1,
46+
person: [{type: 'TEXT', style: 'strong', text: 'Test'}],
47+
message: [{type: 'COMMENT', html: '', text: '', isEdited: false, whisperedTo: [], isDeletedParentAction: false}],
48+
} as ReportAction;
49+
}
50+
51+
function createTransactionThreadReport(parentReportActionID: string | undefined): Report {
52+
return {
53+
reportID: THREAD_REPORT_ID,
54+
parentReportActionID,
55+
} as Report;
56+
}
57+
58+
function renderContextHook(reportID: string | undefined = MONEY_REPORT_ID) {
59+
function wrapper({children}: PropsWithChildren) {
60+
return <MoneyReportTransactionThreadProvider reportID={reportID}>{children}</MoneyReportTransactionThreadProvider>;
61+
}
62+
63+
return renderHook(() => useMoneyReportTransactionThread(), {wrapper});
64+
}
65+
66+
describe('MoneyReportTransactionThreadContext', () => {
67+
beforeEach(() => {
68+
jest.clearAllMocks();
69+
mockUseOnyx.mockReturnValue(asReportActionsOnyxResult(undefined));
70+
mockUseTransactionThreadReport.mockReturnValue({
71+
transactionThreadReportID: undefined,
72+
transactionThreadReport: undefined,
73+
reportActions: [],
74+
});
75+
});
76+
77+
it('returns default values when used outside the provider', () => {
78+
const {result} = renderHook(() => useMoneyReportTransactionThread());
79+
80+
expect(result.current).toEqual({
81+
iouTransactionID: undefined,
82+
requestParentReportAction: null,
83+
transactionThreadReportID: undefined,
84+
reportActions: [],
85+
});
86+
});
87+
88+
it('exposes transaction thread data and resolves the IOU transaction ID from the parent action', () => {
89+
const parentReportAction = createIOUReportAction(PARENT_ACTION_ID);
90+
const filteredReportActions: ReportAction[] = [
91+
{
92+
reportActionID: 'filtered-action',
93+
actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT,
94+
created: '2025-02-14 08:12:05.165',
95+
actorAccountID: 1,
96+
person: [{type: 'TEXT', style: 'strong', text: 'Test'}],
97+
message: [{type: 'COMMENT', html: '', text: '', isEdited: false, whisperedTo: [], isDeletedParentAction: false}],
98+
},
99+
];
100+
const reportActionsForParent: ReportActions = {
101+
[PARENT_ACTION_ID]: parentReportAction,
102+
};
103+
104+
mockUseTransactionThreadReport.mockReturnValue({
105+
transactionThreadReportID: THREAD_REPORT_ID,
106+
transactionThreadReport: createTransactionThreadReport(PARENT_ACTION_ID),
107+
reportActions: filteredReportActions,
108+
});
109+
mockUseOnyx.mockImplementation((key: OnyxKey) => {
110+
if (key === `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MONEY_REPORT_ID}`) {
111+
return asReportActionsOnyxResult(reportActionsForParent);
112+
}
113+
return asReportActionsOnyxResult(undefined);
114+
});
115+
116+
const {result} = renderContextHook();
117+
118+
expect(mockUseTransactionThreadReport).toHaveBeenCalledWith(MONEY_REPORT_ID);
119+
expect(result.current.transactionThreadReportID).toBe(THREAD_REPORT_ID);
120+
expect(result.current.reportActions).toEqual(filteredReportActions);
121+
expect(result.current.requestParentReportAction).toEqual(parentReportAction);
122+
expect(result.current.iouTransactionID).toBe(IOU_TRANSACTION_ID);
123+
});
124+
125+
it('resolves the parent action from the raw Onyx collection even when it is absent from filtered report actions', () => {
126+
const parentReportAction = createIOUReportAction(PARENT_ACTION_ID);
127+
const reportActionsForParent: ReportActions = {
128+
[PARENT_ACTION_ID]: parentReportAction,
129+
};
130+
131+
mockUseTransactionThreadReport.mockReturnValue({
132+
transactionThreadReportID: THREAD_REPORT_ID,
133+
transactionThreadReport: createTransactionThreadReport(PARENT_ACTION_ID),
134+
reportActions: [],
135+
});
136+
mockUseOnyx.mockImplementation((key: OnyxKey) => {
137+
if (key === `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MONEY_REPORT_ID}`) {
138+
return asReportActionsOnyxResult(reportActionsForParent);
139+
}
140+
return asReportActionsOnyxResult(undefined);
141+
});
142+
143+
const {result} = renderContextHook();
144+
145+
expect(result.current.requestParentReportAction).toEqual(parentReportAction);
146+
expect(result.current.iouTransactionID).toBe(IOU_TRANSACTION_ID);
147+
});
148+
149+
it('returns null requestParentReportAction when the parent action is not a money request action', () => {
150+
const nonIOUParentAction: ReportAction = {
151+
reportActionID: PARENT_ACTION_ID,
152+
actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT,
153+
created: '2025-02-14 08:12:05.165',
154+
actorAccountID: 1,
155+
person: [{type: 'TEXT', style: 'strong', text: 'Test'}],
156+
message: [{type: 'COMMENT', html: '', text: '', isEdited: false, whisperedTo: [], isDeletedParentAction: false}],
157+
};
158+
159+
mockUseTransactionThreadReport.mockReturnValue({
160+
transactionThreadReportID: THREAD_REPORT_ID,
161+
transactionThreadReport: createTransactionThreadReport(PARENT_ACTION_ID),
162+
reportActions: [],
163+
});
164+
mockUseOnyx.mockImplementation((key: OnyxKey) => {
165+
if (key === `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${MONEY_REPORT_ID}`) {
166+
return asReportActionsOnyxResult({[PARENT_ACTION_ID]: nonIOUParentAction});
167+
}
168+
return asReportActionsOnyxResult(undefined);
169+
});
170+
171+
const {result} = renderContextHook();
172+
173+
expect(result.current.requestParentReportAction).toBeNull();
174+
expect(result.current.iouTransactionID).toBeUndefined();
175+
});
176+
177+
it('returns null requestParentReportAction when the transaction thread has no parent action ID', () => {
178+
mockUseTransactionThreadReport.mockReturnValue({
179+
transactionThreadReportID: THREAD_REPORT_ID,
180+
transactionThreadReport: createTransactionThreadReport(undefined),
181+
reportActions: [],
182+
});
183+
184+
const {result} = renderContextHook();
185+
186+
expect(result.current.requestParentReportAction).toBeNull();
187+
expect(result.current.iouTransactionID).toBeUndefined();
188+
});
189+
});

0 commit comments

Comments
 (0)