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:
21
src/features/pose/__tests__/pose.test.ts
Normal file
21
src/features/pose/__tests__/pose.test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
31
src/features/pose/index.ts
Normal file
31
src/features/pose/index.ts
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user