Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6636509099 | ||
|
|
40b3caa56b | ||
|
|
d1265a6af7 | ||
|
|
313eff1c95 | ||
|
|
7f949a276c |
@@ -10,6 +10,8 @@
|
||||
"governance.proposal.updateMarket.minVoterBalance",
|
||||
"governance.proposal.updateNetParam.minProposerBalance",
|
||||
"governance.proposal.updateNetParam.minVoterBalance",
|
||||
"governance.proposal.updateAsset.minProposerBalance",
|
||||
"governance.proposal.updateAsset.minVoterBalance",
|
||||
"reward.staking.delegation.maxPayoutPerEpoch",
|
||||
"reward.staking.delegation.maxPayoutPerParticipant",
|
||||
"reward.staking.delegation.minimumValidatorStake",
|
||||
@@ -19,9 +21,6 @@
|
||||
"validators.delegation.minAmount"
|
||||
],
|
||||
"fiveDecimal": [
|
||||
"governance.proposal.updateAsset.minProposerBalance",
|
||||
"governance.proposal.updateAsset.minVoterBalance",
|
||||
"governance.proposal.updateAsset.requiredParticipation",
|
||||
"market.fee.factors.infrastructureFee",
|
||||
"market.fee.factors.makerFee",
|
||||
"market.liquidity.bondPenaltyParameter",
|
||||
@@ -77,6 +76,7 @@
|
||||
"governance.proposal.updateNetParam.requiredMajority",
|
||||
"governance.proposal.updateNetParam.requiredParticipation",
|
||||
"governance.proposal.updateMarket.minProposerEquityLikeShare",
|
||||
"governance.proposal.updateAsset.requiredParticipation",
|
||||
"validators.vote.required"
|
||||
],
|
||||
"duration": [
|
||||
|
||||
@@ -138,7 +138,7 @@ context('Network parameters page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
||||
it.skip('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
||||
cy.get_network_parameters().then((network_parameters) => {
|
||||
network_parameters = Object.entries(network_parameters);
|
||||
network_parameters.forEach((network_parameter) => {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
const PERCENTAGE_PARAMS = [
|
||||
'governance.proposal.asset.requiredMajority',
|
||||
'governance.proposal.asset.requiredParticipation',
|
||||
'governance.proposal.updateAsset.requiredParticipation',
|
||||
'governance.proposal.freeform.requiredMajority',
|
||||
'governance.proposal.freeform.requiredParticipation',
|
||||
'governance.proposal.market.requiredMajority',
|
||||
|
||||
@@ -101,15 +101,15 @@ context(
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-release-tag').should(
|
||||
'have.text',
|
||||
'Vega release tagv1'
|
||||
'Vega release tag: v1'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-block-height').should(
|
||||
'have.text',
|
||||
'Upgrade block height2015942'
|
||||
'Upgrade block height: 2015942'
|
||||
);
|
||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||
'have.text',
|
||||
'Approved'
|
||||
'Approved '
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ export const ConnectToVega = () => {
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
variant="primary"
|
||||
>
|
||||
{t('connectVegaWallet')}
|
||||
</Button>
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Heading = ({
|
||||
})}
|
||||
>
|
||||
<h1
|
||||
className={classNames('font-alpha calt text-5xl', {
|
||||
className={classNames('font-alpha calt text-5xl break-words', {
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
})}
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
"NewFreeform": "Freeform",
|
||||
"tokenVotes": "Token votes",
|
||||
"liquidityVotes": "Liquidity votes",
|
||||
"yourVote": "Your vote",
|
||||
"castYourVote": "Cast your vote",
|
||||
"for": "For",
|
||||
"against": "Against",
|
||||
"majorityRequired": "Majority Required",
|
||||
@@ -587,7 +587,7 @@
|
||||
"tokensAgainstProposal": "Tokens against proposal",
|
||||
"participationRequired": "Participation required",
|
||||
"numberOfVotingParties": "Number of voting parties",
|
||||
"totalTokensVotes": "Total yes tokens",
|
||||
"totalTokensVotes": "Total tokens voted",
|
||||
"totalTokenVotedPercentage": "Total tokens voted percentage",
|
||||
"numberOfForVotes": "Number of votes for",
|
||||
"numberOfAgainstVotes": "Number of votes against",
|
||||
@@ -631,7 +631,10 @@
|
||||
"New market": "New market",
|
||||
"Market change": "Market change",
|
||||
"Network parameter": "Network parameter",
|
||||
"Change": "Change",
|
||||
"Unknown proposal": "Unknown proposal",
|
||||
"ERC20ContractAddress": "ERC20 contract address",
|
||||
"MaxFaucetAmountMint": "Max faucet amount mint",
|
||||
"Code": "Code",
|
||||
"settled future": "settled future",
|
||||
"Symbol": "Symbol",
|
||||
@@ -677,6 +680,7 @@
|
||||
"NewProposal": "New proposal",
|
||||
"ProposalTypeQuestion": "What type of proposal would you like to make?",
|
||||
"NetworkParameterProposal": "Update network parameter proposal",
|
||||
"parameter": "parameter",
|
||||
"NewMarketProposal": "New market proposal",
|
||||
"UpdateMarketProposal": "Update market proposal",
|
||||
"NewAssetProposal": "New asset proposal",
|
||||
@@ -694,6 +698,7 @@
|
||||
"UpdateMarket": "Update market",
|
||||
"NewAsset": "New asset",
|
||||
"UpdateAsset": "Update asset",
|
||||
"AssetID": "Asset ID",
|
||||
"Freeform": "Freeform",
|
||||
"RawProposal": "Let me choose (raw proposal)",
|
||||
"UseMin": "Use minimum",
|
||||
@@ -737,6 +742,7 @@
|
||||
"ProposalNotFound": "Proposal not found",
|
||||
"ProposalNotFoundDetails": "The proposal you are looking for is not here, it may have been enacted before the last chain restore. You could check the Vega forums/discord instead for information about it.",
|
||||
"FreeformProposal": "Freeform proposal",
|
||||
"Id": "ID",
|
||||
"unknownReason": "unknown reason",
|
||||
"votingEnded": "Voting has ended.",
|
||||
"STATUS": "STATUS",
|
||||
@@ -791,5 +797,7 @@
|
||||
"67% voting power required": "67% voting power required",
|
||||
"Token": "Token",
|
||||
"associateVegaNow": "Associate $VEGA now",
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app."
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app.",
|
||||
"connectAVegaWalletToVote": "Connect a Vega wallet with $VEGA tokens to vote on a proposal.",
|
||||
"findOutMoreAboutHowToVote": "Find out more about how to vote on Vega"
|
||||
}
|
||||
|
||||
+62
-14
@@ -1,7 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
@@ -9,19 +13,63 @@ export const CurrentProposalState = ({
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
let className = 'text-white';
|
||||
let proposalStatus: ReactNode;
|
||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||
|
||||
if (
|
||||
proposal?.state === Schema.ProposalState.STATE_DECLINED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_FAILED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_REJECTED
|
||||
) {
|
||||
className = 'text-danger';
|
||||
} else if (
|
||||
proposal?.state === Schema.ProposalState.STATE_ENACTED ||
|
||||
proposal?.state === Schema.ProposalState.STATE_PASSED
|
||||
) {
|
||||
className = 'text-white';
|
||||
switch (proposal?.state) {
|
||||
case ProposalState.STATE_ENACTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Enacted')}</span>
|
||||
<Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_PASSED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Passed')}</span>
|
||||
<Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_WaitingForNodeVote')}</span>
|
||||
<Icon name={'time'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_OPEN: {
|
||||
variant = 'primary' as ProposalInfoLabelVariant;
|
||||
proposalStatus = <>{t('voteState_Open')}</>;
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_DECLINED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Declined')}</span>
|
||||
<Icon name={'cross'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_REJECTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<span className="mr-2">{t('voteState_Rejected')}</span>
|
||||
<Icon name={'warning-sign'} />
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return <span className={className}>{t(`${proposal?.state}`)}</span>;
|
||||
|
||||
return (
|
||||
<ProposalInfoLabel variant={variant}>{proposalStatus}</ProposalInfoLabel>
|
||||
);
|
||||
};
|
||||
|
||||
-16
@@ -16,17 +16,12 @@ it('Renders all data for table', () => {
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
expect(screen.getByText('ID')).toBeInTheDocument();
|
||||
expect(screen.getByText(proposal?.id as string)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('State')).toBeInTheDocument();
|
||||
expect(screen.getByText('Open')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Closes on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed enactment')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -35,17 +30,12 @@ it('Renders all data for table', () => {
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed by')).toBeInTheDocument();
|
||||
expect(screen.getByText(proposal?.party.id ?? '')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(formatDateWithLocalTimezone(new Date(proposal?.datetime)))
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Type')).toBeInTheDocument();
|
||||
expect(screen.getByText('Network parameter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Changes data based on if data is in future or past', () => {
|
||||
@@ -53,17 +43,12 @@ it('Changes data based on if data is in future or past', () => {
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
});
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
|
||||
expect(screen.getByText('State')).toBeInTheDocument();
|
||||
expect(screen.getByText('Enacted')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Closed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Enacted on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -104,7 +89,6 @@ it('Renders error details and rejection reason if present', () => {
|
||||
render(<ProposalChangeTable proposal={proposal} />);
|
||||
expect(screen.getByText('Error details')).toBeInTheDocument();
|
||||
expect(screen.getByText(errorDetails)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Rejection reason')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE)
|
||||
|
||||
+6
-13
@@ -6,7 +6,6 @@ import {
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -20,16 +19,12 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
const terms = proposal?.terms;
|
||||
|
||||
return (
|
||||
<RoundedWrapper>
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<KeyValueTable data-testid="proposal-change-table">
|
||||
<KeyValueTableRow>
|
||||
{t('id')}
|
||||
{proposal?.id}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('state')}
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(terms?.closingDatetime))
|
||||
? t('closesOn')
|
||||
@@ -50,26 +45,24 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
{t('proposedBy')}
|
||||
<span style={{ wordBreak: 'break-word' }}>{proposal?.party.id}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<KeyValueTableRow
|
||||
noBorder={!proposal?.rejectionReason && !proposal?.errorDetails}
|
||||
>
|
||||
{t('proposedOn')}
|
||||
{formatDateWithLocalTimezone(new Date(proposal?.datetime))}
|
||||
</KeyValueTableRow>
|
||||
{proposal?.rejectionReason ? (
|
||||
<KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={!proposal?.errorDetails}>
|
||||
{t('rejectionReason')}
|
||||
{proposal.rejectionReason}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
{proposal?.errorDetails ? (
|
||||
<KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
{t('errorDetails')}
|
||||
{proposal.errorDetails}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
<KeyValueTableRow>
|
||||
{t('type')}
|
||||
{t(`${proposal?.terms.change.__typename}`)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
);
|
||||
|
||||
+234
-169
@@ -1,41 +1,45 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import {
|
||||
generateNoVotes,
|
||||
generateProposal,
|
||||
generateYesVotes,
|
||||
} from '../../test-helpers/generate-proposals';
|
||||
import { ProposalHeader } from './proposal-header';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import { lastWeek, nextWeek } from '../../test-helpers/mocks';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: ProposalQuery['proposal'],
|
||||
isListItem = true
|
||||
) => <ProposalHeader proposal={proposal} isListItem={isListItem} />;
|
||||
) => render(<ProposalHeader proposal={proposal} isListItem={isListItem} />);
|
||||
|
||||
describe('Proposal header', () => {
|
||||
it('Renders New market proposal', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
description: 'A new some market',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
instrument: {
|
||||
__typename: 'InstrumentConfiguration',
|
||||
name: 'Some market',
|
||||
code: 'FX:BTCUSD/DEC99',
|
||||
futureProduct: {
|
||||
__typename: 'FutureProduct',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
symbol: 'tGBP',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
description: 'A new some market',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
instrument: {
|
||||
__typename: 'InstrumentConfiguration',
|
||||
name: 'Some market',
|
||||
code: 'FX:BTCUSD/DEC99',
|
||||
futureProduct: {
|
||||
__typename: 'FutureProduct',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
symbol: 'tGBP',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New some market'
|
||||
@@ -47,20 +51,18 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it('Renders Update market proposal', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
marketId: 'MarketId',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
marketId: 'MarketId',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New market id'
|
||||
@@ -77,53 +79,49 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it('Renders New asset proposal - ERC20', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
description: '',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'FAKE',
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x0',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
description: '',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'FAKE',
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: '0x0',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New asset: Fake currency'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset');
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Symbol: FAKE. ERC20 0x0'
|
||||
'Symbol: FAKE. ERC20 contract address: 0x0'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'BIA',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '300',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
name: 'Fake currency',
|
||||
symbol: 'BIA',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '300',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
@@ -135,24 +133,22 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it('Renders Update network', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
networkParameter: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'Network key',
|
||||
value: 'Network value',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
networkParameter: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'Network key',
|
||||
value: 'Network value',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Network parameter'
|
||||
@@ -165,47 +161,42 @@ describe('Proposal header', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders Freeform network - short rationale', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
it('Renders Freeform proposal - short rationale', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('short');
|
||||
});
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
@@ -213,27 +204,24 @@ describe('Proposal header', () => {
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('long');
|
||||
});
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - details', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
}),
|
||||
false
|
||||
)
|
||||
},
|
||||
}),
|
||||
false
|
||||
);
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
/Class aptent/
|
||||
@@ -242,43 +230,36 @@ describe('Proposal header', () => {
|
||||
|
||||
// Remove once proposals have rationale and re-enable above tests
|
||||
it('Renders Freeform proposal - id for title', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
title: 'freeform',
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
title: 'freeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('freeform');
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-details')).toHaveTextContent(
|
||||
'freeform id'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders asset change proposal header', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateAsset',
|
||||
assetId: 'foo',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateAsset',
|
||||
assetId: 'foo',
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-type')).toHaveTextContent(
|
||||
'Update asset'
|
||||
@@ -287,20 +268,104 @@ describe('Proposal header', () => {
|
||||
});
|
||||
|
||||
it("Renders unknown proposal if it's a different proposal type", () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
// @ts-ignore unknown proposal
|
||||
__typename: 'Foo',
|
||||
},
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
// @ts-ignore unknown proposal
|
||||
__typename: 'Foo',
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders proposal state: Enacted', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Enacted');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Passed', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
closingDatetime: lastWeek.toString(),
|
||||
enactmentDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Passed');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Waiting for node vote', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
terms: {
|
||||
enactmentDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent(
|
||||
'Waiting for node vote'
|
||||
);
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_OPEN,
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: generateYesVotes(3000, 1000000000000000000),
|
||||
no: generateNoVotes(0),
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: nextWeek.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Declined - majority not reached', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_DECLINED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
votes: {
|
||||
no: generateNoVotes(1, 1000000000000000000),
|
||||
yes: generateYesVotes(1, 1000000000000000000),
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
});
|
||||
|
||||
it('Renders proposal state: Rejected', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
terms: {
|
||||
enactmentDatetime: lastWeek.toString(),
|
||||
},
|
||||
rejectionReason:
|
||||
ProposalRejectionReason.PROPOSAL_ERROR_INVALID_FUTURE_PRODUCT,
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
||||
});
|
||||
});
|
||||
|
||||
+72
-52
@@ -1,5 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Intent, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -7,6 +7,8 @@ import type { ProposalFieldsFragment } from '../../proposals/__generated__/Propo
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
@@ -19,7 +21,7 @@ export const ProposalHeader = ({
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType: ReactNode;
|
||||
let proposalType = '';
|
||||
|
||||
let title = proposal?.rationale.title.trim();
|
||||
let description = proposal?.rationale.description.trim();
|
||||
@@ -32,10 +34,12 @@ export const ProposalHeader = ({
|
||||
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType = t('NewMarket');
|
||||
proposalType = 'NewMarket';
|
||||
details = (
|
||||
<>
|
||||
{t('Code')}: {change.instrument.code}.{' '}
|
||||
<span>
|
||||
{t('Code')}: {change.instrument.code}.
|
||||
</span>{' '}
|
||||
{change.instrument.futureProduct?.settlementAsset.symbol ? (
|
||||
<>
|
||||
<span className="font-semibold">
|
||||
@@ -51,53 +55,60 @@ export const ProposalHeader = ({
|
||||
break;
|
||||
}
|
||||
case 'UpdateMarket': {
|
||||
proposalType = t('UpdateMarket');
|
||||
details = `${t('Market change')}: ${change.marketId}`;
|
||||
proposalType = 'UpdateMarket';
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Market change')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'NewAsset': {
|
||||
proposalType = t('NewAsset');
|
||||
proposalType = 'NewAsset';
|
||||
details = (
|
||||
<>
|
||||
{t('Symbol')}: {change.symbol}.{' '}
|
||||
<Lozenge>
|
||||
{change.source.__typename === 'ERC20' &&
|
||||
`ERC20 ${change.source.contractAddress}`}
|
||||
{change.source.__typename === 'BuiltinAsset' &&
|
||||
`${t('Max faucet amount mint')}: ${
|
||||
change.source.maxFaucetAmountMint
|
||||
}`}
|
||||
</Lozenge>
|
||||
<span>{t('Symbol')}:</span> <Lozenge>{change.symbol}.</Lozenge>{' '}
|
||||
{change.source.__typename === 'ERC20' && (
|
||||
<>
|
||||
<span>{t('ERC20ContractAddress')}:</span>{' '}
|
||||
<Lozenge>{change.source.contractAddress}</Lozenge>
|
||||
</>
|
||||
)}{' '}
|
||||
{change.source.__typename === 'BuiltinAsset' && (
|
||||
<>
|
||||
<span>{t('MaxFaucetAmountMint')}:</span>{' '}
|
||||
<Lozenge>{change.source.maxFaucetAmountMint}</Lozenge>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'UpdateNetworkParameter': {
|
||||
proposalType = t('NetworkParameter');
|
||||
const parametersClasses = 'font-mono leading-none';
|
||||
proposalType = 'NetworkParameter';
|
||||
details = (
|
||||
<>
|
||||
<span className={`${parametersClasses} mr-2`}>
|
||||
{change.networkParameter.key}
|
||||
</span>{' '}
|
||||
{t('to')}{' '}
|
||||
<span className={`${parametersClasses} ml-2`}>
|
||||
{change.networkParameter.value}
|
||||
<span>{t('Change')}:</span>{' '}
|
||||
<Lozenge>{change.networkParameter.key}</Lozenge>{' '}
|
||||
<span>{t('to')}</span>{' '}
|
||||
<span className="whitespace-nowrap">
|
||||
<Lozenge>{change.networkParameter.value}</Lozenge>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'NewFreeform': {
|
||||
proposalType = t('Freeform');
|
||||
details = `${t('FreeformProposal')}: ${proposal?.id}`;
|
||||
proposalType = 'Freeform';
|
||||
details = <span />;
|
||||
break;
|
||||
}
|
||||
case 'UpdateAsset': {
|
||||
proposalType = t('UpdateAsset');
|
||||
proposalType = 'UpdateAsset';
|
||||
details = (
|
||||
<>
|
||||
<span>{t('Asset ID')}:</span>
|
||||
<span>{t('AssetID')}:</span>{' '}
|
||||
<Lozenge>{truncateMiddle(change.assetId)}</Lozenge>
|
||||
</>
|
||||
);
|
||||
@@ -106,7 +117,7 @@ export const ProposalHeader = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-sm mb-2">
|
||||
<>
|
||||
<div data-testid="proposal-title">
|
||||
{isListItem ? (
|
||||
<header>
|
||||
@@ -118,30 +129,39 @@ export const ProposalHeader = ({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
{proposalType && (
|
||||
<div data-testid="proposal-type">
|
||||
<Lozenge variant={Intent.None}>{proposalType}</Lozenge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{description && !isListItem && (
|
||||
<div data-testid="proposal-description" className="mb-4">
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
<div data-testid="proposal-type">
|
||||
<ProposalInfoLabel variant="secondary">
|
||||
{t(`${proposalType}`)}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
|
||||
<div data-testid="proposal-status">
|
||||
<CurrentProposalState proposal={proposal} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{details && <div data-testid="proposal-details">{details}</div>}
|
||||
</div>
|
||||
{details && (
|
||||
<div data-testid="proposal-details" className="break-words my-10">
|
||||
{details}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{description && !isListItem && (
|
||||
<div data-testid="proposal-description">
|
||||
{/*<div className="uppercase mr-2">{t('ProposalDescription')}:</div>*/}
|
||||
<SubHeading title={t('ProposalDescription')} />
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './proposal-info-label';
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export type ProposalInfoLabelVariant =
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'tertiary'
|
||||
| 'highlight';
|
||||
|
||||
const base = 'rounded-md px-2 py-1 font-alpha';
|
||||
const primary = 'bg-vega-light-150 text-black';
|
||||
const secondary = 'bg-vega-dark-200 text-white';
|
||||
const tertiary = 'bg-vega-dark-150 text-white';
|
||||
const highlight = 'bg-vega-yellow text-black';
|
||||
|
||||
const getClassname = (variant: ProposalInfoLabelVariant) => {
|
||||
return classNames(base, {
|
||||
[primary]: variant === 'primary',
|
||||
[secondary]: variant === 'secondary',
|
||||
[tertiary]: variant === 'tertiary',
|
||||
[highlight]: variant === 'highlight',
|
||||
});
|
||||
};
|
||||
|
||||
interface ProposalInfoLabelProps {
|
||||
children: ReactNode;
|
||||
variant?: ProposalInfoLabelVariant;
|
||||
}
|
||||
|
||||
export const ProposalInfoLabel = ({
|
||||
children,
|
||||
variant = 'primary',
|
||||
}: ProposalInfoLabelProps) => {
|
||||
return <div className={getClassname(variant)}>{children}</div>;
|
||||
};
|
||||
+21
-3
@@ -1,8 +1,10 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
export const ProposalTermsJson = ({
|
||||
terms,
|
||||
@@ -10,10 +12,26 @@ export const ProposalTermsJson = ({
|
||||
terms: PartialDeep<Schema.ProposalTerms>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<section>
|
||||
<SubHeading title={t('proposalTerms')} />
|
||||
<SyntaxHighlighter data={terms} />
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="proposal-terms-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalTerms')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showDetails && <SyntaxHighlighter data={terms} />}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-3
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalVotesTable } from './proposal-votes-table';
|
||||
@@ -46,6 +46,7 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('should show vote breakdown fields, excluding custom update market fields', () => {
|
||||
renderComponent();
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('Expected to pass')).toBeInTheDocument();
|
||||
expect(screen.getByText('Token majority met')).toBeInTheDocument();
|
||||
expect(screen.getByText('Token participation met')).toBeInTheDocument();
|
||||
@@ -55,7 +56,7 @@ describe('Proposal Votes Table', () => {
|
||||
expect(screen.getByText('Participation required')).toBeInTheDocument();
|
||||
expect(screen.getByText('Majority Required')).toBeInTheDocument();
|
||||
expect(screen.getByText('Number of voting parties')).toBeInTheDocument();
|
||||
expect(screen.getByText('Total yes tokens')).toBeInTheDocument();
|
||||
expect(screen.getByText('Total tokens voted')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Total tokens voted percentage')
|
||||
).toBeInTheDocument();
|
||||
@@ -70,13 +71,14 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('displays different breakdown fields for update market proposal', () => {
|
||||
renderComponent(updateMarketProposal, updateMarketProposalType);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('Liquidity majority met')).toBeInTheDocument();
|
||||
expect(screen.getByText('Liquidity participation met')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Liquidity shares for proposal')
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText('Number of voting parties')).toBeNull();
|
||||
expect(screen.queryByText('Total yes tokens')).toBeNull();
|
||||
expect(screen.queryByText('Total tokens voted')).toBeNull();
|
||||
expect(screen.queryByText('Total tokens voted percentage')).toBeNull();
|
||||
expect(screen.queryByText('Number of votes for')).toBeNull();
|
||||
expect(screen.queryByText('Number of votes against')).toBeNull();
|
||||
@@ -86,6 +88,7 @@ describe('Proposal Votes Table', () => {
|
||||
|
||||
it('displays if an update market proposal will pass by token vote', () => {
|
||||
renderComponent(updateMarketProposal, updateMarketProposalType);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('👍 by token vote')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -110,6 +113,7 @@ describe('Proposal Votes Table', () => {
|
||||
}),
|
||||
updateMarketProposalType
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('vote-breakdown-toggle'));
|
||||
expect(screen.getByText('👍 by liquidity vote')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+112
-91
@@ -1,9 +1,12 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Thumbs,
|
||||
RoundedWrapper,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
@@ -26,6 +29,7 @@ export const ProposalVotesTable = ({
|
||||
const {
|
||||
appState: { totalSupply },
|
||||
} = useAppState();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const {
|
||||
willPassByTokenVote,
|
||||
willPassByLPVote,
|
||||
@@ -53,113 +57,130 @@ export const ProposalVotesTable = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLiquidityVote');
|
||||
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<RoundedWrapper>
|
||||
<KeyValueTable
|
||||
data-testid="proposal-votes-table"
|
||||
numerical={true}
|
||||
headingLevel={4}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
{t('expectedToPass')}
|
||||
{isUpdateMarket ? (
|
||||
updateMarketWillPass ? (
|
||||
<Thumbs up={true} text={updateMarketVotePassMethod} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)
|
||||
) : willPassByTokenVote ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityMet')}
|
||||
{majorityMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="vote-breakdown-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{showDetails && (
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<KeyValueTable
|
||||
data-testid="proposal-votes-table"
|
||||
numerical={true}
|
||||
headingLevel={4}
|
||||
>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityLPMet')}
|
||||
{majorityLPMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('participationMet')}
|
||||
{participationMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('participationLPMet')}
|
||||
{participationLPMet ? (
|
||||
{t('expectedToPass')}
|
||||
{isUpdateMarket ? (
|
||||
updateMarketWillPass ? (
|
||||
<Thumbs up={true} text={updateMarketVotePassMethod} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)
|
||||
) : willPassByTokenVote ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('tokenForProposal')}
|
||||
{formatNumber(yesTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('tokenLPForProposal')}
|
||||
{formatNumber(yesEquityLikeShareWeight, 2)}
|
||||
{t('majorityMet')}
|
||||
{majorityMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('totalSupply')}
|
||||
{formatNumber(totalSupply, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('tokensAgainstProposal')}
|
||||
{formatNumber(noTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('participationRequired')}
|
||||
{formatNumberPercentage(requiredParticipation)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityRequired')}
|
||||
{formatNumberPercentage(requiredMajorityPercentage)}
|
||||
</KeyValueTableRow>
|
||||
{!isUpdateMarket && (
|
||||
<>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfVotingParties')}
|
||||
{formatNumber(totalVotes, 0)}
|
||||
{t('majorityLPMet')}
|
||||
{majorityLPMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('participationMet')}
|
||||
{participationMet ? <Thumbs up={true} /> : <Thumbs up={false} />}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokensVotes')}
|
||||
{formatNumber(totalTokensVoted, 2)}
|
||||
{t('participationLPMet')}
|
||||
{participationLPMet ? (
|
||||
<Thumbs up={true} />
|
||||
) : (
|
||||
<Thumbs up={false} />
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('tokenForProposal')}
|
||||
{formatNumber(yesTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
{isUpdateMarket && (
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokenVotedPercentage')}
|
||||
{formatNumberPercentage(totalTokensPercentage, 2)}
|
||||
{t('tokenLPForProposal')}
|
||||
{formatNumber(yesEquityLikeShareWeight, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfForVotes')}
|
||||
{formatNumber(yesVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfAgainstVotes')}
|
||||
{formatNumber(noVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('yesPercentage')}
|
||||
{formatNumberPercentage(yesPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
{t('noPercentage')}
|
||||
{formatNumberPercentage(noPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
</>
|
||||
)}
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('totalSupply')}
|
||||
{formatNumber(totalSupply, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('tokensAgainstProposal')}
|
||||
{formatNumber(noTokens, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('participationRequired')}
|
||||
{formatNumberPercentage(requiredParticipation)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('majorityRequired')}
|
||||
{formatNumberPercentage(requiredMajorityPercentage)}
|
||||
</KeyValueTableRow>
|
||||
{!isUpdateMarket && (
|
||||
<>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfVotingParties')}
|
||||
{formatNumber(totalVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokensVotes')}
|
||||
{formatNumber(totalTokensVoted, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('totalTokenVotedPercentage')}
|
||||
{formatNumberPercentage(totalTokensPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfForVotes')}
|
||||
{formatNumber(yesVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('numberOfAgainstVotes')}
|
||||
{formatNumber(noVotes, 0)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('yesPercentage')}
|
||||
{formatNumberPercentage(yesPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
{t('noPercentage')}
|
||||
{formatNumberPercentage(noPercentage, 2)}
|
||||
</KeyValueTableRow>
|
||||
</>
|
||||
)}
|
||||
</KeyValueTable>
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
@@ -78,7 +78,7 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||
<section data-testid="proposal">
|
||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||
<div className="mb-10">
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
@@ -91,14 +91,18 @@ export const Proposal = ({ proposal }: ProposalProps) => {
|
||||
/>
|
||||
) : null}
|
||||
<div className="mb-12">
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={params?.spam_protection_voting_min_tokens}
|
||||
/>
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
<div className="mb-10">
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||
</div>
|
||||
<ProposalTermsJson terms={proposal.terms} />
|
||||
|
||||
+2
-21
@@ -97,7 +97,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Enacted');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
format(lastWeek, DATE_FORMAT_DETAILED)
|
||||
);
|
||||
@@ -113,7 +112,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Passed');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
`Enacts on ${format(nextWeek, DATE_FORMAT_DETAILED)}`
|
||||
);
|
||||
@@ -128,9 +126,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent(
|
||||
'Waiting for node vote'
|
||||
);
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
`Enacts on ${format(nextWeek, DATE_FORMAT_DETAILED)}`
|
||||
);
|
||||
@@ -221,7 +216,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 minutes left to vote'
|
||||
);
|
||||
@@ -236,7 +230,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 hours left to vote'
|
||||
);
|
||||
@@ -251,7 +244,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-details')).toHaveTextContent(
|
||||
'5 days left to vote'
|
||||
);
|
||||
@@ -268,10 +260,7 @@ describe('Proposals list item details', () => {
|
||||
networkParamsQueryMock,
|
||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_YES),
|
||||
]);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
|
||||
expect(await screen.findByText('You voted')).toBeInTheDocument();
|
||||
expect(await screen.findByText('For')).toBeInTheDocument();
|
||||
expect(await screen.findByText('You voted For')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open - user voted against', async () => {
|
||||
@@ -285,9 +274,7 @@ describe('Proposals list item details', () => {
|
||||
networkParamsQueryMock,
|
||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_NO),
|
||||
]);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(await screen.findByText('You voted')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Against')).toBeInTheDocument();
|
||||
expect(await screen.findByText('You voted Against')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders proposal state: Open - participation not reached', () => {
|
||||
@@ -303,7 +290,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Participation not reached'
|
||||
);
|
||||
@@ -322,7 +308,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Majority not reached'
|
||||
);
|
||||
@@ -342,7 +327,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent('Set to pass');
|
||||
});
|
||||
|
||||
@@ -359,7 +343,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Participation not reached'
|
||||
);
|
||||
@@ -378,7 +361,6 @@ describe('Proposals list item details', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Majority not reached'
|
||||
);
|
||||
@@ -395,7 +377,6 @@ describe('Proposals list item details', () => {
|
||||
ProposalRejectionReason.PROPOSAL_ERROR_INVALID_FUTURE_PRODUCT,
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||
'Invalid future product'
|
||||
);
|
||||
|
||||
+12
-55
@@ -1,11 +1,8 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import {
|
||||
StatusPass,
|
||||
StatusFail,
|
||||
} from '../current-proposal-status/current-proposal-status';
|
||||
import { StatusPass } from '../current-proposal-status/current-proposal-status';
|
||||
import { format, formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
@@ -22,7 +19,7 @@ const MajorityNotReached = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{t('Majority')} <StatusFail>{t('not reached')}</StatusFail>
|
||||
{t('Majority')} {t('not reached')}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -30,7 +27,7 @@ const ParticipationNotReached = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{t('Participation')} <StatusFail>{t('not reached')}</StatusFail>
|
||||
{t('Participation')} {t('not reached')}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -57,17 +54,11 @@ export const ProposalsListItemDetails = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLPVote');
|
||||
|
||||
let proposalStatus: ReactNode;
|
||||
let voteDetails: ReactNode;
|
||||
let voteStatus: ReactNode;
|
||||
|
||||
switch (state) {
|
||||
case ProposalState.STATE_ENACTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Enacted')} <Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.enactmentDatetime && (
|
||||
<>
|
||||
{format(
|
||||
@@ -79,11 +70,6 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_PASSED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Passed')} <Icon name={'tick'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
||||
<>
|
||||
{t('toEnactOn')}{' '}
|
||||
@@ -97,11 +83,6 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_WaitingForNodeVote')} <Icon name={'time'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
||||
<>
|
||||
{t('toEnactOn')}{' '}
|
||||
@@ -115,19 +96,14 @@ export const ProposalsListItemDetails = ({
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_OPEN: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Open')} <Icon name={'hand'} />
|
||||
</>
|
||||
);
|
||||
voteDetails = (voteState === 'Yes' && (
|
||||
<>
|
||||
{t('youVoted')} <StatusPass>{t('voteState_Yes')}</StatusPass>
|
||||
{t('youVoted')} {t('voteState_Yes')}
|
||||
</>
|
||||
)) ||
|
||||
(voteState === 'No' && (
|
||||
<>
|
||||
{t('youVoted')} <StatusFail>{t('voteState_No')}</StatusFail>
|
||||
{t('youVoted')} {t('voteState_No')}
|
||||
</>
|
||||
)) || (
|
||||
<>
|
||||
@@ -148,40 +124,29 @@ export const ProposalsListItemDetails = ({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t('Set to')} <StatusFail>{t('fail')}</StatusFail>
|
||||
{t('Set to')} {t('fail')}
|
||||
</>
|
||||
))) ||
|
||||
(!participationMet && <ParticipationNotReached />) ||
|
||||
(!majorityMet && <MajorityNotReached />) ||
|
||||
(willPassByTokenVote ? (
|
||||
<>
|
||||
{t('Set to')} <StatusPass>{t('pass')}</StatusPass>
|
||||
{t('Set to')} {t('pass')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t('Set to')} <StatusFail>{t('fail')}</StatusFail>
|
||||
{t('Set to')} {t('fail')}
|
||||
</>
|
||||
));
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_DECLINED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
{t('voteState_Declined')} <Icon name={'cross'} />
|
||||
</>
|
||||
);
|
||||
voteStatus =
|
||||
(!participationMet && <ParticipationNotReached />) ||
|
||||
(!majorityMet && <MajorityNotReached />);
|
||||
break;
|
||||
}
|
||||
case ProposalState.STATE_REJECTED: {
|
||||
proposalStatus = (
|
||||
<>
|
||||
<StatusFail>{t('voteState_Rejected')}</StatusFail>{' '}
|
||||
<Icon name={'warning-sign'} />
|
||||
</>
|
||||
);
|
||||
voteStatus = proposal?.rejectionReason && (
|
||||
<>{t(ProposalRejectionReasonMapping[proposal.rejectionReason])}</>
|
||||
);
|
||||
@@ -190,16 +155,10 @@ export const ProposalsListItemDetails = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-[1fr_auto] mt-2 items-start gap-2 text-sm">
|
||||
<div
|
||||
className="col-start-1 row-start-1 flex items-center gap-2 text-white"
|
||||
data-testid="proposal-status"
|
||||
>
|
||||
{proposalStatus}
|
||||
</div>
|
||||
<div className="grid grid-cols-[1fr_auto] mt-4 items-start gap-2 text-sm">
|
||||
{voteDetails && (
|
||||
<div
|
||||
className="col-start-1 row-start-2 text-neutral-500"
|
||||
className="col-start-1 row-start-2 text-vega-light-300"
|
||||
data-testid="vote-details"
|
||||
>
|
||||
{voteDetails}
|
||||
@@ -216,9 +175,7 @@ export const ProposalsListItemDetails = ({
|
||||
{proposal?.id && (
|
||||
<div className="col-start-2 row-start-2 justify-self-end">
|
||||
<Link to={`${Routes.PROPOSALS}/${proposal.id}`}>
|
||||
<Button data-testid="view-proposal-btn" size="sm">
|
||||
{t('View')}
|
||||
</Button>
|
||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -106,7 +106,7 @@ export const ProposalsList = ({
|
||||
{proposals.length > 0 && (
|
||||
<ProposalsListFilter setFilterString={setFilterString} />
|
||||
)}
|
||||
<section className="-mx-4 p-4 mb-8 bg-neutral-800">
|
||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||
<SubHeading title={t('openProposals')} />
|
||||
{sortedProposals.open.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||
|
||||
+30
-32
@@ -3,15 +3,15 @@ import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Intent,
|
||||
Lozenge,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { stripFullStops } from '@vegaprotocol/utils';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import Routes from '../../../routes';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
interface ProtocolProposalsListItemProps {
|
||||
@@ -29,30 +29,30 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
switch (proposal.status) {
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
||||
<Icon name={'cross'} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-pending">
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-pending">
|
||||
<Icon name={'time'} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-approved">
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-approved">
|
||||
<Icon name={'tick'} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED:
|
||||
proposalStatusIcon = (
|
||||
<div data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
||||
<span data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
||||
<Icon name={'disable'} />
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -71,18 +71,28 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-type"
|
||||
className="flex items-center gap-2 mb-4"
|
||||
>
|
||||
<Lozenge variant={Intent.Success}>{t('networkUpgrade')}</Lozenge>
|
||||
<div className="flex gap-2">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-type"
|
||||
className="flex items-center gap-2 mb-4"
|
||||
>
|
||||
<ProposalInfoLabel variant="highlight">
|
||||
{t('networkUpgrade')}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
|
||||
<div data-testid="protocol-upgrade-proposal-status">
|
||||
<ProposalInfoLabel>
|
||||
{t(`${proposal.status}`)} {proposalStatusIcon}
|
||||
</ProposalInfoLabel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-release-tag"
|
||||
className="mb-2"
|
||||
>
|
||||
<span className="pr-2">{t('vegaReleaseTag')}</span>
|
||||
<span>{t('vegaReleaseTag')}:</span>{' '}
|
||||
<Lozenge>{proposal.vegaReleaseTag}</Lozenge>
|
||||
</div>
|
||||
|
||||
@@ -90,30 +100,18 @@ export const ProtocolUpgradeProposalsListItem = ({
|
||||
data-testid="protocol-upgrade-proposal-block-height"
|
||||
className="mb-2"
|
||||
>
|
||||
<span className="pr-2">{t('upgradeBlockHeight')}</span>
|
||||
<span>{t('upgradeBlockHeight')}:</span>{' '}
|
||||
<Lozenge>{proposal.upgradeBlockHeight}</Lozenge>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[1fr_auto] mt-3 items-start gap-2">
|
||||
<div className="col-start-1 row-start-1 text-white">
|
||||
<div
|
||||
data-testid="protocol-upgrade-proposal-status"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<span>{t(`${proposal.status}`)}</span>
|
||||
<span>{proposalStatusIcon}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-start-2 row-start-2 justify-self-end">
|
||||
<div className="grid grid-cols-1 mt-3">
|
||||
<div className="justify-self-end">
|
||||
<Link
|
||||
to={`${Routes.PROPOSALS}/protocol-upgrade/${stripFullStops(
|
||||
proposal.vegaReleaseTag
|
||||
)}`}
|
||||
>
|
||||
<Button data-testid="view-proposal-btn" size="sm">
|
||||
{t('View')}
|
||||
</Button>
|
||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,10 +107,6 @@ export const VoteButtons = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStakeAvailable.isLessThanOrEqualTo(0)) {
|
||||
return t('noGovernanceTokens');
|
||||
}
|
||||
|
||||
if (minVoterBalance && spamProtectionMinTokens) {
|
||||
const formattedMinVoterBalance = new BigNumber(
|
||||
addDecimal(minVoterBalance, 18)
|
||||
@@ -163,24 +159,30 @@ export const VoteButtons = ({
|
||||
return (
|
||||
<>
|
||||
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
|
||||
<div className="flex gap-4" data-testid="vote-buttons">
|
||||
<div className="flex-1">
|
||||
<>
|
||||
{currentStakeAvailable.isLessThanOrEqualTo(0) && (
|
||||
<p data-testid="no-stake-available">{t('noGovernanceTokens')}</p>
|
||||
)}
|
||||
|
||||
<div className="flex gap-4" data-testid="vote-buttons">
|
||||
<Button
|
||||
data-testid="vote-for"
|
||||
onClick={() => submitVote(VoteValue.VALUE_YES)}
|
||||
variant="primary"
|
||||
disabled={currentStakeAvailable.isLessThanOrEqualTo(0)}
|
||||
>
|
||||
{t('voteFor')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
data-testid="vote-against"
|
||||
onClick={() => submitVote(VoteValue.VALUE_NO)}
|
||||
variant="primary"
|
||||
disabled={currentStakeAvailable.isLessThanOrEqualTo(0)}
|
||||
>
|
||||
{t('voteAgainst')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
(voteState === VoteState.Yes || voteState === VoteState.No) && (
|
||||
<p data-testid="you-voted">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
||||
@@ -199,10 +200,11 @@ export const VoteDetails = ({
|
||||
{proposalType === ProposalType.PROPOSAL_UPDATE_MARKET && (
|
||||
<p>{t('votingThresholdInfo')}</p>
|
||||
)}
|
||||
{pubKey ? (
|
||||
<section className="mt-10">
|
||||
<SubHeading title={t('yourVote')} />
|
||||
{proposal && (
|
||||
|
||||
<section className="mt-10">
|
||||
<SubHeading title={t('castYourVote')} />
|
||||
{pubKey ? (
|
||||
proposal && (
|
||||
<VoteButtonsContainer
|
||||
voteState={voteState}
|
||||
voteDatetime={voteDatetime}
|
||||
@@ -214,11 +216,19 @@ export const VoteDetails = ({
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
) : (
|
||||
<ConnectToVega />
|
||||
)}
|
||||
)
|
||||
) : (
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Icon name={'info-sign'} />
|
||||
<div>{t('connectAVegaWalletToVote')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConnectToVega />
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "19242.125",
|
||||
"total_removed": "1523.8177488329475",
|
||||
"locked_amount": "7532.72773939043192534875",
|
||||
"locked_amount": "7371.31511443865825548625",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "188",
|
||||
@@ -877,7 +877,7 @@
|
||||
"tranche_start": "2023-04-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-06T00:00:00.000Z",
|
||||
"total_added": "14610",
|
||||
"total_removed": "5697.45090157707",
|
||||
"total_removed": "6141.45090157707",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -1057,6 +1057,26 @@
|
||||
"user": "0x15024E62134A8BFFCce11f5ce58CeCDe853038D7",
|
||||
"tx": "0x9be84231ff156bc8b8de9a99250f03b8ebf9d59c252e3b778f10051489e5758a"
|
||||
},
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"tx": "0xdfae5add3e12eed919c0481d9289aa33688884db68ca90332d1df31107e4fbc6"
|
||||
},
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
"tx": "0x7195dc5808870b0f00824c6593eda1e994976189cf3373cf84c5dae7a60d8b62"
|
||||
},
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tx": "0x016970cfdaaab7a663a5c8b897b44c5d302e5cd110638de231073c3b5e23ddf8"
|
||||
},
|
||||
{
|
||||
"amount": "132",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
|
||||
},
|
||||
{
|
||||
"amount": "106.53500000286",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1114,10 +1134,17 @@
|
||||
"tx": "0x83cb92910e0725b82b3459e025260c448f931224f7cb00a376351dbd75ae2733"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "90",
|
||||
"user": "0x05659B08a8079E003eE37F26c29e52532728c034",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x016970cfdaaab7a663a5c8b897b44c5d302e5cd110638de231073c3b5e23ddf8"
|
||||
}
|
||||
],
|
||||
"total_tokens": "90",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "90"
|
||||
"withdrawn_tokens": "90",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
@@ -1129,10 +1156,17 @@
|
||||
"tx": "0x1229e077f48b796678436bfa8143cea3ae71df58b589ac922be205c4482be235"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0x90Cf7B9958C3BbBCddAb52Ecc408535D4f3C4241",
|
||||
"tranche_id": 54,
|
||||
"tx": "0xdfae5add3e12eed919c0481d9289aa33688884db68ca90332d1df31107e4fbc6"
|
||||
}
|
||||
],
|
||||
"total_tokens": "111",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "111"
|
||||
"withdrawn_tokens": "111",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x83D7eD53E7CB97b542F1F71f40561d51F8019C8B",
|
||||
@@ -1483,10 +1517,17 @@
|
||||
"tx": "0x99aeaedef27b5fb693485f4e21d434e343b30fa100945a4ff65386de8801c87e"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "111",
|
||||
"user": "0xAc1c2783d64c29CDB79608f5B66a37Fd1ea35dD6",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x7195dc5808870b0f00824c6593eda1e994976189cf3373cf84c5dae7a60d8b62"
|
||||
}
|
||||
],
|
||||
"total_tokens": "111",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "111"
|
||||
"withdrawn_tokens": "111",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x8D416A61bCccf4E6aF5598302BC4e41f97401652",
|
||||
@@ -1513,10 +1554,17 @@
|
||||
"tx": "0xcb0f255003872ac506798efa97744868e11ceab2f2f03d96da605d8674f783f6"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "132",
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
|
||||
}
|
||||
],
|
||||
"total_tokens": "132",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "132"
|
||||
"withdrawn_tokens": "132",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xC0f02AA8bA8b509D223C9De6108AFA1C03f03341",
|
||||
@@ -4813,7 +4861,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "50039.4836272332284177113",
|
||||
"locked_amount": "49979.7329096604869903443",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -4879,7 +4927,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "326.1553406084655",
|
||||
"locked_amount": "322.698698870573775",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -4912,7 +4960,7 @@
|
||||
"tranche_end": "2023-11-01T00:00:00.000Z",
|
||||
"total_added": "15000.000000000000015",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "14408.4871301328495144084871301328495",
|
||||
"locked_amount": "14387.9727128623185143879727128623185",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1.5e-14",
|
||||
@@ -5000,7 +5048,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "11008.27121703904875",
|
||||
"locked_amount": "10984.33773022342925",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -5267,7 +5315,7 @@
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "18077.0118744",
|
||||
"locked_amount": "17557.4811042050325",
|
||||
"locked_amount": "17505.34501611418125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -5686,7 +5734,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "49993.828894632632305527",
|
||||
"locked_amount": "49934.132692071551825937",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -5719,7 +5767,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "48633.25940786130414603646",
|
||||
"locked_amount": "48596.0321414648663214997",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -5752,7 +5800,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "20193.92112506342202",
|
||||
"locked_amount": "20150.76253805175029",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -5945,7 +5993,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1804.7138191273465",
|
||||
"locked_amount": "1801.26664764079125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -7014,7 +7062,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "1709370.7872515768348",
|
||||
"locked_amount": "131511.7777855967480100472",
|
||||
"locked_amount": "130174.32462609789781957",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -40886,7 +40934,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "715655.108029600523393",
|
||||
"locked_amount": "226589.1396478949202049633456",
|
||||
"locked_amount": "224533.9397934779571663606186",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -42279,7 +42327,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "871680.07831804700259352",
|
||||
"locked_amount": "6103158.12641901073651283988990528359908086",
|
||||
"locked_amount": "6095870.51969169065811272227952994009813466",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -59022,7 +59070,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "44479.0535455583416",
|
||||
"locked_amount": "35904.344415950602504988809538328",
|
||||
"locked_amount": "35578.671272696564429696212379508",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { isBefore, isAfter, addSeconds, subSeconds } from 'date-fns';
|
||||
import { createOrder } from '../support/create-order';
|
||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const orderSize = 'size';
|
||||
const orderType = 'type';
|
||||
@@ -22,15 +23,13 @@ const assetSelectField = 'select[name="asset"]';
|
||||
const amountField = 'input[name="amount"]';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
||||
const btcName =
|
||||
'BTC (local)5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c - tBTC';
|
||||
const vegaName =
|
||||
'Vegab4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b - VEGA';
|
||||
const btcName = 0;
|
||||
const vegaName = 4;
|
||||
const btcSymbol = 'tBTC';
|
||||
const vegaSymbol = 'VEGA';
|
||||
const usdcSymbol = 'fUSDC';
|
||||
const toastContent = 'toast-content';
|
||||
const ordersTab = 'Orders';
|
||||
const openOrdersTab = 'Open';
|
||||
const depositsTab = 'Deposits';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
@@ -50,6 +49,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.get('@markets').then((markets) => {
|
||||
cy.wrap(markets[0]).as('market');
|
||||
});
|
||||
cy.visit('/#/portfolio');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -73,7 +73,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
selectAsset(btcName);
|
||||
cy.getByTestId('approve-default').should(
|
||||
'contain.text',
|
||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
||||
@@ -81,6 +81,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(approveSubmit).click();
|
||||
cy.getByTestId('approve-pending').should('exist');
|
||||
cy.getByTestId('approve-confirmed').should('exist');
|
||||
cy.get(amountField).focus();
|
||||
cy.get(amountField).clear().type('10');
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -115,7 +116,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
|
||||
it('can key to key transfers', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(collateralTab).click();
|
||||
@@ -147,8 +147,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
selectAsset(0);
|
||||
cy.get(amountField).focus();
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -170,7 +170,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Error occurredprocessing response error'
|
||||
'Error occurredcannot estimate gas'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(completeWithdrawalBtn).should(
|
||||
@@ -180,7 +180,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('capsule', { tags: '@slow' }, () => {
|
||||
describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.updateCapsuleMultiSig();
|
||||
});
|
||||
@@ -231,7 +231,7 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerSubmit order - activeTEST.24h+${order.size} @ ${order.price}.00 ${usdcSymbol}`,
|
||||
`Order submittedYour transaction has been confirmed View in block explorerSubmit order - activeTEST.24h+${order.size} @ ${order.price}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
@@ -242,9 +242,9 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
.get(`[data-testid="bid-vol-${rawPrice}"]`)
|
||||
.should('contain.text', order.size);
|
||||
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId('edit', txTimeout).should('contain.text', 'Edit');
|
||||
cy.getByTestId('tab-orders').within(() => {
|
||||
cy.getByTestId('tab-open-orders').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -283,7 +283,9 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
it('can edit order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
const market = this.market;
|
||||
cy.visit(`/#/markets/${market.id}`);
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId('edit').first().should('be.visible').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
||||
cy.get('#limitPrice').focus().clear().type(newPrice);
|
||||
@@ -291,12 +293,12 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+${size} @ ${price}.00 ${usdcSymbol}+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
`Order submittedYour transaction has been confirmed View in block explorerEdit order - activeTEST.24h+${size} @ ${price}.00 ${usdcSymbol}+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -307,18 +309,20 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
|
||||
});
|
||||
});
|
||||
// comment because of bug #2695
|
||||
it.skip('can cancel order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
|
||||
it('can cancel order', function () {
|
||||
const market = this.market;
|
||||
cy.visit(`/#/markets/${market.id}`);
|
||||
cy.getByTestId(openOrdersTab).click();
|
||||
cy.getByTestId('cancel').first().click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
`ConfirmedYour transaction has been confirmed View in block explorerCancel order - cancelledTEST.24h+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
`Order cancelledYour transaction has been confirmed View in block explorerCancel order - cancelledTEST.24h+${size} @ ${newPrice}.00 ${usdcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
|
||||
cy.getByTestId('tab-orders')
|
||||
cy.getByTestId('Closed').click();
|
||||
cy.getByTestId('tab-closed-orders')
|
||||
.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
@@ -348,7 +352,7 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
selectAsset(btcName);
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
@@ -371,19 +375,15 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
'contain.text',
|
||||
'Transaction confirmed'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlockedYour funds have been unlocked for withdrawalView in block explorerWithdraw 1.00 tBTCComplete withdrawal'
|
||||
);
|
||||
|
||||
cy.wrap(null).then(() => {
|
||||
try {
|
||||
cy.getByTestId(completeWithdrawalBtn)
|
||||
.eq(0, txTimeout)
|
||||
.should('not.exist');
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Assertion failed, but we are continuing because this is our wait to complete transaction'
|
||||
);
|
||||
}
|
||||
});
|
||||
cy.get('.ag-center-cols-container')
|
||||
.find('[col-id="status"]')
|
||||
.eq(0, txTimeout)
|
||||
.should('contain.text', 'Completed');
|
||||
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
@@ -404,17 +404,20 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
// comment because of bug #2819
|
||||
// cy.getByTestId('withdraw-dialog-button').click();
|
||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '0')
|
||||
|
||||
cy.getByTestId('withdraw-dialog-button').click({ force: true });
|
||||
cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '7.999');
|
||||
});
|
||||
|
||||
it('approved amount is less than deposit', function () {
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
||||
cy.contains('Use maximum').should('be.visible');
|
||||
cy.get(amountField).clear().type('20000000');
|
||||
@@ -422,11 +425,7 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId('input-error-text').should(
|
||||
'contain.text',
|
||||
'Amount is above approved amount'
|
||||
);
|
||||
cy.getByTestId('reapprove-default').should(
|
||||
'contain.text',
|
||||
'Approve again to deposit more than'
|
||||
`You can't deposit more than you have in your Ethereum wallet`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -440,11 +439,11 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
selectAsset(vegaName);
|
||||
cy.getByTestId('approve-submit').click();
|
||||
cy.getByTestId('approve-confirmed').should(
|
||||
'contain.text',
|
||||
'You can now make deposits in VEGA, up to a maximum of'
|
||||
'You approved deposits of up to VEGA'
|
||||
);
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
@@ -478,9 +477,9 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('Withdrawals').click(txTimeout);
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
|
||||
selectAsset(1);
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
|
||||
@@ -14,210 +14,226 @@ const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
const marketListContent = 'popover-content';
|
||||
|
||||
describe('Console - market list - live env', { tags: '@live' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('shows the market list page', () => {
|
||||
cy.get('main', { timeout: 20000 });
|
||||
|
||||
// Overlay should be shown
|
||||
cy.getByTestId(selectMarketOverlay).should('exist');
|
||||
cy.contains('Select a market to get started').should('be.visible');
|
||||
|
||||
// I expect the market overlay table to contain at least one row
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.should('have.length.greaterThan', 1);
|
||||
|
||||
// each market shown in overlay table contains content under the last price and change fields
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.getByTestId('price')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('redirects to a default market', () => {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(selectMarketOverlay).should('not.exist');
|
||||
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Console - market info - live env', { tags: '@live' }, () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
});
|
||||
const titles = ['Market data', 'Market specification', 'Market governance'];
|
||||
const subtitles = [
|
||||
'Current fees',
|
||||
'Market price',
|
||||
'Market volume',
|
||||
'Insurance pool',
|
||||
'Key details',
|
||||
'Instrument',
|
||||
'Settlement asset',
|
||||
'Metadata',
|
||||
'Risk model',
|
||||
'Risk parameters',
|
||||
'Risk factors',
|
||||
'Price monitoring bounds 1',
|
||||
'Liquidity monitoring parameters',
|
||||
'Liquidity',
|
||||
'Liquidity price range',
|
||||
'Oracle',
|
||||
'Proposal',
|
||||
];
|
||||
|
||||
it('market info titles are displayed', () => {
|
||||
cy.getByTestId('split-view-view')
|
||||
.find('.text-lg')
|
||||
.each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('market info subtitles are displayed', () => {
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
|
||||
cy.wrap(element).should('have.text', subtitles[index]);
|
||||
describe(
|
||||
'Console - market list - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly liquidity in trading tab', () => {
|
||||
cy.getByTestId('Liquidity').click();
|
||||
cy.contains('Loading').should('not.exist');
|
||||
cy.contains('Something went wrong').should('not.exist');
|
||||
cy.contains('Application error').should('not.exist');
|
||||
cy.getByTestId('tab-liquidity').within(() => {
|
||||
cy.get('[col-id="party.id"]').eq(1).should('not.be.empty');
|
||||
it('shows the market list page', () => {
|
||||
cy.get('main', { timeout: 20000 });
|
||||
|
||||
// Overlay should be shown
|
||||
cy.getByTestId(selectMarketOverlay).should('exist');
|
||||
cy.contains('Select a market to get started').should('be.visible');
|
||||
|
||||
// I expect the market overlay table to contain at least one row
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.should('have.length.greaterThan', 1);
|
||||
|
||||
// each market shown in overlay table contains content under the last price and change fields
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table tr')
|
||||
.getByTestId('price')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Console - market summary - live env', { tags: '@live' }, () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
it('redirects to a default market', () => {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(selectMarketOverlay).should('not.exist');
|
||||
|
||||
it('must display market name', () => {
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('must see market expiry', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
describe(
|
||||
'Console - market info - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
});
|
||||
const titles = ['Market data', 'Market specification', 'Market governance'];
|
||||
const subtitles = [
|
||||
'Current fees',
|
||||
'Market price',
|
||||
'Market volume',
|
||||
'Insurance pool',
|
||||
'Key details',
|
||||
'Instrument',
|
||||
'Settlement asset',
|
||||
'Metadata',
|
||||
'Risk model',
|
||||
'Risk parameters',
|
||||
'Risk factors',
|
||||
'Price monitoring bounds 1',
|
||||
'Liquidity monitoring parameters',
|
||||
'Liquidity',
|
||||
'Liquidity price range',
|
||||
'Oracle',
|
||||
'Proposal',
|
||||
];
|
||||
|
||||
it('market info titles are displayed', () => {
|
||||
cy.getByTestId('split-view-view')
|
||||
.find('.text-lg')
|
||||
.each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('market info subtitles are displayed', () => {
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
|
||||
cy.wrap(element).should('have.text', subtitles[index]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market price', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
it('renders correctly liquidity in trading tab', () => {
|
||||
cy.getByTestId('Liquidity').click();
|
||||
cy.contains('Loading').should('not.exist');
|
||||
cy.contains('Something went wrong').should('not.exist');
|
||||
cy.contains('Application error').should('not.exist');
|
||||
cy.getByTestId('tab-liquidity').within(() => {
|
||||
cy.get('[col-id="party.id"]').eq(1).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('must see market change', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
describe(
|
||||
'Console - market summary - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
it('must display market name', () => {
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
|
||||
it('must see market expiry', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
it('must see market price', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market mode', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
it('must see market change', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Console - markets table - live env', { tags: '@live' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
cy.getByTestId('sparkline-svg').should('be.visible');
|
||||
});
|
||||
|
||||
it('renders market list drop down', () => {
|
||||
openMarketDropDown();
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="price"]')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="trading-mode-col"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="taker-fee"]')
|
||||
.should('contain.text', '%');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-volume"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-name"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to select market from dropdown', () => {
|
||||
cy.getByTestId('popover-trigger')
|
||||
.invoke('text')
|
||||
.then((marketName) => {
|
||||
openMarketDropDown();
|
||||
cy.get('[data-testid^=market-link]').eq(1).click();
|
||||
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
|
||||
it('must see market mode', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe(
|
||||
'Console - markets table - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
cy.getByTestId('sparkline-svg').should('be.visible');
|
||||
});
|
||||
|
||||
it('renders market list drop down', () => {
|
||||
openMarketDropDown();
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="price"]')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="trading-mode-col"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="taker-fee"]')
|
||||
.should('contain.text', '%');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-volume"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-name"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to select market from dropdown', () => {
|
||||
cy.getByTestId('popover-trigger')
|
||||
.invoke('text')
|
||||
.then((marketName) => {
|
||||
openMarketDropDown();
|
||||
cy.get('[data-testid^=market-link]').eq(1).click();
|
||||
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function openMarketDropDown() {
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
|
||||
@@ -9,9 +9,8 @@ declare global {
|
||||
}
|
||||
|
||||
export function addGetNetworkParameters() {
|
||||
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
|
||||
Cypress.Commands.add('get_network_parameters', () => {
|
||||
const mutation = `
|
||||
const query = `
|
||||
{
|
||||
networkParametersConnection {
|
||||
edges {
|
||||
@@ -26,17 +25,17 @@ export function addGetNetworkParameters() {
|
||||
method: 'POST',
|
||||
url: `http://localhost:3008/graphql`,
|
||||
body: {
|
||||
query: mutation,
|
||||
query,
|
||||
},
|
||||
headers: { 'content-type': 'application/json' },
|
||||
})
|
||||
.its('body.data.networkParametersConnection.edges')
|
||||
.then(function (response) {
|
||||
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
|
||||
const object = response.reduce(function (r, e) {
|
||||
const { value, key } = e.node;
|
||||
r[key] = value;
|
||||
return r;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const object = response.reduce(function (obj: any, edge: any) {
|
||||
const { value, key } = edge.node;
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
}, {});
|
||||
return cy.wrap(object);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ export const getIntentBorder = (intent = Intent.None) => {
|
||||
'border-warning': intent === Intent.Warning,
|
||||
'border-neutral-500': intent === Intent.None,
|
||||
'border-vega-blue-300': intent === Intent.Primary,
|
||||
'border-success': intent === Intent.Success,
|
||||
'border-vega-green': intent === Intent.Success,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import once from 'lodash/once';
|
||||
import { format } from 'date-fns';
|
||||
import { getUserLocale } from '../get-user-locale';
|
||||
import { utcToZonedTime, format as tzFormat } from 'date-fns-tz';
|
||||
|
||||
export const isValidDate = (date: Date) =>
|
||||
date instanceof Date && !isNaN(date.getTime());
|
||||
@@ -53,15 +53,24 @@ export const formatForInput = (date: Date) => {
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}:${secs}`;
|
||||
};
|
||||
|
||||
/** Returns a user's local time zone abbreviation */
|
||||
export const getTimeZoneAbbreviation = (date: Date) => {
|
||||
const formatter = new Intl.DateTimeFormat(undefined, {
|
||||
timeZoneName: 'short',
|
||||
});
|
||||
const parts = formatter.formatToParts(date);
|
||||
const timeZoneAbbreviation = parts.find(
|
||||
(part) => part.type === 'timeZoneName'
|
||||
)?.value;
|
||||
return timeZoneAbbreviation || '';
|
||||
};
|
||||
|
||||
/** Format a user's local date and time with the time zone abbreviation */
|
||||
export const formatDateWithLocalTimezone = (
|
||||
date: Date,
|
||||
formatStr = 'dd MMMM yyyy HH:mm (z)'
|
||||
formatStr = 'dd MMMM yyyy HH:mm'
|
||||
) => {
|
||||
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const localDatetime = utcToZonedTime(date, userTimeZone);
|
||||
|
||||
return tzFormat(localDatetime, formatStr, {
|
||||
timeZone: userTimeZone,
|
||||
});
|
||||
const formattedDate = format(date, formatStr);
|
||||
const timeZoneAbbreviation = getTimeZoneAbbreviation(date);
|
||||
return `${formattedDate} (${timeZoneAbbreviation})`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user