From 076d0c336cc77a577229292d9a4947e4cc3facfd Mon Sep 17 00:00:00 2001 From: sam-keen Date: Sun, 20 Aug 2023 21:22:14 +0100 Subject: [PATCH] fix(3522): better vote error handling --- .../components/proposal/proposal.tsx | 3 +- .../vega-transaction-dialog.spec.tsx | 110 ++++++++++++++++++ .../vote-details/vote-buttons.spec.tsx | 9 +- .../components/vote-details/vote-buttons.tsx | 10 +- .../components/vote-details/vote-details.tsx | 5 +- .../vote-details/vote-transaction-dialog.tsx | 8 +- 6 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 apps/governance/src/routes/proposals/components/vote-details/vega-transaction-dialog.spec.tsx diff --git a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx index 076ac0fd5..64790df1f 100644 --- a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx +++ b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx @@ -55,7 +55,7 @@ export const Proposal = ({ mostRecentlyEnactedAssociatedMarketProposal, }: ProposalProps) => { const { t } = useTranslation(); - const { submit, Dialog, finalizedVote } = useVoteSubmit(); + const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit(); const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote); if (!proposal) { @@ -215,6 +215,7 @@ export const Proposal = ({ } submit={submit} dialog={Dialog} + transaction={transaction} voteState={voteState} voteDatetime={voteDatetime} /> diff --git a/apps/governance/src/routes/proposals/components/vote-details/vega-transaction-dialog.spec.tsx b/apps/governance/src/routes/proposals/components/vote-details/vega-transaction-dialog.spec.tsx new file mode 100644 index 000000000..6ef513333 --- /dev/null +++ b/apps/governance/src/routes/proposals/components/vote-details/vega-transaction-dialog.spec.tsx @@ -0,0 +1,110 @@ +import { render, screen } from '@testing-library/react'; +import { VoteTransactionDialog } from './vote-transaction-dialog'; +import { VoteState } from './use-user-vote'; +import { VegaTxStatus } from '@vegaprotocol/wallet'; + +describe('VoteTransactionDialog', () => { + const mockTransactionDialog = jest.fn(({ title, content }) => ( +
+
{title}
+
{content?.Complete}
+
+ )); + + it('renders without crashing', () => { + render( + + ); + + expect(screen.getByTestId('vote-transaction-dialog')).toBeInTheDocument(); + }); + + it('renders with txRequested title when voteState is Requested', () => { + render( + + ); + + expect(screen.getByText('txRequested')).toBeInTheDocument(); + }); + + it('renders with votePending title when voteState is Pending', () => { + render( + + ); + + expect(screen.getByText('votePending')).toBeInTheDocument(); + }); + + it('renders with no title when voteState is neither Requested nor Pending', () => { + render( + + ); + + expect(screen.queryByText('txRequested')).not.toBeInTheDocument(); + expect(screen.queryByText('votePending')).not.toBeInTheDocument(); + }); + + it('renders custom error message when voteState is Failed and error message exists', () => { + render( + + ); + + expect(screen.getByText('Custom error test message')).toBeInTheDocument(); + }); + + it('renders default error message when voteState is failed and no error message exists on the tx', () => { + render( + + ); + + expect(screen.getByText('voteError')).toBeInTheDocument(); + }); + + it('renders default ui (i.e. not error) when not in a failed state', () => { + render( + + ); + + expect(screen.queryByText('voteError')).not.toBeInTheDocument(); + }); +}); diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx index 7f1ae36b6..9eab79c44 100644 --- a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx +++ b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import BigNumber from 'bignumber.js'; import { VoteButtons } from './vote-buttons'; import { VoteState } from './use-user-vote'; @@ -24,6 +24,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(1)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -47,6 +48,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(1)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -81,6 +83,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(1)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -105,6 +108,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(0)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -132,6 +136,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(1)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -159,6 +164,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(10)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> @@ -183,6 +189,7 @@ describe('Vote buttons', () => { currentStakeAvailable={new BigNumber(10)} dialog={() =>
Blah
} submit={() => Promise.resolve()} + transaction={null} /> diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx index 919c6b423..7199c9365 100644 --- a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx +++ b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx @@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote'; import { ProposalMinRequirements, ProposalUserAction } from '../shared'; import { VoteTransactionDialog } from './vote-transaction-dialog'; import { useVoteButtonsQuery } from './__generated__/Stake'; -import type { DialogProps } from '@vegaprotocol/wallet'; +import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet'; interface VoteButtonsContainerProps { voteState: VoteState | null; @@ -27,6 +27,7 @@ interface VoteButtonsContainerProps { minVoterBalance: string | null | undefined; spamProtectionMinTokens: string | null | undefined; submit: (voteValue: VoteValue, proposalId: string | null) => Promise; + transaction: VegaTxState | null; dialog: (props: DialogProps) => JSX.Element; className?: string; } @@ -67,6 +68,7 @@ export const VoteButtons = ({ minVoterBalance, spamProtectionMinTokens, submit, + transaction, dialog: Dialog, }: VoteButtonsProps) => { const { t } = useTranslation(); @@ -208,7 +210,11 @@ export const VoteButtons = ({

) )} - + ); }; diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx index db36adfce..b8fbb7c0a 100644 --- a/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx +++ b/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx @@ -12,7 +12,7 @@ import { VoteButtonsContainer } from './vote-buttons'; import { SubHeading } from '../../../../components/heading'; import { ProposalType } from '../proposal/proposal'; import type { VoteValue } from '@vegaprotocol/types'; -import type { DialogProps } from '@vegaprotocol/wallet'; +import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { VoteState } from './use-user-vote'; @@ -22,6 +22,7 @@ interface VoteDetailsProps { minVoterBalance: string | null | undefined; spamProtectionMinTokens: string | null | undefined; proposalType: ProposalType | null; + transaction: VegaTxState | null; submit: (voteValue: VoteValue, proposalId: string | null) => Promise; dialog: (props: DialogProps) => JSX.Element; voteState: VoteState | null; @@ -34,6 +35,7 @@ export const VoteDetails = ({ spamProtectionMinTokens, proposalType, submit, + transaction, dialog, voteState, voteDatetime, @@ -228,6 +230,7 @@ export const VoteDetails = ({ spamProtectionMinTokens={spamProtectionMinTokens} className="flex" submit={submit} + transaction={transaction} dialog={dialog} /> ) diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-transaction-dialog.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-transaction-dialog.tsx index 59f04d705..c0d9b3da9 100644 --- a/apps/governance/src/routes/proposals/components/vote-details/vote-transaction-dialog.tsx +++ b/apps/governance/src/routes/proposals/components/vote-details/vote-transaction-dialog.tsx @@ -1,9 +1,10 @@ import { t } from '@vegaprotocol/i18n'; import { VoteState } from './use-user-vote'; -import type { DialogProps } from '@vegaprotocol/wallet'; +import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet'; interface VoteTransactionDialogProps { voteState: VoteState; + transaction: VegaTxState | null; TransactionDialog: (props: DialogProps) => JSX.Element; } @@ -20,12 +21,15 @@ const dialogTitle = (voteState: VoteState): string | undefined => { export const VoteTransactionDialog = ({ voteState, + transaction, TransactionDialog, }: VoteTransactionDialogProps) => { // Render a custom message if the voting fails otherwise // pass undefined so that the default vega transaction dialog UI gets used const customMessage = - voteState === VoteState.Failed ?

{t('voteError')}

: undefined; + voteState === VoteState.Failed ? ( +

{transaction?.error?.message || t('voteError')}

+ ) : undefined; return (