Skip to content

Commit

Permalink
fix: transaction approval screen showing odd half loading state (#1218)
Browse files Browse the repository at this point in the history
Fixes #1201  
- Fixes loading state only being partially applied to TxApprove
components
- Adds unit tests for TxApprove (as a dumb component)

## Before
<img width="348" alt="Screenshot 2024-04-08 at 20 27 43"
src="https://github.com/FuelLabs/fuels-wallet/assets/114662397/6bbcdbbd-c631-415f-9151-6d70fa182a35">

## After


https://github.com/FuelLabs/fuels-wallet/assets/3487334/60518df0-3b84-4d4c-881b-cc26dfc9b081
  • Loading branch information
arthurgeron authored Apr 10, 2024
1 parent 8f94aee commit 616f091
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-lamps-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fuels-wallet": patch
---

Fixes Approve Transaction screen staying in a partially loading state after approving a transaction
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { TransactionStatus } from 'fuels';

import { TxRequestStatus } from '~/systems/DApp/machines/transactionRequestMachine';
import { TxApprove } from './TxApprove';

const mockNavigate = jest.fn();

jest.mock('~/systems/DApp', () => ({
useTransactionRequest: jest.fn(),
}));
jest.mock('~/systems/Asset', () => ({
useAssets: jest.fn(),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'), // use actual for all non-hook parts
useNavigate: () => ({ navigate: mockNavigate }),
}));
jest.mock('~/systems/Store', () => ({
store: {
refreshNetworks: jest.fn(),
refreshAccounts: jest.fn(),
refreshBalance: jest.fn(),
},
StoreProvider: ({ children }: { children: React.ReactNode }) => (
<>{children}</>
),
}));

import { useAssets } from '~/systems/Asset';
import { useTransactionRequest } from '~/systems/DApp';

const mockUseTransactionRequest = useTransactionRequest as jest.Mock;
const mockUseAssets = useAssets as jest.Mock;

const mockTxResult = {
id: 'tx_123',
status: TransactionStatus.success,
type: 'transfer',
};

describe('TxApprove', () => {
afterEach(() => {
jest.clearAllMocks();
});

const setup = (
transactionRequestOverrides = {},
assetsOverrides = {},
mockStatus?: { status: TxRequestStatus; result: boolean }
) => {
const mockCustomStatus = mockStatus?.status ?? 'never';
mockUseTransactionRequest.mockReturnValue({
isLoading: false,
showActions: true,
status: jest.fn().mockImplementation((status) => {
switch (status) {
case mockCustomStatus:
if (!mockStatus?.status) {
throw new Error('Invalid mock status');
}
return mockStatus?.result ?? false;
case 'sending':
return false;
case 'success':
return false;
case 'failed':
return false;
case 'idle':
return true;
default:
return false;
}
}),
txResult: mockTxResult,
approveStatus: jest.fn().mockReturnValue(TransactionStatus.success),
handlers: {
closeDialog: jest.fn(),
approve: jest.fn(),
tryAgain: jest.fn(),
},
shouldShowLoader: false,
shouldShowTx: true,
title: 'Transaction Approval',
providerUrl: 'https://example.com',
...transactionRequestOverrides,
});

mockUseAssets.mockReturnValue({
assets: [],
isLoading: false,
...assetsOverrides,
});

render(<TxApprove />);
};

it('calls the approve handler when approve button is clicked', () => {
setup();
const approveButton = screen.getByText(/approve/i);
fireEvent.click(approveButton);
expect(mockUseTransactionRequest().handlers.approve).toHaveBeenCalled();
});

it('displays a loading indicator when assets are loading', () => {
setup(
{},
{ isLoading: true },
{ status: TxRequestStatus.idle, result: true }
);
expect(screen.getByText(/loading/i)).toBeDefined();
});

it('displays a loading indicator when status is sending', () => {
setup({}, {}, { status: TxRequestStatus.sending, result: true });
expect(screen.getByText(/loading/i)).toBeDefined();
});

it('displays a loading indicator when the transaction is being processed', () => {
setup({}, {}, { status: TxRequestStatus.loading, result: true });
expect(screen.getByText(/loading/i)).toBeDefined();
});

it('displays success when a transaction was completed', () => {
setup({}, {}, { status: TxRequestStatus.success, result: true });
expect(screen.getByText(/success/i)).toBeDefined();
});

it('displays an error message when the transaction fails', () => {
setup(
{ approveStatus: jest.fn().mockReturnValue(TransactionStatus.failure) },
{},
{ status: TxRequestStatus.failed, result: true }
);
expect(screen.getByText(/failure/i)).toBeDefined();
});

it('does not show the approve button show actions is false', () => {
setup({ showActions: false });
expect(screen.queryByText(/approve/i)).toBeNull();
});

it('shows the try again button when the transaction has failed', () => {
setup(
{ txResult: { ...mockTxResult, status: TransactionStatus.failure } },
{},
{ status: TxRequestStatus.failed, result: true }
);
expect(screen.getByText(/try again/i)).toBeDefined();
});

it('calls the try again handler when try again button is clicked', () => {
setup(
{ txResult: { ...mockTxResult, status: TransactionStatus.failure } },
{},
{ status: TxRequestStatus.failed, result: true }
);
fireEvent.click(screen.getByText(/try again/i));
expect(mockUseTransactionRequest().handlers.tryAgain).toHaveBeenCalled();
});

it('calls the close dialog handler when back button is clicked', () => {
setup();
fireEvent.click(screen.getByText(/back/i));
expect(mockUseTransactionRequest().handlers.closeDialog).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { TxContent, TxHeader } from '~/systems/Transaction';
export const TxApprove = () => {
const ctx = useTransactionRequest();
const navigate = useNavigate();
const { assets } = useAssets();
const { assets, isLoading: isLoadingAssets } = useAssets();
const isSuccess = ctx.status('success');
const isLoading =
ctx.status('loading') || ctx.status('sending') || isLoadingAssets;

const goToWallet = () => {
ctx.handlers.closeDialog();
Expand Down Expand Up @@ -40,7 +42,7 @@ export const TxApprove = () => {
<TxContent.Info
showDetails
tx={ctx.txResult}
isLoading={ctx.status('loading')}
isLoading={isLoading}
header={Header}
assets={assets}
/>
Expand Down Expand Up @@ -79,14 +81,14 @@ export const TxApprove = () => {
<>
<Button
variant="ghost"
isDisabled={ctx.isLoading}
isDisabled={isLoading}
onPress={ctx.handlers.closeDialog}
>
Back
</Button>
<Button
intent="primary"
isLoading={ctx.isLoading}
isLoading={isLoading}
onPress={ctx.handlers.approve}
>
Approve
Expand Down

0 comments on commit 616f091

Please sign in to comment.