From adlc-react-frontend
Test-Driven Development workflow for React including Red-Green-Refactor cycle, error recovery, and component testing strategy.
npx claudepluginhub sumanpapanaboina1983/adlc-accelerator-kit-pluginsThis skill uses the workspace's default tool permissions.
| Gate | Threshold | Status |
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
| Gate | Threshold | Status |
|---|---|---|
| TypeScript | 0 type errors | REQUIRED |
| Tests | 100% pass (0 failures) | REQUIRED |
| Coverage | ≥ 80% line coverage | REQUIRED |
| ESLint | 0 errors (warnings OK) | REQUIRED |
| Build | must pass | REQUIRED |
⛔ BLOCKING: TDD workflow is NOT complete until ALL gates pass.
Before starting ANY TDD cycle, verify Vitest coverage thresholds are configured:
grep -l "thresholds" vitest.config.ts
If NOT configured, update vitest.config.ts FIRST:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'text-summary', 'html', 'lcov'],
reportsDirectory: './coverage',
thresholds: {
lines: 80,
branches: 70,
functions: 80,
statements: 80,
},
include: ['src/**/*.{ts,tsx}'],
exclude: [
'node_modules/',
'src/test/',
'src/**/*.d.ts',
'src/main.tsx',
'src/**/*.test.{ts,tsx}',
],
},
},
});
⛔ Do NOT start TDD without coverage thresholds configured.
┌──────────────────┐
│ RED │
│ Write a failing │
│ test │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ GREEN │
│ Write minimal │
│ code to pass │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ REFACTOR │
│ Improve code │
│ keeping green │
└────────┬─────────┘
│
└─────────────────┐
│
Repeat for ▼
next behavior ◄──┘
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import TodoItem from './TodoItem';
describe('TodoItem', () => {
it('should render the todo title', () => {
render(
<TodoItem
id="1"
title="Test todo"
completed={false}
onToggle={() => {}}
onDelete={() => {}}
/>
);
expect(screen.getByText('Test todo')).toBeInTheDocument();
});
});
Run: npm test -- TodoItem.test.tsx
Expected: FAIL (component doesn't exist yet)
// TodoItem.tsx
interface TodoItemProps {
id: string;
title: string;
completed: boolean;
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
export default function TodoItem({ title }: TodoItemProps) {
return <div>{title}</div>;
}
Run: npm test -- TodoItem.test.tsx
Expected: PASS
// Add proper structure and styling
export default function TodoItem({
id,
title,
completed,
onToggle,
onDelete,
}: TodoItemProps) {
return (
<li className="flex items-center gap-3 p-4 bg-white rounded-lg shadow-sm">
<input
type="checkbox"
checked={completed}
onChange={() => onToggle(id)}
/>
<span className={completed ? 'line-through text-gray-400' : ''}>
{title}
</span>
<button onClick={() => onDelete(id)}>Delete</button>
</li>
);
}
Run: npm test
Expected: ALL PASS
it('should show checkbox as checked when completed', () => {
render(<TodoItem {...defaultProps} completed={true} />);
expect(screen.getByRole('checkbox')).toBeChecked();
});
Repeat RED → GREEN → REFACTOR.
describe('TodoInput', () => {
// 1. Renders
it('should render an input field', () => {
render(<TodoInput onAdd={() => {}} />);
expect(screen.getByRole('textbox')).toBeInTheDocument();
});
// 2. Content
it('should render a submit button', () => {
render(<TodoInput onAdd={() => {}} />);
expect(screen.getByRole('button', { name: /add/i })).toBeInTheDocument();
});
// 3. State
it('should update input value on change', async () => {
const user = userEvent.setup();
render(<TodoInput onAdd={() => {}} />);
await user.type(screen.getByRole('textbox'), 'New todo');
expect(screen.getByRole('textbox')).toHaveValue('New todo');
});
// 4. Interaction
it('should call onAdd with input value on submit', async () => {
const onAdd = vi.fn();
const user = userEvent.setup();
render(<TodoInput onAdd={onAdd} />);
await user.type(screen.getByRole('textbox'), 'New todo');
await user.click(screen.getByRole('button', { name: /add/i }));
expect(onAdd).toHaveBeenCalledWith('New todo');
});
// 5. Edge cases
it('should not call onAdd when input is empty', async () => {
const onAdd = vi.fn();
const user = userEvent.setup();
render(<TodoInput onAdd={onAdd} />);
await user.click(screen.getByRole('button', { name: /add/i }));
expect(onAdd).not.toHaveBeenCalled();
});
it('should show error message when submitting empty', async () => {
const user = userEvent.setup();
render(<TodoInput onAdd={() => {}} />);
await user.click(screen.getByRole('button', { name: /add/i }));
expect(screen.getByText(/required/i)).toBeInTheDocument();
});
it('should clear input after successful submit', async () => {
const user = userEvent.setup();
render(<TodoInput onAdd={() => {}} />);
await user.type(screen.getByRole('textbox'), 'New todo');
await user.click(screen.getByRole('button', { name: /add/i }));
expect(screen.getByRole('textbox')).toHaveValue('');
});
});
ATTEMPT 1:
- Read error message carefully
- Check the specific assertion that failed
- Make targeted fix
- Run test
ATTEMPT 2 (if still failing):
- Use screen.debug() to see actual DOM
- Check component props and state
- Make targeted fix
- Run test
ATTEMPT 3 (if still failing):
- Check react-verification skill for error patterns
- Add console.log to component
- Make targeted fix
- Run test
AFTER 3 ATTEMPTS:
- STOP
- Summarize what you've tried
- Ask developer for guidance
Do NOT:
any type to fix TypeScript errorsDO:
// Test individual components in isolation
describe('TodoItem', () => {
it('should render title', () => {});
it('should call onToggle when clicked', () => {});
});
// Test components working together
describe('TodoList with TodoInput', () => {
it('should add new todo to list', async () => {
render(<TodoApp />);
await user.type(screen.getByRole('textbox'), 'New todo');
await user.click(screen.getByRole('button', { name: /add/i }));
expect(screen.getByText('New todo')).toBeInTheDocument();
});
});
// Full user flows (use Playwright)
// See e2e-testing plugin
| Component Type | Target | Focus |
|---|---|---|
| UI Components | 80%+ | Rendering, interactions |
| Form Components | 90%+ | Validation, submission |
| Hooks | 80%+ | State changes, side effects |
| Utils | 90%+ | All branches |
Before declaring any implementation complete, you MUST verify coverage:
# Generate coverage report
npm test -- --coverage --run
# Check coverage in browser
open coverage/lcov-report/index.html
| Metric | Minimum | Action if Below |
|---|---|---|
| Line Coverage | 80% | Add more tests |
| Branch Coverage | 75% | Test if/else paths |
| Function Coverage | 80% | Test all functions |
| Statement Coverage | 80% | Cover all statements |
Run coverage report:
npm test -- --coverage --run
Check percentage:
coverage/lcov-report/index.htmlcoverage/coverage-summary.jsonIf below 80%:
Only then declare done
⛔ NEVER mark complete with coverage < 80%
A pre-completion hook enforces this requirement automatically.