How to Write Vitest Tests Co-Located with Components in OpenHuman

To write Vitest tests co-located with components in OpenHuman, place a *.test.tsx file in the same directory as your component and ensure it matches the glob pattern src/**/*.test.{ts,tsx} defined in app/test/vitest.config.ts.

The OpenHuman repository adopts a co-located testing strategy for its frontend workspace that keeps unit tests side-by-side with the React components they verify. This approach simplifies relative imports and ensures that tests remain tightly coupled to the implementation they cover, following the behavioral testing philosophy documented in the project's knowledge base.

Understanding the Co-Located Test Pattern

In OpenHuman, every component under app/src/… has a corresponding unit test in the same folder, sharing the same base name but with a .test.tsx suffix. For example, Button.tsx pairs with Button.test.tsx in app/src/components/ui/. This convention eliminates deep import paths and keeps related files discoverable.

The repository also supports placing tests in a __tests__ subdirectory within the component's folder, as demonstrated by app/src/components/skills/__tests__/SkillsExplorerTab.test.tsx testing app/src/components/skills/SkillsExplorerTab.tsx.

Configuring Vitest for Co-Located Tests

The Vitest configuration file at app/test/vitest.config.ts enforces the co-location pattern through a specific glob pattern. The configuration includes src/**/*.test.{ts,tsx}, which automatically discovers test files nested within the source tree regardless of their specific directory depth.

Coverage reports are configured to include only component source code under src/**/*.{ts,tsx} while explicitly excluding test files from coverage calculations. Global mocks are initialized once in app/src/test/setup.ts, with clearMocks set to true but mockReset set to false to preserve mock implementations across tests.

Writing a Co-Located Component Test

File Naming Convention

Follow the strict naming pattern: ComponentName.test.tsx. The test file must reside in the same directory as the component it tests (e.g., app/src/components/ui/) or within a __tests__ subdirectory. This proximity enables simple relative imports (./Button) and immediate access to directory-scoped helper utilities.

Test Structure and Setup

Tests import the component using relative paths and utilize Testing Library (@testing-library/react) for rendering. Queries should target accessible roles and behavioral attributes rather than implementation details. Use Vitest's expect with helpers like toBeInTheDocument, toHaveAttribute, and toMatch for assertions.

Complete Example: Button Component

Here is a minimal implementation and corresponding test from the OpenHuman source code, demonstrating the co-located pattern:

// File: app/src/components/ui/Button.tsx
import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from 'react';

export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  leadingIcon?: ReactNode;
  trailingIcon?: ReactNode;
}

const BASE = 'inline-flex items-center justify-center gap-2 ...';
const VARIANTS: Record<ButtonVariant, string> = { /* … */ };
const SIZES: Record<ButtonSize, string> = { /* … */ };

const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
  const {
    variant = 'primary',
    size = 'md',
    leadingIcon,
    trailingIcon,
    className,
    type,
    children,
    ...rest
  } = props;

  const classes = [BASE, VARIANTS[variant], SIZES[size], className ?? '']
    .filter(Boolean)
    .join(' ');

  return (
    <button ref={ref} type={type ?? 'button'} className={classes} {...rest}>
      {leadingIcon}
      {children}
      {trailingIcon}
    </button>
  );
});

Button.displayName = 'Button';
export default Button;
// File: app/src/components/ui/Button.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import Button from './Button';

describe('Button', () => {
  it('renders children and defaults to primary/md with type=button', () => {
    render(<Button>Send</Button>);
    const btn = screen.getByRole('button', { name: 'Send' });
    expect(btn).toBeInTheDocument();
    expect(btn).toHaveAttribute('type', 'button');
    expect(btn.className).toMatch(/bg-primary-500/);
    expect(btn.className).toMatch(/h-9/);
  });

  it('applies secondary variant classes', () => {
    render(<Button variant="secondary">Cancel</Button>);
    const btn = screen.getByRole('button', { name: 'Cancel' });
    expect(btn.className).toMatch(/border-neutral-300/);
    expect(btn.className).toMatch(/bg-neutral-0/);
  });
});

Testing Philosophy and Best Practices

According to the GitBook documentation in gitbooks/developing/testing-strategy.md, OpenHuman emphasizes "behavior over implementation" and "no real network" for unit tests. Co-located Vitest tests should focus on what the UI does—rendered output, accessibility roles, and interaction—rather than internal component state or implementation details.

Use vi.fn() from Vitest to verify event handlers fire correctly, and query elements using getByRole to ensure tests validate accessibility. Assert on CSS class patterns when testing variant or size props, but avoid testing the exact internal structure of the component's DOM.

Summary

  • Co-locate tests by placing *.test.tsx files in the same directory as components (e.g., app/src/components/ui/Button.test.tsx alongside Button.tsx)
  • Configure Vitest with the glob pattern src/**/*.test.{ts,tsx} in app/test/vitest.config.ts to automatically discover tests
  • Use relative imports (./ComponentName) and Testing Library for maintainable, portable tests
  • Focus on behavioral assertions (rendered output, accessibility roles, event handling) rather than implementation details
  • Reference global mocks in app/src/test/setup.ts for consistent test environments across the suite

Frequently Asked Questions

What is the naming convention for co-located Vitest tests in OpenHuman?

Tests must use the pattern ComponentName.test.tsx and reside in the same directory as the component or within a __tests__ subdirectory. For example, SkillsExplorerTab.tsx is tested by __tests__/SkillsExplorerTab.test.tsx in app/src/components/skills/.

How does Vitest discover tests located in component directories?

The configuration file app/test/vitest.config.ts specifies the include pattern src/**/*.test.{ts,tsx}, which recursively searches the src directory for any files matching the test extension pattern, regardless of their specific folder depth.

What testing library does OpenHuman use for component rendering?

OpenHuman uses @testing-library/react for rendering components and querying the DOM. Tests utilize render and screen methods to simulate user interactions and verify accessibility roles, following the "behavior over implementation" philosophy.

Should tests focus on implementation details or user behavior?

Tests should focus on user behavior and rendered output rather than internal implementation. Query elements by accessible roles (e.g., getByRole('button')), assert on DOM attributes and CSS classes, and verify event handlers fire correctly, but avoid testing internal state or component structure directly.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →