Skip to content

Add doc and test for gameTest#12

Merged
nerikebosch merged 1 commit intodevelopfrom
feature/fix-accuracy
Jan 17, 2026
Merged

Add doc and test for gameTest#12
nerikebosch merged 1 commit intodevelopfrom
feature/fix-accuracy

Conversation

@quangptt0910
Copy link
Copy Markdown
Owner

@quangptt0910 quangptt0910 commented Jan 17, 2026

PR Type

Tests, Enhancement, Documentation


Description

  • Add comprehensive test suite for IrisFaceMeshTracker with 614 test cases covering initialization, iris position calculation, gaze prediction, saccade detection, and CSV export

  • Add JSDoc documentation to IrisFaceMeshTracker class methods explaining parameters, return values, and functionality

  • Enhance saccadeData tests with additional test cases for delayed saccades, anti-saccade latency bounds, hypometric rate calculation, and error handling

  • Clean up test file comments and improve test data structure consistency


Diagram Walkthrough

flowchart LR
  A["IrisFaceMeshTracker<br/>Implementation"] -->|"Test Coverage"| B["iris-facemesh.test.js<br/>614 test cases"]
  A -->|"Documentation"| C["JSDoc Comments<br/>Method descriptions"]
  D["saccadeData<br/>Analysis"] -->|"Enhanced Tests"| E["saccadeData.test.js<br/>Additional scenarios"]
  B -->|"Validates"| F["Initialization & Tracking"]
  B -->|"Validates"| G["Iris Position & Gaze"]
  B -->|"Validates"| H["Saccade Detection"]
  E -->|"Tests"| I["Latency Classification"]
  E -->|"Tests"| J["Data Quality Metrics"]
Loading

File Walkthrough

Relevant files
Tests
iris-facemesh.test.js
Comprehensive test suite for iris tracking functionality 

src/tests/utils/iris-facemesh.test.js

  • Created comprehensive test suite with 614 lines covering all
    IrisFaceMeshTracker functionality
  • Tests include constructor initialization, iris position calculation,
    gaze prediction with calibration models, trial context management,
    saccade detection, CSV export, and cleanup operations
  • Includes helper function createLandmarks() to generate mock MediaPipe
    landmark data with configurable overrides
  • Mocks MediaPipe FaceLandmarker and detectSaccade dependencies for
    isolated unit testing
  • Covers edge cases like missing landmarks, invalid eye sides, null
    calibration models, and empty tracking data
  • Integration tests verify trial context persistence and bilateral iris
    position consistency
+614/-0 
saccadeData.test.js
Enhanced saccade analysis tests with additional scenarios

src/tests/utils/saccadeData.test.js

  • Added mock for detectSaccade.calculateAdaptiveThreshold function
  • Added test case for delayed saccade classification with physiological
    plausibility check
  • Added test case for anti-saccade latency bounds validation
  • Added test case for hypometric rate calculation in
    aggregateTrialStatistics
  • Added test case for reduced anti-saccade cost identification in
    compareProVsAnti
  • Added test case for error handling in compareProVsAnti with null
    values
  • Improved test data structure consistency by adding missing quality
    metrics
  • Cleaned up inline comments and improved code readability
+112/-24
Documentation
iris-facemesh.js
Add comprehensive JSDoc documentation to tracker class     

src/views/GameTest/utils/iris-facemesh.js

  • Added comprehensive JSDoc documentation to class definition explaining
    overall responsibility
  • Added JSDoc comments to getRelativeIrisPos() method with parameter and
    return value descriptions
  • Added JSDoc comments to initialize() method explaining MediaPipe
    FaceLandmarker setup
  • Added JSDoc comments to setCalibrationModel() method documenting model
    parameter structure
  • Added JSDoc comments to predictGaze() method with iris coordinates and
    eye parameter documentation
  • Added JSDoc comments to startCamera() method explaining camera stream
    initialization
  • Added JSDoc comments to processFrame() method describing the main
    video processing loop
  • Added JSDoc comments to performSaccadeDetection() method explaining
    saccade detection logic
  • Added JSDoc comments to startTracking() and stopTracking() methods
  • Added JSDoc comments to addTrialContext() method explaining trial
    context attachment
  • Added JSDoc comments to exportCSV() method
  • Added JSDoc comments to cleanup() method explaining resource cleanup
