Skip to main content
ClaudeWave
Slash Command65 repo starsupdated yesterday

test-ts

Run TypeScript tests for Solana frontends and Anchor programs

Install in Claude Code
Copy
mkdir -p ~/.claude/commands && curl -fsSL https://raw.githubusercontent.com/solanabr/solana-ai-kit/HEAD/.claude/commands/test-ts.md -o ~/.claude/commands/test-ts.md
Then start a new Claude Code session; the slash command loads automatically.

test-ts.md

You are running TypeScript tests. This command covers Anchor program tests, frontend component tests, and integration tests.

## Related Skills

- [testing.md](../skills/testing.md) - Testing strategy details
- [frontend-framework-kit.md](../skills/frontend-framework-kit.md) - React/Next.js patterns
- [programs/anchor.md](../skills/ext/solana-dev/skill/references/programs/anchor.md) - Anchor test patterns

## Step 1: Identify Test Type

```bash
echo "🔍 Detecting TypeScript test configuration..."

# Check for Anchor tests
if [ -f "Anchor.toml" ] && [ -d "tests" ]; then
    echo "⚓ Anchor TypeScript tests detected"
fi

# Check for Vitest
if grep -q "vitest" package.json 2>/dev/null; then
    echo "⚡ Vitest configured"
fi

# Check for Jest
if grep -q "jest" package.json 2>/dev/null; then
    echo "🃏 Jest configured"
fi

# Check for Playwright
if grep -q "playwright" package.json 2>/dev/null; then
    echo "🎭 Playwright E2E tests configured"
fi
```

---

## Anchor Program Tests

### Run Anchor Tests

```bash
echo "⚓ Running Anchor TypeScript tests..."

# Build first
anchor build

# Run all tests
anchor test

# Skip rebuild (faster iteration)
anchor test --skip-build

# Run specific test file
anchor test tests/vault.ts

# Run with logs
RUST_LOG=debug anchor test
```

### Anchor Test Pattern

```typescript
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { MyProgram } from "../target/types/my_program";
import { expect } from "chai";

describe("my_program", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.MyProgram as Program<MyProgram>;

  it("initializes vault", async () => {
    const [vaultPda] = anchor.web3.PublicKey.findProgramAddressSync(
      [Buffer.from("vault"), provider.wallet.publicKey.toBuffer()],
      program.programId
    );

    await program.methods
      .initialize()
      .accounts({
        vault: vaultPda,
        authority: provider.wallet.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId,
      })
      .rpc();

    const vault = await program.account.vault.fetch(vaultPda);
    expect(vault.authority.toString()).to.equal(
      provider.wallet.publicKey.toString()
    );
  });

  it("deposits funds", async () => {
    const [vaultPda] = anchor.web3.PublicKey.findProgramAddressSync(
      [Buffer.from("vault"), provider.wallet.publicKey.toBuffer()],
      program.programId
    );

    const depositAmount = new anchor.BN(1_000_000_000); // 1 SOL

    await program.methods
      .deposit(depositAmount)
      .accounts({
        vault: vaultPda,
        authority: provider.wallet.publicKey,
      })
      .rpc();

    const vault = await program.account.vault.fetch(vaultPda);
    expect(vault.balance.toNumber()).to.equal(depositAmount.toNumber());
  });

  it("fails with insufficient funds", async () => {
    const [vaultPda] = anchor.web3.PublicKey.findProgramAddressSync(
      [Buffer.from("vault"), provider.wallet.publicKey.toBuffer()],
      program.programId
    );

    try {
      await program.methods
        .withdraw(new anchor.BN(999_000_000_000)) // More than balance
        .accounts({
          vault: vaultPda,
          authority: provider.wallet.publicKey,
        })
        .rpc();
      expect.fail("Should have thrown");
    } catch (err) {
      expect(err.message).to.include("InsufficientFunds");
    }
  });
});
```

### LiteSVM TypeScript Tests

For faster tests without validator:

```bash
npm install --save-dev litesvm
```

```typescript
import { LiteSVM } from 'litesvm';
import { PublicKey, Transaction, Keypair } from '@solana/web3.js';

describe("litesvm tests", () => {
  let svm: LiteSVM;
  const programId = new PublicKey("YourProgramId...");

  beforeAll(() => {
    svm = new LiteSVM();
    svm.addProgramFromFile(programId, "target/deploy/program.so");
  });

  it("processes instruction", () => {
    const payer = Keypair.generate();
    svm.airdrop(payer.publicKey, 1_000_000_000);

    const tx = new Transaction();
    tx.recentBlockhash = svm.latestBlockhash();
    tx.add(/* your instruction */);
    tx.sign(payer);

    const result = svm.sendTransaction(tx);
    expect(result.err).toBeNull();
  });
});
```

