mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Add more unit / integration tests for Swaps (#16040)
This commit is contained in:
parent
22f07aefe3
commit
c8067e9351
@ -11,7 +11,7 @@ module.exports = {
|
|||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
branches: 44,
|
branches: 44,
|
||||||
functions: 42,
|
functions: 46.8,
|
||||||
lines: 52,
|
lines: 52,
|
||||||
statements: 52,
|
statements: 52,
|
||||||
},
|
},
|
||||||
|
@ -120,6 +120,13 @@ export const createSwapsMockStore = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
fromToken: 'ETH',
|
fromToken: 'ETH',
|
||||||
|
toToken: {
|
||||||
|
symbol: 'USDC',
|
||||||
|
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||||
|
occurances: 4,
|
||||||
|
},
|
||||||
|
currentSmartTransactionsErrorMessageDismissed: false,
|
||||||
|
swapsSTXLoading: false,
|
||||||
},
|
},
|
||||||
metamask: {
|
metamask: {
|
||||||
networkDetails: {
|
networkDetails: {
|
||||||
@ -314,10 +321,10 @@ export const createSwapsMockStore = () => {
|
|||||||
fetchParams: {
|
fetchParams: {
|
||||||
metaData: {
|
metaData: {
|
||||||
sourceTokenInfo: {
|
sourceTokenInfo: {
|
||||||
symbol: 'BAT',
|
symbol: 'ETH',
|
||||||
},
|
},
|
||||||
destinationTokenInfo: {
|
destinationTokenInfo: {
|
||||||
symbol: 'ETH',
|
symbol: 'USDC',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -326,6 +333,10 @@ export const createSwapsMockStore = () => {
|
|||||||
quotesLastFetched: 1519211809934,
|
quotesLastFetched: 1519211809934,
|
||||||
swapsQuoteRefreshTime: 60000,
|
swapsQuoteRefreshTime: 60000,
|
||||||
swapsQuotePrefetchingRefreshTime: 60000,
|
swapsQuotePrefetchingRefreshTime: 60000,
|
||||||
|
swapsStxBatchStatusRefreshTime: 5000,
|
||||||
|
swapsStxGetTransactionsRefreshTime: 5000,
|
||||||
|
swapsStxMaxFeeMultiplier: 1.5,
|
||||||
|
swapsStxStatusDeadline: 150000,
|
||||||
customMaxGas: '',
|
customMaxGas: '',
|
||||||
customGasPrice: null,
|
customGasPrice: null,
|
||||||
selectedAggId: 'TEST_AGG_2',
|
selectedAggId: 'TEST_AGG_2',
|
||||||
@ -412,6 +423,7 @@ export const createSwapsMockStore = () => {
|
|||||||
{
|
{
|
||||||
uuid: 'uuid2',
|
uuid: 'uuid2',
|
||||||
status: 'success',
|
status: 'success',
|
||||||
|
cancellable: false,
|
||||||
statusMetadata: {
|
statusMetadata: {
|
||||||
cancellationFeeWei: 36777567771000,
|
cancellationFeeWei: 36777567771000,
|
||||||
cancellationReason: 'not_cancelled',
|
cancellationReason: 'not_cancelled',
|
||||||
@ -424,6 +436,7 @@ export const createSwapsMockStore = () => {
|
|||||||
{
|
{
|
||||||
uuid: 'uuid2',
|
uuid: 'uuid2',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
|
cancellable: true,
|
||||||
statusMetadata: {
|
statusMetadata: {
|
||||||
cancellationFeeWei: 36777567771000,
|
cancellationFeeWei: 36777567771000,
|
||||||
cancellationReason: 'not_cancelled',
|
cancellationReason: 'not_cancelled',
|
||||||
|
@ -92,7 +92,7 @@ import {
|
|||||||
hexWEIToDecGWEI,
|
hexWEIToDecGWEI,
|
||||||
} from '../../../shared/lib/transactions-controller-utils';
|
} from '../../../shared/lib/transactions-controller-utils';
|
||||||
|
|
||||||
const GAS_PRICES_LOADING_STATES = {
|
export const GAS_PRICES_LOADING_STATES = {
|
||||||
INITIAL: 'INITIAL',
|
INITIAL: 'INITIAL',
|
||||||
LOADING: 'LOADING',
|
LOADING: 'LOADING',
|
||||||
FAILED: 'FAILED',
|
FAILED: 'FAILED',
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
import { MOCKS, createSwapsMockStore } from '../../../test/jest';
|
import { MOCKS, createSwapsMockStore } from '../../../test/jest';
|
||||||
import { setSwapsLiveness, setSwapsFeatureFlags } from '../../store/actions';
|
import { setSwapsLiveness, setSwapsFeatureFlags } from '../../store/actions';
|
||||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||||
import { setStorageItem } from '../../../shared/lib/storage-helpers';
|
import { setStorageItem } from '../../../shared/lib/storage-helpers';
|
||||||
import * as swaps from './swaps';
|
import swapsReducer, * as swaps from './swaps';
|
||||||
|
|
||||||
|
const middleware = [thunk];
|
||||||
|
|
||||||
jest.mock('../../store/actions.js', () => ({
|
jest.mock('../../store/actions.js', () => ({
|
||||||
setSwapsLiveness: jest.fn(),
|
setSwapsLiveness: jest.fn(),
|
||||||
@ -176,24 +180,6 @@ describe('Ducks - Swaps', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getCustomSwapsGas', () => {
|
|
||||||
it('returns "customMaxGas"', () => {
|
|
||||||
const state = createSwapsMockStore();
|
|
||||||
const customMaxGas = '29000';
|
|
||||||
state.metamask.swapsState.customMaxGas = customMaxGas;
|
|
||||||
expect(swaps.getCustomSwapsGas(state)).toBe(customMaxGas);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getCustomMaxFeePerGas', () => {
|
|
||||||
it('returns "customMaxFeePerGas"', () => {
|
|
||||||
const state = createSwapsMockStore();
|
|
||||||
const customMaxFeePerGas = '20';
|
|
||||||
state.metamask.swapsState.customMaxFeePerGas = customMaxFeePerGas;
|
|
||||||
expect(swaps.getCustomMaxFeePerGas(state)).toBe(customMaxFeePerGas);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getCustomMaxPriorityFeePerGas', () => {
|
describe('getCustomMaxPriorityFeePerGas', () => {
|
||||||
it('returns "customMaxPriorityFeePerGas"', () => {
|
it('returns "customMaxPriorityFeePerGas"', () => {
|
||||||
const state = createSwapsMockStore();
|
const state = createSwapsMockStore();
|
||||||
@ -206,6 +192,188 @@ describe('Ducks - Swaps', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getAggregatorMetadata', () => {
|
||||||
|
it('returns agg metadata', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getAggregatorMetadata(state)).toBe(
|
||||||
|
state.swaps.aggregatorMetadata,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBalanceError', () => {
|
||||||
|
it('returns a balance error', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.balanceError = 'balanceError';
|
||||||
|
expect(swaps.getBalanceError(state)).toBe(state.swaps.balanceError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFromToken', () => {
|
||||||
|
it('returns fromToken', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getFromToken(state)).toBe(state.swaps.fromToken);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFromTokenError', () => {
|
||||||
|
it('returns fromTokenError', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.fromTokenError = 'fromTokenError';
|
||||||
|
expect(swaps.getFromTokenError(state)).toBe(state.swaps.fromTokenError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFromTokenInputValue', () => {
|
||||||
|
it('returns fromTokenInputValue', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getFromTokenInputValue(state)).toBe(
|
||||||
|
state.swaps.fromTokenInputValue,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getIsFeatureFlagLoaded', () => {
|
||||||
|
it('returns isFeatureFlagLoaded', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getIsFeatureFlagLoaded(state)).toBe(
|
||||||
|
state.swaps.isFeatureFlagLoaded,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsSTXLoading', () => {
|
||||||
|
it('returns swapsSTXLoading', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsSTXLoading(state)).toBe(state.swaps.swapsSTXLoading);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMaxSlippage', () => {
|
||||||
|
it('returns maxSlippage', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getMaxSlippage(state)).toBe(state.swaps.maxSlippage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTopAssets', () => {
|
||||||
|
it('returns topAssets', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getTopAssets(state)).toBe(state.swaps.topAssets);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getToToken', () => {
|
||||||
|
it('returns toToken', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getToToken(state)).toBe(state.swaps.toToken);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFetchingQuotes', () => {
|
||||||
|
it('returns fetchingQuotes', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getFetchingQuotes(state)).toBe(state.swaps.fetchingQuotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getQuotesFetchStartTime', () => {
|
||||||
|
it('returns quotesFetchStartTime', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getQuotesFetchStartTime(state)).toBe(
|
||||||
|
state.swaps.quotesFetchStartTime,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getReviewSwapClickedTimestamp', () => {
|
||||||
|
it('returns reviewSwapClickedTimestamp', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getReviewSwapClickedTimestamp(state)).toBe(
|
||||||
|
state.swaps.reviewSwapClickedTimestamp,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsCustomizationModalPrice', () => {
|
||||||
|
it('returns customGas.price', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsCustomizationModalPrice(state)).toBe(
|
||||||
|
state.swaps.customGas.price,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsCustomizationModalLimit', () => {
|
||||||
|
it('returns customGas.limit', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsCustomizationModalLimit(state)).toBe(
|
||||||
|
state.swaps.customGas.limit,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapGasPriceEstimateIsLoading', () => {
|
||||||
|
it('returns true for swapGasPriceEstimateIsLoading', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.customGas.loading = swaps.GAS_PRICES_LOADING_STATES.LOADING;
|
||||||
|
expect(swaps.swapGasPriceEstimateIsLoading(state)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapGasEstimateLoadingHasFailed', () => {
|
||||||
|
it('returns true for swapGasEstimateLoadingHasFailed', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.customGas.loading = swaps.GAS_PRICES_LOADING_STATES.INITIAL;
|
||||||
|
expect(swaps.swapGasEstimateLoadingHasFailed(state)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapGasPriceEstimateData', () => {
|
||||||
|
it('returns customGas.priceEstimates', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapGasPriceEstimateData(state)).toBe(
|
||||||
|
state.swaps.customGas.priceEstimates,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsFallbackGasPrice', () => {
|
||||||
|
it('returns customGas.fallBackPrice', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsFallbackGasPrice(state)).toBe(
|
||||||
|
state.swaps.customGas.fallBackPrice,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCurrentSmartTransactionsError', () => {
|
||||||
|
it('returns currentSmartTransactionsError', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.currentSmartTransactionsError =
|
||||||
|
'currentSmartTransactionsError';
|
||||||
|
expect(swaps.getCurrentSmartTransactionsError(state)).toBe(
|
||||||
|
state.swaps.currentSmartTransactionsError,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCurrentSmartTransactionsErrorMessageDismissed', () => {
|
||||||
|
it('returns currentSmartTransactionsErrorMessageDismissed', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(
|
||||||
|
swaps.getCurrentSmartTransactionsErrorMessageDismissed(state),
|
||||||
|
).toBe(state.swaps.currentSmartTransactionsErrorMessageDismissed);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('shouldShowCustomPriceTooLowWarning', () => {
|
||||||
|
it('returns false for showCustomPriceTooLowWarning', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.shouldShowCustomPriceTooLowWarning(state)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getSwapsFeatureIsLive', () => {
|
describe('getSwapsFeatureIsLive', () => {
|
||||||
it('returns true for "swapsFeatureIsLive"', () => {
|
it('returns true for "swapsFeatureIsLive"', () => {
|
||||||
const state = createSwapsMockStore();
|
const state = createSwapsMockStore();
|
||||||
@ -222,19 +390,22 @@ describe('Ducks - Swaps', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getUsedQuote', () => {
|
describe('getSmartTransactionsError', () => {
|
||||||
it('returns selected quote', () => {
|
it('returns smartTransactionsError', () => {
|
||||||
const state = createSwapsMockStore();
|
const state = createSwapsMockStore();
|
||||||
expect(swaps.getUsedQuote(state)).toMatchObject(
|
state.appState.smartTransactionsError = 'stxError';
|
||||||
state.metamask.swapsState.quotes.TEST_AGG_2,
|
expect(swaps.getSmartTransactionsError(state)).toBe(
|
||||||
|
state.appState.smartTransactionsError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('returns best quote', () => {
|
describe('getSmartTransactionsErrorMessageDismissed', () => {
|
||||||
|
it('returns smartTransactionsErrorMessageDismissed', () => {
|
||||||
const state = createSwapsMockStore();
|
const state = createSwapsMockStore();
|
||||||
state.metamask.swapsState.selectedAggId = null;
|
state.appState.smartTransactionsErrorMessageDismissed = true;
|
||||||
expect(swaps.getUsedQuote(state)).toMatchObject(
|
expect(swaps.getSmartTransactionsErrorMessageDismissed(state)).toBe(
|
||||||
state.metamask.swapsState.quotes.TEST_AGG_BEST,
|
state.appState.smartTransactionsErrorMessageDismissed,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -288,6 +459,234 @@ describe('Ducks - Swaps', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getCurrentSmartTransactionsEnabled', () => {
|
||||||
|
it('returns true if STX are enabled and there is no current STX error', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getCurrentSmartTransactionsEnabled(state)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if STX are enabled and there is an current STX error', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.swaps.currentSmartTransactionsError =
|
||||||
|
'currentSmartTransactionsError';
|
||||||
|
expect(swaps.getCurrentSmartTransactionsEnabled(state)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsQuoteRefreshTime', () => {
|
||||||
|
it('returns swapsQuoteRefreshTime', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsQuoteRefreshTime(state)).toBe(
|
||||||
|
state.metamask.swapsState.swapsQuoteRefreshTime,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsQuotePrefetchingRefreshTime', () => {
|
||||||
|
it('returns swapsQuotePrefetchingRefreshTime', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsQuotePrefetchingRefreshTime(state)).toBe(
|
||||||
|
state.metamask.swapsState.swapsQuotePrefetchingRefreshTime,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBackgroundSwapRouteState', () => {
|
||||||
|
it('returns routeState', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getBackgroundSwapRouteState(state)).toBe(
|
||||||
|
state.metamask.swapsState.routeState,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustomSwapsGas', () => {
|
||||||
|
it('returns "customMaxGas"', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
const customMaxGas = '29000';
|
||||||
|
state.metamask.swapsState.customMaxGas = customMaxGas;
|
||||||
|
expect(swaps.getCustomSwapsGas(state)).toBe(customMaxGas);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustomSwapsGasPrice', () => {
|
||||||
|
it('returns customGasPrice', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getCustomSwapsGasPrice(state)).toBe(
|
||||||
|
state.metamask.swapsState.customGasPrice,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCustomMaxFeePerGas', () => {
|
||||||
|
it('returns "customMaxFeePerGas"', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
const customMaxFeePerGas = '20';
|
||||||
|
state.metamask.swapsState.customMaxFeePerGas = customMaxFeePerGas;
|
||||||
|
expect(swaps.getCustomMaxFeePerGas(state)).toBe(customMaxFeePerGas);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsUserFeeLevel', () => {
|
||||||
|
it('returns swapsUserFeeLevel', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsUserFeeLevel(state)).toBe(
|
||||||
|
state.metamask.swapsState.swapsUserFeeLevel,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getFetchParams', () => {
|
||||||
|
it('returns fetchParams', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getFetchParams(state)).toBe(
|
||||||
|
state.metamask.swapsState.fetchParams,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getQuotes', () => {
|
||||||
|
it('returns quotes for Swaps', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getQuotes(state)).toBe(state.metamask.swapsState.quotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getQuotesLastFetched', () => {
|
||||||
|
it('returns quotesLastFetched', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getQuotesLastFetched(state)).toBe(
|
||||||
|
state.metamask.swapsState.quotesLastFetched,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSelectedQuote', () => {
|
||||||
|
it('returns selected quote', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSelectedQuote(state)).toBe(
|
||||||
|
state.metamask.swapsState.quotes.TEST_AGG_2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsErrorKey', () => {
|
||||||
|
it('returns errorKey', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsErrorKey(state)).toBe(
|
||||||
|
state.metamask.swapsState.errorKey,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getShowQuoteLoadingScreen', () => {
|
||||||
|
it('returns showQuoteLoadingScreen', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getShowQuoteLoadingScreen(state)).toBe(
|
||||||
|
state.swaps.showQuoteLoadingScreen,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsTokens', () => {
|
||||||
|
it('returns tokens', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsTokens(state)).toBe(
|
||||||
|
state.metamask.swapsState.tokens,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsWelcomeMessageSeenStatus', () => {
|
||||||
|
it('returns', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getSwapsWelcomeMessageSeenStatus(state)).toBe(
|
||||||
|
state.metamask.swapsState.swapsWelcomeMessageHasBeenShown,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTopQuote', () => {
|
||||||
|
it('returns a top quote', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getTopQuote(state)).toBe(
|
||||||
|
state.metamask.swapsState.quotes.TEST_AGG_BEST,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getApproveTxId', () => {
|
||||||
|
it('returns approveTxId', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getApproveTxId(state)).toBe(
|
||||||
|
state.metamask.swapsState.approveTxId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTradeTxId', () => {
|
||||||
|
it('returns tradeTxId', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getTradeTxId(state)).toBe(
|
||||||
|
state.metamask.swapsState.tradeTxId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getUsedQuote', () => {
|
||||||
|
it('returns selected quote', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getUsedQuote(state)).toMatchObject(
|
||||||
|
state.metamask.swapsState.quotes.TEST_AGG_2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns best quote', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.metamask.swapsState.selectedAggId = null;
|
||||||
|
expect(swaps.getUsedQuote(state)).toMatchObject(
|
||||||
|
state.metamask.swapsState.quotes.TEST_AGG_BEST,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDestinationTokenInfo', () => {
|
||||||
|
it('returns destinationTokenInfo', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
expect(swaps.getDestinationTokenInfo(state)).toBe(
|
||||||
|
state.metamask.swapsState.fetchParams.metaData.destinationTokenInfo,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getUsedSwapsGasPrice', () => {
|
||||||
|
it('returns customGasPrice', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.metamask.swapsState.customGasPrice = 5;
|
||||||
|
expect(swaps.getUsedSwapsGasPrice(state)).toBe(
|
||||||
|
state.metamask.swapsState.customGasPrice,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getApproveTxParams', () => {
|
||||||
|
it('returns approveTxParams', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
state.metamask.swapsState.quotes.TEST_AGG_2.approvalNeeded = {
|
||||||
|
data: '0x095ea7b300000000000000000000000095e6f48254609a6ee006f7d493c8e5fb97094cef0000000000000000000000000000000000000000004a817c7ffffffdabf41c00',
|
||||||
|
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||||
|
value: '0x0',
|
||||||
|
from: '0x2369267687A84ac7B494daE2f1542C40E37f4455',
|
||||||
|
gas: '0x12',
|
||||||
|
gasPrice: '0x34',
|
||||||
|
};
|
||||||
|
expect(swaps.getApproveTxParams(state)).toMatchObject({
|
||||||
|
...state.metamask.swapsState.quotes.TEST_AGG_2.approvalNeeded,
|
||||||
|
gasPrice: 5,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getSmartTransactionsOptInStatus', () => {
|
describe('getSmartTransactionsOptInStatus', () => {
|
||||||
it('returns STX opt in status', () => {
|
it('returns STX opt in status', () => {
|
||||||
const state = createSwapsMockStore();
|
const state = createSwapsMockStore();
|
||||||
@ -325,4 +724,261 @@ describe('Ducks - Swaps', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getSmartTransactionEstimatedGas', () => {
|
||||||
|
it('returns unsigned transactions and estimates', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
const smartTransactionFees = swaps.getSmartTransactionEstimatedGas(state);
|
||||||
|
expect(smartTransactionFees).toBe(
|
||||||
|
state.metamask.smartTransactionsState.estimatedGas,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSwapsNetworkConfig', () => {
|
||||||
|
it('returns Swaps network config', () => {
|
||||||
|
const state = createSwapsMockStore();
|
||||||
|
const {
|
||||||
|
swapsQuoteRefreshTime,
|
||||||
|
swapsQuotePrefetchingRefreshTime,
|
||||||
|
swapsStxGetTransactionsRefreshTime,
|
||||||
|
swapsStxBatchStatusRefreshTime,
|
||||||
|
swapsStxStatusDeadline,
|
||||||
|
swapsStxMaxFeeMultiplier,
|
||||||
|
} = state.metamask.swapsState;
|
||||||
|
const swapsNetworkConfig = swaps.getSwapsNetworkConfig(state);
|
||||||
|
expect(swapsNetworkConfig).toMatchObject({
|
||||||
|
quoteRefreshTime: swapsQuoteRefreshTime,
|
||||||
|
quotePrefetchingRefreshTime: swapsQuotePrefetchingRefreshTime,
|
||||||
|
stxGetTransactionsRefreshTime: swapsStxGetTransactionsRefreshTime,
|
||||||
|
stxBatchStatusRefreshTime: swapsStxBatchStatusRefreshTime,
|
||||||
|
stxStatusDeadline: swapsStxStatusDeadline,
|
||||||
|
stxMaxFeeMultiplier: swapsStxMaxFeeMultiplier,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('actions + reducers', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
store.clearActions();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('clearSwapsState', () => {
|
||||||
|
it('calls the "swaps/clearSwapsState" action', () => {
|
||||||
|
store.dispatch(swaps.clearSwapsState());
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions).toHaveLength(1);
|
||||||
|
expect(actions[0].type).toBe('swaps/clearSwapsState');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dismissCurrentSmartTransactionsErrorMessage', () => {
|
||||||
|
it('calls the "swaps/dismissCurrentSmartTransactionsErrorMessage" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
store.dispatch(swaps.dismissCurrentSmartTransactionsErrorMessage());
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe(
|
||||||
|
'swaps/dismissCurrentSmartTransactionsErrorMessage',
|
||||||
|
);
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.currentSmartTransactionsErrorMessageDismissed).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setAggregatorMetadata', () => {
|
||||||
|
it('calls the "swaps/setAggregatorMetadata" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = {
|
||||||
|
name: 'agg1',
|
||||||
|
};
|
||||||
|
store.dispatch(swaps.setAggregatorMetadata(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setAggregatorMetadata');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.aggregatorMetadata).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setBalanceError', () => {
|
||||||
|
it('calls the "swaps/setBalanceError" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 'balanceError';
|
||||||
|
store.dispatch(swaps.setBalanceError(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setBalanceError');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.balanceError).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setFetchingQuotes', () => {
|
||||||
|
it('calls the "swaps/setFetchingQuotes" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = true;
|
||||||
|
store.dispatch(swaps.setFetchingQuotes(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setFetchingQuotes');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.fetchingQuotes).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setSwapsFromToken', () => {
|
||||||
|
it('calls the "swaps/setFromToken" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 'ETH';
|
||||||
|
store.dispatch(swaps.setSwapsFromToken(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setFromToken');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.fromToken).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setFromTokenError', () => {
|
||||||
|
it('calls the "swaps/setFromTokenError" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 'fromTokenError';
|
||||||
|
store.dispatch(swaps.setFromTokenError(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setFromTokenError');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.fromTokenError).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setFromTokenInputValue', () => {
|
||||||
|
it('calls the "swaps/setFromTokenInputValue" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = '5';
|
||||||
|
store.dispatch(swaps.setFromTokenInputValue(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setFromTokenInputValue');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.fromTokenInputValue).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setIsFeatureFlagLoaded', () => {
|
||||||
|
it('calls the "swaps/setIsFeatureFlagLoaded" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = true;
|
||||||
|
store.dispatch(swaps.setIsFeatureFlagLoaded(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setIsFeatureFlagLoaded');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.isFeatureFlagLoaded).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setMaxSlippage', () => {
|
||||||
|
it('calls the "swaps/setMaxSlippage" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 3;
|
||||||
|
store.dispatch(swaps.setMaxSlippage(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setMaxSlippage');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.maxSlippage).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setSwapQuotesFetchStartTime', () => {
|
||||||
|
it('calls the "swaps/setQuotesFetchStartTime" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = '1664461886';
|
||||||
|
store.dispatch(swaps.setSwapQuotesFetchStartTime(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setQuotesFetchStartTime');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.quotesFetchStartTime).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setReviewSwapClickedTimestamp', () => {
|
||||||
|
it('calls the "swaps/setReviewSwapClickedTimestamp" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = '1664461886';
|
||||||
|
store.dispatch(swaps.setReviewSwapClickedTimestamp(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setReviewSwapClickedTimestamp');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.reviewSwapClickedTimestamp).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setTopAssets', () => {
|
||||||
|
it('calls the "swaps/setTopAssets" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = {
|
||||||
|
'0x514910771af9ca656af840dff83e8264ecf986ca': {
|
||||||
|
index: '0',
|
||||||
|
},
|
||||||
|
'0x04fa0d235c4abf4bcf4787af4cf447de572ef828': {
|
||||||
|
index: '1',
|
||||||
|
},
|
||||||
|
'0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e': {
|
||||||
|
index: '2',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
store.dispatch(swaps.setTopAssets(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setTopAssets');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.topAssets).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setSwapToToken', () => {
|
||||||
|
it('calls the "swaps/setToToken" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 'USDC';
|
||||||
|
store.dispatch(swaps.setSwapToToken(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/setToToken');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.toToken).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapCustomGasModalPriceEdited', () => {
|
||||||
|
it('calls the "swaps/swapCustomGasModalPriceEdited" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 5;
|
||||||
|
store.dispatch(swaps.swapCustomGasModalPriceEdited(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/swapCustomGasModalPriceEdited');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.customGas.price).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapCustomGasModalLimitEdited', () => {
|
||||||
|
it('calls the "swaps/swapCustomGasModalLimitEdited" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
const actionPayload = 100;
|
||||||
|
store.dispatch(swaps.swapCustomGasModalLimitEdited(actionPayload));
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/swapCustomGasModalLimitEdited');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.customGas.limit).toBe(actionPayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapCustomGasModalClosed', () => {
|
||||||
|
it('calls the "swaps/swapCustomGasModalClosed" action', () => {
|
||||||
|
const state = store.getState().swaps;
|
||||||
|
store.dispatch(swaps.swapCustomGasModalClosed());
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toBe('swaps/swapCustomGasModalClosed');
|
||||||
|
const newState = swapsReducer(state, actions[0]);
|
||||||
|
expect(newState.customGas.price).toBe(null);
|
||||||
|
expect(newState.customGas.limit).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ exports[`AwaitingSwap renders the component with initial props 1`] = `
|
|||||||
<span
|
<span
|
||||||
class="awaiting-swap__amount-and-symbol"
|
class="awaiting-swap__amount-and-symbol"
|
||||||
>
|
>
|
||||||
ETH
|
USDC
|
||||||
</span>
|
</span>
|
||||||
will be added to your account once this transaction has processed.
|
will be added to your account once this transaction has processed.
|
||||||
|
|
||||||
|
@ -283,6 +283,7 @@ export default function AwaitingSwap({
|
|||||||
) : null}
|
) : null}
|
||||||
<SwapsFooter
|
<SwapsFooter
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (errorKey === OFFLINE_FOR_MAINTENANCE) {
|
if (errorKey === OFFLINE_FOR_MAINTENANCE) {
|
||||||
await dispatch(prepareToLeaveSwaps());
|
await dispatch(prepareToLeaveSwaps());
|
||||||
history.push(DEFAULT_ROUTE);
|
history.push(DEFAULT_ROUTE);
|
||||||
|
@ -1,13 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
|
setBackgroundConnection,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
import { SLIPPAGE } from '../../../../shared/constants/swaps';
|
import {
|
||||||
|
SLIPPAGE,
|
||||||
|
QUOTES_EXPIRED_ERROR,
|
||||||
|
SWAP_FAILED_ERROR,
|
||||||
|
ERROR_FETCHING_QUOTES,
|
||||||
|
QUOTES_NOT_AVAILABLE_ERROR,
|
||||||
|
CONTRACT_DATA_DISABLED_ERROR,
|
||||||
|
OFFLINE_FOR_MAINTENANCE,
|
||||||
|
} from '../../../../shared/constants/swaps';
|
||||||
import AwaitingSwap from '.';
|
import AwaitingSwap from '.';
|
||||||
|
|
||||||
|
const middleware = [thunk];
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
return {
|
return {
|
||||||
swapComplete: false,
|
swapComplete: false,
|
||||||
@ -20,20 +33,25 @@ const createProps = (customProps = {}) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setBackgroundConnection({
|
||||||
|
stopPollingForQuotes: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
describe('AwaitingSwap', () => {
|
describe('AwaitingSwap', () => {
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const store = configureMockStore()(createSwapsMockStore());
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
const { getByText } = renderWithProvider(
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
<AwaitingSwap {...createProps()} />,
|
<AwaitingSwap {...createProps()} />,
|
||||||
store,
|
store,
|
||||||
);
|
);
|
||||||
expect(getByText('Processing')).toBeInTheDocument();
|
expect(getByText('Processing')).toBeInTheDocument();
|
||||||
expect(getByText('ETH')).toBeInTheDocument();
|
expect(getByText('USDC')).toBeInTheDocument();
|
||||||
expect(getByText('View in activity')).toBeInTheDocument();
|
expect(getByText('View in activity')).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
document.querySelector('.awaiting-swap__main-description'),
|
document.querySelector('.awaiting-swap__main-description'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(getByText('View in activity')).toBeInTheDocument();
|
expect(getByText('View in activity')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('page-container-footer-next')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the component with for completed swap', () => {
|
it('renders the component with for completed swap', () => {
|
||||||
@ -43,8 +61,110 @@ describe('AwaitingSwap', () => {
|
|||||||
store,
|
store,
|
||||||
);
|
);
|
||||||
expect(getByText('Transaction complete')).toBeInTheDocument();
|
expect(getByText('Transaction complete')).toBeInTheDocument();
|
||||||
expect(getByText('tokens received: ETH')).toBeInTheDocument();
|
expect(getByText('tokens received: USDC')).toBeInTheDocument();
|
||||||
expect(getByText('View Swap at etherscan.io')).toBeInTheDocument();
|
expect(getByText('View Swap at etherscan.io')).toBeInTheDocument();
|
||||||
expect(getByText('Create a new swap')).toBeInTheDocument();
|
expect(getByText('Create a new swap')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "OFFLINE_FOR_MAINTENANCE" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: OFFLINE_FOR_MAINTENANCE,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Offline for maintenance')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'MetaMask Swaps is undergoing maintenance. Please check back later.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "SWAP_FAILED_ERROR" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: SWAP_FAILED_ERROR,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Swap failed')).toBeInTheDocument();
|
||||||
|
fireEvent.click(getByText('metamask-flask.zendesk.com'));
|
||||||
|
expect(getByText('Try again')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "QUOTES_EXPIRED_ERROR" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: QUOTES_EXPIRED_ERROR,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Quotes timeout')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText('Please request new quotes to get the latest rates.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Try again')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "ERROR_FETCHING_QUOTES" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: ERROR_FETCHING_QUOTES,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Error fetching quotes')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'Hmmm... something went wrong. Try again, or if errors persist, contact customer support.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Back')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "QUOTES_NOT_AVAILABLE_ERROR" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: QUOTES_NOT_AVAILABLE_ERROR,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('No quotes available')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText('Try adjusting the amount or slippage settings and try again.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Try again')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with the "CONTRACT_DATA_DISABLED_ERROR" error', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
errorKey: CONTRACT_DATA_DISABLED_ERROR,
|
||||||
|
});
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<AwaitingSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getByText('Contract data is not enabled on your Ledger'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'In the Ethereum app on your Ledger, go to "Settings" and allow contract data. Then, try your swap again.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Try again')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -481,6 +481,7 @@ export default function BuildQuote({
|
|||||||
className="build-quote__token-etherscan-link build-quote__underline"
|
className="build-quote__token-etherscan-link build-quote__underline"
|
||||||
key="build-quote-etherscan-link"
|
key="build-quote-etherscan-link"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
/* istanbul ignore next */
|
||||||
trackEvent({
|
trackEvent({
|
||||||
event: EVENT_NAMES.EXTERNAL_LINK_CLICKED,
|
event: EVENT_NAMES.EXTERNAL_LINK_CLICKED,
|
||||||
category: EVENT.CATEGORIES.SWAPS,
|
category: EVENT.CATEGORIES.SWAPS,
|
||||||
@ -678,6 +679,7 @@ export default function BuildQuote({
|
|||||||
onSelect={onFromSelect}
|
onSelect={onFromSelect}
|
||||||
itemsToSearch={tokensToSearchSwapFrom}
|
itemsToSearch={tokensToSearchSwapFrom}
|
||||||
onInputChange={(value) => {
|
onInputChange={(value) => {
|
||||||
|
/* istanbul ignore next */
|
||||||
onInputChange(value, fromTokenBalance);
|
onInputChange(value, fromTokenBalance);
|
||||||
}}
|
}}
|
||||||
inputValue={fromTokenInputValue}
|
inputValue={fromTokenInputValue}
|
||||||
@ -732,6 +734,7 @@ export default function BuildQuote({
|
|||||||
<div className="build-quote__swap-arrows-row">
|
<div className="build-quote__swap-arrows-row">
|
||||||
<button
|
<button
|
||||||
className="build-quote__swap-arrows"
|
className="build-quote__swap-arrows"
|
||||||
|
data-testid="build-quote__swap-arrows"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onToSelect(selectedFromToken);
|
onToSelect(selectedFromToken);
|
||||||
onFromSelect(selectedToToken);
|
onFromSelect(selectedToToken);
|
||||||
@ -781,6 +784,7 @@ export default function BuildQuote({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
primaryAction={
|
primaryAction={
|
||||||
|
/* istanbul ignore next */
|
||||||
verificationClicked
|
verificationClicked
|
||||||
? null
|
? null
|
||||||
: {
|
: {
|
||||||
@ -809,6 +813,7 @@ export default function BuildQuote({
|
|||||||
className="build-quote__token-etherscan-link"
|
className="build-quote__token-etherscan-link"
|
||||||
key="build-quote-etherscan-link"
|
key="build-quote-etherscan-link"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
/* istanbul ignore next */
|
||||||
trackEvent({
|
trackEvent({
|
||||||
event: 'Clicked Block Explorer Link',
|
event: 'Clicked Block Explorer Link',
|
||||||
category: EVENT.CATEGORIES.SWAPS,
|
category: EVENT.CATEGORIES.SWAPS,
|
||||||
@ -861,30 +866,33 @@ export default function BuildQuote({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<SwapsFooter
|
<SwapsFooter
|
||||||
onSubmit={async () => {
|
onSubmit={
|
||||||
// We need this to know how long it took to go from clicking on the Review swap button to rendered View Quote page.
|
/* istanbul ignore next */
|
||||||
dispatch(setReviewSwapClickedTimestamp(Date.now()));
|
async () => {
|
||||||
// In case that quotes prefetching is waiting to be executed, but hasn't started yet,
|
// We need this to know how long it took to go from clicking on the Review swap button to rendered View Quote page.
|
||||||
// we want to cancel it and fetch quotes from here.
|
dispatch(setReviewSwapClickedTimestamp(Date.now()));
|
||||||
if (timeoutIdForQuotesPrefetching) {
|
// In case that quotes prefetching is waiting to be executed, but hasn't started yet,
|
||||||
clearTimeout(timeoutIdForQuotesPrefetching);
|
// we want to cancel it and fetch quotes from here.
|
||||||
dispatch(
|
if (timeoutIdForQuotesPrefetching) {
|
||||||
fetchQuotesAndSetQuoteState(
|
clearTimeout(timeoutIdForQuotesPrefetching);
|
||||||
history,
|
dispatch(
|
||||||
fromTokenInputValue,
|
fetchQuotesAndSetQuoteState(
|
||||||
maxSlippage,
|
history,
|
||||||
trackEvent,
|
fromTokenInputValue,
|
||||||
),
|
maxSlippage,
|
||||||
);
|
trackEvent,
|
||||||
} else if (areQuotesPresent) {
|
),
|
||||||
// If there are prefetched quotes already, go directly to the View Quote page.
|
);
|
||||||
history.push(VIEW_QUOTE_ROUTE);
|
} else if (areQuotesPresent) {
|
||||||
} else {
|
// If there are prefetched quotes already, go directly to the View Quote page.
|
||||||
// If the "Review swap" button was clicked while quotes are being fetched, go to the Loading Quotes page.
|
history.push(VIEW_QUOTE_ROUTE);
|
||||||
await dispatch(setBackgroundSwapRouteState('loading'));
|
} else {
|
||||||
history.push(LOADING_QUOTES_ROUTE);
|
// If the "Review swap" button was clicked while quotes are being fetched, go to the Loading Quotes page.
|
||||||
|
await dispatch(setBackgroundSwapRouteState('loading'));
|
||||||
|
history.push(LOADING_QUOTES_ROUTE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
submitText={t('swapReviewSwap')}
|
submitText={t('swapReviewSwap')}
|
||||||
disabled={isReviewSwapButtonDisabled}
|
disabled={isReviewSwapButtonDisabled}
|
||||||
hideCancel
|
hideCancel
|
||||||
|
@ -6,7 +6,13 @@ import {
|
|||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
setBackgroundConnection,
|
setBackgroundConnection,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
|
import {
|
||||||
|
setSwapsFromToken,
|
||||||
|
setSwapToToken,
|
||||||
|
setFromTokenInputValue,
|
||||||
|
} from '../../../ducks/swaps/swaps';
|
||||||
import BuildQuote from '.';
|
import BuildQuote from '.';
|
||||||
|
|
||||||
const middleware = [thunk];
|
const middleware = [thunk];
|
||||||
@ -27,16 +33,46 @@ setBackgroundConnection({
|
|||||||
clearSwapsQuotes: jest.fn(),
|
clearSwapsQuotes: jest.fn(),
|
||||||
stopPollingForQuotes: jest.fn(),
|
stopPollingForQuotes: jest.fn(),
|
||||||
clearSmartTransactionFees: jest.fn(),
|
clearSmartTransactionFees: jest.fn(),
|
||||||
|
setSwapsFromToken: jest.fn(),
|
||||||
|
setSwapToToken: jest.fn(),
|
||||||
|
setFromTokenInputValue: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../../../ducks/swaps/swaps', () => {
|
||||||
|
const actual = jest.requireActual('../../../ducks/swaps/swaps');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
setSwapsFromToken: jest.fn(),
|
||||||
|
setSwapToToken: jest.fn(),
|
||||||
|
setFromTokenInputValue: jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../swaps.util', () => {
|
||||||
|
const actual = jest.requireActual('../swaps.util');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
fetchTokenBalance: jest.fn(() => Promise.resolve()),
|
||||||
|
fetchTokenPrice: jest.fn(() => Promise.resolve()),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BuildQuote', () => {
|
describe('BuildQuote', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const store = configureMockStore(middleware)(createSwapsMockStore());
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
const props = createProps();
|
const props = createProps();
|
||||||
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
||||||
expect(getByText('Swap from')).toBeInTheDocument();
|
expect(getByText('Swap from')).toBeInTheDocument();
|
||||||
expect(getByText('Swap to')).toBeInTheDocument();
|
expect(getByText('Swap to')).toBeInTheDocument();
|
||||||
expect(getByText('ETH')).toBeInTheDocument();
|
expect(getByText('Select')).toBeInTheDocument();
|
||||||
expect(getByText('Slippage tolerance')).toBeInTheDocument();
|
expect(getByText('Slippage tolerance')).toBeInTheDocument();
|
||||||
expect(getByText('2%')).toBeInTheDocument();
|
expect(getByText('2%')).toBeInTheDocument();
|
||||||
expect(getByText('3%')).toBeInTheDocument();
|
expect(getByText('3%')).toBeInTheDocument();
|
||||||
@ -45,4 +81,86 @@ describe('BuildQuote', () => {
|
|||||||
document.querySelector('.slippage-buttons__button-group'),
|
document.querySelector('.slippage-buttons__button-group'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('switches swap from and to tokens', () => {
|
||||||
|
const setSwapFromTokenMock = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSwapsFromToken.mockImplementation(setSwapFromTokenMock);
|
||||||
|
const setSwapToTokenMock = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSwapToToken.mockImplementation(setSwapToTokenMock);
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
|
<BuildQuote {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Swap from')).toBeInTheDocument();
|
||||||
|
fireEvent.click(getByTestId('build-quote__swap-arrows'));
|
||||||
|
expect(setSwapsFromToken).toHaveBeenCalledWith(mockStore.swaps.toToken);
|
||||||
|
expect(setSwapToToken).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the block explorer link, only 1 verified source', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
mockStore.swaps.toToken.occurances = 1;
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
||||||
|
expect(getByText('Swap from')).toBeInTheDocument();
|
||||||
|
expect(getByText('Only verified on 1 source.')).toBeInTheDocument();
|
||||||
|
expect(getByText('Etherscan')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the block explorer link, 0 verified sources', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
mockStore.swaps.toToken.occurances = 0;
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
||||||
|
expect(getByText('Swap from')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText('This token has been added manually.'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Etherscan')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on a block explorer link', () => {
|
||||||
|
global.platform = { openTab: jest.fn() };
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
mockStore.swaps.toToken.occurances = 1;
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
||||||
|
const blockExplorer = getByText('Etherscan');
|
||||||
|
expect(blockExplorer).toBeInTheDocument();
|
||||||
|
fireEvent.click(blockExplorer);
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: 'https://etherscan.io/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on the "max" link', () => {
|
||||||
|
const setFromTokenInputValueMock = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setFromTokenInputValue.mockImplementation(setFromTokenInputValueMock);
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
mockStore.swaps.fromToken = 'DAI';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<BuildQuote {...props} />, store);
|
||||||
|
const maxLink = getByText('Max');
|
||||||
|
fireEvent.click(maxLink);
|
||||||
|
expect(setFromTokenInputValue).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import configureMockStore from 'redux-mock-store';
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
|
fireEvent,
|
||||||
|
setBackgroundConnection,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
|
import {
|
||||||
|
setSwapsFromToken,
|
||||||
|
navigateBackToBuildQuote,
|
||||||
|
} from '../../../ducks/swaps/swaps';
|
||||||
import CreateNewSwap from '.';
|
import CreateNewSwap from '.';
|
||||||
|
|
||||||
|
const middleware = [thunk];
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
return {
|
return {
|
||||||
sensitiveProperties: {},
|
sensitiveProperties: {},
|
||||||
@ -14,7 +22,28 @@ const createProps = (customProps = {}) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const backgroundConnection = {
|
||||||
|
navigateBackToBuildQuote: jest.fn(),
|
||||||
|
setBackgroundSwapRouteState: jest.fn(),
|
||||||
|
navigatedBackToBuildQuote: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
setBackgroundConnection(backgroundConnection);
|
||||||
|
|
||||||
|
jest.mock('../../../ducks/swaps/swaps', () => {
|
||||||
|
const actual = jest.requireActual('../../../ducks/swaps/swaps');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
setSwapsFromToken: jest.fn(),
|
||||||
|
navigateBackToBuildQuote: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe('CreateNewSwap', () => {
|
describe('CreateNewSwap', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const store = configureMockStore()(createSwapsMockStore());
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
const props = createProps();
|
const props = createProps();
|
||||||
@ -24,4 +53,28 @@ describe('CreateNewSwap', () => {
|
|||||||
);
|
);
|
||||||
expect(getByText('Create a new swap')).toBeInTheDocument();
|
expect(getByText('Create a new swap')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clicks on the Make another swap link', async () => {
|
||||||
|
const setSwapFromTokenMock = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSwapsFromToken.mockImplementation(setSwapFromTokenMock);
|
||||||
|
const navigateBackToBuildQuoteMock = jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_ACTION',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
navigateBackToBuildQuote.mockImplementation(navigateBackToBuildQuoteMock);
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<CreateNewSwap {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
await fireEvent.click(getByText('Create a new swap'));
|
||||||
|
expect(setSwapFromTokenMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(navigateBackToBuildQuoteMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,11 +4,13 @@ import configureMockStore from 'redux-mock-store';
|
|||||||
import {
|
import {
|
||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
import DropdownInputPair from '.';
|
import DropdownInputPair from '.';
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
return {
|
return {
|
||||||
|
onInputChange: jest.fn(),
|
||||||
...customProps,
|
...customProps,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -26,4 +28,17 @@ describe('DropdownInputPair', () => {
|
|||||||
document.querySelector('.dropdown-input-pair__input'),
|
document.querySelector('.dropdown-input-pair__input'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('changes the input field', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByPlaceholderText } = renderWithProvider(
|
||||||
|
<DropdownInputPair {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
fireEvent.change(getByPlaceholderText('0'), {
|
||||||
|
target: { value: 1.1 },
|
||||||
|
});
|
||||||
|
expect(props.onInputChange).toHaveBeenCalledWith('1.1');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,7 @@ exports[`DropdownSearchList renders the component with initial props 1`] = `
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="dropdown-search-list"
|
class="dropdown-search-list"
|
||||||
|
data-testid="dropdown-search-list"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -91,6 +91,7 @@ export default function DropdownSearchList({
|
|||||||
setIsImportTokenModalOpen(true);
|
setIsImportTokenModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
const onImportTokenClick = () => {
|
const onImportTokenClick = () => {
|
||||||
trackEvent({
|
trackEvent({
|
||||||
event: 'Token Imported',
|
event: 'Token Imported',
|
||||||
@ -167,6 +168,7 @@ export default function DropdownSearchList({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('dropdown-search-list', className)}
|
className={classnames('dropdown-search-list', className)}
|
||||||
|
data-testid="dropdown-search-list"
|
||||||
onClick={onClickSelector}
|
onClick={onClickSelector}
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
@ -216,6 +218,7 @@ export default function DropdownSearchList({
|
|||||||
<SearchableItemList
|
<SearchableItemList
|
||||||
itemsToSearch={loading ? [] : itemsToSearch}
|
itemsToSearch={loading ? [] : itemsToSearch}
|
||||||
Placeholder={() =>
|
Placeholder={() =>
|
||||||
|
/* istanbul ignore next */
|
||||||
loading ? (
|
loading ? (
|
||||||
<div className="dropdown-search-list__loading-item">
|
<div className="dropdown-search-list__loading-item">
|
||||||
<PulseLoader />
|
<PulseLoader />
|
||||||
@ -286,6 +289,7 @@ export default function DropdownSearchList({
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="dropdown-search-list__close-area"
|
className="dropdown-search-list__close-area"
|
||||||
|
data-testid="dropdown-search-list__close-area"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
@ -4,6 +4,7 @@ import configureMockStore from 'redux-mock-store';
|
|||||||
import {
|
import {
|
||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
import DropdownSearchList from '.';
|
import DropdownSearchList from '.';
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ const createProps = (customProps = {}) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.mock('../searchable-item-list', () => jest.fn(() => null));
|
||||||
|
|
||||||
describe('DropdownSearchList', () => {
|
describe('DropdownSearchList', () => {
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const store = configureMockStore()(createSwapsMockStore());
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
@ -28,4 +31,20 @@ describe('DropdownSearchList', () => {
|
|||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
expect(getByText('symbol')).toBeInTheDocument();
|
expect(getByText('symbol')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the component, opens the list and closes it', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<DropdownSearchList {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
const dropdownSearchList = getByTestId('dropdown-search-list');
|
||||||
|
expect(dropdownSearchList).toBeInTheDocument();
|
||||||
|
fireEvent.click(dropdownSearchList);
|
||||||
|
const closeButton = getByTestId('dropdown-search-list__close-area');
|
||||||
|
expect(closeButton).toBeInTheDocument();
|
||||||
|
fireEvent.click(closeButton);
|
||||||
|
expect(closeButton).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ exports[`ExchangeRateDisplay renders the component with initial props 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="exchange-rate-display__bold"
|
class="exchange-rate-display__bold"
|
||||||
|
data-testid="exchange-rate-display__base-symbol"
|
||||||
>
|
>
|
||||||
ETH
|
ETH
|
||||||
</span>
|
</span>
|
||||||
@ -26,6 +27,7 @@ exports[`ExchangeRateDisplay renders the component with initial props 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="exchange-rate-display__switch-arrows"
|
class="exchange-rate-display__switch-arrows"
|
||||||
|
data-testid="exchange-rate-display__switch-arrows"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
|
@ -66,6 +66,7 @@ export default function ExchangeRateDisplay({
|
|||||||
<span>1</span>
|
<span>1</span>
|
||||||
<span
|
<span
|
||||||
className={classnames({ 'exchange-rate-display__bold': boldSymbols })}
|
className={classnames({ 'exchange-rate-display__bold': boldSymbols })}
|
||||||
|
data-testid="exchange-rate-display__base-symbol"
|
||||||
>
|
>
|
||||||
{baseSymbol}
|
{baseSymbol}
|
||||||
</span>
|
</span>
|
||||||
@ -80,6 +81,7 @@ export default function ExchangeRateDisplay({
|
|||||||
className={classnames('exchange-rate-display__switch-arrows', {
|
className={classnames('exchange-rate-display__switch-arrows', {
|
||||||
'exchange-rate-display__switch-arrows-rotate': rotating,
|
'exchange-rate-display__switch-arrows-rotate': rotating,
|
||||||
})}
|
})}
|
||||||
|
data-testid="exchange-rate-display__switch-arrows"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowPrimaryToSecondary(!showPrimaryToSecondary);
|
setShowPrimaryToSecondary(!showPrimaryToSecondary);
|
||||||
setRotating(true);
|
setRotating(true);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { renderWithProvider } from '../../../../test/jest';
|
import { renderWithProvider, fireEvent } from '../../../../test/jest';
|
||||||
import ExchangeRateDisplay from '.';
|
import ExchangeRateDisplay from '.';
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
@ -25,4 +25,18 @@ describe('ExchangeRateDisplay', () => {
|
|||||||
expect(getByText(props.secondaryTokenSymbol)).toBeInTheDocument();
|
expect(getByText(props.secondaryTokenSymbol)).toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clicks on the switch link', () => {
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<ExchangeRateDisplay {...props} />,
|
||||||
|
);
|
||||||
|
expect(getByTestId('exchange-rate-display__base-symbol')).toHaveTextContent(
|
||||||
|
'ETH',
|
||||||
|
);
|
||||||
|
fireEvent.click(getByTestId('exchange-rate-display__switch-arrows'));
|
||||||
|
expect(getByTestId('exchange-rate-display__base-symbol')).toHaveTextContent(
|
||||||
|
'BAT',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,3 +3,32 @@
|
|||||||
exports[`FeeCard renders the component with EIP-1559 enabled 1`] = `null`;
|
exports[`FeeCard renders the component with EIP-1559 enabled 1`] = `null`;
|
||||||
|
|
||||||
exports[`FeeCard renders the component with initial props 1`] = `null`;
|
exports[`FeeCard renders the component with initial props 1`] = `null`;
|
||||||
|
|
||||||
|
exports[`FeeCard renders the component with initial props 2`] = `
|
||||||
|
<div
|
||||||
|
class="info-tooltip"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="fee-card__row-label fee-card__info-tooltip-container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-describedby="tippy-tooltip-1"
|
||||||
|
class="info-tooltip__tooltip-container fee-card__info-tooltip-content-container"
|
||||||
|
data-original-title="null"
|
||||||
|
data-tooltipped=""
|
||||||
|
style="display: inline;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
|
||||||
|
fill="var(--color-icon-alternative)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
@ -31,6 +31,7 @@ export default function FeeCard({
|
|||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
const getTranslatedNetworkName = () => {
|
const getTranslatedNetworkName = () => {
|
||||||
switch (chainId) {
|
switch (chainId) {
|
||||||
case CHAIN_IDS.MAINNET:
|
case CHAIN_IDS.MAINNET:
|
||||||
@ -84,6 +85,7 @@ export default function FeeCard({
|
|||||||
<a
|
<a
|
||||||
className="fee-card__link"
|
className="fee-card__link"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
/* istanbul ignore next */
|
||||||
trackEvent({
|
trackEvent({
|
||||||
event: 'Clicked "Gas Fees: Learn More" Link',
|
event: 'Clicked "Gas Fees: Learn More" Link',
|
||||||
category: EVENT.CATEGORIES.SWAPS,
|
category: EVENT.CATEGORIES.SWAPS,
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
setBackgroundConnection,
|
setBackgroundConnection,
|
||||||
MOCKS,
|
MOCKS,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
import { CHAIN_IDS } from '../../../../shared/constants/network';
|
import { CHAIN_IDS } from '../../../../shared/constants/network';
|
||||||
|
|
||||||
@ -104,6 +105,8 @@ describe('FeeCard', () => {
|
|||||||
expect(
|
expect(
|
||||||
document.querySelector('.fee-card__top-bordered-row'),
|
document.querySelector('.fee-card__top-bordered-row'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
|
expect(document.querySelector('.info-tooltip')).toMatchSnapshot();
|
||||||
|
expect(getByText('Edit limit')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the component with EIP-1559 enabled', () => {
|
it('renders the component with EIP-1559 enabled', () => {
|
||||||
@ -145,4 +148,23 @@ describe('FeeCard', () => {
|
|||||||
expect(getByText(`: ${props.secondaryFee.maxFee}`)).toBeInTheDocument();
|
expect(getByText(`: ${props.secondaryFee.maxFee}`)).toBeInTheDocument();
|
||||||
expect(queryByTestId('fee-card__edit-link')).not.toBeInTheDocument();
|
expect(queryByTestId('fee-card__edit-link')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the component with hidden token approval row', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
hideTokenApprovalRow: true,
|
||||||
|
});
|
||||||
|
const { queryByText } = renderWithProvider(<FeeCard {...props} />, store);
|
||||||
|
expect(queryByText('Edit limit')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('approves a token', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({
|
||||||
|
onTokenApprovalClick: jest.fn(),
|
||||||
|
});
|
||||||
|
const { queryByText } = renderWithProvider(<FeeCard {...props} />, store);
|
||||||
|
fireEvent.click(queryByText('Edit limit'));
|
||||||
|
expect(props.onTokenApprovalClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -77,6 +77,7 @@ exports[`MainQuoteSummary renders the component with initial props 4`] = `
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class=""
|
class=""
|
||||||
|
data-testid="exchange-rate-display__base-symbol"
|
||||||
>
|
>
|
||||||
ETH
|
ETH
|
||||||
</span>
|
</span>
|
||||||
@ -93,6 +94,7 @@ exports[`MainQuoteSummary renders the component with initial props 4`] = `
|
|||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="exchange-rate-display__switch-arrows"
|
class="exchange-rate-display__switch-arrows"
|
||||||
|
data-testid="exchange-rate-display__switch-arrows"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
|
@ -39,6 +39,7 @@ exports[`SearchableItemList renders the component with initial props 1`] = `
|
|||||||
exports[`SearchableItemList renders the component with initial props 2`] = `
|
exports[`SearchableItemList renders the component with initial props 2`] = `
|
||||||
<div
|
<div
|
||||||
class="searchable-item-list__item searchable-item-list__item--selected"
|
class="searchable-item-list__item searchable-item-list__item--selected"
|
||||||
|
data-testid="searchable-item-list__item"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ItemList renders the component with initial props 1`] = `
|
||||||
|
<div
|
||||||
|
class="searchable-item-list__item searchable-item-list__item--selected"
|
||||||
|
data-testid="searchable-item-list__item"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="primaryLabel"
|
||||||
|
class="url-icon"
|
||||||
|
src="iconUrl"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="searchable-item-list__labels"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="searchable-item-list__item-labels"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="searchable-item-list__primary-label"
|
||||||
|
>
|
||||||
|
primaryLabel
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="searchable-item-list__secondary-label"
|
||||||
|
>
|
||||||
|
secondaryLabel
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="searchable-item-list__right-labels"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="searchable-item-list__right-primary-label"
|
||||||
|
>
|
||||||
|
rightPrimaryLabel
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="searchable-item-list__right-secondary-label"
|
||||||
|
>
|
||||||
|
rightSecondaryLabel
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -89,6 +89,7 @@ export default function ItemList({
|
|||||||
'searchable-item-list__item--selected': selected,
|
'searchable-item-list__item--selected': selected,
|
||||||
'searchable-item-list__item--disabled': disabled,
|
'searchable-item-list__item--disabled': disabled,
|
||||||
})}
|
})}
|
||||||
|
data-testid="searchable-item-list__item"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onKeyUp={(e) => e.key === 'Enter' && onClick()}
|
onKeyUp={(e) => e.key === 'Enter' && onClick()}
|
||||||
key={`searchable-item-list-item-${i}`}
|
key={`searchable-item-list-item-${i}`}
|
||||||
@ -150,6 +151,7 @@ export default function ItemList({
|
|||||||
<a
|
<a
|
||||||
key="searchable-item-list__etherscan-link"
|
key="searchable-item-list__etherscan-link"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
/* istanbul ignore next */
|
||||||
trackEvent({
|
trackEvent({
|
||||||
event: 'Clicked Block Explorer Link',
|
event: 'Clicked Block Explorer Link',
|
||||||
category: EVENT.CATEGORIES.SWAPS,
|
category: EVENT.CATEGORIES.SWAPS,
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
renderWithProvider,
|
||||||
|
createSwapsMockStore,
|
||||||
|
fireEvent,
|
||||||
|
} from '../../../../../test/jest';
|
||||||
|
import ItemList from '.';
|
||||||
|
|
||||||
|
const createProps = (customProps = {}) => {
|
||||||
|
return {
|
||||||
|
defaultToAll: true,
|
||||||
|
listTitle: 'listTitle',
|
||||||
|
onClickItem: jest.fn(),
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
iconUrl: 'iconUrl',
|
||||||
|
selected: true,
|
||||||
|
primaryLabel: 'primaryLabel',
|
||||||
|
secondaryLabel: 'secondaryLabel',
|
||||||
|
rightPrimaryLabel: 'rightPrimaryLabel',
|
||||||
|
rightSecondaryLabel: 'rightSecondaryLabel',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fuseSearchKeys: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
weight: 0.499,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'symbol',
|
||||||
|
weight: 0.499,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
weight: 0.002,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
...customProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ItemList', () => {
|
||||||
|
it('renders the component with initial props', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<ItemList {...props} />, store);
|
||||||
|
expect(getByText(props.listTitle)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
document.querySelector('.searchable-item-list__item'),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on a list item', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
|
<ItemList {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText(props.listTitle)).toBeInTheDocument();
|
||||||
|
fireEvent.click(getByTestId('searchable-item-list__item'));
|
||||||
|
expect(props.onClickItem).toHaveBeenCalledWith(props.results[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('presses the "Enter" key on a list item', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
|
<ItemList {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText(props.listTitle)).toBeInTheDocument();
|
||||||
|
fireEvent.keyUp(getByTestId('searchable-item-list__item'), {
|
||||||
|
key: 'Enter',
|
||||||
|
code: 'Enter',
|
||||||
|
charCode: 13,
|
||||||
|
});
|
||||||
|
expect(props.onClickItem).toHaveBeenCalledWith(props.results[0]);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,107 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
|
||||||
|
import {
|
||||||
|
renderWithProvider,
|
||||||
|
createSwapsMockStore,
|
||||||
|
fireEvent,
|
||||||
|
} from '../../../../../test/jest';
|
||||||
|
import ListItemSearch from './list-item-search.component';
|
||||||
|
|
||||||
|
const token = {
|
||||||
|
erc20: true,
|
||||||
|
symbol: 'BAT',
|
||||||
|
decimals: 18,
|
||||||
|
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('../../swaps.util', () => {
|
||||||
|
const original = jest.requireActual('../../swaps.util');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
fetchToken: jest.fn(() => {
|
||||||
|
return token;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const createProps = (customProps = {}) => {
|
||||||
|
return {
|
||||||
|
onSearch: jest.fn(),
|
||||||
|
setSearchQuery: jest.fn(),
|
||||||
|
listToSearch: [
|
||||||
|
{
|
||||||
|
iconUrl: 'iconUrl',
|
||||||
|
selected: true,
|
||||||
|
primaryLabel: 'primaryLabel',
|
||||||
|
secondaryLabel: 'secondaryLabel',
|
||||||
|
rightPrimaryLabel: 'rightPrimaryLabel',
|
||||||
|
rightSecondaryLabel: 'rightSecondaryLabel',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fuseSearchKeys: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
weight: 0.499,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'symbol',
|
||||||
|
weight: 0.499,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
weight: 0.002,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
searchPlaceholderText: 'Search token',
|
||||||
|
defaultToAll: true,
|
||||||
|
...customProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const middleware = [thunk];
|
||||||
|
|
||||||
|
describe('ListItemSearch', () => {
|
||||||
|
it('renders the component with initial props', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<ListItemSearch {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByTestId('search-list-items')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the search query', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<ListItemSearch {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
const input = getByTestId('search-list-items');
|
||||||
|
fireEvent.change(input, { target: { value: 'USD' } });
|
||||||
|
expect(props.setSearchQuery).toHaveBeenCalledWith('USD');
|
||||||
|
expect(props.onSearch).toHaveBeenCalledWith({
|
||||||
|
searchQuery: 'USD',
|
||||||
|
results: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('imports a token', async () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const props = createProps({ shouldSearchForImports: true });
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<ListItemSearch {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
const input = getByTestId('search-list-items');
|
||||||
|
await fireEvent.change(input, { target: { value: token.address } });
|
||||||
|
expect(props.setSearchQuery).toHaveBeenCalledWith(token.address);
|
||||||
|
expect(props.onSearch).toHaveBeenCalledWith({
|
||||||
|
searchQuery: token.address,
|
||||||
|
results: [token],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import configureMockStore from 'redux-mock-store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
renderWithProvider,
|
||||||
|
createSwapsMockStore,
|
||||||
|
} from '../../../../../test/jest';
|
||||||
|
import quoteDataRows from '../mock-quote-data';
|
||||||
|
import QuoteDetails from './quote-details';
|
||||||
|
|
||||||
|
const createProps = (customProps = {}) => {
|
||||||
|
return {
|
||||||
|
...quoteDataRows[0],
|
||||||
|
slippage: 2,
|
||||||
|
liquiditySourceKey: 'swapAggregator',
|
||||||
|
minimumAmountReceived: '2',
|
||||||
|
feeInEth: '0.0003',
|
||||||
|
metaMaskFee: 0.0205,
|
||||||
|
...customProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('ListItemSearch', () => {
|
||||||
|
it('renders the component with initial props', () => {
|
||||||
|
const store = configureMockStore()(createSwapsMockStore());
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<QuoteDetails {...props} />,
|
||||||
|
store,
|
||||||
|
);
|
||||||
|
expect(getByText('Rate')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -3,6 +3,7 @@
|
|||||||
exports[`SortList renders the component with initial props 1`] = `
|
exports[`SortList renders the component with initial props 1`] = `
|
||||||
<div
|
<div
|
||||||
class="select-quote-popover__column-header select-quote-popover__receiving"
|
class="select-quote-popover__column-header select-quote-popover__receiving"
|
||||||
|
data-testid="select-quote-popover__receiving"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="select-quote-popover__receiving-symbol"
|
class="select-quote-popover__receiving-symbol"
|
||||||
@ -50,6 +51,7 @@ exports[`SortList renders the component with initial props 1`] = `
|
|||||||
exports[`SortList renders the component with initial props 2`] = `
|
exports[`SortList renders the component with initial props 2`] = `
|
||||||
<div
|
<div
|
||||||
class="select-quote-popover__column-header select-quote-popover__network-fees select-quote-popover__network-fees-header"
|
class="select-quote-popover__column-header select-quote-popover__network-fees select-quote-popover__network-fees-header"
|
||||||
|
data-testid="select-quote-popover__network-fees-header"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
Estimated network fees
|
Estimated network fees
|
||||||
@ -88,6 +90,7 @@ exports[`SortList renders the component with initial props 2`] = `
|
|||||||
exports[`SortList renders the component with initial props 3`] = `
|
exports[`SortList renders the component with initial props 3`] = `
|
||||||
<div
|
<div
|
||||||
class="select-quote-popover__row select-quote-popover__row--selected"
|
class="select-quote-popover__row select-quote-popover__row--selected"
|
||||||
|
data-testid="select-quote-popover-row-1"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="select-quote-popover__receiving"
|
class="select-quote-popover__receiving"
|
||||||
@ -124,6 +127,7 @@ exports[`SortList renders the component with initial props 3`] = `
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="select-quote-popover__caret-right"
|
class="select-quote-popover__caret-right"
|
||||||
|
data-testid="select-quote-popover__caret-right-1"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="fa fa-angle-up"
|
class="fa fa-angle-up"
|
||||||
|
@ -80,6 +80,7 @@ export default function SortList({
|
|||||||
<div className="select-quote-popover__column-headers">
|
<div className="select-quote-popover__column-headers">
|
||||||
<div
|
<div
|
||||||
className="select-quote-popover__column-header select-quote-popover__receiving"
|
className="select-quote-popover__column-header select-quote-popover__receiving"
|
||||||
|
data-testid="select-quote-popover__receiving"
|
||||||
onClick={() => onColumnHeaderClick('destinationTokenValue')}
|
onClick={() => onColumnHeaderClick('destinationTokenValue')}
|
||||||
>
|
>
|
||||||
<span className="select-quote-popover__receiving-symbol">
|
<span className="select-quote-popover__receiving-symbol">
|
||||||
@ -96,6 +97,7 @@ export default function SortList({
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="select-quote-popover__column-header select-quote-popover__network-fees select-quote-popover__network-fees-header"
|
className="select-quote-popover__column-header select-quote-popover__network-fees select-quote-popover__network-fees-header"
|
||||||
|
data-testid="select-quote-popover__network-fees-header"
|
||||||
onClick={() => onColumnHeaderClick('rawNetworkFees')}
|
onClick={() => onColumnHeaderClick('rawNetworkFees')}
|
||||||
>
|
>
|
||||||
{!hideEstimatedGasFee && (
|
{!hideEstimatedGasFee && (
|
||||||
@ -111,6 +113,7 @@ export default function SortList({
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="select-quote-popover__column-header select-quote-popover__quote-source"
|
className="select-quote-popover__column-header select-quote-popover__quote-source"
|
||||||
|
data-testid="select-quote-popover__quote-source"
|
||||||
onClick={() => onColumnHeaderClick('quoteSource')}
|
onClick={() => onColumnHeaderClick('quoteSource')}
|
||||||
>
|
>
|
||||||
{t('swapQuoteSource')}
|
{t('swapQuoteSource')}
|
||||||
@ -137,6 +140,7 @@ export default function SortList({
|
|||||||
})}
|
})}
|
||||||
onClick={() => onSelect(aggId)}
|
onClick={() => onSelect(aggId)}
|
||||||
key={`select-quote-popover-row-${i}`}
|
key={`select-quote-popover-row-${i}`}
|
||||||
|
data-testid={`select-quote-popover-row-${i}`}
|
||||||
>
|
>
|
||||||
<div className="select-quote-popover__receiving">
|
<div className="select-quote-popover__receiving">
|
||||||
<div className="select-quote-popover__receiving-value">
|
<div className="select-quote-popover__receiving-value">
|
||||||
@ -178,6 +182,7 @@ export default function SortList({
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="select-quote-popover__caret-right"
|
className="select-quote-popover__caret-right"
|
||||||
|
data-testid={`select-quote-popover__caret-right-${i}`}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
onCaretClick(aggId);
|
onCaretClick(aggId);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { renderWithProvider } from '../../../../../test/jest';
|
import { renderWithProvider, fireEvent } from '../../../../../test/jest';
|
||||||
import SortList from './sort-list';
|
import SortList from './sort-list';
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
@ -86,4 +86,32 @@ describe('SortList', () => {
|
|||||||
document.querySelector('.select-quote-popover__row--selected'),
|
document.querySelector('.select-quote-popover__row--selected'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clicks on the "destinationTokenValue" header', () => {
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(<SortList {...props} />);
|
||||||
|
fireEvent.click(getByTestId('select-quote-popover__receiving'));
|
||||||
|
expect(props.setSortColumn).toHaveBeenCalledWith('destinationTokenValue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on the "rawNetworkFees" header', () => {
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(<SortList {...props} />);
|
||||||
|
fireEvent.click(getByTestId('select-quote-popover__network-fees-header'));
|
||||||
|
expect(props.setSortColumn).toHaveBeenCalledWith('rawNetworkFees');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on the first aggregator', () => {
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(<SortList {...props} />);
|
||||||
|
fireEvent.click(getByTestId('select-quote-popover-row-0'));
|
||||||
|
expect(props.onSelect).toHaveBeenCalledWith('Agg1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on a caret for the first aggregator', () => {
|
||||||
|
const props = createProps();
|
||||||
|
const { getByTestId } = renderWithProvider(<SortList {...props} />);
|
||||||
|
fireEvent.click(getByTestId('select-quote-popover__caret-right-0'));
|
||||||
|
expect(props.onCaretClick).toHaveBeenCalledWith('Agg1');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,7 @@ exports[`SlippageButtons renders the component with initial props 2`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`SlippageButtons renders the component with the smart transaction opt-in button available 1`] = `
|
exports[`SlippageButtons renders the component with the smart transaction opt-in button available, opt into STX 1`] = `
|
||||||
<button
|
<button
|
||||||
class="slippage-buttons__header slippage-buttons__header--open"
|
class="slippage-buttons__header slippage-buttons__header--open"
|
||||||
>
|
>
|
||||||
@ -62,7 +62,7 @@ exports[`SlippageButtons renders the component with the smart transaction opt-in
|
|||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`SlippageButtons renders the component with the smart transaction opt-in button available 2`] = `
|
exports[`SlippageButtons renders the component with the smart transaction opt-in button available, opt into STX 2`] = `
|
||||||
<div
|
<div
|
||||||
class="button-group slippage-buttons__button-group radio-button-group"
|
class="button-group slippage-buttons__button-group radio-button-group"
|
||||||
role="radiogroup"
|
role="radiogroup"
|
||||||
|
@ -170,6 +170,7 @@ export default function SlippageButtons({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
data-testid="slippage-buttons__custom-slippage"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
const { value } = event.target;
|
const { value } = event.target;
|
||||||
const isValueNumeric = !isNaN(Number(value));
|
const isValueNumeric = !isNaN(Number(value));
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { renderWithProvider } from '../../../../test/jest';
|
import { renderWithProvider, fireEvent } from '../../../../test/jest';
|
||||||
|
import { SLIPPAGE } from '../../../../shared/constants/swaps';
|
||||||
import SlippageButtons from '.';
|
import SlippageButtons from '.';
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
return {
|
return {
|
||||||
onSelect: jest.fn(),
|
onSelect: jest.fn(),
|
||||||
maxAllowedSlippage: 15,
|
maxAllowedSlippage: 15,
|
||||||
currentSlippage: 3,
|
currentSlippage: SLIPPAGE.HIGH,
|
||||||
smartTransactionsEnabled: false,
|
smartTransactionsEnabled: false,
|
||||||
...customProps,
|
...customProps,
|
||||||
};
|
};
|
||||||
@ -15,7 +16,7 @@ const createProps = (customProps = {}) => {
|
|||||||
|
|
||||||
describe('SlippageButtons', () => {
|
describe('SlippageButtons', () => {
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const { getByText, queryByText } = renderWithProvider(
|
const { getByText, queryByText, getByTestId } = renderWithProvider(
|
||||||
<SlippageButtons {...createProps()} />,
|
<SlippageButtons {...createProps()} />,
|
||||||
);
|
);
|
||||||
expect(getByText('2%')).toBeInTheDocument();
|
expect(getByText('2%')).toBeInTheDocument();
|
||||||
@ -29,11 +30,21 @@ describe('SlippageButtons', () => {
|
|||||||
document.querySelector('.slippage-buttons__button-group'),
|
document.querySelector('.slippage-buttons__button-group'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(queryByText('Smart transaction')).not.toBeInTheDocument();
|
expect(queryByText('Smart transaction')).not.toBeInTheDocument();
|
||||||
|
expect(getByTestId('button-group__button1')).toHaveAttribute(
|
||||||
|
'aria-checked',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the component with the smart transaction opt-in button available', () => {
|
it('renders the component with the smart transaction opt-in button available, opt into STX', () => {
|
||||||
|
const setSmartTransactionsOptInStatus = jest.fn();
|
||||||
const { getByText } = renderWithProvider(
|
const { getByText } = renderWithProvider(
|
||||||
<SlippageButtons {...createProps({ smartTransactionsEnabled: true })} />,
|
<SlippageButtons
|
||||||
|
{...createProps({
|
||||||
|
smartTransactionsEnabled: true,
|
||||||
|
setSmartTransactionsOptInStatus,
|
||||||
|
})}
|
||||||
|
/>,
|
||||||
);
|
);
|
||||||
expect(getByText('2%')).toBeInTheDocument();
|
expect(getByText('2%')).toBeInTheDocument();
|
||||||
expect(getByText('3%')).toBeInTheDocument();
|
expect(getByText('3%')).toBeInTheDocument();
|
||||||
@ -46,5 +57,69 @@ describe('SlippageButtons', () => {
|
|||||||
document.querySelector('.slippage-buttons__button-group'),
|
document.querySelector('.slippage-buttons__button-group'),
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(getByText('Smart transaction')).toBeInTheDocument();
|
expect(getByText('Smart transaction')).toBeInTheDocument();
|
||||||
|
expect(document.querySelector('.toggle-button--off')).toBeInTheDocument();
|
||||||
|
fireEvent.click(document.querySelector('.toggle-button'));
|
||||||
|
expect(setSmartTransactionsOptInStatus).toHaveBeenCalledWith(true, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders slippage with a custom value', () => {
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<SlippageButtons {...createProps({ currentSlippage: 2.5 })} />,
|
||||||
|
);
|
||||||
|
expect(getByText('2.5')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the default slippage with Advanced options hidden', () => {
|
||||||
|
const { getByText, queryByText } = renderWithProvider(
|
||||||
|
<SlippageButtons
|
||||||
|
{...createProps({ currentSlippage: SLIPPAGE.DEFAULT })}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
expect(getByText('Advanced options')).toBeInTheDocument();
|
||||||
|
expect(document.querySelector('.fa-angle-down')).toBeInTheDocument();
|
||||||
|
expect(queryByText('2%')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens the Advanced options section and sets a default slippage', () => {
|
||||||
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
|
<SlippageButtons
|
||||||
|
{...createProps({ currentSlippage: SLIPPAGE.DEFAULT })}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
fireEvent.click(getByText('Advanced options'));
|
||||||
|
fireEvent.click(getByTestId('button-group__button0'));
|
||||||
|
expect(getByTestId('button-group__button0')).toHaveAttribute(
|
||||||
|
'aria-checked',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens the Advanced options section and sets a high slippage', () => {
|
||||||
|
const { getByText, getByTestId } = renderWithProvider(
|
||||||
|
<SlippageButtons
|
||||||
|
{...createProps({ currentSlippage: SLIPPAGE.DEFAULT })}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
fireEvent.click(getByText('Advanced options'));
|
||||||
|
fireEvent.click(getByTestId('button-group__button1'));
|
||||||
|
expect(getByTestId('button-group__button1')).toHaveAttribute(
|
||||||
|
'aria-checked',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets a custom slippage value', () => {
|
||||||
|
const { getByTestId } = renderWithProvider(
|
||||||
|
<SlippageButtons {...createProps()} />,
|
||||||
|
);
|
||||||
|
fireEvent.click(getByTestId('button-group__button2'));
|
||||||
|
expect(getByTestId('button-group__button2')).toHaveAttribute(
|
||||||
|
'aria-checked',
|
||||||
|
'true',
|
||||||
|
);
|
||||||
|
const input = getByTestId('slippage-buttons__custom-slippage');
|
||||||
|
fireEvent.change(input, { target: { value: 5 } });
|
||||||
|
fireEvent.click(document);
|
||||||
|
expect(input).toHaveAttribute('value', '5');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,9 @@ import {
|
|||||||
renderWithProvider,
|
renderWithProvider,
|
||||||
createSwapsMockStore,
|
createSwapsMockStore,
|
||||||
setBackgroundConnection,
|
setBackgroundConnection,
|
||||||
|
fireEvent,
|
||||||
} from '../../../../test/jest';
|
} from '../../../../test/jest';
|
||||||
|
import { CHAIN_IDS } from '../../../../shared/constants/network';
|
||||||
import SmartTransactionStatus from '.';
|
import SmartTransactionStatus from '.';
|
||||||
|
|
||||||
const middleware = [thunk];
|
const middleware = [thunk];
|
||||||
@ -15,6 +17,28 @@ setBackgroundConnection({
|
|||||||
setBackgroundSwapRouteState: jest.fn(),
|
setBackgroundSwapRouteState: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => {
|
||||||
|
const original = jest.requireActual('react-router-dom');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
useHistory: () => ({
|
||||||
|
push: jest.fn(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../../../ducks/swaps/swaps', () => {
|
||||||
|
const original = jest.requireActual('../../../ducks/swaps/swaps');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
prepareToLeaveSwaps: jest.fn(() => {
|
||||||
|
return {
|
||||||
|
type: 'MOCK_TYPE',
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe('SmartTransactionStatus', () => {
|
describe('SmartTransactionStatus', () => {
|
||||||
it('renders the component with initial props', () => {
|
it('renders the component with initial props', () => {
|
||||||
const store = configureMockStore(middleware)(createSwapsMockStore());
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
@ -22,4 +46,109 @@ describe('SmartTransactionStatus', () => {
|
|||||||
expect(getByText('Publicly submitting your Swap...')).toBeInTheDocument();
|
expect(getByText('Publicly submitting your Swap...')).toBeInTheDocument();
|
||||||
expect(getByText('Close')).toBeInTheDocument();
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the "success" STX status', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const latestSmartTransaction =
|
||||||
|
mockStore.metamask.smartTransactionsState.smartTransactions[
|
||||||
|
CHAIN_IDS.MAINNET
|
||||||
|
][1];
|
||||||
|
latestSmartTransaction.status = 'success';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Swap complete!')).toBeInTheDocument();
|
||||||
|
expect(getByText('Your USDC is now available.')).toBeInTheDocument();
|
||||||
|
expect(getByText('Create a new swap')).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the "reverted" STX status', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const latestSmartTransaction =
|
||||||
|
mockStore.metamask.smartTransactionsState.smartTransactions[
|
||||||
|
CHAIN_IDS.MAINNET
|
||||||
|
][1];
|
||||||
|
latestSmartTransaction.status = 'reverted';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Swap failed')).toBeInTheDocument();
|
||||||
|
expect(getByText('customer support')).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the "cancelled_user_cancelled" STX status', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const latestSmartTransaction =
|
||||||
|
mockStore.metamask.smartTransactionsState.smartTransactions[
|
||||||
|
CHAIN_IDS.MAINNET
|
||||||
|
][1];
|
||||||
|
latestSmartTransaction.status = 'cancelled_user_cancelled';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Swap cancelled')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'Your transaction has been cancelled and you did not pay any unnecessary gas fees.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the "deadline_missed" STX status', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const latestSmartTransaction =
|
||||||
|
mockStore.metamask.smartTransactionsState.smartTransactions[
|
||||||
|
CHAIN_IDS.MAINNET
|
||||||
|
][1];
|
||||||
|
latestSmartTransaction.status = 'deadline_missed';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Swap would have failed')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'Your transaction would have failed and was cancelled to protect you from paying unnecessary gas fees.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the "unknown" STX status', () => {
|
||||||
|
const mockStore = createSwapsMockStore();
|
||||||
|
const latestSmartTransaction =
|
||||||
|
mockStore.metamask.smartTransactionsState.smartTransactions[
|
||||||
|
CHAIN_IDS.MAINNET
|
||||||
|
][1];
|
||||||
|
latestSmartTransaction.status = 'unknown';
|
||||||
|
const store = configureMockStore(middleware)(mockStore);
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Status unknown')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'A transaction has been successful but we’re unsure what it is. This may be due to submitting another transaction while this swap was processing.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Close')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancels a transaction', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Publicly submitting your Swap...')).toBeInTheDocument();
|
||||||
|
const cancelLink = getByText('Cancel swap for ~0');
|
||||||
|
expect(cancelLink).toBeInTheDocument();
|
||||||
|
fireEvent.click(cancelLink);
|
||||||
|
expect(
|
||||||
|
getByText('Trying to cancel your transaction...'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(cancelLink).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicks on the Close button', () => {
|
||||||
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
|
const { getByText } = renderWithProvider(<SmartTransactionStatus />, store);
|
||||||
|
expect(getByText('Publicly submitting your Swap...')).toBeInTheDocument();
|
||||||
|
const closeButton = getByText('Close');
|
||||||
|
expect(closeButton).toBeInTheDocument();
|
||||||
|
fireEvent.click(closeButton);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { renderWithProvider } from '../../../../test/jest';
|
import { renderWithProvider, fireEvent } from '../../../../test/jest';
|
||||||
import SwapsFooter from '.';
|
import SwapsFooter from '.';
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
@ -25,4 +25,15 @@ describe('SwapsFooter', () => {
|
|||||||
expect(getByText('Terms of service')).toBeInTheDocument();
|
expect(getByText('Terms of service')).toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clicks on a block explorer link', () => {
|
||||||
|
global.platform = { openTab: jest.fn() };
|
||||||
|
const props = createProps();
|
||||||
|
const { getByText } = renderWithProvider(<SwapsFooter {...props} />);
|
||||||
|
expect(getByText(props.submitText)).toBeInTheDocument();
|
||||||
|
fireEvent.click(getByText('Terms of service'));
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: 'https://metamask.io/terms.html',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -34,6 +34,8 @@ import {
|
|||||||
getSwapsLivenessForNetwork,
|
getSwapsLivenessForNetwork,
|
||||||
countDecimals,
|
countDecimals,
|
||||||
showRemainingTimeInMinAndSec,
|
showRemainingTimeInMinAndSec,
|
||||||
|
getFeeForSmartTransaction,
|
||||||
|
formatSwapsValueForDisplay,
|
||||||
} from './swaps.util';
|
} from './swaps.util';
|
||||||
|
|
||||||
jest.mock('../../../shared/lib/storage-helpers', () => ({
|
jest.mock('../../../shared/lib/storage-helpers', () => ({
|
||||||
@ -534,6 +536,10 @@ describe('Swaps Util', () => {
|
|||||||
shouldEnableDirectWrapping(CHAIN_IDS.MAINNET, WETH_CONTRACT_ADDRESS),
|
shouldEnableDirectWrapping(CHAIN_IDS.MAINNET, WETH_CONTRACT_ADDRESS),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns false if source and destination tokens are undefined', () => {
|
||||||
|
expect(shouldEnableDirectWrapping(CHAIN_IDS.MAINNET)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('showRemainingTimeInMinAndSec', () => {
|
describe('showRemainingTimeInMinAndSec', () => {
|
||||||
@ -551,9 +557,48 @@ describe('Swaps Util', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getFeeForSmartTransaction', () => {
|
describe('getFeeForSmartTransaction', () => {
|
||||||
it('returns estimated for for STX', () => {
|
it('returns estimated fee for STX', () => {
|
||||||
// TODO: Implement tests for this function.
|
const expected = {
|
||||||
expect(true).toBe(true);
|
feeInUsd: '0.02',
|
||||||
|
feeInFiat: '$0.02',
|
||||||
|
feeInEth: '0.00323 ETH',
|
||||||
|
rawEthFee: '0.00323',
|
||||||
|
};
|
||||||
|
const actual = getFeeForSmartTransaction({
|
||||||
|
chainId: CHAIN_IDS.MAINNET,
|
||||||
|
currentCurrency: 'usd',
|
||||||
|
conversionRate: 5,
|
||||||
|
USDConversionRate: 5,
|
||||||
|
nativeCurrencySymbol: 'ETH',
|
||||||
|
feeInWeiDec: 3225623412028924,
|
||||||
|
});
|
||||||
|
expect(actual).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns estimated fee for STX for JPY currency', () => {
|
||||||
|
const expected = {
|
||||||
|
feeInUsd: '0.02',
|
||||||
|
feeInFiat: '£0.02',
|
||||||
|
feeInEth: '0.00323 ETH',
|
||||||
|
rawEthFee: '0.00323',
|
||||||
|
};
|
||||||
|
const actual = getFeeForSmartTransaction({
|
||||||
|
chainId: CHAIN_IDS.MAINNET,
|
||||||
|
currentCurrency: 'gbp',
|
||||||
|
conversionRate: 5,
|
||||||
|
USDConversionRate: 5,
|
||||||
|
nativeCurrencySymbol: 'ETH',
|
||||||
|
feeInWeiDec: 3225623412028924,
|
||||||
|
});
|
||||||
|
expect(actual).toMatchObject(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatSwapsValueForDisplay', () => {
|
||||||
|
it('gets swaps value for display', () => {
|
||||||
|
expect(formatSwapsValueForDisplay('39.6493201125465000000')).toBe(
|
||||||
|
'39.6493201125',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { renderWithProvider } from '../../../../test/jest';
|
import { renderWithProvider, fireEvent } from '../../../../test/jest';
|
||||||
import ViewOnBlockExplorer from '.';
|
import ViewOnBlockExplorer from '.';
|
||||||
|
|
||||||
const createProps = (customProps = {}) => {
|
const createProps = (customProps = {}) => {
|
||||||
@ -20,4 +20,17 @@ describe('ViewOnBlockExplorer', () => {
|
|||||||
expect(getByText('View Swap at etherscan.io')).toBeInTheDocument();
|
expect(getByText('View Swap at etherscan.io')).toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clicks on the block explorer link', () => {
|
||||||
|
global.platform = { openTab: jest.fn() };
|
||||||
|
const { getByText } = renderWithProvider(
|
||||||
|
<ViewOnBlockExplorer {...createProps()} />,
|
||||||
|
);
|
||||||
|
const link = getByText('View Swap at etherscan.io');
|
||||||
|
expect(link).toBeInTheDocument();
|
||||||
|
fireEvent.click(link);
|
||||||
|
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||||
|
url: 'https://etherscan.io',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -38,6 +38,7 @@ exports[`ViewQuote renders the component with EIP-1559 enabled 2`] = `
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class=""
|
class=""
|
||||||
|
data-testid="exchange-rate-display__base-symbol"
|
||||||
>
|
>
|
||||||
DAI
|
DAI
|
||||||
</span>
|
</span>
|
||||||
@ -54,6 +55,7 @@ exports[`ViewQuote renders the component with EIP-1559 enabled 2`] = `
|
|||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="exchange-rate-display__switch-arrows"
|
class="exchange-rate-display__switch-arrows"
|
||||||
|
data-testid="exchange-rate-display__switch-arrows"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
@ -110,6 +112,7 @@ exports[`ViewQuote renders the component with initial props 2`] = `
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class=""
|
class=""
|
||||||
|
data-testid="exchange-rate-display__base-symbol"
|
||||||
>
|
>
|
||||||
DAI
|
DAI
|
||||||
</span>
|
</span>
|
||||||
@ -126,6 +129,7 @@ exports[`ViewQuote renders the component with initial props 2`] = `
|
|||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="exchange-rate-display__switch-arrows"
|
class="exchange-rate-display__switch-arrows"
|
||||||
|
data-testid="exchange-rate-display__switch-arrows"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill="none"
|
fill="none"
|
||||||
|
@ -659,6 +659,7 @@ export default function ViewQuote() {
|
|||||||
|
|
||||||
const metaMaskFee = usedQuote.fee;
|
const metaMaskFee = usedQuote.fee;
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
const onFeeCardTokenApprovalClick = () => {
|
const onFeeCardTokenApprovalClick = () => {
|
||||||
trackEditSpendLimitOpened();
|
trackEditSpendLimitOpened();
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -891,19 +892,22 @@ export default function ViewQuote() {
|
|||||||
'view-quote__content_modal': disableSubmissionDueToPriceWarning,
|
'view-quote__content_modal': disableSubmissionDueToPriceWarning,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{selectQuotePopoverShown && (
|
{
|
||||||
<SelectQuotePopover
|
/* istanbul ignore next */
|
||||||
quoteDataRows={renderablePopoverData}
|
selectQuotePopoverShown && (
|
||||||
onClose={() => setSelectQuotePopoverShown(false)}
|
<SelectQuotePopover
|
||||||
onSubmit={(aggId) => dispatch(swapsQuoteSelected(aggId))}
|
quoteDataRows={renderablePopoverData}
|
||||||
swapToSymbol={destinationTokenSymbol}
|
onClose={() => setSelectQuotePopoverShown(false)}
|
||||||
initialAggId={usedQuote.aggregator}
|
onSubmit={(aggId) => dispatch(swapsQuoteSelected(aggId))}
|
||||||
onQuoteDetailsIsOpened={trackQuoteDetailsOpened}
|
swapToSymbol={destinationTokenSymbol}
|
||||||
hideEstimatedGasFee={
|
initialAggId={usedQuote.aggregator}
|
||||||
smartTransactionsEnabled && smartTransactionsOptInStatus
|
onQuoteDetailsIsOpened={trackQuoteDetailsOpened}
|
||||||
}
|
hideEstimatedGasFee={
|
||||||
/>
|
smartTransactionsEnabled && smartTransactionsOptInStatus
|
||||||
)}
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classnames('view-quote__warning-wrapper', {
|
className={classnames('view-quote__warning-wrapper', {
|
||||||
@ -914,7 +918,10 @@ export default function ViewQuote() {
|
|||||||
{(showInsufficientWarning || tokenBalanceUnavailable) && (
|
{(showInsufficientWarning || tokenBalanceUnavailable) && (
|
||||||
<ActionableMessage
|
<ActionableMessage
|
||||||
message={actionableBalanceErrorMessage}
|
message={actionableBalanceErrorMessage}
|
||||||
onClose={() => setWarningHidden(true)}
|
onClose={
|
||||||
|
/* istanbul ignore next */
|
||||||
|
() => setWarningHidden(true)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -971,10 +978,13 @@ export default function ViewQuote() {
|
|||||||
onTokenApprovalClick={onFeeCardTokenApprovalClick}
|
onTokenApprovalClick={onFeeCardTokenApprovalClick}
|
||||||
metaMaskFee={String(metaMaskFee)}
|
metaMaskFee={String(metaMaskFee)}
|
||||||
numberOfQuotes={Object.values(quotes).length}
|
numberOfQuotes={Object.values(quotes).length}
|
||||||
onQuotesClick={() => {
|
onQuotesClick={
|
||||||
trackAllAvailableQuotesOpened();
|
/* istanbul ignore next */
|
||||||
setSelectQuotePopoverShown(true);
|
() => {
|
||||||
}}
|
trackAllAvailableQuotesOpened();
|
||||||
|
setSelectQuotePopoverShown(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
chainId={chainId}
|
chainId={chainId}
|
||||||
isBestQuote={isBestQuote}
|
isBestQuote={isBestQuote}
|
||||||
maxPriorityFeePerGasDecGWEI={hexWEIToDecGWEI(
|
maxPriorityFeePerGasDecGWEI={hexWEIToDecGWEI(
|
||||||
@ -986,37 +996,39 @@ export default function ViewQuote() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<SwapsFooter
|
<SwapsFooter
|
||||||
onSubmit={() => {
|
onSubmit={
|
||||||
setSubmitClicked(true);
|
/* istanbul ignore next */ () => {
|
||||||
if (!balanceError) {
|
setSubmitClicked(true);
|
||||||
if (
|
if (!balanceError) {
|
||||||
currentSmartTransactionsEnabled &&
|
if (
|
||||||
smartTransactionsOptInStatus &&
|
currentSmartTransactionsEnabled &&
|
||||||
smartTransactionFees?.tradeTxFees
|
smartTransactionsOptInStatus &&
|
||||||
) {
|
smartTransactionFees?.tradeTxFees
|
||||||
dispatch(
|
) {
|
||||||
signAndSendSwapsSmartTransaction({
|
dispatch(
|
||||||
unsignedTransaction,
|
signAndSendSwapsSmartTransaction({
|
||||||
trackEvent,
|
unsignedTransaction,
|
||||||
history,
|
trackEvent,
|
||||||
additionalTrackingParams,
|
history,
|
||||||
}),
|
additionalTrackingParams,
|
||||||
);
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dispatch(
|
||||||
|
signAndSendTransactions(
|
||||||
|
history,
|
||||||
|
trackEvent,
|
||||||
|
additionalTrackingParams,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
||||||
|
history.push(DEFAULT_ROUTE);
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
||||||
signAndSendTransactions(
|
|
||||||
history,
|
|
||||||
trackEvent,
|
|
||||||
additionalTrackingParams,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (destinationToken.symbol === defaultSwapsToken.symbol) {
|
|
||||||
history.push(DEFAULT_ROUTE);
|
|
||||||
} else {
|
|
||||||
history.push(`${ASSET_ROUTE}/${destinationToken.address}`);
|
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
submitText={
|
submitText={
|
||||||
currentSmartTransactionsEnabled &&
|
currentSmartTransactionsEnabled &&
|
||||||
smartTransactionsOptInStatus &&
|
smartTransactionsOptInStatus &&
|
||||||
|
Loading…
Reference in New Issue
Block a user