Agent Skill
2/7/2026

tdd-workflow

Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.

V
venables
1GitHub Stars
3Views
npx skills add venables/dotfiles

SKILL.md

Nametdd-workflow
DescriptionUse this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.

name: tdd-workflow description: Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.

Test-Driven Development Workflow

This skill ensures all code development follows TDD principles with comprehensive test coverage.

When to Activate

  • Writing new features or functionality
  • Fixing bugs or issues
  • Refactoring existing code
  • Adding API endpoints
  • Creating new components

Core Principles

1. Tests BEFORE Code

ALWAYS write tests first, then implement code to make tests pass.

2. Coverage Requirements

  • Minimum 80% coverage (unit + integration + E2E)
  • All edge cases covered
  • Error scenarios tested
  • Boundary conditions verified

3. Test Types

Unit Tests

  • Individual functions and utilities
  • Component logic
  • Pure functions
  • Helpers and utilities

Integration Tests

  • API endpoints
  • Database operations
  • Service interactions
  • External API calls

E2E Tests (Playwright)

  • Critical user flows
  • Complete workflows
  • Browser automation
  • UI interactions

TDD Workflow Steps

Step 1: Write User Journeys

As a [role], I want to [action], so that [benefit]

Example:
As a user, I want to search for markets semantically,
so that I can find relevant markets even without exact keywords.

Step 2: Generate Test Cases

For each user journey, create comprehensive test cases:

describe("Semantic Search", () => {
  it("returns relevant markets for query", async () => {
    // Test implementation
  })

  it("handles empty query gracefully", async () => {
    // Test edge case
  })

  it("falls back to substring search when Redis unavailable", async () => {
    // Test fallback behavior
  })

  it("sorts results by similarity score", async () => {
    // Test sorting logic
  })
})

Step 3: Run Tests (They Should Fail)

npm test
# Tests should fail - we haven't implemented yet

Step 4: Implement Code

Write minimal code to make tests pass:

// Implementation guided by tests
export async function searchMarkets(query: string) {
  // Implementation here
}

Step 5: Run Tests Again

npm test
# Tests should now pass

Step 6: Refactor

Improve code quality while keeping tests green:

  • Remove duplication
  • Improve naming
  • Optimize performance
  • Enhance readability

Step 7: Verify Coverage

npm run test:coverage
# Verify 80%+ coverage achieved

Testing Patterns

Unit Test Pattern (Jest/Vitest)

import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'

describe('Button Component', () => {
  it('renders with correct text', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('calls onClick when clicked', () => {
    const handleClick = jest.fn()
    render(<Button onClick={handleClick}>Click</Button>)

    fireEvent.click(screen.getByRole('button'))

    expect(handleClick).toHaveBeenCalledTimes(1)
  })

  it('is disabled when disabled prop is true', () => {
    render(<Button disabled>Click</Button>)
    expect(screen.getByRole('button')).toBeDisabled()
  })
})

API Integration Test Pattern

import { NextRequest } from "next/server"
import { GET } from "./route"

describe("GET /api/markets", () => {
  it("returns markets successfully", async () => {
    const request = new NextRequest("http://localhost/api/markets")
    const response = await GET(request)
    const data = await response.json()

    expect(response.status).toBe(200)
    expect(data.success).toBe(true)
    expect(Array.isArray(data.data)).toBe(true)
  })

  it("validates query parameters", async () => {
    const request = new NextRequest(
      "http://localhost/api/markets?limit=invalid"
    )
    const response = await GET(request)

    expect(response.status).toBe(400)
  })

  it("handles database errors gracefully", async () => {
    // Mock database failure
    const request = new NextRequest("http://localhost/api/markets")
    // Test error handling
  })
})

E2E Test Pattern (Playwright)

import { test, expect } from "@playwright/test"

test("user can search and filter markets", async ({ page }) => {
  // Navigate to markets page
  await page.goto("/")
  await page.click('a[href="/markets"]')

  // Verify page loaded
  await expect(page.locator("h1")).toContainText("Markets")

  // Search for markets
  await page.fill('input[placeholder="Search markets"]', "election")

  // Wait for debounce and results
  await page.waitForTimeout(600)

  // Verify search results displayed
  const results = page.locator('[data-testid="market-card"]')
  await expect(results).toHaveCount(5, { timeout: 5000 })

  // Verify results contain search term
  const firstResult = results.first()
  await expect(firstResult).toContainText("election", { ignoreCase: true })

  // Filter by status
  await page.click('button:has-text("Active")')

  // Verify filtered results
  await expect(results).toHaveCount(3)
})

test("user can create a new market", async ({ page }) => {
  // Login first
  await page.goto("/creator-dashboard")

  // Fill market creation form
  await page.fill('input[name="name"]', "Test Market")
  await page.fill('textarea[name="description"]', "Test description")
  await page.fill('input[name="endDate"]', "2025-12-31")

  // Submit form
  await page.click('button[type="submit"]')

  // Verify success message
  await expect(page.locator("text=Market created successfully")).toBeVisible()

  // Verify redirect to market page
  await expect(page).toHaveURL(/\/markets\/test-market/)
})

Test File Organization

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx          # Unit tests
│   │   └── Button.stories.tsx       # Storybook
│   └── MarketCard/
│       ├── MarketCard.tsx
│       └── MarketCard.test.tsx
├── app/
│   └── api/
│       └── markets/
│           ├── route.ts
│           └── route.test.ts         # Integration tests
└── e2e/
    ├── markets.spec.ts               # E2E tests
    ├── trading.spec.ts
    └── auth.spec.ts

Mocking External Services

Supabase Mock

jest.mock("@/lib/supabase", () => ({
  supabase: {
    from: jest.fn(() => ({
      select: jest.fn(() => ({
        eq: jest.fn(() =>
          Promise.resolve({
            data: [{ id: 1, name: "Test Market" }],
            error: null
          })
        )
      }))
    }))
  }
}))

Redis Mock

jest.mock("@/lib/redis", () => ({
  searchMarketsByVector: jest.fn(() =>
    Promise.resolve([{ slug: "test-market", similarity_score: 0.95 }])
  ),
  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))
}))

