From 0f12be57cf6ddf9310877567d097adbeb5b0174c Mon Sep 17 00:00:00 2001 From: gahusb Date: Sun, 24 May 2026 15:51:43 +0900 Subject: [PATCH] =?UTF-8?q?feat(pose):=20TS=20wrapper=20+=20PoseResult=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20+=20Jest=20mock=20test=20(v0-plan=20Task?= =?UTF-8?q?=204=20=EC=9D=BC=EB=B6=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/features/pose/.gitkeep | 0 src/features/pose/__tests__/pose.test.ts | 21 ++++++++++++++++ src/features/pose/index.ts | 31 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+) delete mode 100644 src/features/pose/.gitkeep create mode 100644 src/features/pose/__tests__/pose.test.ts create mode 100644 src/features/pose/index.ts diff --git a/src/features/pose/.gitkeep b/src/features/pose/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/features/pose/__tests__/pose.test.ts b/src/features/pose/__tests__/pose.test.ts new file mode 100644 index 0000000..234ed38 --- /dev/null +++ b/src/features/pose/__tests__/pose.test.ts @@ -0,0 +1,21 @@ +import { NativeModules } from 'react-native'; +import { detectPose } from '..'; + +beforeAll(() => { + (NativeModules as unknown as Record).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); + }); +}); diff --git a/src/features/pose/index.ts b/src/features/pose/index.ts new file mode 100644 index 0000000..e193cef --- /dev/null +++ b/src/features/pose/index.ts @@ -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>; + +interface PoseNativeModule { + detectPose: (imageUri: string) => Promise; +} + +export async function detectPose(imageUri: string): Promise { + const PoseModule = (NativeModules as Record).PoseModule; + if (!PoseModule?.detectPose) { + throw new Error('PoseModule not linked'); + } + return PoseModule.detectPose(imageUri); +}