Skip to content

Commit 139bd29

Browse files
authored
Merge pull request Expensify#64938 from software-mansion-labs/@szymczak/add-useNewTransaction-tests
[NoQA] Add useNewTransactions tests
2 parents e7e1cf9 + 8ab5e1a commit 139bd29

1 file changed

Lines changed: 299 additions & 0 deletions

File tree

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
import {renderHook} from '@testing-library/react-native';
2+
import useNewTransactions from '@hooks/useNewTransactions';
3+
import type {Transaction} from '@src/types/onyx';
4+
5+
// We need to mock requestAnimationFrame to mimic long Onyx merge overhead
6+
jest.spyOn(global, 'requestAnimationFrame').mockImplementation((callback: FrameRequestCallback) => {
7+
setTimeout(() => {
8+
callback(performance.now());
9+
}, 30);
10+
return 0;
11+
});
12+
13+
const delay = (ms: number) =>
14+
new Promise((resolve) => {
15+
setTimeout(resolve, ms);
16+
});
17+
18+
describe('useNewTransactions with empty cache', () => {
19+
const transactionsAlreadyInReport = [
20+
{transactionID: '2', amount: 200, created: '2023-10-02', currency: 'USD', reportID: 'report1', merchant: ''},
21+
{transactionID: '3', amount: 300, created: '2023-10-03', currency: 'USD', reportID: 'report1', merchant: ''},
22+
];
23+
const newTransaction = {transactionID: '1', amount: 100, created: '2023-10-01T00:00:00Z', currency: 'USD', reportID: 'report1', merchant: ''};
24+
25+
it("doesn't return new transactions when no transactions are added", () => {
26+
// 1. Report and transactions data is not loaded yet
27+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
28+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
29+
{
30+
initialProps: {
31+
hasOnceLoadedReportActions: false,
32+
transactions: [],
33+
},
34+
},
35+
);
36+
37+
// 2. Report is loaded and it has no transactions so there are no further rerenders
38+
rerender({
39+
hasOnceLoadedReportActions: true,
40+
transactions: [],
41+
});
42+
expect(result.current).toEqual([]);
43+
});
44+
45+
it('returns no new transactions when transactions come from initial Report load', () => {
46+
// 1. Report and transactions data is not loaded yet
47+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
48+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
49+
{
50+
initialProps: {
51+
hasOnceLoadedReportActions: false,
52+
transactions: [],
53+
},
54+
},
55+
);
56+
expect(result.current).toEqual([]);
57+
58+
// 2. Report is loaded and transactions data is not loaded yet
59+
rerender({
60+
hasOnceLoadedReportActions: true,
61+
transactions: [],
62+
});
63+
expect(result.current).toEqual([]);
64+
65+
// 3. Report is loaded and transactions data is loaded
66+
rerender({
67+
hasOnceLoadedReportActions: true,
68+
transactions: transactionsAlreadyInReport,
69+
});
70+
// there is no new transactions, because the transactions that were already in the report are not considered new
71+
expect(result.current).toEqual([]);
72+
});
73+
74+
it('returns new transactions when transactions are added after initial load', () => {
75+
// 1. Report and transactions data is not loaded yet
76+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
77+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
78+
{
79+
initialProps: {
80+
hasOnceLoadedReportActions: false,
81+
transactions: [],
82+
},
83+
},
84+
);
85+
expect(result.current).toEqual([]);
86+
87+
// 2. Report is loaded and transactions data is not loaded yet
88+
rerender({
89+
hasOnceLoadedReportActions: true,
90+
transactions: [],
91+
});
92+
expect(result.current).toEqual([]);
93+
94+
// 3. Report is loaded and transactions data is loaded
95+
rerender({
96+
hasOnceLoadedReportActions: true,
97+
transactions: transactionsAlreadyInReport,
98+
});
99+
expect(result.current).toEqual([]);
100+
101+
// 4. User added new transaction
102+
rerender({
103+
hasOnceLoadedReportActions: true,
104+
transactions: [...transactionsAlreadyInReport, newTransaction],
105+
});
106+
expect(result.current).toEqual([newTransaction]);
107+
});
108+
109+
it('returns new transactions when adding transactions to empty report', async () => {
110+
// 1. Report and transactions data is not loaded yet
111+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
112+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
113+
{
114+
initialProps: {
115+
hasOnceLoadedReportActions: false,
116+
transactions: [],
117+
},
118+
},
119+
);
120+
121+
// 2. Report is loaded and it has no transactions so there are no further rerenders
122+
rerender({
123+
hasOnceLoadedReportActions: true,
124+
transactions: [],
125+
});
126+
await delay(1000); // We need to wait to ensure that the skipFirstTransactionsChange is set to false by the useEffect
127+
expect(result.current).toEqual([]);
128+
129+
// 3. User added new transaction
130+
rerender({
131+
hasOnceLoadedReportActions: true,
132+
transactions: [newTransaction],
133+
});
134+
expect(result.current).toEqual([newTransaction]);
135+
});
136+
137+
it('returns no new transactions when transactions are removed', () => {
138+
// 1. Report and transactions data is not loaded yet
139+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
140+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
141+
{
142+
initialProps: {
143+
hasOnceLoadedReportActions: false,
144+
transactions: [],
145+
},
146+
},
147+
);
148+
expect(result.current).toEqual([]);
149+
150+
// 2. Report is loaded and transactions data is not loaded yet
151+
rerender({
152+
hasOnceLoadedReportActions: true,
153+
transactions: [],
154+
});
155+
expect(result.current).toEqual([]);
156+
157+
// 3. Report is loaded and transactions data is loaded
158+
rerender({
159+
hasOnceLoadedReportActions: true,
160+
transactions: transactionsAlreadyInReport,
161+
});
162+
expect(result.current).toEqual([]);
163+
164+
// 4. User removes a transaction
165+
rerender({
166+
hasOnceLoadedReportActions: true,
167+
transactions: transactionsAlreadyInReport.slice(1),
168+
});
169+
expect(result.current).toEqual([]);
170+
});
171+
});
172+
173+
describe('useNewTransactions with transactions in cache', () => {
174+
const transactionsAlreadyInReport = [
175+
{transactionID: '2', amount: 200, created: '2023-10-02', currency: 'USD', reportID: 'report1', merchant: ''},
176+
{transactionID: '3', amount: 300, created: '2023-10-03', currency: 'USD', reportID: 'report1', merchant: ''},
177+
];
178+
const newTransaction = {transactionID: '1', amount: 100, created: '2023-10-01T00:00:00Z', currency: 'USD', reportID: 'report1', merchant: ''};
179+
180+
it("doesn't return new transactions when no transactions are added", () => {
181+
// 1. Report and transactions data is loaded from Onyx
182+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
183+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
184+
{
185+
initialProps: {
186+
hasOnceLoadedReportActions: true,
187+
transactions: [],
188+
},
189+
},
190+
);
191+
192+
// 2. Report is loaded and transactions data is loaded, but there were no new transactions
193+
rerender({
194+
hasOnceLoadedReportActions: true,
195+
transactions: [],
196+
});
197+
expect(result.current).toEqual([]);
198+
});
199+
200+
it('returns new transactions when newly added transactions come from initial Report load', () => {
201+
// 1. Report and transactions data is loaded from Onyx
202+
const {rerender, result} = renderHook((props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions), {
203+
initialProps: {
204+
hasOnceLoadedReportActions: true,
205+
transactions: transactionsAlreadyInReport,
206+
},
207+
});
208+
expect(result.current).toEqual([]);
209+
210+
// 2. New transaction comes in when report is loaded
211+
rerender({
212+
hasOnceLoadedReportActions: true,
213+
transactions: [...transactionsAlreadyInReport, newTransaction],
214+
});
215+
expect(result.current).toEqual([newTransaction]);
216+
});
217+
218+
it('returns new transactions when transactions are added after initial load', () => {
219+
// 1. Report and transactions data is loaded from Onyx
220+
const {rerender, result} = renderHook((props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions), {
221+
initialProps: {
222+
hasOnceLoadedReportActions: true,
223+
transactions: transactionsAlreadyInReport,
224+
},
225+
});
226+
expect(result.current).toEqual([]);
227+
228+
// 2. Report is loaded and transactions data is loaded, but there were no new transactions
229+
rerender({
230+
hasOnceLoadedReportActions: true,
231+
transactions: transactionsAlreadyInReport,
232+
});
233+
expect(result.current).toEqual([]);
234+
235+
// 3. User added new transaction
236+
rerender({
237+
hasOnceLoadedReportActions: true,
238+
transactions: [...transactionsAlreadyInReport, newTransaction],
239+
});
240+
expect(result.current).toEqual([newTransaction]);
241+
});
242+
243+
it('returns new transactions when adding transactions to empty report', async () => {
244+
// 1. Report and transactions data is loaded from Onyx
245+
const {rerender, result} = renderHook<Transaction[], {transactions: Transaction[]; hasOnceLoadedReportActions: boolean}>(
246+
(props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions),
247+
{
248+
initialProps: {
249+
hasOnceLoadedReportActions: true,
250+
transactions: [],
251+
},
252+
},
253+
);
254+
255+
// 2. Report is loaded and it has no transactions, so there are no further rerenders
256+
rerender({
257+
hasOnceLoadedReportActions: true,
258+
transactions: [],
259+
});
260+
await delay(1000);
261+
expect(result.current).toEqual([]);
262+
263+
// 3. User added new transaction
264+
rerender({
265+
hasOnceLoadedReportActions: true,
266+
transactions: [newTransaction],
267+
});
268+
expect(result.current).toEqual([newTransaction]);
269+
});
270+
271+
it('returns no new transactions when transactions are removed', () => {
272+
// 1. Report and transactions data is loaded from Onyx
273+
const {rerender, result} = renderHook((props) => useNewTransactions(props.hasOnceLoadedReportActions, props.transactions), {
274+
initialProps: {
275+
hasOnceLoadedReportActions: true,
276+
transactions: transactionsAlreadyInReport,
277+
},
278+
});
279+
expect(result.current).toEqual([]);
280+
281+
// 2. Report is loaded and transactions data is loaded, but there were no new transactions
282+
rerender({
283+
hasOnceLoadedReportActions: true,
284+
transactions: transactionsAlreadyInReport,
285+
});
286+
expect(result.current).toEqual([]);
287+
288+
// 3. User removes a transaction
289+
rerender({
290+
hasOnceLoadedReportActions: true,
291+
transactions: transactionsAlreadyInReport.slice(1),
292+
});
293+
expect(result.current).toEqual([]);
294+
});
295+
});
296+
297+
afterAll(() => {
298+
jest.restoreAllMocks();
299+
});

0 commit comments

Comments
 (0)