fix: insufficient balance error doesn't reset validation after balance has been fulfilled (#2336)
* fix: live validation in deal ticket - reset validation after market state or account balance changes * fix: live validation in deal ticket - reset fix lint error * fix: live validation in deal ticket - adjust failing int test
This commit is contained in:
parent
5db3b9c0b9
commit
46a85b8e65
@ -7,6 +7,7 @@ import { generateEstimateOrder } from '../support/mocks/generate-fees';
|
||||
import { aliasQuery, mockConnectWallet } from '@vegaprotocol/cypress';
|
||||
import { testOrder } from '../support/deal-ticket-transaction';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { generateAccounts } from '../support/mocks/generate-accounts';
|
||||
|
||||
const orderSizeField = 'order-size';
|
||||
const orderPriceField = 'order-price';
|
||||
@ -622,55 +623,91 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
||||
});
|
||||
|
||||
describe('account validation', { tags: '@regression' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(
|
||||
req,
|
||||
'EstimateOrder',
|
||||
generateEstimateOrder({
|
||||
estimateOrder: {
|
||||
marginLevels: {
|
||||
__typename: 'MarginLevels',
|
||||
initialLevel: '1000000000',
|
||||
describe('zero balance error', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(
|
||||
req,
|
||||
'Accounts',
|
||||
generateAccounts({
|
||||
party: {
|
||||
accountsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
|
||||
balance: '0',
|
||||
market: null,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
cy.mockGQLSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.connectVegaWallet();
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
cy.getByTestId('place-order').should('not.be.disabled');
|
||||
cy.getByTestId('place-order').click();
|
||||
cy.getByTestId('place-order').should('be.disabled');
|
||||
//7002-SORD-003
|
||||
cy.getByTestId('dealticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'Insufficient balance. Deposit ' + 'tBTC'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
cy.mockGQLSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.connectVegaWallet();
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
cy.getByTestId('place-order').should('not.be.disabled');
|
||||
cy.getByTestId('place-order').click();
|
||||
cy.getByTestId('place-order').should('be.disabled');
|
||||
//7002-SORD-003
|
||||
cy.getByTestId('dealticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'Insufficient balance. Deposit ' + 'tBTC'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
|
||||
it('should display info and button for deposit', () => {
|
||||
//7002-SORD-003
|
||||
// warning should show immediately
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position'
|
||||
);
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'10,000.00 tBTC currently required, 1,000.00 tBTC available'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('dialog-content')
|
||||
.find('h1')
|
||||
.eq(0)
|
||||
.should('have.text', 'Deposit');
|
||||
describe('not enough balance warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(
|
||||
req,
|
||||
'EstimateOrder',
|
||||
generateEstimateOrder({
|
||||
estimateOrder: {
|
||||
marginLevels: {
|
||||
__typename: 'MarginLevels',
|
||||
initialLevel: '1000000000',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
cy.mockGQLSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.connectVegaWallet();
|
||||
cy.wait('@Market');
|
||||
});
|
||||
it('should display info and button for deposit', () => {
|
||||
//7002-SORD-003
|
||||
// warning should show immediately
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position'
|
||||
);
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'10,000.00 tBTC currently required, 1,000.00 tBTC available'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('dialog-content')
|
||||
.find('h1')
|
||||
.eq(0)
|
||||
.should('have.text', 'Deposit');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { fireEvent, render, screen, act } from '@testing-library/react';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
act,
|
||||
waitFor,
|
||||
} from '@testing-library/react';
|
||||
import { generateMarket } from '../../test-helpers';
|
||||
import { DealTicket } from './deal-ticket';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
@ -9,6 +15,14 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { ChainIdQuery } from '@vegaprotocol/react-helpers';
|
||||
import { ChainIdDocument, addDecimal } from '@vegaprotocol/react-helpers';
|
||||
import * as utils from '../../utils';
|
||||
|
||||
let mockHasNoBalance = false;
|
||||
jest.mock('../../hooks/use-has-no-balance', () => {
|
||||
return {
|
||||
useHasNoBalance: () => mockHasNoBalance,
|
||||
};
|
||||
});
|
||||
|
||||
const market = generateMarket();
|
||||
const submit = jest.fn();
|
||||
@ -31,7 +45,7 @@ function generateJsx(order?: OrderSubmissionBody['orderSubmission']) {
|
||||
};
|
||||
return (
|
||||
<MockedProvider mocks={[chainIdMock]}>
|
||||
<VegaWalletContext.Provider value={{} as any}>
|
||||
<VegaWalletContext.Provider value={{ pubKey: mockChainId } as any}>
|
||||
<DealTicket
|
||||
defaultOrder={order}
|
||||
market={market}
|
||||
@ -45,7 +59,11 @@ function generateJsx(order?: OrderSubmissionBody['orderSubmission']) {
|
||||
|
||||
describe('DealTicket', () => {
|
||||
beforeEach(() => window.localStorage.clear());
|
||||
afterEach(() => window.localStorage.clear());
|
||||
afterEach(() => {
|
||||
window.localStorage.clear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should display ticket defaults', () => {
|
||||
render(generateJsx());
|
||||
|
||||
@ -165,4 +183,48 @@ describe('DealTicket', () => {
|
||||
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
|
||||
);
|
||||
});
|
||||
|
||||
it('validation should be reset', async () => {
|
||||
mockHasNoBalance = true;
|
||||
jest.spyOn(utils, 'validateMarketState').mockReturnValue('Wrong state');
|
||||
jest
|
||||
.spyOn(utils, 'validateMarketTradingMode')
|
||||
.mockReturnValue('Wrong trading mode');
|
||||
const { rerender } = render(generateJsx());
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('place-order'));
|
||||
});
|
||||
await waitFor(async () => {
|
||||
expect(
|
||||
await screen.getByTestId('dealticket-error-message-summary')
|
||||
).toHaveTextContent('Wrong state');
|
||||
});
|
||||
|
||||
jest.spyOn(utils, 'validateMarketState').mockReturnValue(true);
|
||||
await act(async () => {
|
||||
rerender(generateJsx());
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('place-order'));
|
||||
});
|
||||
await waitFor(async () => {
|
||||
expect(
|
||||
await screen.getByTestId('dealticket-error-message-zero-balance')
|
||||
).toHaveTextContent('Insufficient balance.');
|
||||
});
|
||||
|
||||
mockHasNoBalance = false;
|
||||
await act(async () => {
|
||||
rerender(generateJsx());
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('place-order'));
|
||||
});
|
||||
await waitFor(async () => {
|
||||
expect(
|
||||
await screen.getByTestId('dealticket-error-message-summary')
|
||||
).toHaveTextContent('Wrong trading mode');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -23,7 +23,7 @@ import {
|
||||
validateType,
|
||||
} from '../../utils';
|
||||
import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
|
||||
import { AccountValidationType } from '../../constants';
|
||||
import { SummaryValidationType } from '../../constants';
|
||||
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
|
||||
import type { MarketDealTicket } from '@vegaprotocol/market-list';
|
||||
|
||||
@ -55,18 +55,42 @@ export const DealTicket = ({
|
||||
handleSubmit,
|
||||
watch,
|
||||
setError,
|
||||
clearErrors,
|
||||
formState: { errors },
|
||||
} = useForm<DealTicketFormFields>({
|
||||
defaultValues: persistedOrder || getDefaultOrder(market),
|
||||
});
|
||||
|
||||
const order = watch();
|
||||
// When order state changes persist it in local storage
|
||||
useEffect(() => setPersistedOrder(order), [order, setPersistedOrder]);
|
||||
|
||||
const marketStateError = validateMarketState(market.data.marketState);
|
||||
const hasNoBalance = useHasNoBalance(
|
||||
market.tradableInstrument.instrument.product.settlementAsset.id
|
||||
);
|
||||
const marketTradingModeError = validateMarketTradingMode(
|
||||
market.data.marketTradingMode
|
||||
);
|
||||
useEffect(() => {
|
||||
if (
|
||||
(!hasNoBalance &&
|
||||
errors.summary?.type === SummaryValidationType.NoCollateral) ||
|
||||
(marketStateError === true &&
|
||||
errors.summary?.type === SummaryValidationType.MarketState) ||
|
||||
(marketTradingModeError === true &&
|
||||
errors.summary?.type === SummaryValidationType.TradingMode)
|
||||
) {
|
||||
clearErrors('summary');
|
||||
}
|
||||
}, [
|
||||
hasNoBalance,
|
||||
marketStateError,
|
||||
marketTradingModeError,
|
||||
clearErrors,
|
||||
errors.summary?.message,
|
||||
errors.summary?.type,
|
||||
]);
|
||||
|
||||
// When order state changes persist it in local storage
|
||||
useEffect(() => setPersistedOrder(order), [order, setPersistedOrder]);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(order: OrderSubmissionBody['orderSubmission']) => {
|
||||
@ -75,22 +99,27 @@ export const DealTicket = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const marketStateError = validateMarketState(market.data.marketState);
|
||||
if (marketStateError !== true) {
|
||||
setError('summary', { message: marketStateError });
|
||||
setError('summary', {
|
||||
message: marketStateError,
|
||||
type: SummaryValidationType.MarketState,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasNoBalance) {
|
||||
setError('summary', { message: AccountValidationType.NoCollateral });
|
||||
setError('summary', {
|
||||
message: SummaryValidationType.NoCollateral,
|
||||
type: SummaryValidationType.NoCollateral,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const marketTradingModeError = validateMarketTradingMode(
|
||||
market.data.marketTradingMode
|
||||
);
|
||||
if (marketTradingModeError !== true) {
|
||||
setError('summary', { message: marketTradingModeError });
|
||||
setError('summary', {
|
||||
message: marketTradingModeError,
|
||||
type: SummaryValidationType.TradingMode,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,8 +139,8 @@ export const DealTicket = ({
|
||||
hasNoBalance,
|
||||
market.positionDecimalPlaces,
|
||||
market.decimalPlaces,
|
||||
market.data.marketState,
|
||||
market.data.marketTradingMode,
|
||||
marketStateError,
|
||||
marketTradingModeError,
|
||||
setError,
|
||||
]
|
||||
);
|
||||
@ -210,7 +239,7 @@ const SummaryMessage = memo(
|
||||
market,
|
||||
order,
|
||||
});
|
||||
if (errorMessage === AccountValidationType.NoCollateral) {
|
||||
if (errorMessage === SummaryValidationType.NoCollateral) {
|
||||
return (
|
||||
<ZeroBalanceError
|
||||
asset={market.tradableInstrument.instrument.product.settlementAsset}
|
||||
|
@ -30,6 +30,8 @@ export enum MarketModeValidationType {
|
||||
Auction = 'Auction',
|
||||
}
|
||||
|
||||
export enum AccountValidationType {
|
||||
export enum SummaryValidationType {
|
||||
NoCollateral = 'NoCollateral',
|
||||
TradingMode = 'MarketTradingMode',
|
||||
MarketState = 'MarketState',
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user