---

## Frontend Component Tests

### Vitest Setup

```bash
echo "⚡ Running Vitest tests..."
npm run test
# or
npx vitest run
```

### React Component Test

```typescript
// components/__tests__/WalletButton.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { WalletButton } from '../WalletButton';

// Mock wallet hooks
vi.mock('@solana/wallet-adapter-react', () => ({
  useWallet: () => ({
    connected: false,
    connect: vi.fn(),
    disconnect: vi.fn(),
    publicKey: null,
  }),
}));

describe('WalletButton', () => {
  it('renders connect button when not connected', () => {
    render(<WalletButton />);
    expect(screen.getByText('Connect Wallet')).toBeInTheDocument();
  });

  it('calls connect on click', async () => {
    const { useWallet } = await import('@solana/wallet-adapter-react');
    const mockConnect = vi.fn();
    vi.mocked(useWallet).mockReturnValue({
      connected: false,
      connect: mockConnect,
      disconnect: vi.fn(),
      publicKey: null,
    });

    render(<WalletButton />);
    fireEvent.click(screen.getByText('Connect Wallet'));
    expect(mockConnect).toHaveBeenCalled();
  });
});
```

### Hook Testing

```typescript
// hooks/__tests__/useBalance.test.tsx
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { useBalance } from '../useBalance';

describe('useBalance', () => {
  it('fetches balance for address', async () => {
    const mockAddress = 'So11111111111111111111111111111111111111112';

    const { result } = renderHook(() => useBalance(mockAddress));

    await waitFor(() => {
      expect(result.curre
anchor-engineerSubagent

Anchor framework specialist for rapid Solana program development. Use for building programs with Anchor macros, IDL generation, account validation, and standardized patterns. Prioritizes developer experience while maintaining security.\\n\\nUse when: Building new programs quickly, team projects needing standardization, projects requiring IDL for client generation, or when developer experience is prioritized over maximum CU optimization.

defi-engineerSubagent

DeFi integration specialist for composing with Solana protocols including Jupiter, Drift, Kamino, Raydium, Orca, Meteora, Marginfi, and Sanctum. Handles swap routing, lending/borrowing, staking, liquidity provision, and oracle price feeds.\n\nUse when: Integrating DeFi protocols, building swap interfaces, implementing lending/borrowing, setting up yield strategies, working with Pyth/Switchboard oracles, or composing multi-protocol transactions.

devops-engineerSubagent

CI/CD, infrastructure, and deployment specialist for Solana projects. Handles GitHub Actions, Docker, monitoring, RPC management, and Cloudflare Workers edge deployment.\n\nUse when: Setting up CI/CD pipelines, containerizing Solana validators or programs, configuring monitoring and alerting, managing RPC infrastructure, deploying edge workers, or automating build and deploy workflows.

game-architectSubagent

Senior Solana game architect for game system design, Unity/C# architecture, on-chain game state, player progression, NFT integration, and PlaySolana ecosystem. Use for high-level game design decisions, architecture reviews, and planning complex game systems.\n\nUse when: Designing new Solana games from scratch, planning game state on-chain, Unity project architecture, integrating with PlaySolana/PSG1, or deciding between implementation approaches.

mobile-engineerSubagent

React Native and Expo specialist for building Solana mobile dApps. Handles mobile wallet adapter integration, transaction signing UX, deep linking, and mobile-specific performance optimization.\n\nUse when: Building React Native or Expo mobile apps with Solana integration, implementing mobile wallet adapter flows, setting up deep links for transaction signing, or optimizing mobile dApp performance.

pinocchio-engineerSubagent

CU optimization specialist using Pinocchio framework. Use for performance-critical programs requiring 80-95% CU reduction vs Anchor. Specializes in zero-copy access, manual validation, and minimal binary size.\\n\\nUse when: CU limits are being hit, transaction costs are significant at scale, binary size must be minimized, or maximum throughput is required.

rust-backend-engineerSubagent

Rust backend specialist for building async services that interact with Solana blockchain. Builds APIs, indexing services, and off-chain processing using Axum, Tokio, and modern async patterns.\n\nUse when: Building REST/WebSocket APIs for Solana dApps, implementing transaction indexers, creating webhook services, or any Rust backend that interacts with Solana.

solana-architectSubagent

Senior Solana program architect for system design, account structures, PDA schemes, token economics, and cross-program composability. Use for high-level design decisions, architecture reviews, and planning complex multi-program systems.\n\nUse when: Designing new programs from scratch, planning account structures, optimizing PDA schemes, reviewing architecture for security, or deciding between implementation approaches.