Skip to content

Commit

Permalink
test: vote detail modal
Browse files Browse the repository at this point in the history
  • Loading branch information
fmorency committed Nov 18, 2024
1 parent 16c9c64 commit abb9199
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 8 deletions.
115 changes: 115 additions & 0 deletions components/groups/modals/__tests__/voteDetailsModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { describe, test, expect, jest, mock, afterEach } from 'bun:test';
import React from 'react';
import { screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
import VoteDetailsModal from '../voteDetailsModal';
import {
ProposalSDKType,
MemberSDKType,
VoteSDKType,
ProposalStatus,
ProposalExecutorResult,
} from '@liftedinit/manifestjs/dist/codegen/cosmos/group/v1/types';
import { QueryTallyResultResponseSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/group/v1/query';
import matchers from '@testing-library/jest-dom/matchers';
import { renderWithChainProvider } from '@/tests/render';
import {
manifestAddr1,
mockGroupFormData,
mockMembers,
mockProposals,
mockTally,
mockVotes,
} from '@/tests/mock';

expect.extend(matchers);

mock.module('react-apexcharts', () => ({
default: jest.fn(),
}));

mock.module('@cosmos-kit/react', () => ({
useChain: jest.fn().mockReturnValue({
address: mockProposals['test_policy_address'][0].proposers[0],
chain: { fees: null },
}),
}));

const mockProposal = mockProposals['test_policy_address'][0];

describe('VoteDetailsModal', () => {
const defaultProps = {
tallies: mockTally,
votes: mockVotes,
members: mockMembers,
proposal: mockProposal,
onClose: jest.fn(),
modalId: 'voteDetailsModal',
refetchVotes: jest.fn(),
refetchTally: jest.fn(),
refetchProposals: jest.fn(),
};

afterEach(() => {
mock.restore();
cleanup();
});

test('renders the component with provided props', () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
expect(screen.getByText(`#${mockProposal.id.toString()}`)).toBeInTheDocument();
expect(screen.getByText(mockProposal.title)).toBeInTheDocument();
expect(screen.getByText('SUMMARY')).toBeInTheDocument();
expect(screen.getByText(mockProposal.summary)).toBeInTheDocument();
});

test('renders the tally chart', () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
expect(screen.getByLabelText('chart-tally')).toBeInTheDocument();
});

test('renders voting countdown timer', () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
expect(screen.getByLabelText('voting-countdown-1')).toBeInTheDocument();
expect(screen.getByLabelText('voting-countdown-2')).toBeInTheDocument();
});

test('renders messages section with correct data', () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
expect(screen.getByText('MESSAGES')).toBeInTheDocument();
expect(screen.getByText('Send')).toBeInTheDocument();
expect(screen.getByText('from_address:')).toBeInTheDocument();
expect(screen.getByText('to_address:')).toBeInTheDocument();
});

test('conditionally renders execute button when proposal is accepted', () => {
const props = {
...defaultProps,
proposal: {
...mockProposal,
status: 'PROPOSAL_STATUS_ACCEPTED',
executor_result: 'PROPOSAL_EXECUTOR_RESULT_NOT_RUN',
},
};
renderWithChainProvider(<VoteDetailsModal {...props} />);
expect(screen.getByText('Execute')).toBeInTheDocument();
});

test('conditionally renders vote button when proposal is open and user has not voted', () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
expect(screen.getByText('Vote')).toBeInTheDocument();
});

test('does not render vote button when user has already voted', () => {
const props = { ...defaultProps, votes: [{ ...mockVotes[0], voter: 'proposer1' }] };
renderWithChainProvider(<VoteDetailsModal {...props} />);
const btn = screen.getByLabelText('action-btn');
expect(btn.textContent).not.toBe('Vote');
});