+56/-3   

@quangptt0910
Copy link
Copy Markdown
Owner Author

please review the tests :D

@qodo-code-review
Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing input validation: getRelativeIrisPos() does not validate eyeSide and will throw when EYE_INDICES[eyeSide] is
undefined instead of handling the edge case gracefully.

Referred Code
getRelativeIrisPos(landmarks, eyeSide) {
    const indices = EYE_INDICES[eyeSide];
    const pInner = landmarks[indices.inner];

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated input parameter: getRelativeIrisPos() uses the externally-provided eyeSide parameter without validation,
allowing crashes (DoS-style) when an unexpected value is passed.

Referred Code
getRelativeIrisPos(landmarks, eyeSide) {
    const indices = EYE_INDICES[eyeSide];
    const pInner = landmarks[indices.inner];

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Logs include model: The tracker logs the full model object via console.log, which could inadvertently include
sensitive or high-volume data and is unstructured for auditing.

Referred Code
setCalibrationModel(model) {
    this.calibrationModel = model;
    console.log("Calibration model set in tracker:", model);
}

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Decompose the monolithic IrisFaceMeshTracker class

The IrisFaceMeshTracker class handles too many responsibilities, such as camera
management, data processing, and exporting. It should be refactored into
smaller, more focused classes to improve modularity.

Examples:

src/views/GameTest/utils/iris-facemesh.js [17-505]
class IrisFaceMeshTracker {
    constructor() {
        this.faceLandmarker = null;
        this.isTracking = false;
        this.trackingData = [];
        this.videoElement = null;
        this.lastVideoTime = -1;

        this.startTime = null;
        this.animationId = null;

 ... (clipped 479 lines)

Solution Walkthrough:

Before:

class IrisFaceMeshTracker {
    // State properties
    faceLandmarker;
    isTracking;
    trackingData;
    videoElement;
    calibrationModel;

    // Responsibilities
    async initialize() { /* MediaPipe & video setup */ }
    async startCamera() { /* getUserMedia */ }
    processFrame() { /* Main processing loop */ }
    getRelativeIrisPos(...) { /* Data calculation */ }
    predictGaze(...) { /* Data calculation */ }
    startTracking() { /* State management */ }
    stopTracking() { /* State management */ }
    addTrialContext(...) { /* Context management */ }
    exportCSV() { /* Data export */ }
    cleanup() { /* Resource cleanup */ }
}

After:

class CameraManager {
    videoElement;
    initialize() { /* Creates and manages video element */ }
    start() { /* Starts camera stream */ }
    stop() { /* Stops camera stream */ }
    cleanup() { /* Removes video element */ }
}

class GazeProcessor {
    faceLandmarker;
    calibrationModel;
    initialize() { /* Sets up MediaPipe */ }
    processFrame(videoFrame) { /* Processes a single frame */ }
    // ... other calculation methods
}

class TrackingSession {
    trackingData;
    isTracking;
    start() { /* Manages session state */ }
    stop() { /* Manages session state */ }
    addTrialContext(...) { /* Adds context to data */ }
    exportCSV() { /* Exports session data */ }
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical architectural flaw (God Class) in IrisFaceMeshTracker and astutely uses the PR's new 600+ line test file as strong evidence for the class's excessive complexity.

High
Possible issue
Prevent errors from missing iris data

In the exportCSV function, add optional chaining (?.) when accessing
point.leftIris, point.rightIris, and point.avgIris properties to prevent
TypeError if this data is missing.

src/views/GameTest/utils/iris-facemesh.js [448-471]

 // CSV header
 let csv = 'timestamp,leftIris_x,leftIris_y,rightIris_x,rightIris_y,avgIris_x,avgIris_y,cal_left_x,cal_left_y,cal_right_x,cal_right_y,cal_avg_x,cal_avg_y,isSaccade,velocity,trial,dotPosition,targetX,targetY\n';
 
 
 // CSV rows
 this.trackingData.forEach(point => {
 
     csv += `${point.startTime || point.timestamp},`;
-    csv += `${point.leftIris.x},${point.leftIris.y},`;
-    csv += `${point.rightIris.x},${point.rightIris.y},`;
-    csv += `${point.avgIris.x},${point.avgIris.y},`;
+    csv += `${point.leftIris?.x ?? ''},${point.leftIris?.y ?? ''},`;
+    csv += `${point.rightIris?.x ?? ''},${point.rightIris?.y ?? ''},`;
+    csv += `${point.avgIris?.x ?? ''},${point.avgIris?.y ?? ''},`;
     
     // Add calibrated data to CSV
     const c = point.calibrated || {};
     // Use nullish coalescing operator (??) to allow 0 values but handle null/undefined
     csv += `${c.left?.x ?? ''},${c.left?.y ?? ''},`;
     csv += `${c.right?.x ?? ''},${c.right?.y ?? ''},`;
     csv += `${c.avg?.x ?? ''},${c.avg?.y ?? ''},`;
 
     csv += `${point.isSaccade || false},`;
     csv += `${point.velocity || 0},`; // Added velocity column
     csv += `${point.trial || ''},${point.dotPosition || ''},`;
     csv += `${point.targetX ?? ''},${point.targetY ?? ''}\n`;
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a potential TypeError in the exportCSV function if iris data is missing for a frame and provides a robust fix using optional chaining, preventing a runtime crash.

Medium
Add null check for quality object

In aggregateTrialStatistics, add a check for t.quality before accessing
t.quality.dataQuality to prevent a TypeError if the quality object is missing
from a trial.

src/views/GameTest/utils/saccadeData.js [198-203]

 export const aggregateTrialStatistics = (trialsData, trialType = 'unknown') => {
     const validTrials = trialsData.filter(t =>
         t.isSaccade &&
         t.isPhysiologicallyPlausible &&
-        t.quality.dataQuality > 0.7 // Only include high-quality trials
+        t.quality && t.quality.dataQuality > 0.7 // Only include high-quality trials
     );

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential TypeError when filtering trials if the quality object is missing and provides a simple, effective guard to prevent the application from crashing.

Medium
Use modern fake timers for testing

To fix a failing test for getRelativeTime, switch to modern fake timers in Jest,
which correctly mock Date.now(), ensuring the time advancement is properly
simulated.

src/tests/utils/iris-facemesh.test.js [397-407]

 test('should return elapsed time since start', () => {
-    const beforeTime = Date.now();
-    tracker.startTime = beforeTime;
+    jest.useFakeTimers(); // Modern timers mock Date.now()
 
-    jest.useFakeTimers();
+    tracker.startTime = Date.now();
     jest.advanceTimersByTime(100);
 
-    expect(tracker.getRelativeTime()).toBeCloseTo(100, -1);
+    expect(tracker.getRelativeTime()).toBe(100);
 
     jest.useRealTimers();
 });
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the test for getRelativeTime is flawed because legacy fake timers do not mock Date.now(), and proposes a valid fix using modern timers, ensuring the test's correctness.

Medium
General
Prevent divide by zero

In predictGaze, guard against division-by-zero by ensuring window.innerWidth and
window.innerHeight are at least 1 before using them as divisors.

src/views/GameTest/utils/iris-facemesh.js [162-164]

     if (Math.abs(rawX) > 2.0 || Math.abs(rawY) > 2.0) {
-        return { x: rawX / window.innerWidth, y: rawY / window.innerHeight };
+        const w = window.innerWidth || 1;
+        const h = window.innerHeight || 1;
+        return { x: rawX / w, y: rawY / h };
     }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: This suggestion correctly identifies a potential division-by-zero error if window.innerWidth or innerHeight is zero and provides a simple guard, improving the code's robustness in edge cases.

Low
Null out references in cleanup

In the cleanup method, set this.faceLandmarker and this.videoElement to null
after their resources are released to aid garbage collection.

src/views/GameTest/utils/iris-facemesh.js [494-504]

     cleanup() {
         this.stopTracking();
 
         if (this.faceLandmarker) {
             this.faceLandmarker.close();
+            this.faceLandmarker = null;
         }
 
         if (this.videoElement && this.videoElement.parentNode) {
             this.videoElement.parentNode.removeChild(this.videoElement);
+            this.videoElement = null;
         }
     }
  • Apply / Chat
Suggestion importance[1-10]: 3

__

Why: The suggestion offers a minor improvement for memory management by nullifying references in the cleanup method, which is good practice but has a low impact on functionality.

Low
  • More

@nerikebosch nerikebosch merged commit ca26b44 into develop Jan 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants