diff --git a/ui/mobile/src/__tests__/hooks/usePoseStream.test.ts b/ui/mobile/src/__tests__/hooks/usePoseStream.test.ts index 4f14deb00..5afc8b03a 100644 --- a/ui/mobile/src/__tests__/hooks/usePoseStream.test.ts +++ b/ui/mobile/src/__tests__/hooks/usePoseStream.test.ts @@ -1,5 +1,6 @@ -// usePoseStream is a React hook that uses useEffect, zustand stores, and wsService. -// We test its interface shape and the module export. +import React from 'react'; +import { Text } from 'react-native'; +import { render, screen } from '@testing-library/react-native'; jest.mock('@/services/ws.service', () => ({ wsService: { @@ -10,36 +11,43 @@ jest.mock('@/services/ws.service', () => ({ }, })); +import { usePoseStream } from '@/hooks/usePoseStream'; import { usePoseStore } from '@/stores/poseStore'; +const HookConsumer = () => { + const { connectionStatus, lastFrame, isSimulated } = usePoseStream(); + + return React.createElement( + React.Fragment, + null, + React.createElement(Text, { testID: 'status' }, connectionStatus), + React.createElement(Text, { testID: 'simulated' }, String(isSimulated)), + React.createElement(Text, { testID: 'frame' }, lastFrame ? 'present' : 'none'), + ); +}; + describe('usePoseStream', () => { beforeEach(() => { usePoseStore.getState().reset(); + const { wsService } = require('@/services/ws.service'); + wsService.subscribe.mockClear(); + wsService.connect.mockClear(); }); - it('module exports usePoseStream function', () => { - const mod = require('@/hooks/usePoseStream'); - expect(typeof mod.usePoseStream).toBe('function'); - }); - - it('exports UsePoseStreamResult interface (module shape)', () => { - // Verify the module has the expected named exports - const mod = require('@/hooks/usePoseStream'); - expect(mod).toHaveProperty('usePoseStream'); - }); + it('returns the expected store-backed values', () => { + render(React.createElement(HookConsumer)); - it('usePoseStream has the expected return type shape', () => { - // We cannot call hooks outside of React components, but we can verify - // the store provides the data the hook returns. - const state = usePoseStore.getState(); - expect(state).toHaveProperty('connectionStatus'); - expect(state).toHaveProperty('lastFrame'); - expect(state).toHaveProperty('isSimulated'); + expect(screen.getByTestId('status').props.children).toBe('disconnected'); + expect(screen.getByTestId('simulated').props.children).toBe('false'); + expect(screen.getByTestId('frame').props.children).toBe('none'); }); - it('wsService.subscribe is callable', () => { + it('does not subscribe or reconnect on render', () => { const { wsService } = require('@/services/ws.service'); - const unsub = wsService.subscribe(jest.fn()); - expect(typeof unsub).toBe('function'); + + render(React.createElement(HookConsumer)); + + expect(wsService.subscribe).not.toHaveBeenCalled(); + expect(wsService.connect).not.toHaveBeenCalled(); }); }); diff --git a/ui/mobile/src/hooks/usePoseStream.ts b/ui/mobile/src/hooks/usePoseStream.ts index c3fe8046e..ec6a2836c 100644 --- a/ui/mobile/src/hooks/usePoseStream.ts +++ b/ui/mobile/src/hooks/usePoseStream.ts @@ -1,7 +1,4 @@ -import { useEffect } from 'react'; -import { wsService } from '@/services/ws.service'; import { usePoseStore } from '@/stores/poseStore'; -import { useSettingsStore } from '@/stores/settingsStore'; export interface UsePoseStreamResult { connectionStatus: ReturnType['connectionStatus']; @@ -13,20 +10,6 @@ export function usePoseStream(): UsePoseStreamResult { const connectionStatus = usePoseStore((state) => state.connectionStatus); const lastFrame = usePoseStore((state) => state.lastFrame); const isSimulated = usePoseStore((state) => state.isSimulated); - const serverUrl = useSettingsStore((state) => state.serverUrl); - - useEffect(() => { - const unsubscribe = wsService.subscribe((frame) => { - usePoseStore.getState().handleFrame(frame); - }); - - // Auto-connect to sensing server on mount - wsService.connect(serverUrl); - - return () => { - unsubscribe(); - }; - }, [serverUrl]); return { connectionStatus, lastFrame, isSimulated }; }