test('handles vote button click and opens voting modal', async () => {
renderWithChainProvider(<VoteDetailsModal {...defaultProps} />);
const voteButton = screen.getByText('Vote');
fireEvent.click(voteButton);
await waitFor(() => expect(screen.getByLabelText('vote-modal')).toBeInTheDocument());
});
});
7 changes: 4 additions & 3 deletions components/groups/modals/voteDetailsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ function VoteDetailsModal({
})}
</div>
</div>
<div className="hidden md:block w-full">
<div aria-label="voting-countdown-1" className="hidden md:block w-full">
<p className="text-sm font-light text-gray-500 dark:text-gray-400 mb-2">
VOTING COUNTDOWN
</p>
Expand All @@ -508,7 +508,7 @@ function VoteDetailsModal({
<div className="flex flex-col w-full relative flex-grow items-start justify-start p-6 space-y-6">
<div className="w-full">
<p className="text-sm font-light text-gray-500 dark:text-gray-400 mb-2">TALLY</p>
<div className="bg-base-300 rounded-[12px] w-full">
<div aria-label="chart-tally" className="bg-base-300 rounded-[12px] w-full">
<Chart options={options} series={[{ data: chartData }]} type="bar" height={200} />
</div>
</div>
Expand Down Expand Up @@ -556,7 +556,7 @@ function VoteDetailsModal({
</div>
</div>
</div>
<div className="md:hidden block w-full">
<div aria-label="voting-countdown-2" className="md:hidden block w-full">
<p className="text-sm font-light text-gray-500 dark:text-gray-400 mb-2">
VOTING COUNTDOWN
</p>
Expand All @@ -565,6 +565,7 @@ function VoteDetailsModal({
<div className="w-full relative">
{getButtonState.action && (
<button
aria-label="action-btn"
disabled={
isSigning ||
(getButtonState.action === 'remove' &&
Expand Down
6 changes: 5 additions & 1 deletion components/groups/modals/voteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ function VotingPopup({ proposalId, refetch }: { proposalId: bigint; refetch: ()

return (
<>
<dialog id="vote_modal" className="modal modal-bottom sm:modal-middle z-[1000]">
<dialog
aria-label="vote-modal"
id="vote_modal"
className="modal modal-bottom sm:modal-middle z-[1000]"
>
<form method="dialog" className="modal-box relative dark:bg-[#1D192D] bg-[#FFFFFF]">
<h3 className="font-bold text-lg mb-4">
Cast Your Vote for Proposal #{proposalId.toString()}
Expand Down
46 changes: 42 additions & 4 deletions tests/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { ExtendedValidatorSDKType, TransactionGroup } from '@/components';
import { CombinedBalanceInfo } from '@/utils/types';
import { ExtendedGroupType } from '@/hooks';
import {
MemberSDKType,
ProposalExecutorResult,
ProposalSDKType,
ProposalStatus,
VoteOption,
} from '@liftedinit/manifestjs/dist/codegen/cosmos/group/v1/types';
import { MetadataSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank';
import { FormData, ProposalFormData } from '@/helpers';
Expand Down Expand Up @@ -454,6 +456,45 @@ export const mockProposals: { [key: string]: ProposalSDKType[] } = {
],
};

export const mockVotes = [
{
proposal_id: 1n,
voter: manifestAddr1,
option: VoteOption.VOTE_OPTION_YES,
metadata: 'metadata1',
submit_time: new Date(),
},
{
proposal_id: 1n,
voter: manifestAddr2,
option: VoteOption.VOTE_OPTION_YES,
metadata: 'metadata2',
submit_time: new Date(),
},
];

export const mockTally = {
tally: {
yes_count: '10',
no_count: '5',
abstain_count: '2',
no_with_veto_count: '1',
},
};

export const mockMembers: MemberSDKType[] = [
{
address: manifestAddr1,
name: 'Member 1',
weight: '1',
},
{
address: manifestAddr2,
name: 'Member 2',
weight: '2',
},
];

// TODO: Re-use mockDenomMeta1 here
export const mockTokenFormData = {
name: 'Name Test Token',
Expand All @@ -480,10 +521,7 @@ export const mockGroupFormData: FormData = {

votingPeriod: { seconds: BigInt(3600), nanos: 0 },
votingThreshold: '2',
members: [
{ address: manifestAddr1, name: 'Member 1', weight: '1' },
{ address: manifestAddr2, name: 'Member 2', weight: '2' },
],
members: mockMembers,
};
export const mockProposalFormData: ProposalFormData = {
title: 'Test Proposal',
Expand Down

0 comments on commit abb9199

Please sign in to comment.