Skip to content

Commit 48052f4

Browse files
committed
test coverage
1 parent e230d66 commit 48052f4

2 files changed

Lines changed: 193 additions & 1 deletion

File tree

app/components/Views/confirmations/hooks/pay/useUpdateTransactionPayAmount.test.ts

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { useUpdateTransactionPayAmount } from './useUpdateTransactionPayAmount';
44
import { simpleSendTransactionControllerMock } from '../../__mocks__/controllers/transaction-controller-mock';
55
import { transactionApprovalControllerMock } from '../../__mocks__/controllers/approval-controller-mock';
66
import { otherControllersMock } from '../../__mocks__/controllers/other-controllers-mock';
7-
import { updateAtomicBatchData } from '../../../../../util/transaction-controller';
7+
import {
8+
updateAtomicBatchData,
9+
updateTransaction,
10+
} from '../../../../../util/transaction-controller';
811
import {
912
updateMoneyAccountDepositTokenAmount,
1013
updateMoneyAccountWithdrawTokenAmount,
@@ -15,11 +18,15 @@ import {
1518
} from '@metamask/transaction-controller';
1619
import { useUpdateTokenAmount } from '../transactions/useUpdateTokenAmount';
1720
import Logger from '../../../../../util/Logger';
21+
import { useTransactionPayRequiredTokens } from './useTransactionPayData';
22+
import { TransactionPayRequiredToken } from '@metamask/transaction-pay-controller';
23+
import { Hex } from '@metamask/utils';
1824

1925
jest.mock('../../../../../util/transaction-controller');
2026
jest.mock('../../../../UI/Money/utils/moneyAccountTransactions');
2127
jest.mock('../transactions/useUpdateTokenAmount');
2228
jest.mock('../../../../../util/Logger');
29+
jest.mock('./useTransactionPayData');
2330

2431
const moneyAccountDepositMeta: Partial<TransactionMeta> = {
2532
type: TransactionType.moneyAccountDeposit,
@@ -57,13 +64,17 @@ function runHook({
5764

5865
describe('useUpdateTransactionPayAmount', () => {
5966
const updateAtomicBatchDataMock = jest.mocked(updateAtomicBatchData);
67+
const updateTransactionMock = jest.mocked(updateTransaction);
6068
const updateMoneyAccountDepositTokenAmountMock = jest.mocked(
6169
updateMoneyAccountDepositTokenAmount,
6270
);
6371
const updateMoneyAccountWithdrawTokenAmountMock = jest.mocked(
6472
updateMoneyAccountWithdrawTokenAmount,
6573
);
6674
const useUpdateTokenAmountMock = jest.mocked(useUpdateTokenAmount);
75+
const useTransactionPayRequiredTokensMock = jest.mocked(
76+
useTransactionPayRequiredTokens,
77+
);
6778
const updateTokenAmountMock = jest.fn();
6879
const loggerErrorMock = jest.mocked(Logger.error);
6980

@@ -73,6 +84,7 @@ describe('useUpdateTransactionPayAmount', () => {
7384
useUpdateTokenAmountMock.mockReturnValue({
7485
updateTokenAmount: updateTokenAmountMock,
7586
});
87+
useTransactionPayRequiredTokensMock.mockReturnValue([]);
7688
});
7789

7890
async function flushPromises() {
@@ -259,4 +271,152 @@ describe('useUpdateTransactionPayAmount', () => {
259271
expect.stringContaining('Failed to prepare Money Account withdraw'),
260272
);
261273
});
274+
275+
describe('syncMoneyAccountDepositRequiredAssets', () => {
276+
const TOKEN_ADDRESS_MOCK = '0xToken' as Hex;
277+
const existingRequiredAsset = {
278+
address: TOKEN_ADDRESS_MOCK,
279+
amount: '0x0' as Hex,
280+
standard: 'erc20',
281+
};
282+
const moneyAccountDepositMetaWithRequiredAssets = {
283+
...moneyAccountDepositMeta,
284+
requiredAssets: [existingRequiredAsset],
285+
};
286+
287+
beforeEach(() => {
288+
updateMoneyAccountDepositTokenAmountMock.mockResolvedValue([]);
289+
useTransactionPayRequiredTokensMock.mockReturnValue([
290+
{ decimals: 6 } as TransactionPayRequiredToken,
291+
]);
292+
});
293+
294+
it('calls updateTransaction with hex-encoded amount when requiredAssets exist', async () => {
295+
const { result } = runHook({
296+
transactionMeta: moneyAccountDepositMetaWithRequiredAssets,
297+
});
298+
299+
result.current.updateTransactionPayAmount('1');
300+
301+
await flushPromises();
302+
303+
expect(updateTransactionMock).toHaveBeenCalledTimes(1);
304+
expect(updateTransactionMock).toHaveBeenCalledWith(
305+
expect.objectContaining({
306+
requiredAssets: [{ ...existingRequiredAsset, amount: '0xf4240' }],
307+
}),
308+
'Money Account deposit: sync requiredAssets amount',
309+
);
310+
});
311+
312+
it('rounds fractional atomic amounts up before encoding', async () => {
313+
const { result } = runHook({
314+
transactionMeta: moneyAccountDepositMetaWithRequiredAssets,
315+
});
316+
317+
result.current.updateTransactionPayAmount('1.0000005');
318+
319+
await flushPromises();
320+
321+
expect(updateTransactionMock).toHaveBeenCalledWith(
322+
expect.objectContaining({
323+
requiredAssets: [{ ...existingRequiredAsset, amount: '0xf4241' }],
324+
}),
325+
expect.any(String),
326+
);
327+
});
328+
329+
it('does not call updateTransaction when transactionMeta has no requiredAssets', async () => {
330+
const { result } = runHook({ transactionMeta: moneyAccountDepositMeta });
331+
332+
result.current.updateTransactionPayAmount('1');
333+
334+
await flushPromises();
335+
336+
expect(updateTransactionMock).not.toHaveBeenCalled();
337+
});
338+
339+
it('does not call updateTransaction when no required tokens are available', async () => {
340+
useTransactionPayRequiredTokensMock.mockReturnValue([]);
341+
342+
const { result } = runHook({
343+
transactionMeta: moneyAccountDepositMetaWithRequiredAssets,
344+
});
345+
346+
result.current.updateTransactionPayAmount('1');
347+
348+
await flushPromises();
349+
350+
expect(updateTransactionMock).not.toHaveBeenCalled();
351+
});
352+
353+
it('does not call updateTransaction when computed amount matches existing amount', async () => {
354+
const { result } = runHook({
355+
transactionMeta: {
356+
...moneyAccountDepositMeta,
357+
requiredAssets: [{ ...existingRequiredAsset, amount: '0xf4240' }],
358+
},
359+
});
360+
361+
result.current.updateTransactionPayAmount('1');
362+
363+
await flushPromises();
364+
365+
expect(updateTransactionMock).not.toHaveBeenCalled();
366+
});
367+
368+
it('does not run sync logic for non-deposit transaction types', async () => {
369+
updateMoneyAccountWithdrawTokenAmountMock.mockResolvedValue([]);
370+
371+
const { result } = runHook({
372+
transactionMeta: {
373+
...moneyAccountWithdrawMeta,
374+
requiredAssets: [existingRequiredAsset],
375+
},
376+
});
377+
378+
result.current.updateTransactionPayAmount('1');
379+
380+
await flushPromises();
381+
382+
expect(updateTransactionMock).not.toHaveBeenCalled();
383+
});
384+
385+
it('logs an error when updateTransaction throws', async () => {
386+
const error = new Error('updateTransaction failed');
387+
updateTransactionMock.mockImplementation(() => {
388+
throw error;
389+
});
390+
391+
const { result } = runHook({
392+
transactionMeta: moneyAccountDepositMetaWithRequiredAssets,
393+
});
394+
395+
result.current.updateTransactionPayAmount('1');
396+
397+
await flushPromises();
398+
399+
expect(loggerErrorMock).toHaveBeenCalledWith(
400+
error,
401+
'Failed to sync Money Account deposit requiredAssets amount',
402+
);
403+
});
404+
405+
it('still applies money account deposit updates after syncing requiredAssets', async () => {
406+
updateMoneyAccountDepositTokenAmountMock.mockResolvedValue([
407+
{ nestedTransactionIndex: 0, transactionData: '0xaaaa' },
408+
]);
409+
410+
const { result } = runHook({
411+
transactionMeta: moneyAccountDepositMetaWithRequiredAssets,
412+
});
413+
414+
result.current.updateTransactionPayAmount('1');
415+
416+
await flushPromises();
417+
418+
expect(updateTransactionMock).toHaveBeenCalledTimes(1);
419+
expect(updateAtomicBatchDataMock).toHaveBeenCalledTimes(1);
420+
});
421+
});
262422
});

app/components/Views/confirmations/utils/transaction-pay.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,38 @@ describe('Transaction Pay Utils', () => {
185185

186186
expect(getTokenAddress(transactionMeta)).toBe(TO_MOCK);
187187
});
188+
189+
it('returns first requiredAsset address if no nested transfer and requiredAssets present', () => {
190+
const requiredAssetAddress =
191+
'0xrequiredAssetAddress00000000000000000000' as Hex;
192+
const transactionMeta = {
193+
txParams: {
194+
data: '0x1234',
195+
to: TO_MOCK,
196+
},
197+
requiredAssets: [
198+
{
199+
address: requiredAssetAddress,
200+
amount: '0x1' as Hex,
201+
standard: 'erc20',
202+
},
203+
],
204+
} as TransactionMeta;
205+
206+
expect(getTokenAddress(transactionMeta)).toBe(requiredAssetAddress);
207+
});
208+
209+
it('falls back to txParams.to when requiredAssets is empty', () => {
210+
const transactionMeta = {
211+
txParams: {
212+
data: '0x1234',
213+
to: TO_MOCK,
214+
},
215+
requiredAssets: [],
216+
} as unknown as TransactionMeta;
217+
218+
expect(getTokenAddress(transactionMeta)).toBe(TO_MOCK);
219+
});
188220
});
189221

190222
describe('getAvailableTokens', () => {

0 commit comments

Comments
 (0)