| name | Mandatory Testing & Linting Guard |
| description | Enforce mandatory npm test and npm run lint execution after every code change with no exceptions for code quality and test coverage compliance |
Mandatory Testing & Linting Guard
This skill enforces the CRITICAL requirement: After EVERY code change, you MUST run npm test and npm run lint. No exceptions for "small changes" or "trivial updates".
When to Use This Skill
ALWAYS. This skill should be invoked automatically after:
- Any code modification (TypeScript, JavaScript, CSS)
- Any component creation or update
- Any service or utility change
- Any configuration file modification
- Before creating a git commit
- Before creating a pull request
Trigger phrases:
- "I've made changes to..."
- "I updated the code..."
- "I created a new component..."
- "I fixed the bug..."
- "I added a feature..."
Critical Requirement
From CLAUDE.md:
CRITICAL: After EVERY code change, you MUST run:
npm test- Ensure all tests passnpm run lint- Verify code qualityNever proceed without both passing. No exceptions for "small changes".
Standard Workflow
Workflow Overview
Code Change → npm test → npm run lint → Commit/PR
↓ ↓ ↓ ↓
Edit 875 tests ESLint Git commit
MUST pass MUST pass (only if both pass)
Step-by-Step Process
1. After Making Code Changes
Action: Run the test suite:
npm test
Expected output:
✓ 875 tests passed
Test Files 46 passed (46)
Tests 875 passed (875)
Duration 12.5s
What to check:
- All 875+ tests pass (or current total)
- No test failures or errors
- No warnings about missing coverage
- Coverage threshold (50%) is met
If tests fail:
- DO NOT proceed to linting
- Fix the failing tests first
- Re-run
npm testuntil all pass
2. After Tests Pass
Action: Run ESLint:
npm run lint
Expected output:
✔ No ESLint warnings or errors found
What to check:
- Zero errors
- Zero warnings
- All files comply with style guide
If linting fails:
- Try auto-fix first:
npm run lint:fix - Manually fix remaining issues
- Re-run
npm run lintuntil clean
3. Both Pass - Ready to Commit
Action: Only NOW can you proceed with:
- Creating a git commit
- Pushing to remote
- Creating a pull request
- Marking task as complete
Test Suite Overview
Current Statistics
- Total Tests: 875+
- Test Files: 46
- Coverage Threshold: 50% (statements)
- Test Framework: Vitest + React Testing Library
- Environment: happy-dom
Test Categories
Components (60% coverage):
- 40+ React component tests
- Rendering, interactions, state changes
- Accessibility checks
- Dark mode compatibility
Services (80% coverage):
- StorageManager singleton tests
- PromptManager business logic
- Validation and error handling
- Levenshtein distance algorithms
Hooks (70% coverage):
- Custom React hooks
- usePrompts, useCategories, useSettings
- Async state management
Content Scripts (40% coverage):
- Platform strategy tests
- Injection and insertion logic
- DOM manipulation
Utilities (90% coverage):
- Helper functions
- Formatters and validators
- Logger utilities
Key Test Files
Critical test locations:
src/
├── components/__tests__/ # Component tests
│ ├── PromptCard.test.tsx
│ ├── AddPromptForm.test.tsx
│ └── ... (40+ files)
├── services/__tests__/ # Service tests
│ ├── storage.test.ts
│ └── promptManager.test.ts
├── hooks/__tests__/ # Hook tests
│ └── usePrompts.test.ts
└── content/platforms/__tests__/ # Platform tests
├── claude-strategy.test.ts
└── ... (platform strategies)
Common Test Errors
Error 1: Test Timeout
Symptom:
Error: Test timed out after 5000ms
Solutions:
Add timeout to slow async tests:
it('loads large dataset', async () => { // ... }, 10000); // 10 second timeoutUse
waitForfor async state updates:await waitFor(() => { expect(screen.getByText('Loaded')).toBeInTheDocument(); });
Error 2: Mock Not Called
Symptom:
AssertionError: expected mock to have been called
Solutions:
Verify mock is setup correctly:
expect(mockFn).toHaveBeenCalled();Check you're calling the right mock:
expect(StorageManager.getInstance().getPrompts).toHaveBeenCalled();Clear mocks between tests:
beforeEach(() => { vi.clearAllMocks(); });
Error 3: DOM Element Not Found
Symptom:
Unable to find element with text: "Submit"
Solutions:
Use correct query method:
// Wait for async rendering await screen.findByText('Submit'); // Query by role screen.getByRole('button', { name: /submit/i });Debug the rendered output:
const { debug } = render(<Component />); debug(); // Prints current DOM
Error 4: Coverage Below Threshold
Symptom:
ERROR: Coverage for statements (48%) is below threshold (50%)
Solutions:
Generate coverage report to see gaps:
npm run test:coverage open coverage/index.htmlAdd tests for uncovered code paths:
- Error handling branches
- Edge cases
- Conditional logic
Check if new files need tests:
# See which files have low coverage npm run test:coverage | grep -E "^[^│]*│[^│]*│[^│]*[0-9]+\.[0-9]+%"
Error 5: React 19 Hook Errors
Symptom:
Error: useActionState is not defined
Note: React 19 hooks (useActionState, useOptimistic) are not yet supported in Node.js test environments.
Solutions:
- Test these hooks manually in browser
- Skip automated tests for React 19-specific hooks
- Use integration tests instead of unit tests
- Document manual testing in PR description
ESLint Common Issues
Issue 1: Unused Variables
Symptom:
'variable' is defined but never used
Solutions:
- Remove unused variables
- Prefix with underscore if intentionally unused:
const _unusedParam = value; // Intentionally unused - Use in function signature but not body:
function canHandle(_element: HTMLElement): boolean { // _element is part of interface but not needed }
Issue 2: Missing Return Type
Symptom:
Missing return type on function
Solutions:
- Add explicit return type:
// Before async function getData() { return fetch('/api'); } // After async function getData(): Promise<Response> { return fetch('/api'); }
Issue 3: Any Type Usage
Symptom:
Unexpected any. Specify a different type
Solutions:
Use specific type instead:
// Before const data: any = JSON.parse(str); // After const data: Prompt = JSON.parse(str);Use
unknownfor truly unknown types:try { // ... } catch (error) { const err = error as Error; // Cast from unknown }
Issue 4: Missing Dependencies in useEffect
Symptom:
React Hook useEffect has missing dependencies
Solutions:
Add missing dependencies:
useEffect(() => { loadData(id); }, [id]); // Add id to dependency arrayIf intentionally omitted, add comment:
useEffect(() => { loadOnce(); }, []); // eslint-disable-line react-hooks/exhaustive-deps
Issue 5: Console Statements
Symptom:
Unexpected console statement
Solution:
Use the centralized Logger instead of console.*:
// ❌ DON'T
console.log('Debug message');
console.error('Error occurred');
// ✅ DO
import { Logger } from '../utils';
Logger.debug('Debug message', { component: 'MyComponent' });
Logger.error('Error occurred', toError(err), { component: 'MyComponent' });
Auto-Fix Strategies
Strategy 1: Auto-Fix Linting Issues
Action:
npm run lint:fix
This automatically fixes:
- Formatting issues
- Missing semicolons
- Import order
- Unused imports
- Simple style violations
Remaining issues must be fixed manually.
Strategy 2: Test-Driven Fixes
When tests fail, use this workflow:
Identify the failing test:
npm test -- --reporter=verboseRun only that test file:
npm test -- PromptCard.test.tsxEnable watch mode for rapid iteration:
npm test -- PromptCard.test.tsx --watchFix the code until test passes
Run full suite again:
npm test
Strategy 3: Coverage-Driven Testing
When coverage is low:
Generate coverage report:
npm run test:coverageOpen HTML report:
open coverage/index.htmlFind red/yellow highlighted lines (uncovered code)
Add tests for those code paths
Re-run coverage check
Emergency Override Scenarios
Scenario 1: Pre-commit Hook Failure
When: Git commit fails due to test/lint errors
NEVER override with:
git commit --no-verify # ❌ NEVER DO THIS
Instead:
- Fix the errors properly
- Run tests and linting again
- Then commit normally
Scenario 2: CI/CD Pipeline Failure
When: GitHub Actions fails on PR
NEVER merge with:
- Disabling required checks
- Admin override
- Force push
Instead:
- Pull latest changes:
git pull origin main - Fix the errors locally
- Run tests and linting
- Push the fixes
- Wait for CI to pass
Scenario 3: Urgent Hotfix Needed
When: Production bug needs immediate fix
Even for hotfixes:
- Create the fix
- Run
npm test - Run
npm run lint - Both MUST pass
- Then deploy
Rationale: Broken tests = broken code. Never skip.
Integration with Development Workflow
Git Commit Workflow
# 1. Make your changes
vim src/components/MyComponent.tsx
# 2. MANDATORY: Run tests
npm test
# 3. MANDATORY: Run linting
npm run lint
# 4. If both pass, stage changes
git add src/components/MyComponent.tsx
# 5. Commit
git commit -m "feat: add MyComponent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
# 6. Push
git push origin feature-branch
Pull Request Workflow
Before creating PR:
# 1. Ensure on latest main
git checkout main
git pull origin main
# 2. Rebase feature branch
git checkout feature-branch
git rebase main
# 3. Run full test suite
npm test
# 4. Run linting
npm run lint
# 5. Run build to ensure no build errors
npm run build
# 6. All three must pass before creating PR
In PR description, include:
## Testing Checklist
- [x] All 875+ tests pass locally
- [x] ESLint shows no errors or warnings
- [x] Coverage threshold maintained (50%+)
- [x] Manual testing completed on:
- [ ] Chrome browser
- [ ] AI platforms: Claude, ChatGPT, Perplexity
- [ ] Light and dark mode
Continuous Integration
GitHub Actions Checks
The project has automated CI checks that run on every PR:
PR Checks (.github/workflows/pr-checks.yml):
- name: Run Tests
run: npm test
- name: Run Linting
run: npm run lint
- name: Check Coverage
run: npm run test:coverage
All checks must pass before PR can be merged.
Pre-commit Hooks
The project uses Husky for pre-commit hooks:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"vitest related --run"
]
}
}
This automatically runs:
- ESLint on changed files
- Related tests for changed code
Quick Reference
Essential Commands
| Command | Purpose | When to Run |
|---|---|---|
npm test |
Run all 875+ tests | After EVERY change |
npm run lint |
Run ESLint checks | After EVERY change |
npm run lint:fix |
Auto-fix linting issues | When lint errors occur |
npm run test:coverage |
Generate coverage report | Weekly or when adding features |
npm run test:ui |
Interactive test UI | Debugging test failures |
npm test -- --watch |
Watch mode for tests | During active development |
Success Indicators
Tests Pass:
✓ 875 tests passed
✓ Test Files 46 passed (46)
✓ Coverage: 55% (exceeds 50% threshold)
Linting Pass:
✔ No ESLint warnings or errors found
Failure Indicators
Tests Fail:
✖ 3 tests failed
✖ Expected: true, Received: false
Action: Fix tests before proceeding
Linting Fail:
✖ 5 problems (3 errors, 2 warnings)
Action: Run npm run lint:fix, then fix remaining issues
Enforcement Checklist
Before marking ANY task complete, verify:
- All code changes are complete
-
npm testhas been run - All 875+ tests pass (or current total)
-
npm run linthas been run - Zero ESLint errors
- Zero ESLint warnings
- Coverage threshold (50%) maintained
- No console errors in test output
- Build succeeds (
npm run build)
Only after ALL checkboxes are checked can you:
- Create a git commit
- Push to remote repository
- Create a pull request
- Mark the task as complete
Related Documentation
- Testing Guide:
docs/TESTING.md- Comprehensive testing strategies - Development Workflow:
CLAUDE.md- Essential commands and guidelines - Architecture:
docs/ARCHITECTURE.md- System design patterns
Philosophy
Why This Matters:
- Test Coverage: 875+ tests ensure nothing breaks
- Code Quality: ESLint catches bugs before they ship
- Consistency: Every developer follows the same standards
- Confidence: Changes don't introduce regressions
- Documentation: Tests serve as living documentation
The Rule:
"If it's too small to test, it's too small to break."
Even "trivial" changes can cause cascading failures. Always test. Always lint. No exceptions.
Last Updated: 2025-10-18 Skill Version: 1.0.0