Best for
- Writing unit, integration, or E2E tests
- Fixing bugs and debugging
- Improving test coverage
travisjneuman/.claude/skills/test-specialist/SKILL.md
This skill should be used when writing test cases, fixing bugs, analyzing code for potential issues, or improving test coverage for JavaScript/TypeScript applications. Use this for unit tests, integration tests, end-to-end tests, debugging runtime errors, logic bugs, performance issues, security vulnerabilities, and systematic code analysis.
Decision brief
Systematic testing methodologies and debugging techniques for JS/TS applications.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/travisjneuman/.claude --skill "skills/test-specialist"Inspect the Agent Skill "test-specialist" from https://github.com/travisjneuman/.claude/blob/b8b4dd55d61b9f25d33e3b5427870641a1c8c39c/skills/test-specialist/SKILL.md at commit b8b4dd55d61b9f25d33e3b5427870641a1c8c39c. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
1. Reproduce - Document exact steps, expected vs actual 2. Isolate - Binary search, minimal reproduction 3. Root Cause - Trace execution, check assumptions, git blame 4. Fix - Write failing test first, implement fix 5. Validate - Run full suite, test edge cases
Review the “Workflow Decision Tree” section in the pinned source before continuing.
Writing unit, integration, or E2E tests
Review the “Testing Stack by Project” section in the pinned source before continuing.
Review the “Test Patterns” section in the pinned source before continuing.
Permission review
The documentation includes network, browsing, or remote request actions.
await request(app).post("/api/items").expect(401);The documentation asks the agent to run terminal commands or scripts.
npx chromatic --project-token=<token>Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 96/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 94 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Systematic testing methodologies and debugging techniques for JS/TS applications.
Use for:
Don't use when:
generic-code-reviewertech-debt-analyzergeneric-feature-developer| Project Type | Unit Tests | Component | E2E |
|---|---|---|---|
| React/Next.js | Vitest/Jest | Testing Library | Playwright |
| Node.js | Vitest/Jest | Supertest | Playwright |
| Static | Jest | - | Playwright |
describe("calculateTotal", () => {
test("sums amounts correctly", () => {
// Arrange
const items = [{ amount: 100 }, { amount: 50 }];
// Act
const total = calculateTotal(items);
// Assert
expect(total).toBe(150);
});
test("handles empty list", () => {
expect(calculateTotal([])).toBe(0);
});
});
// ✅ Test user behavior, not implementation
it('creates item when user clicks Add', async () => {
const user = userEvent.setup();
render(<ItemList />);
await user.click(screen.getByRole('button', { name: /add/i }));
await user.type(screen.getByLabelText(/title/i), 'New item');
await user.click(screen.getByRole('button', { name: /save/i }));
expect(screen.getByText('New item')).toBeInTheDocument();
});
import { test, expect } from "@playwright/test";
test("user can complete checkout", async ({ page }) => {
await page.goto("/products");
// Add to cart
await page.click('button:has-text("Add to Cart")');
await page.click('a:has-text("Cart")');
// Checkout
await page.click('button:has-text("Checkout")');
await page.fill('[name="email"]', "[email protected]");
await page.click('button:has-text("Place Order")');
// Verify
await expect(page.locator("h1")).toContainText("Order Confirmed");
});
test("POST /items creates item", async () => {
const response = await request(app)
.post("/api/items")
.send({ name: "Test" })
.expect(201);
expect(response.body).toMatchObject({ id: expect.any(Number) });
});
When debugging an issue:
test("handles concurrent updates", async () => {
const promises = Array.from({ length: 100 }, () => increment());
await Promise.all(promises);
expect(getCount()).toBe(100);
});
test.each([null, undefined, "", 0])("handles invalid input: %p", (input) => {
expect(() => process(input)).toThrow("Invalid");
});
test("handles edge cases", () => {
expect(paginate([], 1, 10)).toEqual([]); // empty
expect(paginate([item], 1, 10)).toEqual([item]); // single
expect(paginate(items25, 3, 10)).toHaveLength(5); // partial last page
});
test("prevents SQL injection", async () => {
const malicious = "'; DROP TABLE users; --";
await expect(search(malicious)).resolves.not.toThrow();
});
test("sanitizes XSS", () => {
const xss = '<script>alert("xss")</script>';
expect(sanitize(xss)).not.toContain("<script>");
});
test("requires auth", async () => {
await request(app).post("/api/items").expect(401);
});
test("handles large datasets efficiently", () => {
const largeList = Array.from({ length: 10000 }, (_, i) => ({ value: i }));
const start = performance.now();
process(largeList);
expect(performance.now() - start).toBeLessThan(100);
});
| Code Type | Target |
|---|---|
| Critical paths | 90%+ |
| Business logic | 85%+ |
| UI components | 75%+ |
| Utilities | 70%+ |
| Situation | Action |
|---|---|
| Adding feature | Write test first (TDD) |
| Fixing bug | Write failing test, then fix |
| Improving coverage | Find gaps, prioritize critical paths |
| Code review | Check edge cases, error handling |
import pytest
from myapp.services import UserService
@pytest.fixture
def user_service(db_session):
"""Provide a UserService with test database."""
return UserService(session=db_session)
@pytest.fixture
def sample_user(user_service):
"""Create and return a sample user."""
return user_service.create(name="Test User", email="[email protected]")
class TestUserService:
def test_create_user(self, user_service):
user = user_service.create(name="John", email="[email protected]")
assert user.name == "John"
assert user.id is not None
def test_get_user_not_found(self, user_service):
with pytest.raises(UserNotFoundError, match="User 999 not found"):
user_service.get(999)
@pytest.mark.parametrize("email,is_valid", [
("[email protected]", True),
("[email protected]", True),
("invalid", False),
("@example.com", False),
("", False),
])
def test_validate_email(self, user_service, email: str, is_valid: bool):
assert user_service.validate_email(email) == is_valid
from unittest.mock import Mock, patch, AsyncMock
def test_send_notification(user_service):
with patch("myapp.services.email_client") as mock_email:
mock_email.send = Mock(return_value=True)
user_service.notify(user_id=1, message="Hello")
mock_email.send.assert_called_once_with(
to="[email protected]",
body="Hello",
)
# Async mocking
@pytest.mark.asyncio
async def test_fetch_data():
with patch("myapp.client.fetch", new_callable=AsyncMock) as mock_fetch:
mock_fetch.return_value = {"status": "ok"}
result = await process_data()
assert result["status"] == "ok"
# tests/conftest.py
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
@pytest.fixture(scope="session")
def engine():
return create_engine("sqlite:///:memory:")
@pytest.fixture(scope="function")
def db_session(engine):
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.rollback()
session.close()
Base.metadata.drop_all(engine)
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
amount float64
code string
want float64
wantErr bool
}{
{
name: "valid 10% discount",
amount: 100.0,
code: "SAVE10",
want: 90.0,
},
{
name: "no discount",
amount: 100.0,
code: "",
want: 100.0,
},
{
name: "invalid code",
amount: 100.0,
code: "INVALID",
wantErr: true,
},
{
name: "zero amount",
amount: 0,
code: "SAVE10",
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CalculateDiscount(tt.amount, tt.code)
if (err != nil) != tt.wantErr {
t.Errorf("CalculateDiscount() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("CalculateDiscount() = %v, want %v", got, tt.want)
}
})
}
}
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
// Mock
type MockUserRepo struct {
mock.Mock
}
func (m *MockUserRepo) FindByID(id string) (*User, error) {
args := m.Called(id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*User), args.Error(1)
}
func TestGetUser(t *testing.T) {
repo := new(MockUserRepo)
repo.On("FindByID", "123").Return(&User{ID: "123", Name: "John"}, nil)
service := NewUserService(repo)
user, err := service.GetUser("123")
require.NoError(t, err)
assert.Equal(t, "John", user.Name)
repo.AssertExpectations(t)
}
// HTTP handler testing
func TestGetUserHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/users/123", nil)
w := httptest.NewRecorder()
handler := NewHandler(mockService)
handler.GetUser(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var user User
err := json.NewDecoder(w.Body).Decode(&user)
require.NoError(t, err)
assert.Equal(t, "123", user.ID)
}
// src/lib.rs - Unit tests (same file)
pub fn calculate_discount(amount: f64, percentage: f64) -> Result<f64, DiscountError> {
if percentage < 0.0 || percentage > 100.0 {
return Err(DiscountError::InvalidPercentage(percentage));
}
Ok(amount * (1.0 - percentage / 100.0))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_discount() {
let result = calculate_discount(100.0, 10.0).unwrap();
assert!((result - 90.0).abs() < f64::EPSILON);
}
#[test]
fn test_zero_discount() {
assert_eq!(calculate_discount(100.0, 0.0).unwrap(), 100.0);
}
#[test]
fn test_invalid_percentage() {
assert!(matches!(
calculate_discount(100.0, 150.0),
Err(DiscountError::InvalidPercentage(_))
));
}
#[test]
fn test_negative_percentage() {
assert!(calculate_discount(100.0, -10.0).is_err());
}
}
// tests/integration_test.rs - Integration tests (separate file)
use my_crate::calculate_discount;
#[test]
fn test_full_workflow() {
let original = 200.0;
let discounted = calculate_discount(original, 25.0).unwrap();
assert_eq!(discounted, 150.0);
}
use proptest::prelude::*;
proptest! {
#[test]
fn discount_never_exceeds_original(amount in 0.0f64..10000.0, pct in 0.0f64..100.0) {
let result = calculate_discount(amount, pct).unwrap();
prop_assert!(result <= amount);
prop_assert!(result >= 0.0);
}
#[test]
fn roundtrip_serialization(name in "[a-zA-Z]{1,50}", age in 0u32..150) {
let user = User { name: name.clone(), age };
let json = serde_json::to_string(&user).unwrap();
let deserialized: User = serde_json::from_str(&json).unwrap();
prop_assert_eq!(user, deserialized);
}
}
import { test, expect } from "@playwright/test";
test("homepage visual regression", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveScreenshot("homepage.png", {
maxDiffPixelRatio: 0.01,
});
});
test("component states", async ({ page }) => {
await page.goto("/components");
// Default state
await expect(page.locator(".card")).toHaveScreenshot("card-default.png");
// Hover state
await page.locator(".card").hover();
await expect(page.locator(".card")).toHaveScreenshot("card-hover.png");
// Full page with specific viewport
await page.setViewportSize({ width: 375, height: 812 }); // iPhone
await expect(page).toHaveScreenshot("homepage-mobile.png");
});
// Update snapshots: npx playwright test --update-snapshots
import percySnapshot from "@percy/playwright";
test("visual test with Percy", async ({ page }) => {
await page.goto("/dashboard");
await percySnapshot(page, "Dashboard");
// Percy compares across browsers and viewport sizes
});
# Run Chromatic on Storybook stories
npx chromatic --project-token=<token>
# CI integration
# Chromatic captures every story as a visual snapshot
# and detects pixel-level changes across PRs
| Tool | Approach | Best For |
|---|---|---|
| Playwright | Local screenshot comparison | E2E visual tests, free |
| Percy | Cloud cross-browser comparison | Multi-browser, team review |
| Chromatic | Storybook story snapshots | Component library visual QA |
CLAUDE.md - Testing rulesFrequently asked questions
Systematic testing methodologies and debugging techniques for JS/TS applications.
The source record exposes this install command: npx skills add https://github.com/travisjneuman/.claude --skill "skills/test-specialist". Inspect the command and pinned source before running it.
Static rules flagged network, exec-script in the source; the page lists the matching lines and excerpts.
Alternatives
Jamie-BitFlight/claude_skills
Progressive Python quality improvement with static analysis, type refinement, modernization planning, plan review, and test-driven implementation. Use when addressing technical debt, eliminating Any types, applying modern Python patterns, or refactoring for better design.
yonatangross/orchestkit
Grade work that already exists and decide whether it can merge. Runs the project's current unit, integration, and E2E suites plus security scanning and type checking, scores every dimension 0-10, and returns a merge verdict with a VERIFIED-vs-CLAIMED evidence manifest. Writes no test files and edits no source. Use when verifying changes are ready to merge. Use /ork:cover instead when the tests still have to be written.
microsoft/Sico
Execute Android UI workflows on a sandbox device, review results, and produce a structured execution report.
laurigates/claude-plugins
Analyze test results and create a fix plan with subagents. Use when triaging failing tests, analyzing JUnit XML, planning fixes for accessibility/security, or categorizing flaky/E2E failures.