import type {NativePPGProcessingResult} from 'react-native-haptika-ppg'; import {mapNativeProcessingResult} from '../measurement/measurementCoordinator'; const nativeResult = (): NativePPGProcessingResult => ({ valid: false, invalidReason: 'Signal exploitable insuffisant', bpm: { bpm: 72, temporalBpm: Number.NaN, frequencyBpm: 72.4, methodAgreementBpm: 0.4, validPeakCount: 19.7, rejectedPeakCount: 8.2, confidence: 0.51, selectedChannel: 'red-inverted', }, prv: { estimatedRmssdMs: Number.POSITIVE_INFINITY, estimatedSdnnMs: 31, meanIpiMs: 833, medianIpiMs: 829, pnn50Percent: 7, rangeIpiMs: 118, modeIpiMs: 825, amo50Percent: 42, coefficientOfVariation: 0.05, validIntervalCount: 18.8, rejectedIntervalCount: 7.4, rejectedIntervalRatio: 0.28, reliable: false, }, quality: { score: 0.43, level: 'invalid', usableDurationSeconds: 21.5, droppedFrameRatio: 0.08, saturationRatio: 0.12, rejectedIntervalRatio: 0.28, signalToNoiseRatio: 3.2, motionArtifactRatio: 0.31, methodAgreementScore: 0.6, timestampJitterRatio: 0.04, longestGapSeconds: 0.38, issues: ['motionArtifacts', 'futureNativeDiagnostic'], }, startedAtMonotonicSeconds: 100, endedAtMonotonicSeconds: 160, rawSampleCount: 1_642.4, resampledSampleCount: 5_501.7, }); describe('measurementCoordinator diagnostics', () => { test('preserves acquisition diagnostics used to explain an invalid result', () => { const result = mapNativeProcessingResult(nativeResult()); expect(result).toMatchObject({ valid: false, invalidReason: 'Signal exploitable insuffisant', rawSampleCount: 1_642, resampledSampleCount: 5_502, quality: { usableDurationSeconds: 21.5, droppedFrameRatio: 0.08, saturationRatio: 0.12, rejectedIntervalRatio: 0.28, signalToNoiseRatio: 3.2, motionArtifactRatio: 0.31, timestampJitterRatio: 0.04, longestGapSeconds: 0.38, }, bpm: { selectedChannel: 'red-inverted', validPeakCount: 20, rejectedPeakCount: 8, }, prv: { validIntervalCount: 19, rejectedIntervalCount: 7, }, }); }); test('normalizes non-finite values and unknown native issues safely', () => { const result = mapNativeProcessingResult(nativeResult()); expect(result.bpm.temporalBpm).toBeNull(); expect(result.prv.estimatedRmssdMs).toBeNull(); expect(result.quality.issues).toEqual([ 'motionArtifacts', 'nonFiniteProcessingResult', ]); }); });