Check and configure testing frameworks and infrastructure
Validates and configures testing frameworks (Vitest, Jest, pytest, cargo-nextest) against best practices.
/plugin marketplace add laurigates/claude-plugins/plugin install configure-plugin@lgates-claude-plugins[--check-only] [--fix] [--framework <vitest|jest|pytest|nextest>]configure/Check and configure testing frameworks against best practices (Vitest, Jest, pytest, cargo-nextest).
This command validates testing infrastructure and optionally configures or upgrades to modern frameworks.
Modern testing stack preferences:
CRITICAL: Before flagging outdated versions, verify latest releases:
Use WebSearch or WebFetch to verify current versions before reporting outdated frameworks.
Detect project language and existing test framework:
| Indicator | Language | Detected Framework |
|---|---|---|
vitest.config.* | JavaScript/TypeScript | Vitest |
jest.config.* | JavaScript/TypeScript | Jest |
pyproject.toml [tool.pytest] | Python | pytest |
pytest.ini | Python | pytest |
Cargo.toml | Rust | cargo test |
.nextest.toml | Rust | cargo-nextest |
For each detected framework, check configuration:
Vitest:
vitest.config.ts or vitest.config.js existsglobals: true configured for compatibilityenvironment set appropriately (jsdom, happy-dom, node)@vitest/coverage-v8 or @vitest/coverage-istanbulJest:
jest.config.js or jest.config.ts existstestEnvironment configuredpytest:
pyproject.toml has [tool.pytest.ini_options] sectiontestpaths configuredaddopts includes useful flags (-v, --strict-markers)markers defined for test categorizationpytest-cov installedcargo-nextest:
.nextest.toml existsGenerate formatted compliance report:
Testing Framework Compliance Report
====================================
Project: [name]
Language: [TypeScript | Python | Rust]
Framework: [Vitest 2.x | pytest 8.x | cargo-nextest 0.9.x]
Configuration:
Config file vitest.config.ts [✅ EXISTS | ❌ MISSING]
Test directory tests/ or __tests__/ [✅ EXISTS | ⚠️ NON-STANDARD]
Coverage provider @vitest/coverage-v8 [✅ CONFIGURED | ❌ MISSING]
Environment jsdom [✅ CONFIGURED | ⚠️ NOT SET]
Watch exclusions node_modules, dist [✅ CONFIGURED | ⚠️ INCOMPLETE]
Test Organization:
Unit tests src/**/*.test.ts [✅ FOUND | ❌ NONE]
Integration tests tests/integration/ [✅ FOUND | ⏭️ N/A]
E2E tests tests/e2e/ [✅ FOUND | ⏭️ N/A]
Scripts:
test command package.json scripts [✅ CONFIGURED | ❌ MISSING]
test:watch package.json scripts [✅ CONFIGURED | ⚠️ MISSING]
test:coverage package.json scripts [✅ CONFIGURED | ❌ MISSING]
Overall: [X issues found]
Recommendations:
- Add coverage configuration with 80% threshold
- Configure watch mode exclusions
- Add test:ui script for Vitest UI
Install dependencies:
npm install --save-dev vitest @vitest/ui @vitest/coverage-v8
# or
bun add --dev vitest @vitest/ui @vitest/coverage-v8
Create vitest.config.ts:
import { defineConfig } from 'vitest/config';
import { resolve } from 'path';
export default defineConfig({
test: {
// Enable globals for compatibility with Jest-style tests
globals: true,
// Test environment (jsdom for DOM testing, node for backend)
environment: 'jsdom', // or 'node', 'happy-dom'
// Setup files to run before tests
setupFiles: ['./tests/setup.ts'],
// Coverage configuration
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'json', 'html', 'lcov'],
exclude: [
'node_modules/',
'dist/',
'tests/',
'**/*.config.*',
'**/*.d.ts',
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
// Watch mode exclusions
watchExclude: ['**/node_modules/**', '**/dist/**', '**/.next/**'],
// Test timeout
testTimeout: 10000,
// Include/exclude patterns
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
exclude: ['node_modules', 'dist', '.next', 'out'],
},
// Resolve aliases (if using path aliases)
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
});
Add npm scripts to package.json:
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:ci": "vitest run --coverage --reporter=junit --reporter=default"
}
}
Create jest.config.ts:
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/src', '<rootDir>/tests'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.json',
}],
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx',
],
coverageThresholds: {
global: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
};
export default config;
Update pyproject.toml:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--strict-config",
"--cov=src",
"--cov-report=term-missing",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"e2e: End-to-end tests",
"slow: Slow running tests",
]
[tool.coverage.run]
source = ["src"]
omit = [
"*/tests/*",
"*/migrations/*",
"*/__init__.py",
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if __name__ == .__main__.:",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if False:",
"if TYPE_CHECKING:",
]
Install dependencies:
uv add --group dev pytest pytest-cov pytest-asyncio pytest-mock
Install cargo-nextest:
cargo install cargo-nextest --locked
Create .nextest.toml:
[profile.default]
retries = 0
fail-fast = false
# Run tests with all features enabled
test-threads = "num-cpus"
[profile.ci]
retries = 2
fail-fast = true
test-threads = 2
# JUnit output for CI
[profile.ci.junit]
path = "target/nextest/ci/junit.xml"
[profile.default.junit]
path = "target/nextest/default/junit.xml"
Add to .cargo/config.toml (optional):
[alias]
test = "nextest run"
Create standard test directory structure:
JavaScript/TypeScript:
tests/
├── setup.ts # Test setup and global mocks
├── unit/ # Unit tests
│ └── utils.test.ts
├── integration/ # Integration tests
│ └── api.test.ts
└── e2e/ # E2E tests
└── user-flow.test.ts
Python:
tests/
├── conftest.py # pytest fixtures and configuration
├── unit/ # Unit tests
│ └── test_utils.py
├── integration/ # Integration tests
│ └── test_api.py
└── e2e/ # E2E tests
└── test_user_flow.py
Rust:
tests/
├── integration_test.rs # Integration tests
└── common/ # Shared test utilities
└── mod.rs
Add test commands to GitHub Actions workflow:
JavaScript/TypeScript (Vitest):
- name: Run tests
run: npm test -- --reporter=junit --reporter=default --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
Python (pytest):
- name: Run tests
run: |
uv run pytest --junitxml=junit.xml --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
Rust (cargo-nextest):
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --profile ci --no-fail-fast
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: target/nextest/ci/junit.xml
Update .fvh-standards.yaml:
standards_version: "2025.1"
last_configured: "[timestamp]"
components:
tests: "2025.1"
tests_framework: "[vitest|jest|pytest|nextest]"
tests_coverage_threshold: 80
tests_ci_integrated: true
Jest → Vitest Migration:
Update dependencies:
npm uninstall jest @types/jest
npm install --save-dev vitest @vitest/ui @vitest/coverage-v8
Rename config file:
mv jest.config.ts vitest.config.ts
Update test imports:
// Before (Jest)
import { describe, it, expect } from '@jest/globals';
// After (Vitest with globals)
// No import needed if globals: true in config
Update package.json scripts:
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}
unittest → pytest Migration (Python):
Install pytest:
uv add --group dev pytest pytest-cov
Convert test files:
# Before (unittest)
import unittest
class TestExample(unittest.TestCase):
def test_something(self):
self.assertEqual(1, 1)
# After (pytest)
def test_something():
assert 1 == 1
Convert assertions:
self.assertEqual(a, b) → assert a == bself.assertTrue(x) → assert xself.assertRaises(Error) → with pytest.raises(Error):Testing Configuration Complete
===============================
Framework: Vitest 2.x (preferred)
Language: TypeScript
Configuration Applied:
✅ vitest.config.ts created with best practices
✅ Coverage provider configured (@vitest/coverage-v8)
✅ Test environment set (jsdom)
✅ Watch exclusions configured
✅ Coverage thresholds set (80%)
Scripts Added:
✅ npm test (run tests once)
✅ npm run test:watch (watch mode)
✅ npm run test:ui (Vitest UI)
✅ npm run test:coverage (with coverage report)
Test Structure:
✅ tests/ directory created
✅ setup.ts configured
✅ Example tests added
Next Steps:
1. Write your first test:
npm run test:watch
2. View test UI:
npm run test:ui
3. Check coverage:
npm run test:coverage
4. Verify CI integration:
Check .github/workflows/ for test job
Documentation: docs/TESTING.md
| Flag | Description |
|---|---|
--check-only | Report status without offering fixes |
--fix | Apply all fixes automatically without prompting |
--framework <framework> | Override framework detection (vitest, jest, pytest, nextest) |
# Check compliance and offer fixes
/configure:tests
# Check only, no modifications
/configure:tests --check-only
# Auto-fix all issues
/configure:tests --fix
# Force Vitest even if Jest is detected
/configure:tests --framework vitest
/configure:coverage - Configure coverage thresholds and reporting/configure:all - Run all FVH compliance checks/test:run - Universal test runner/test:setup - Comprehensive testing infrastructure setup