Skip to content

Commit

Permalink
chore: add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
infiniteflower committed Feb 25, 2025
1 parent 71d4370 commit c33da15
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 42 deletions.
74 changes: 33 additions & 41 deletions app/components/UI/Swaps/utils/useSwapsSmartTransaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { selectEvmChainId, selectIsEIP1559Network } from '../../../../selectors/networkController';
import { selectSwapsApprovalTransaction } from '../../../../reducers/swaps';
import { useSwapsSmartTransaction } from './useSwapsSmartTransaction';
import { ORIGIN_METAMASK } from '@metamask/controller-utils';
import { TransactionType } from '@metamask/transaction-controller';
import { renderHookWithProvider } from '../../../../util/test/renderWithProvider';
import Engine from '../../../../core/Engine';

// Mock selectors directly
jest.mock('../../../../selectors/networkController', () => ({
...jest.requireActual('../../../../selectors/networkController'),
selectEvmChainId: jest.fn(),
selectIsEIP1559Network: jest.fn(),
}));

jest.mock('../../../../reducers/swaps', () => ({
...jest.requireActual('../../../../reducers/swaps'),
selectSwapsApprovalTransaction: jest.fn(),
}));

// Mock Engine and its methods properly with jest.fn()
const mockGetFees = jest.fn();
const mockSubmitSignedTransactions = jest.fn();
const mockUpdateSmartTransaction = jest.fn();
const mockApproveTransactionsWithSameNonce = jest.fn();

jest.mock('../../../../core/Engine', () => ({
context: {
SmartTransactionsController: {
getFees: mockGetFees,
submitSignedTransactions: mockSubmitSignedTransactions,
updateSmartTransaction: mockUpdateSmartTransaction,
getFees: jest.fn(),
submitSignedTransactions: jest.fn(),
updateSmartTransaction: jest.fn(),
},
TransactionController: {
approveTransactionsWithSameNonce: mockApproveTransactionsWithSameNonce,
approveTransactionsWithSameNonce: jest.fn(),
},
},
}));
Expand Down Expand Up @@ -96,44 +94,42 @@ describe('useSwapsSmartTransaction', () => {
jest.clearAllMocks();

// Setup selector default return values
(selectEvmChainId as jest.Mock).mockReturnValue(mockChainId);
(selectIsEIP1559Network as jest.Mock).mockReturnValue(mockIsEIP1559Network);
(selectSwapsApprovalTransaction as jest.Mock).mockReturnValue(null);
(selectEvmChainId as unknown as jest.Mock).mockReturnValue(mockChainId);
(selectIsEIP1559Network as unknown as jest.Mock).mockReturnValue(mockIsEIP1559Network);
(selectSwapsApprovalTransaction as unknown as jest.Mock).mockReturnValue(null);

// Setup controller mocks
mockGetFees.mockResolvedValue(mockSmartTransactionFees);
mockSubmitSignedTransactions.mockResolvedValue(mockTradeResponse);
mockUpdateSmartTransaction.mockResolvedValue(undefined);
mockApproveTransactionsWithSameNonce.mockResolvedValue(['0xsignedtx1', '0xsignedtx2']);
(Engine.context.SmartTransactionsController.getFees as jest.Mock).mockResolvedValue(mockSmartTransactionFees);
(Engine.context.TransactionController.approveTransactionsWithSameNonce as jest.Mock).mockResolvedValue(['0xsignedtx1', '0xsignedtx2']);
});

