Skip to content

Commit 519d6d7

Browse files
committed
fix: tempo disable confirm button until gas autoselect
1 parent d4daf1d commit 519d6d7

10 files changed

Lines changed: 374 additions & 30 deletions

File tree

app/components/UI/Predict/components/PredictBalance/PredictBalance.test.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ import renderWithProvider from '../../../../../util/test/renderWithProvider';
55
import PredictBalance from './PredictBalance';
66
import { strings } from '../../../../../../locales/i18n';
77

8-
// Mock React Query
9-
jest.mock('@tanstack/react-query', () => ({
10-
useQueryClient: () => ({
11-
invalidateQueries: jest.fn(),
12-
}),
13-
}));
14-
158
// Mock React Navigation
169
jest.mock('@react-navigation/native', () => ({
1710
...jest.requireActual('@react-navigation/native'),

app/components/UI/Predict/components/PredictPositionsHeader/PredictPositionsHeader.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jest.mock('../../hooks/usePredictBalance', () => ({
7171

7272
const mockInvalidateQueries = jest.fn();
7373
jest.mock('@tanstack/react-query', () => ({
74+
...jest.requireActual('@tanstack/react-query'),
7475
useQueryClient: () => ({
7576
invalidateQueries: mockInvalidateQueries,
7677
}),

app/components/Views/confirmations/components/footer/footer.test.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { simpleSendTransactionControllerMock } from '../../__mocks__/controllers
2626
import { transactionApprovalControllerMock } from '../../__mocks__/controllers/approval-controller-mock';
2727
import { emptySignatureControllerMock } from '../../__mocks__/controllers/signature-controller-mock';
2828
import { useIsTransactionPayLoading } from '../../hooks/pay/useTransactionPayData';
29+
import { useIsGaslessLoading } from '../../hooks/gas/useIsGaslessLoading';
2930

3031
const mockConfirmSpy = jest.fn();
3132
const mockRejectSpy = jest.fn();
@@ -78,6 +79,12 @@ jest.mock('../../hooks/ui/useFullScreenConfirmation', () => ({
7879
})),
7980
}));
8081

82+
jest.mock('../../hooks/gas/useIsGaslessLoading', () => ({
83+
useIsGaslessLoading: jest.fn(() => ({
84+
isGaslessLoading: false,
85+
})),
86+
}));
87+
8188
const mockTrackAlertMetrics = jest.fn();
8289

8390
(useConfirmationAlertMetrics as jest.Mock).mockReturnValue({
@@ -101,6 +108,7 @@ describe('Footer', () => {
101108
const useIsTransactionPayLoadingMock = jest.mocked(
102109
useIsTransactionPayLoading,
103110
);
111+
const useIsGaslessLoadingMock = jest.mocked(useIsGaslessLoading);
104112

105113
beforeEach(() => {
106114
jest.clearAllMocks();
@@ -124,6 +132,7 @@ describe('Footer', () => {
124132
});
125133

126134
useIsTransactionPayLoadingMock.mockReturnValue(false);
135+
useIsGaslessLoadingMock.mockReturnValue({ isGaslessLoading: false });
127136
});
128137

129138
it('should render correctly', () => {
@@ -228,9 +237,29 @@ describe('Footer', () => {
228237
).toBe(true);
229238
});
230239

231-
it('disables confirm button if quotes are loading', () => {
240+
it('disables confirm button if transaction pay is loading', () => {
232241
useIsTransactionPayLoadingMock.mockReturnValue(true);
242+
useIsGaslessLoadingMock.mockReturnValue({ isGaslessLoading: false });
243+
const state = merge(
244+
{},
245+
simpleSendTransactionControllerMock,
246+
transactionApprovalControllerMock,
247+
emptySignatureControllerMock,
248+
{ securityAlerts: { alerts: {} } },
249+
);
233250

251+
const { getByTestId } = renderWithProvider(<Footer />, {
252+
state,
253+
});
254+
255+
expect(
256+
getByTestId(ConfirmationFooterSelectorIDs.CONFIRM_BUTTON).props.disabled,
257+
).toBe(true);
258+
});
259+
260+
it('disables confirm button if gasless support is loading', () => {
261+
useIsTransactionPayLoadingMock.mockReturnValue(false);
262+
useIsGaslessLoadingMock.mockReturnValue({ isGaslessLoading: true });
234263
const state = merge(
235264
{},
236265
simpleSendTransactionControllerMock,

app/components/Views/confirmations/components/footer/footer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { PredictClaimFooter } from '../predict-confirmations/predict-claim-foote
3939
import { useIsTransactionPayLoading } from '../../hooks/pay/useTransactionPayData';
4040
import { Skeleton } from '../../../../../component-library/components-temp/Skeleton';
4141
import { useQRHardwareContext } from '../../context/qr-hardware-context';
42+
import { useIsGaslessLoading } from '../../hooks/gas/useIsGaslessLoading';
4243

4344
const HIDE_FOOTER_BY_DEFAULT_TYPES = [
4445
TransactionType.moneyAccountDeposit,
@@ -71,6 +72,7 @@ export const Footer = () => {
7172
TRANSFER_TRANSACTION_TYPES.includes(transactionType) &&
7273
transactionMetadata?.origin === MMM_ORIGIN;
7374
const isPayLoading = useIsTransactionPayLoading();
75+
const { isGaslessLoading } = useIsGaslessLoading();
7476

7577
const { isFooterVisible: isFooterVisibleFlag, isTransactionValueUpdating } =
7678
useConfirmationContext();
@@ -158,7 +160,8 @@ export const Footer = () => {
158160
needsCameraPermission ||
159161
hasBlockingAlerts ||
160162
isTransactionValueUpdating ||
161-
isPayLoading;
163+
isPayLoading ||
164+
isGaslessLoading;
162165

163166
const buttons = [
164167
{

app/components/Views/confirmations/context/qr-hardware-context/qr-hardware-context.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ jest.mock('@react-navigation/native', () => ({
7474
}),
7575
}));
7676

77+
jest.mock('../../hooks/gas/useIsGaslessLoading', () => ({
78+
useIsGaslessLoading: jest.fn(() => ({
79+
isGaslessLoading: false,
80+
})),
81+
}));
82+
7783
const mockPendingScanRequest: QrScanRequest = {
7884
request: {
7985
requestId: 'c95ecc76-d6e9-4a0a-afa3-31429bc80566',
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import { GasFeeToken } from '@metamask/transaction-controller';
2+
import { Hex } from '@metamask/utils';
3+
4+
import { useIsGaslessSupported } from './useIsGaslessSupported';
5+
import { useHasInsufficientBalance } from '../useHasInsufficientBalance';
6+
import { useIsGaslessLoading } from './useIsGaslessLoading';
7+
import { renderHookWithProvider } from '../../../../../util/test/renderWithProvider';
8+
import { useTransactionMetadataRequest } from '../transactions/useTransactionMetadataRequest';
9+
import { selectUseTransactionSimulations } from '../../../../../selectors/preferencesController';
10+
11+
jest.mock('./useIsGaslessSupported');
12+
jest.mock('../useHasInsufficientBalance');
13+
jest.mock('../transactions/useTransactionMetadataRequest');
14+
jest.mock('../../../../../selectors/preferencesController');
15+
16+
const mockedUseIsGaslessSupported = jest.mocked(useIsGaslessSupported);
17+
const mockedUseHasInsufficientBalance = jest.mocked(useHasInsufficientBalance);
18+
const mockedUseTransactionMetadataRequest = jest.mocked(
19+
useTransactionMetadataRequest,
20+
);
21+
const mockSelectUseTransactionSimulations = jest.mocked(
22+
selectUseTransactionSimulations,
23+
);
24+
25+
async function runHook({
26+
simulationEnabled,
27+
gaslessSupported,
28+
insufficientBalance,
29+
pending = false,
30+
isSmartTransaction = true,
31+
gasFeeTokens,
32+
excludeNativeTokenForFee,
33+
selectedGasFeeToken,
34+
}: {
35+
simulationEnabled: boolean;
36+
gaslessSupported: boolean;
37+
insufficientBalance: boolean;
38+
pending?: boolean;
39+
isSmartTransaction?: boolean;
40+
gasFeeTokens?: GasFeeToken[];
41+
excludeNativeTokenForFee?: boolean;
42+
selectedGasFeeToken?: Hex;
43+
}) {
44+
mockedUseIsGaslessSupported.mockReturnValue({
45+
isSupported: gaslessSupported,
46+
isSmartTransaction,
47+
pending,
48+
});
49+
mockedUseHasInsufficientBalance.mockReturnValue({
50+
hasInsufficientBalance: insufficientBalance,
51+
nativeCurrency: 'USD',
52+
});
53+
mockedUseTransactionMetadataRequest.mockReturnValue({
54+
gasFeeTokens,
55+
excludeNativeTokenForFee,
56+
selectedGasFeeToken,
57+
} as unknown as ReturnType<typeof useTransactionMetadataRequest>);
58+
mockSelectUseTransactionSimulations.mockReturnValue(simulationEnabled);
59+
const { result } = renderHookWithProvider(useIsGaslessLoading);
60+
return result.current;
61+
}
62+
63+
describe('useIsGaslessLoading', () => {
64+
beforeEach(() => {
65+
jest.clearAllMocks();
66+
});
67+
68+
it('returns true if pending', async () => {
69+
const result = await runHook({
70+
simulationEnabled: true,
71+
gaslessSupported: true,
72+
insufficientBalance: true,
73+
pending: true,
74+
});
75+
76+
expect(result.isGaslessLoading).toBe(true);
77+
});
78+
79+
it('returns false if simulation is disabled', async () => {
80+
const result = await runHook({
81+
simulationEnabled: false,
82+
gaslessSupported: true,
83+
insufficientBalance: true,
84+
});
85+
86+
expect(result.isGaslessLoading).toBe(false);
87+
});
88+
89+
it('returns false if gasless is not supported', async () => {
90+
const result = await runHook({
91+
simulationEnabled: true,
92+
gaslessSupported: false,
93+
insufficientBalance: true,
94+
});
95+
96+
expect(result.isGaslessLoading).toBe(false);
97+
});
98+
99+
it('returns false if there is no insufficient balance', async () => {
100+
const result = await runHook({
101+
simulationEnabled: true,
102+
gaslessSupported: true,
103+
insufficientBalance: false,
104+
});
105+
106+
expect(result.isGaslessLoading).toBe(false);
107+
});
108+
109+
it('returns true if gas fee tokens are undefined (still loading)', async () => {
110+
const result = await runHook({
111+
simulationEnabled: true,
112+
gaslessSupported: true,
113+
insufficientBalance: true,
114+
gasFeeTokens: undefined, // this triggers loading
115+
});
116+
117+
expect(result.isGaslessLoading).toBe(true);
118+
});
119+
120+
it('returns false if gas fee tokens are present', async () => {
121+
const result = await runHook({
122+
simulationEnabled: true,
123+
gaslessSupported: true,
124+
insufficientBalance: true,
125+
gasFeeTokens: [{ tokenAddress: '0x123' }] as unknown as GasFeeToken[],
126+
});
127+
128+
expect(result.isGaslessLoading).toBe(false);
129+
});
130+
131+
it('returns false if gas fee tokens is an empty array', async () => {
132+
const result = await runHook({
133+
simulationEnabled: true,
134+
gaslessSupported: true,
135+
insufficientBalance: true,
136+
gasFeeTokens: [],
137+
});
138+
139+
expect(result.isGaslessLoading).toBe(false);
140+
});
141+
142+
it('returns false if gas fee tokens are present and dont match selectedGasFeeToken (non-reg)', async () => {
143+
const result = await runHook({
144+
simulationEnabled: true,
145+
gaslessSupported: true,
146+
insufficientBalance: true,
147+
gasFeeTokens: [{ tokenAddress: '0x123' }] as unknown as GasFeeToken[],
148+
selectedGasFeeToken: '0x456',
149+
});
150+
151+
expect(result.isGaslessLoading).toBe(false);
152+
});
153+
154+
it('returns true if gas fee tokens are present and dont match selectedGasFeeToken with excludeNativeTokenForFee being true (Tempo)', async () => {
155+
const result = await runHook({
156+
simulationEnabled: true,
157+
gaslessSupported: true,
158+
insufficientBalance: true,
159+
gasFeeTokens: [{ tokenAddress: '0x123' }] as unknown as GasFeeToken[],
160+
selectedGasFeeToken: '0x456',
161+
excludeNativeTokenForFee: true,
162+
});
163+
164+
expect(result.isGaslessLoading).toBe(true);
165+
});
166+
167+
it('returns false if gas fee tokens are present AND match selectedGasFeeToken with excludeNativeTokenForFee being true (Tempo)', async () => {
168+
const result = await runHook({
169+
simulationEnabled: true,
170+
gaslessSupported: true,
171+
insufficientBalance: true,
172+
gasFeeTokens: [
173+
{ tokenAddress: '0x123' },
174+
{ tokenAddress: '0x789' },
175+
] as unknown as GasFeeToken[],
176+
// Matches the first one
177+
selectedGasFeeToken: '0x123',
178+
excludeNativeTokenForFee: true,
179+
});
180+
181+
expect(result.isGaslessLoading).toBe(false);
182+
});
183+
184+
it('returns false if gas fee tokens are present AND selectedGasFeeToken is undefined with excludeNativeTokenForFee being true (Tempo)', async () => {
185+
const result = await runHook({
186+
simulationEnabled: true,
187+
gaslessSupported: true,
188+
insufficientBalance: true,
189+
gasFeeTokens: [{ tokenAddress: '0x123' }] as unknown as GasFeeToken[],
190+
selectedGasFeeToken: undefined,
191+
excludeNativeTokenForFee: true,
192+
});
193+
194+
expect(result.isGaslessLoading).toBe(false);
195+
});
196+
197+
it('returns false if gas fee tokens is empty array with excludeNativeTokenForFee being true (Tempo)', async () => {
198+
const result = await runHook({
199+
simulationEnabled: true,
200+
gaslessSupported: true,
201+
insufficientBalance: true,
202+
gasFeeTokens: [] as unknown as GasFeeToken[],
203+
selectedGasFeeToken: '0x456',
204+
excludeNativeTokenForFee: true,
205+
});
206+
207+
expect(result.isGaslessLoading).toBe(false);
208+
});
209+
});

0 commit comments

Comments
 (0)