Try   HackMD

Day 3 #butmakeitkawaii

Prompt:
I would like you to help me write a test for the following React component that lives in src/components. Use your knowledge of the tools and subject of TDD to choose the most appropriate library to use to test the ability of this component to render and handle the data correctly.

import { useEffect, useState } from 'react';

import {
  Box,
  Flex,
  Spinner
  Stat,
  StatLabel,
  StatNumber,
  Text,
  Tooltip,
  useColorModeValue,
} from '@chakra-ui/react';
import { formatEther } from '@ethersproject/units';
import { useEtherBalance, useToken, useTokenBalance } from '@usedapp/core';
import { BigNumber } from 'ethers';

import { TokenProps } from '@daoism/lib/constants';
import { displayBalance, getValidChainName } from '@daoism/lib/helpers';
export interface BalanceCardProps {
  token: string;
  user: string;
}
export function BalanceCard(props: BalanceCardProps) {
  const [infoLoading, setInfoLoading] = useState<boolean>(true);
  const { token, user } = props;
  const tokenInfo = useToken(token);
  const balance: BigNumber | undefined = useTokenBalance(token, user);

  useEffect(() => {
    try {
      if (user && balance) {
        setInfoLoading(false);
      }
    } catch (error) {
      // eslint-disable-next-line no-console
      console.log('Balance card error', error);
      setInfoLoading(false);
    }
  }, [balance, token, tokenInfo, user]);

  return (
    <Stat
      px={{ base: 2, md: 4 }}
      py="5"
      shadow="lg"
      borderWidth={0}
      borderColor={useColorModeValue('gray.300', 'gray.700')}
      rounded="lg"
      transition="all 0.3s"
      _hover={{
        transform: 'translateY(2px)',
        shadow: 'xl',
        cursor: 'pointer',
      }}
    >
      <Flex justifyContent="space-between">
        <Box pl={{ base: 2, md: 4 }}>
          <StatLabel fontWeight="medium" color={useColorModeValue('blue.500', 'blue.300')}>
            {tokenInfo?.name}
          </StatLabel>
          <Tooltip
            label={`${tokenInfo?.symbol}: ${displayBalance(balance, tokenInfo).full}`}
            hasArrow
            aria-label={`Balance upto to ${tokenInfo?.decimals} decimals`}
          >
            <StatNumber fontSize="2xl" fontWeight="medium">
              {infoLoading && !balance && <Spinner size="md" />}
              {!infoLoading && balance !== undefined && displayBalance(balance, tokenInfo).short}
            </StatNumber>
          </Tooltip>
        </Box>
        {/* TODO: Add some token info from the CoinGecko API and a link to the token page on CG */}
      </Flex>
    </Stat>
  );
}

GPT Response:
Certainly, this component can be tested using the React Testing Library and Jest. For the purposes of this example, let's focus on testing that the component renders correctly and handles data as expected. Given the asynchronous nature of the data fetch in the component, we'll also use waitFor from the React Testing Library.

First, let's install the necessary dependencies:

npm install --save-dev @testing-library/react @testing-library/jest-dom jest

Then, we can write a test. For the sake of simplicity, let's write a test that checks whether the component renders a spinner while loading data, and then correctly displays the token name and balance once the data is loaded.

import { render, waitFor } from '@testing-library/react';
import { BalanceCard } from './BalanceCard';
import { ethers } from 'ethers';
import { useToken, useTokenBalance } from '@usedapp/core';
import { formatEther } from '@ethersproject/units';

// Mock the hooks from @usedapp/core
jest.mock('@usedapp/core', () => ({
  useToken: jest.fn(),
  useTokenBalance: jest.fn(),
}));

test('renders loading state and then token info', async () => {
  // Mock data
  const mockToken = 'tokenAddress';
  const mockUser = 'userAddress';
  const mockTokenInfo = {
    name: 'Mock Token',
    symbol: 'MKT',
    decimals: 18,
  };
  const mockBalance = ethers.utils.parseEther('1.0');  // 1.0 ether

  // Initial render: loading state
  (useToken as jest.Mock).mockReturnValue(mockTokenInfo);
  (useTokenBalance as jest.Mock).mockReturnValue(undefined);  // Simulate loading state
  const { getByText, getByRole, rerender } = render(<BalanceCard token={mockToken} user={mockUser} />);
  expect(getByRole('progressbar')).toBeInTheDocument();  // Spinner should be in the document

  // Rerender with loaded data
  (useTokenBalance as jest.Mock).mockReturnValue(mockBalance);
  rerender(<BalanceCard token={mockToken} user={mockUser} />);
  
  // Use waitFor to wait for the asynchronous updates
  await waitFor(() => {
    expect(getByText(mockTokenInfo.name)).toBeInTheDocument();
    expect(getByText(formatEther(mockBalance))).toBeInTheDocument();
  });
});

This is a simple example and real-world testing might involve more complex scenarios and edge cases. For example, you might want to test how the component handles errors, or what happens when the user or token props change.