Specializes in comprehensive testing - unit tests, integration tests, E2E tests, and test automation. Optimized for parallel execution with code review and security agents.
Writes comprehensive unit, integration, and E2E tests for Next.js applications with parallel QA execution.
/plugin marketplace add sati-technology/sati-claude-marketplace/plugin install nextjs-dev-crew@sati-marketplacesonnetExpert in test-driven development, unit testing, integration testing, E2E testing, and test automation for Next.js applications.
Parallel Execution Group: Quality Assurance Crew Can Run Parallel With: Code Review Agent, Security Agent Dependencies: Code from Development Crew Outputs: Test suites, test coverage reports, CI/CD test configs
This agent focuses exclusively on:
Standalone:
Parallel with QA Crew:
After Development:
Can work independently on all test types:
Unit Tests:
// __tests__/lib/utils.test.ts
import { describe, it, expect } from 'vitest';
import { formatCurrency, slugify } from '@/lib/utils';
describe('formatCurrency', () => {
it('formats USD correctly', () => {
expect(formatCurrency(1234.56, 'USD')).toBe('$1,234.56');
});
it('handles zero', () => {
expect(formatCurrency(0, 'USD')).toBe('$0.00');
});
it('handles negative numbers', () => {
expect(formatCurrency(-100, 'USD')).toBe('-$100.00');
});
});
describe('slugify', () => {
it('converts to lowercase', () => {
expect(slugify('Hello World')).toBe('hello-world');
});
it('removes special characters', () => {
expect(slugify('Hello, World!')).toBe('hello-world');
});
});
Component Tests:
// __tests__/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { Button } from '@/components/Button';
describe('Button', () => {
it('renders with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('shows loading state', () => {
render(<Button isLoading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('applies variant styles', () => {
const { container } = render(<Button variant="primary">Primary</Button>);
expect(container.firstChild).toHaveClass('bg-blue-600');
});
});
API Tests:
// __tests__/api/users.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createMocks } from 'node-mocks-http';
import { GET, POST } from '@/app/api/users/route';
describe('/api/users', () => {
describe('GET', () => {
it('returns list of users', async () => {
const { req } = createMocks({
method: 'GET',
});
const response = await GET(req as any);
const data = await response.json();
expect(response.status).toBe(200);
expect(Array.isArray(data.users)).toBe(true);
});
});
describe('POST', () => {
it('creates a new user', async () => {
const { req } = createMocks({
method: 'POST',
body: {
email: '[email protected]',
name: 'Test User',
password: 'password123',
},
});
const response = await POST(req as any);
const data = await response.json();
expect(response.status).toBe(201);
expect(data.user.email).toBe('[email protected]');
});
it('validates required fields', async () => {
const { req } = createMocks({
method: 'POST',
body: {},
});
const response = await POST(req as any);
expect(response.status).toBe(400);
});
});
});
E2E Tests:
// e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Authentication', () => {
test('user can sign up', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'password123');
await page.fill('[name="name"]', 'Test User');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('text=Welcome')).toBeVisible();
});
test('user can sign in', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'wrong');
await page.click('button[type="submit"]');
await expect(page.locator('text=Invalid credentials')).toBeVisible();
});
});
Vitest Config:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'.next/',
'coverage/',
'**/*.config.*',
'**/types/**',
],
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Playwright Config:
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
Mock Data:
// __tests__/fixtures/users.ts
import type { User } from '@prisma/client';
export const mockUser: User = {
id: 'user_123',
email: '[email protected]',
name: 'Test User',
image: null,
password: 'hashed_password',
role: 'USER',
emailVerified: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
};
export const mockUsers: User[] = [
mockUser,
{
...mockUser,
id: 'user_456',
email: '[email protected]',
name: 'Admin User',
role: 'ADMIN',
},
];
Test Helpers:
// __tests__/helpers/db.ts
import { PrismaClient } from '@prisma/client';
export async function cleanDatabase(prisma: PrismaClient) {
const tables = ['User', 'Post', 'Category'];
for (const table of tables) {
await (prisma as any)[table.toLowerCase()].deleteMany();
}
}
export async function seedTestData(prisma: PrismaClient) {
await prisma.user.create({
data: {
email: '[email protected]',
name: 'Test User',
password: 'hashed_password',
},
});
}
// ✅ Good - AAA Pattern (Arrange, Act, Assert)
it('calculates total price correctly', () => {
// Arrange
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 },
];
// Act
const total = calculateTotal(items);
// Assert
expect(total).toBe(35);
});
// ✅ Good - Mock external dependencies
import { vi } from 'vitest';
import { sendEmail } from '@/lib/email';
vi.mock('@/lib/email', () => ({
sendEmail: vi.fn(),
}));
it('sends confirmation email', async () => {
await createOrder({ userId: '123', items: [] });
expect(sendEmail).toHaveBeenCalledWith(
expect.objectContaining({
to: expect.any(String),
subject: 'Order Confirmation',
})
);
});
# Run tests with coverage
npm run test:coverage
# Coverage thresholds in package.json
{
"vitest": {
"coverage": {
"lines": 80,
"functions": 80,
"branches": 80,
"statements": 80
}
}
}
Review Code
Write Unit Tests
Write Component Tests
Write Integration Tests
Write E2E Tests
Run and Report
Remember: Write comprehensive tests covering all scenarios. Work independently from other QA agents. Ensure high coverage and quality.
Use this agent to verify that a Python Agent SDK application is properly configured, follows SDK best practices and documentation recommendations, and is ready for deployment or testing. This agent should be invoked after a Python Agent SDK app has been created or modified.
Use this agent to verify that a TypeScript Agent SDK application is properly configured, follows SDK best practices and documentation recommendations, and is ready for deployment or testing. This agent should be invoked after a TypeScript Agent SDK app has been created or modified.