| name | testing-strategy-enforcement |
| description | Enforces comprehensive testing standards for Black Trigram including >90% coverage,
unit tests (Vitest), E2E tests (Cypress), Three.js component testing, performance
testing (60fps target), and accessibility testing. Ensures test quality and coverage.
|
| license | MIT |
Testing Strategy Enforcement Skill
Purpose
This skill ensures that all code changes in Black Trigram maintain exceptional test coverage, proper testing strategies across unit/E2E layers, and validate performance and accessibility standards throughout the codebase.
When to Apply
Automatically trigger this skill when:
- Adding new features or components
- Modifying existing game logic or systems
- Implementing UI components (React or Three.js)
- Working with combat, animation, or vital point systems
- Creating or updating state management logic
- Adding performance-critical code
- Implementing accessibility features
- Refactoring existing code
- Reviewing pull requests with code changes
Core Principles
1. Test Coverage Requirements
ALWAYS maintain these coverage thresholds:
✅ Overall Coverage Targets
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
lines: 90,
functions: 90,
branches: 85,
statements: 90,
exclude: [
'node_modules/',
'dist/',
'coverage/',
'**/*.test.ts',
'**/*.test.tsx',
'src/test/**',
],
},
},
});
✅ Coverage by Component Type
const COVERAGE_TARGETS = {
combatSystem: { unit: 95, e2e: 85 },
vitalPointSystem: { unit: 95, e2e: 80 },
trigramSystem: { unit: 95, e2e: 80 },
animationSystem: { unit: 90, e2e: 75 },
threejsComponents: { unit: 85, e2e: 70 },
sceneComponents: { unit: 85, e2e: 70 },
reactComponents: { unit: 95, e2e: 80 },
screens: { unit: 90, e2e: 85 },
utilities: { unit: 100, e2e: 0 },
constants: { unit: 100, : },
: { : , : },
: { : , : },
} ;
2. Unit Testing Strategy (Vitest)
ALWAYS write comprehensive unit tests:
✅ Test File Organization
src/
├── systems/
│ ├── combat/
│ │ ├── CombatSystem.ts
│ │ └── __tests__/
│ │ ├── CombatSystem.test.ts
│ │ ├── VitalPointSystem.test.ts
│ │ └── DamageCalculation.test.ts
│ ├── trigram/
│ │ ├── TrigramSystem.ts
│ │ └── __tests__/
│ │ └── TrigramSystem.test.ts
✅ Comprehensive Test Structure
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { CombatSystem } from '../CombatSystem';
import { TRIGRAM_STANCES } from '@/types/constants';
describe('CombatSystem', () => {
let combatSystem: CombatSystem;
beforeEach(() => {
combatSystem = new CombatSystem();
});
afterEach(() => {
vi.clearAllMocks();
});
describe('Stance Management', () => {
it('should initialize with Geon stance by default', () => {
expect(combatSystem.currentStance).toBe('GEON');
});
it('should transition between all 8 trigram stances', () => {
Object.keys(TRIGRAM_STANCES).forEach(stance => {
combatSystem.setStance(stance);
(combatSystem.).(stance);
});
});
(, {
( combatSystem.()).();
});
});
(, {
(, {
vitalPoints = combatSystem.();
(vitalPoints).();
});
(, {
damage = combatSystem.({
: ,
: ,
: ,
});
(damage).();
(damage).();
});
(, {
missedDamage = combatSystem.({
: ,
: ,
: ,
});
(missedDamage).();
});
});
(, {
(, {
start = performance.();
( i = ; i < ; i++) {
combatSystem.(, , );
}
duration = performance.() - start;
(duration).();
});
});
(, {
(, {
damage = combatSystem.(, , );
(damage).();
});
(, {
damage = combatSystem.(-, , );
(damage).();
});
(, {
(
combatSystem.(, , )
).();
});
});
});
✅ Testing Three.js Components
import { render } from '@testing-library/react';
import { Canvas } from '@react-three/fiber';
import { describe, it, expect } from 'vitest';
import { CombatCharacter3D } from '../CombatCharacter3D';
describe('CombatCharacter3D', () => {
const render3D = (component: React.ReactElement) => {
return render(
<Canvas data-testid="test-canvas">
<Suspense fallback={null}>
{component}
</Suspense>
</Canvas>
);
};
it('should render without crashing', () => {
const { container } = render3D(
<CombatCharacter3D
position={[0, 0, 0]}
stance="GEON"
/>
);
canvas = container.();
(canvas).();
});
(, {
{ container } = (
);
(container).();
});
(, {
{ rerender } = (
);
(
);
().();
});
});
3. E2E Testing Strategy (Cypress)
ALWAYS write E2E tests for user workflows:
✅ E2E Test Organization
cypress/
├── e2e/
│ ├── combat-system.cy.ts
│ ├── trigram-selection.cy.ts
│ ├── vital-points.cy.ts
│ ├── intro-screen.cy.ts
│ └── performance.cy.ts
✅ Comprehensive E2E Test
describe('Combat System E2E', () => {
beforeEach(() => {
cy.visit('/');
cy.get('[data-testid="start-button"]').click();
cy.get('[data-testid="combat-screen"]').should('be.visible');
});
describe('Trigram Stance Selection', () => {
it('should allow selecting all 8 trigram stances', () => {
const stances = ['GEON', 'TAE', 'LI', 'JIN', 'SON', 'GAM', 'GAN', 'GON'];
stances.forEach(stance => {
cy.get(`[data-testid="stance-${stance}"]`).click();
cy.get('[data-testid="current-stance"]')
.should('contain', stance);
});
});
it('should display bilingual Korean | English text', () => {
cy.get('[data-testid="stance-GEON"]')
.should('contain', '건')
.(, );
});
(, {
cy.()
.(, );
});
});
(, {
(, {
cy.().();
cy.()
.(, );
});
(, {
cy.().();
cy.()
.(, )
.(, );
});
(, {
cy.().();
cy.()
.(, )
.(, , );
});
});
(, {
(, {
cy.().( {
frameCount = ;
lastTime = performance.();
= () => {
currentTime = performance.();
deltaTime = currentTime - lastTime;
(deltaTime >= ) {
fps = frameCount / (deltaTime / );
(fps)...();
frameCount = ;
lastTime = currentTime;
}
frameCount++;
(frameCount < ) {
(measureFPS);
}
};
(measureFPS);
});
});
});
(, {
(, {
cy.().();
cy.().(, );
cy.().();
cy.().(, );
cy.().();
cy.().(, );
});
(, {
cy.()
.(, )
.(, )
.(, );
});
(, {
cy.().($el => {
bgColor = $el.();
textColor = $el.();
(bgColor)..;
(textColor)..;
});
});
});
});
4. Performance Testing Requirements
ALWAYS validate 60fps performance:
✅ Performance Test Pattern
describe('Performance - Combat System', () => {
it('should complete combat calculation in <5ms', () => {
const combatSystem = new CombatSystem();
const iterations = 1000;
const start = performance.now();
for (let i = 0; i < iterations; i++) {
combatSystem.calculateDamage(100, 0.85, 'BAIHUI');
}
const duration = performance.now() - start;
const avgTime = duration / iterations;
expect(avgTime).toBeLessThan(5);
});
it('should handle 1000 vital point checks per frame', () => {
const vitalPointSystem = new VitalPointSystem();
const start = performance.now();
for (let i = 0; i < 1000; i++) {
vitalPointSystem.checkHit([
Math.random() * 2 - 1,
Math.random() * 2 - ,
.() * - ,
]);
}
duration = performance.() - start;
(duration).();
});
(, {
animationSystem = ();
frameCount = ;
start = performance.();
( i = ; i < frameCount; i++) {
animationSystem.();
}
duration = performance.() - start;
avgFrameTime = duration / frameCount;
(avgFrameTime).();
});
});
5. Accessibility Testing Standards
ALWAYS ensure WCAG 2.1 Level AA compliance:
✅ Accessibility Test Pattern
describe('Accessibility - Combat UI', () => {
it('should have proper semantic HTML structure', () => {
const { container } = render(<CombatScreen width={1200} height={800} />);
expect(container.querySelector('main')).toBeInTheDocument();
expect(container.querySelector('nav')).toBeInTheDocument();
});
it('should have ARIA labels on interactive elements', () => {
const { getByRole } = render(<CombatScreen width={1200} height={800} />);
const stanceButton = getByRole('button', { name: /건.*Heaven/i });
expect(stanceButton).toHaveAttribute('aria-label');
});
it('should support keyboard navigation', () => {
const { getByTestId } = render();
firstButton = ();
firstButton.();
(.).(firstButton);
});
(, {
textColor = .;
bgColor = .;
(textColor).();
(bgColor).();
});
(, {
{ getByTestId } = ();
button = ();
button.();
styles = .(button);
(
styles. !== ||
styles. !==
).();
});
});
6. Common Testing Anti-Patterns to REJECT
Immediately flag and reject these patterns:
❌ Missing Test Files
src/systems/grappling/GrapplingSystem.ts
❌ Insufficient Test Coverage
describe('CombatSystem', () => {
it('should work', () => {
const combat = new CombatSystem();
expect(combat).toBeTruthy();
});
});
describe('CombatSystem', () => {
it('should initialize with default values', () => { });
it('should handle valid input', () => { });
it('should reject invalid input', () => { });
it('should handle edge cases', () => { });
it('should meet performance targets', () => { });
});
❌ Missing data-testid Attributes
<button onClick={handleClick}>
공격 | Attack
</button>
<button
data-testid="attack-button"
aria-label="공격 | Attack"
onClick={handleClick}
>
공격 | Attack
</button>
❌ No Performance Tests
export function calculateDamage(force: number, accuracy: number): number {
return result;
}
describe('calculateDamage Performance', () => {
it('should complete in <5ms', () => {
const start = performance.now();
for (let i = 0; i < 1000; i++) {
calculateDamage(100, 0.85);
}
const duration = performance.now() - start;
expect(duration / 1000).toBeLessThan(5);
});
});
❌ No Accessibility Tests
export const StanceSelector: React.FC = () => {
return <div>Stance selection</div>;
};
describe('StanceSelector Accessibility', () => {
it('should have ARIA labels', () => { });
it('should be keyboard navigable', () => { });
it('should meet WCAG contrast standards', () => { });
});
7. Required Testing Patterns
Enforce these testing patterns:
✅ Complete Test Suite Structure
describe('ComponentName', () => {
beforeEach(() => { });
afterEach(() => { });
describe('Initialization', () => {
it('should initialize with default values', () => { });
it('should accept custom configuration', () => { });
});
describe('Core Functionality', () => {
it('should handle valid input correctly', () => { });
it('should reject invalid input', () => { });
it('should maintain state correctly', () => { });
});
describe('Edge Cases', () => {
it('should handle empty input', () => { });
it('should handle boundary values', { });
(, { });
});
(, {
(, { });
(, { });
});
(, {
(, { });
});
});
✅ Mock Audio System for Testing
import { vi } from 'vitest';
global.AudioContext = vi.fn().mockImplementation(() => ({
createGain: vi.fn().mockReturnValue({
connect: vi.fn(),
gain: { value: 1 },
}),
createOscillator: vi.fn().mockReturnValue({
connect: vi.fn(),
start: vi.fn(),
stop: vi.fn(),
}),
}));
vi.mock('howler', () => ({
Howl: vi.fn().mockImplementation(() => ({
play: vi.fn(),
stop: vi.fn(),
volume: vi.fn(),
})),
}));
Enforcement Rules
Rule 1: No New Code Without Tests
IF (new feature or component added)
THEN (unit tests AND E2E tests MUST be included)
ELSE (reject the change)
Rule 2: Maintain Minimum 90% Coverage
IF (pull request submitted)
THEN (verify coverage meets 90% threshold)
ELSE (add tests to meet coverage requirement)
Rule 3: All Interactive Elements Need data-testid
IF (button, input, or interactive element added)
THEN (add data-testid attribute)
ELSE (add test ID before approval)
Rule 4: Performance Tests for Critical Code
IF (code affects combat, animation, or rendering)
THEN (include performance tests with <5ms budget)
ELSE (add performance validation)
Rule 5: Accessibility Tests for UI Components
IF (UI component added or modified)
THEN (include ARIA labels, keyboard nav, WCAG contrast tests)
ELSE (add accessibility tests)
Test Quality Checklist
Before approving any code change:
ISO 27001 Alignment
This skill enforces controls from:
- A.12.1 - Operational Procedures (Testing standards documented)
- A.14.2 - Security in Development Processes (Testing in SDLC)
- A.14.3 - Test Data (Test data management practices)
- A.18.2 - Compliance with Security Policies (Testing compliance)
NIST CSF 2.0 Alignment
- GV.PO-01: Testing policy established and communicated
- ID.RA-01: Asset vulnerabilities identified (via testing)
- PR.DS-06: Integrity checking through automated tests
- DE.AE-02: Event analysis capabilities (test failures)
- RS.AN-03: Analysis performed to understand root cause (test debugging)
CIS Controls v8.1 Alignment
- Control 4: Secure Configuration (validated through tests)
- Control 16: Application Software Security (comprehensive testing)
- Control 17: Incident Response Management (test failure handling)
Remember
Testing is not a checkbox—it is the foundation of quality. Every feature, every component, every line of code must be validated through comprehensive, meaningful tests.
When writing tests:
- COVER - Achieve >90% coverage with meaningful tests
- VALIDATE - Test happy paths, edge cases, and error conditions
- PERFORM - Ensure code meets 60fps performance targets
- ACCESS - Validate WCAG 2.1 Level AA accessibility
- INTEGRATE - Test E2E user workflows with Cypress
- AUTOMATE - Run tests in CI/CD pipeline
흑괘의 품질을 검증하라 - Validate the Quality of the Black Trigram