Skip to content

Latest commit

 

History

History
289 lines (218 loc) · 6.63 KB

File metadata and controls

289 lines (218 loc) · 6.63 KB

Testing Guide

This document outlines the testing strategy and setup for the STO Web Client.

Current Status

Test Coverage: 0%
Test Framework: Not configured
Priority: High - Critical gap

Recommended Testing Setup

1. Unit Testing

Setup

yarn add -D jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom

Configuration

Create jest.config.js:

const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jest-environment-jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/**/*.stories.{ts,tsx}',
    '!src/**/__tests__/**',
  ],
}

module.exports = createJestConfig(customJestConfig)

Example Test

Create src/lib/utils/__tests__/validation.test.ts:

import { isValidAddress, validateAddress, validateAmount } from '../validation';

describe('validation utilities', () => {
  describe('isValidAddress', () => {
    it('should return true for valid addresses', () => {
      expect(isValidAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb')).toBe(true);
    });

    it('should return false for invalid addresses', () => {
      expect(isValidAddress('invalid')).toBe(false);
      expect(isValidAddress('')).toBe(false);
    });
  });

  describe('validateAddress', () => {
    it('should return address for valid input', () => {
      const address = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';
      expect(validateAddress(address)).toBe(address);
    });

    it('should throw for invalid address', () => {
      expect(() => validateAddress('invalid')).toThrow('Invalid Ethereum address');
    });
  });

  describe('validateAmount', () => {
    it('should return number for valid amount', () => {
      expect(validateAmount('100')).toBe(100);
      expect(validateAmount(100)).toBe(100);
    });

    it('should throw for invalid amount', () => {
      expect(() => validateAmount(-1)).toThrow('Amount must be a positive number');
      expect(() => validateAmount('invalid')).toThrow();
    });
  });
});

2. Component Testing

Example Component Test

Create src/components/token/__tests__/GetTokenBalance.test.tsx:

import { render, screen, waitFor } from '@testing-library/react';
import { useAccount, useContractRead } from 'wagmi';
import GetTokenBalance from '../getTokenBalance';

jest.mock('wagmi');

describe('GetTokenBalance', () => {
  const mockUseAccount = useAccount as jest.MockedFunction<typeof useAccount>;
  const mockUseContractRead = useContractRead as jest.MockedFunction<typeof useContractRead>;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should show connect message when wallet not connected', () => {
    mockUseAccount.mockReturnValue({
      address: undefined,
      isConnected: false,
    } as any);

    mockUseContractRead.mockReturnValue({
      data: undefined,
      isLoading: false,
      isError: false,
    } as any);

    render(<GetTokenBalance />);
    expect(screen.getByText(/connect your wallet/i)).toBeInTheDocument();
  });

  it('should display token balance when connected', async () => {
    mockUseAccount.mockReturnValue({
      address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
      isConnected: true,
    } as any);

    mockUseContractRead.mockReturnValue({
      data: { toString: () => '1000' },
      isLoading: false,
      isError: false,
    } as any);

    render(<GetTokenBalance />);
    await waitFor(() => {
      expect(screen.getByText('1000')).toBeInTheDocument();
    });
  });
});

3. Integration Testing

Setup MSW (Mock Service Worker)

yarn add -D msw

Example Integration Test

import { render, screen, waitFor } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import IssueToken from '../components/token/issueToken';

// Mock wagmi hooks
jest.mock('wagmi');

const server = setupServer(
  // Add request handlers
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('IssueToken Integration', () => {
  it('should submit form and execute transaction', async () => {
    // Test implementation
  });
});

4. E2E Testing

Setup Playwright

yarn add -D @playwright/test
npx playwright install

Configuration

Create playwright.config.ts:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Example E2E Test

Create e2e/wallet-connection.spec.ts:

import { test, expect } from '@playwright/test';

test('should connect wallet', async ({ page }) => {
  await page.goto('/');
  
  // Click connect button
  await page.click('text=Connect Wallet');
  
  // Wait for wallet modal
  await expect(page.locator('[data-testid="wallet-modal"]')).toBeVisible();
  
  // Select MetaMask
  await page.click('text=MetaMask');
  
  // Verify connection
  await expect(page.locator('text=Connected')).toBeVisible();
});

Test Coverage Goals

  • Unit Tests: 80%+ coverage for utilities and hooks
  • Component Tests: 70%+ coverage for components
  • Integration Tests: Critical user flows
  • E2E Tests: Main user journeys

Running Tests

# Run all tests
yarn test

# Run in watch mode
yarn test:watch

# Run with coverage
yarn test:coverage

# Run E2E tests
yarn test:e2e

Test Best Practices

  1. Test behavior, not implementation - Focus on what the code does, not how
  2. Use descriptive test names - Clear test descriptions
  3. Keep tests isolated - Each test should be independent
  4. Mock external dependencies - Don't make real API calls in tests
  5. Test edge cases - Include error cases and boundary conditions
  6. Maintain test data - Use factories or fixtures for test data

Next Steps

  1. Set up Jest and React Testing Library
  2. Write tests for utility functions
  3. Write tests for custom hooks
  4. Write tests for components
  5. Set up E2E testing
  6. Add to CI/CD pipeline