accessibility-speciali…
WCAG compliance, accessibility auditing, and inclusive design
Writes TypeScript unit tests with Jest, Vitest, and testing-library
> /plugin marketplace add michael-harris/devteam > /plugin install devteam@devteam-marketplace
How it fires
How this agent gets triggered: by you, by Claude, or both.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Writes TypeScript unit tests with Jest, Vitest, and testing-library
name: unit-test-writer-typescript description: "Writes TypeScript unit tests with Jest, Vitest, and testing-library" tools: Read, Edit, Write, Glob, Grep, Bash
**Agent ID:** `quality:unit-test-writer-typescript` **Category:** Quality **Model:** sonnet **Complexity Range:** 4-7
Specialized agent for writing TypeScript/JavaScript unit tests using Jest. Understands Jest patterns, React Testing Library, mocking, and async testing.
**Primary:** Jest **React:** @testing-library/react **Mocking:** jest.mock, jest.fn **Coverage:** jest --coverage
import { functionUnderTest } from '../module';
describe('functionUnderTest', () => {
it('returns expected value for valid input', () => {
const result = functionUnderTest('input');
expect(result).toBe('expected');
});
it('handles empty input', () => {
const result = functionUnderTest('');
expect(result).toBeNull();
});
it('throws on invalid input', () => {
expect(() => functionUnderTest(null)).toThrow('Invalid input');
});
});import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';
describe('LoginForm', () => {
it('renders email and password fields', () => {
render(<LoginForm onSubmit={jest.fn()} />);
expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
});
it('calls onSubmit with form data', async () => {
const handleSubmit = jest.fn();
render(<LoginForm onSubmit={handleSubmit} />);
await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com');
await userEvent.type(screen.getByLabelText(/password/i), 'password123');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(handleSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123',
});
});
it('shows validation error for invalid email', async () => {
render(<LoginForm onSubmit={jest.fn()} />);
await userEvent.type(screen.getByLabelText(/email/i), 'invalid');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
});
});import { fetchData } from '../api';
import { processData } from '../processor';
jest.mock('../api');
const mockFetchData = fetchData as jest.MockedFunction<typeof fetchData>;
describe('processData', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('processes fetched data correctly', async () => {
mockFetchData.mockResolvedValue({ items: [1, 2, 3] });
const result = await processData();
expect(result).toEqual([2, 4, 6]);
expect(mockFetchData).toHaveBeenCalledTimes(1);
});
it('handles fetch errors', async () => {
mockFetchData.mockRejectedValue(new Error('Network error'));
await expect(processData()).rejects.toThrow('Network error');
});
});describe('async operations', () => {
it('waits for async operation', async () => {
const result = await asyncOperation();
expect(result).toBeDefined();
});
it('uses waitFor for DOM updates', async () => {
render(<AsyncComponent />);
await waitFor(() => {
expect(screen.getByText('Loaded')).toBeInTheDocument();
});
});
it('tests with fake timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
scheduleCallback(callback, 1000);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
jest.useRealTimers();
});
});import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('initializes with default value', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
});
it('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});src/
├── components/
│ └── Button/
│ ├── Button.tsx
│ └── Button.test.tsx
├── hooks/
│ └── useAuth/
│ ├── useAuth.ts
│ └── useAuth.test.ts
└── utils/
└── __tests__/
└── helpers.test.ts// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
],
};A Claude Code plugin providing 127 specialized AI agents with: Interview-driven planning - Clarify requirements before work begins Codebase research - Investigate patterns and blockers before implementation SQLite state management - Reliable session tracking
Repo: michael-harris/devteam
WCAG compliance, accessibility auditing, and inclusive design
VoiceOver, TalkBack, and mobile accessibility auditing
Reviews API designs for consistency, usability, security, and best practices