it('should successfully submit a trade transaction without approval', async () => {
// Mock no approval transaction
(selectSwapsApprovalTransaction as jest.Mock).mockReturnValue(null);
(selectSwapsApprovalTransaction as unknown as jest.Mock).mockReturnValue(null);

(Engine.context.SmartTransactionsController.submitSignedTransactions as jest.Mock).mockResolvedValueOnce(mockTradeResponse);

const { result } = renderHook(() =>
const { result } = renderHookWithProvider(() =>
useSwapsSmartTransaction({
tradeTransaction: mockTradeTransaction,
gasEstimates: mockGasEstimates
})
);

let returnValue;
await act(async () => {
returnValue = await result.current.submitSwapsSmartTransaction();
});
const returnValue = await result.current.submitSwapsSmartTransaction();

// Verify getFees was called with correct params
expect(mockGetFees).toHaveBeenCalledWith(
expect(Engine.context.SmartTransactionsController.getFees).toHaveBeenCalledWith(
mockTradeTransaction,
null
);

// Verify approveTransactionsWithSameNonce was called
expect(mockApproveTransactionsWithSameNonce).toHaveBeenCalled();
expect(Engine.context.TransactionController.approveTransactionsWithSameNonce).toHaveBeenCalled();

// Verify submitSignedTransactions was called
expect(mockSubmitSignedTransactions).toHaveBeenCalledWith({
expect(Engine.context.SmartTransactionsController.submitSignedTransactions).toHaveBeenCalledTimes(1);
expect(Engine.context.SmartTransactionsController.submitSignedTransactions).toHaveBeenCalledWith({
signedTransactions: ['0xsignedtx1', '0xsignedtx2'],
txParams: expect.objectContaining({
from: '0xfrom',
Expand All @@ -148,11 +144,11 @@ describe('useSwapsSmartTransaction', () => {
});

// Verify updateSmartTransaction was called for the trade transaction
expect(mockUpdateSmartTransaction).toHaveBeenCalledWith({
expect(Engine.context.SmartTransactionsController.updateSmartTransaction).toHaveBeenCalledWith({
uuid: 'trade-uuid-456',
origin: ORIGIN_METAMASK,
type: TransactionType.swap,
creationTime: expect.any(Number),
creationTime: 123,
});

// Verify the UUID was returned
Expand All @@ -164,43 +160,39 @@ describe('useSwapsSmartTransaction', () => {

it('should successfully submit both approval and trade transactions', async () => {
// Mock with approval transaction
(selectSwapsApprovalTransaction as jest.Mock).mockReturnValue(mockApprovalTransaction);
(selectSwapsApprovalTransaction as unknown as jest.Mock).mockReturnValue(mockApprovalTransaction);

// Mock different UUIDs for approval and trade transactions
mockSubmitSignedTransactions
.mockResolvedValueOnce(mockApprovalResponse)
(Engine.context.SmartTransactionsController.submitSignedTransactions as jest.Mock).mockResolvedValueOnce(mockApprovalResponse)
.mockResolvedValueOnce(mockTradeResponse);

const { result } = renderHook(() =>
const { result } = renderHookWithProvider(() =>
useSwapsSmartTransaction({
tradeTransaction: mockTradeTransaction,
gasEstimates: mockGasEstimates
})
);

let returnValue;
await act(async () => {
returnValue = await result.current.submitSwapsSmartTransaction();
});
const returnValue = await result.current.submitSwapsSmartTransaction();

// Verify getFees was called with correct params
expect(mockGetFees).toHaveBeenCalledWith(
expect(Engine.context.SmartTransactionsController.getFees).toHaveBeenCalledWith(
mockTradeTransaction,
mockApprovalTransaction
);

// Verify submitSignedTransactions was called twice (for approval and trade)
expect(mockSubmitSignedTransactions).toHaveBeenCalledTimes(2);
expect(Engine.context.SmartTransactionsController.submitSignedTransactions).toHaveBeenCalledTimes(2);

// Verify updateSmartTransaction was called for both transactions
expect(mockUpdateSmartTransaction).toHaveBeenCalledTimes(2);
expect(mockUpdateSmartTransaction).toHaveBeenCalledWith({
expect(Engine.context.SmartTransactionsController.updateSmartTransaction).toHaveBeenCalledTimes(2);
expect(Engine.context.SmartTransactionsController.updateSmartTransaction).toHaveBeenCalledWith({
uuid: 'approval-uuid-123',
origin: ORIGIN_METAMASK,
type: TransactionType.swapApproval,
creationTime: expect.any(Number),
});
expect(mockUpdateSmartTransaction).toHaveBeenCalledWith({
expect(Engine.context.SmartTransactionsController.updateSmartTransaction).toHaveBeenCalledWith({
uuid: 'trade-uuid-456',
origin: ORIGIN_METAMASK,
type: TransactionType.swap,
Expand Down
2 changes: 1 addition & 1 deletion app/components/UI/Swaps/utils/useSwapsSmartTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const useSwapsSmartTransaction = ({ tradeTransaction, gasEstimates }: { t
} }) => {
const chainId = useSelector(selectEvmChainId);
const isEIP1559Network = useSelector(selectIsEIP1559Network);
const approvalTransaction: TxParams = useSelector(selectSwapsApprovalTransaction);
const approvalTransaction: TxParams | null = useSelector(selectSwapsApprovalTransaction);

// We don't need to await on the approval tx to be confirmed on chain. We can simply submit both the approval and trade tx at the same time.
// Sentinel will batch them for us and ensure they are executed in the correct order.
Expand Down

0 comments on commit c33da15

Please sign in to comment.