feat(pose): TS wrapper + PoseResult 타입 + Jest mock test (v0-plan Task 4 일부)

src/features/pose/index.ts:
- Keypoint / Joint / PoseResult 타입 정의 (Vision Framework joint name 매핑)
- detectPose 함수: NativeModules.PoseModule.detectPose wrapper
- wrapper 내부 매번 NativeModules 조회 → 테스트의 NativeModule 주입 가능

src/features/pose/__tests__/pose.test.ts:
- NativeModules.PoseModule 직접 할당 mock (jest.mock 호이스팅 + jest-expo Platform 충돌 회피)
- detectPose 호출 → mock 결과 그대로 반환되는지 검증

검증:
- npx jest src/features/pose: 1 passed
- npx tsc --noEmit: 무에러

남은 부분 (Mac 작업):
- modules/pose/ios/PoseModule.swift (VNDetectHumanBodyPoseRequest 17점 keypoint)
- modules/pose/ios/PoseModule.m (RCT_EXTERN_MODULE)
- 실기기 manual test: keypoint를 사진 위 overlay로 시각 검증

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 15:51:43 +09:00
parent 2919b7b4ac
commit 0f12be57cf
3 changed files with 52 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
import { NativeModules } from 'react-native';
import { detectPose } from '..';
beforeAll(() => {
(NativeModules as unknown as Record<string, unknown>).PoseModule = {
detectPose: jest.fn(async () => ({
left_shoulder_joint: { x: 100, y: 200, confidence: 0.9 },
right_shoulder_joint: { x: 300, y: 200, confidence: 0.9 },
})),
};
});
describe('detectPose', () => {
it('NativeModule.PoseModule.detectPose 결과를 그대로 반환한다', async () => {
const result = await detectPose('file://photo.jpg');
expect(result.left_shoulder_joint?.x).toBe(100);
expect(result.left_shoulder_joint?.y).toBe(200);
expect(result.right_shoulder_joint?.x).toBe(300);
expect(result.left_shoulder_joint?.confidence).toBe(0.9);
});
});

View File

@@ -0,0 +1,31 @@
import { NativeModules } from 'react-native';
export interface Keypoint {
x: number;
y: number;
confidence: number;
}
export type Joint =
| 'left_shoulder_joint'
| 'right_shoulder_joint'
| 'left_hip_joint'
| 'right_hip_joint'
| 'left_ankle_joint'
| 'right_ankle_joint'
| 'neck_1_joint'
| 'root';
export type PoseResult = Partial<Record<Joint, Keypoint>>;
interface PoseNativeModule {
detectPose: (imageUri: string) => Promise<PoseResult>;
}
export async function detectPose(imageUri: string): Promise<PoseResult> {
const PoseModule = (NativeModules as Record<string, PoseNativeModule | undefined>).PoseModule;
if (!PoseModule?.detectPose) {
throw new Error('PoseModule not linked');
}
return PoseModule.detectPose(imageUri);
}