OpenAI Mock

jest.mock("@/lib/openai", () => ({
  generateEmbedding: jest.fn(() =>
    Promise.resolve(
      new Array(1536).fill(0.1) // Mock 1536-dim embedding
    )
  )
}))

Test Coverage Verification

Run Coverage Report

npm run test:coverage

Coverage Thresholds

{
  "jest": {
    "coverageThresholds": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}

Common Testing Mistakes to Avoid

❌ WRONG: Testing Implementation Details

// Don't test internal state
expect(component.state.count).toBe(5)

✅ CORRECT: Test User-Visible Behavior

// Test what users see
expect(screen.getByText("Count: 5")).toBeInTheDocument()

❌ WRONG: Brittle Selectors

// Breaks easily
await page.click(".css-class-xyz")

✅ CORRECT: Semantic Selectors

// Resilient to changes
await page.click('button:has-text("Submit")')
await page.click('[data-testid="submit-button"]')

❌ WRONG: No Test Isolation

// Tests depend on each other
test("creates user", () => {
  /* ... */
})
test("updates same user", () => {
  /* depends on previous test */
})

✅ CORRECT: Independent Tests

// Each test sets up its own data
test("creates user", () => {
  const user = createTestUser()
  // Test logic
})

test("updates user", () => {
  const user = createTestUser()
  // Update logic
})

Continuous Testing

Watch Mode During Development

npm test -- --watch
# Tests run automatically on file changes

Pre-Commit Hook

# Runs before every commit
npm test && npm run lint

CI/CD Integration

# GitHub Actions
- name: Run Tests
  run: npm test -- --coverage
- name: Upload Coverage
  uses: codecov/codecov-action@v3

Best Practices

  1. Write Tests First - Always TDD
  2. One Assert Per Test - Focus on single behavior
  3. Descriptive Test Names - Explain what's tested
  4. Arrange-Act-Assert - Clear test structure
  5. Mock External Dependencies - Isolate unit tests
  6. Test Edge Cases - Null, undefined, empty, large
  7. Test Error Paths - Not just happy paths
  8. Keep Tests Fast - Unit tests < 50ms each
  9. Clean Up After Tests - No side effects
  10. Review Coverage Reports - Identify gaps

Success Metrics

  • 80%+ code coverage achieved
  • All tests passing (green)
  • No skipped or disabled tests
  • Fast test execution (< 30s for unit tests)
  • E2E tests cover critical user flows
  • Tests catch bugs before production

Remember: Tests are not optional. They are the safety net that enables confident refactoring, rapid development, and production reliability.

Skills Info
Original Name:tdd-workflowAuthor:venables