Skip to main content

testing-strategy-enforcement

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.

Datos de origen

Repositorio
Hack23/blacktrigram
Última actividad en el origen
31 de enero de 2026 a las 15:54
Idioma detectado de SKILL.md
inglés
Estrellas
8
Forks
7

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
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** ```typescript // Partial excerpt from vitest.config.ts - Coverage configuration import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: { provider: 'v8', reporter: ['text', 'json', 'html', 'lcov'], lines: 90, // Minimum 90% line coverage functions: 90, // Minimum 90% function coverage branches: 85, // Minimum 85% branch coverage statements: 90, // Minimum 90% statement coverage exclude: [ 'node_modules/', 'dist/', 'coverage/', '**/*.test.ts', '**/*.test.tsx', 'src/test/**', ], }, }, }); ``` ✅ **Coverage by Component Type** ```typescript // Target coverage levels per component category const COVERAGE_TARGETS = { // Core game systems - CRITICAL combatSystem: { unit: 95, e2e: 85 }, vitalPointSystem: { unit: 95, e2e: 80 }, trigramSystem: { unit: 95, e2e: 80 }, animationSystem: { unit: 90, e2e: 75 }, // Three.js components - MODERATE (harder to test) threejsComponents: { unit: 85, e2e: 70 }, sceneComponents: { unit: 85, e2e: 70 }, // UI components - HIGH reactComponents: { unit: 95, e2e: 80 }, screens: { unit: 90, e2e: 85 }, // Utility functions - MAXIMUM utilities: { unit: 100, e2e: 0 }, constants: { unit: 100, e2e: 0 }, // Audio system - HIGH audioSystem: { unit: 90, e2e: 75 }, // State management - CRITICAL stateManagement: { unit: 95, e2e: 80 }, } as const; ``` ### 2. Unit Testing Strategy (Vitest) **ALWAYS write comprehensive unit tests:** ✅ **Test File Organization** ```typescript // ALWAYS place unit tests in __tests__ directory 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** ```typescript 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(() => { // Setup fresh instance for each test combatSystem = new CombatSystem(); }); afterEach(() => { // Cleanup resources vi.clearAllMocks(); }); // Group related tests 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); expect(combatSystem.currentStance).toBe(stance); }); }); it('should reject invalid stance names', () => { expect(() => combatSystem.setStance('INVALID')).toThrow(); }); }); describe('Vital Point Targeting', () => { it('should detect all 70 vital points', () => { const vitalPoints = combatSystem.getVitalPoints(); expect(vitalPoints).toHaveLength(70); }); it('should calculate correct damage for vital point hit', () => { const damage = combatSystem.calculateVitalPointDamage({ point: 'BAIHUI', // GV20 force: 100, accuracy: 0.95, }); expect(damage).toBeGreaterThan(100); // Vital point multiplier expect(damage).toBeLessThanOrEqual(200); }); it('should apply reduced damage for missed vital points', () => { const missedDamage = combatSystem.calculateVitalPointDamage({ point: 'BAIHUI', force: 100, accuracy: 0.3, // Poor accuracy }); expect(missedDamage).toBeLessThan(100); }); }); describe('Performance', () => { it('should calculate combat within 5ms', () => { const start = performance.now(); for (let i = 0; i < 1000; i++) { combatSystem.calculateDamage(100, 0.8, 'BAIHUI'); } const duration = performance.now() - start; expect(duration).toBeLessThan(5); // <5ms per frame budget }); }); describe('Edge Cases', () => { it('should handle zero damage gracefully', () => { const damage = combatSystem.calculateDamage(0, 1.0, 'BAIHUI'); expect(damage).toBe(0); }); it('should handle negative force by clamping to zero', () => { const damage = combatSystem.calculateDamage(-50, 1.0, 'BAIHUI'); expect(damage).toBe(0); }); it('should handle accuracy outside 0-1 range', () => { expect(() => combatSystem.calculateDamage(100, 1.5, 'BAIHUI') ).toThrow(); }); }); }); ``` ✅ **Testing Three.js Components** ```typescript import { render } from '@testing-library/react'; import { Canvas } from '@react-three/fiber'; import { describe, it, expect } from 'vitest'; import { CombatCharacter3D } from '../CombatCharacter3D'; describe('CombatCharacter3D', () => { // Helper to render Three.js components 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" /> ); const canvas = container.querySelector('canvas'); expect(canvas).toBeInTheDocument(); }); it('should apply Korean theming colors', () => { const { container } = render3D( <CombatCharacter3D position={[0, 0, 0]} stance="GEON" data-testid="combat-character" /> ); // Verify component renders (color testing requires more complex setup) expect(container).toBeTruthy(); }); it('should update stance color when stance changes', () => { const { rerender } = render3D( <CombatCharacter3D position={[0, 0, 0]} stance="GEON" /> ); rerender( <Canvas> <CombatCharacter3D position={[0, 0, 0]} stance="TAE" /> </Canvas> ); // Verify re-render succeeds expect(true).toBe(true); }); }); ``` ### 3. E2E Testing Strategy (Cypress) **ALWAYS write E2E tests for user workflows:** ✅ **E2E Test Organization** ```typescript // cypress/e2e/ - User workflow tests cypress/ ├── e2e/ │ ├── combat-system.cy.ts // Combat gameplay flows │ ├── trigram-selection.cy.ts // Stance selection UI │ ├── vital-points.cy.ts // Vital point targeting │ ├── intro-screen.cy.ts // Navigation flows │ └── performance.cy.ts // 60fps validation ``` ✅ **Comprehensive E2E Test** ```typescript 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', '건') .and('contain', 'Heaven'); }); it('should show trigram symbol (☰)', () => { cy.get('[data-testid="stance-GEON"]') .should('contain', '☰'); }); }); describe('Vital Point Targeting', () => { it('should highlight all 70 vital points', () => { cy.get('[data-testid="vital-point-overlay"]').click(); cy.get('[data-testid^="vital-point-"]') .should('have.length', 70); }); it('should show Korean and TCM names on hover', () => { cy.get('[data-testid="vital-point-BAIHUI"]').trigger('mouseover'); cy.get('[data-testid="vital-point-tooltip"]') .should('contain', '백회') .and('contain', 'Baihui (GV20)'); }); it('should calculate critical damage on precise hit', () => { cy.get('[data-testid="vital-point-BAIHUI"]').click(); cy.get('[data-testid="damage-indicator"]') .should('contain', 'CRITICAL') .and('have.css', 'color', 'rgb(255, 68, 68)'); // KOREAN_COLORS.CRITICAL_HIT }); }); describe('Combat Performance', () => { it('should maintain 60fps during combat', () => { cy.window().then(win => { let frameCount = 0; let lastTime = performance.now(); const measureFPS = () => { const currentTime = performance.now(); const deltaTime = currentTime - lastTime; if (deltaTime >= 1000) { const fps = frameCount / (deltaTime / 1000); expect(fps).to.be.greaterThan(55); // Allow 5fps tolerance frameCount = 0; lastTime = currentTime; } frameCount++; if (frameCount < 300) { // Test for 5 seconds requestAnimationFrame(measureFPS); } }; requestAnimationFrame(measureFPS); }); }); }); describe('Accessibility', () => { it('should be keyboard navigable', () => {
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub