|
| 1 | +import { render } from '@testing-library/react-native'; |
| 2 | +import { useWindowDimensions } from 'react-native'; |
| 3 | +import { useSafeAreaInsets } from 'react-native-safe-area-context'; |
| 4 | + |
| 5 | +import TabsLayout from '@/app/(tabs)/_layout'; |
| 6 | + |
| 7 | +jest.mock('react-native/Libraries/Utilities/useWindowDimensions', () => ({ __esModule: true, default: jest.fn() })); |
| 8 | +jest.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: jest.fn() })); |
| 9 | +jest.mock('@expo/vector-icons/MaterialIcons', () => () => null); |
| 10 | +jest.mock('@/theme/ThemeContext', () => ({ |
| 11 | + useTheme: () => ({ |
| 12 | + scheme: 'light', |
| 13 | + colors: { brand: '#D90429', brandText: '#FFFFFF', border: '#CCCCCC', surface: '#FFFFFF', textMuted: '#555555' }, |
| 14 | + }), |
| 15 | +})); |
| 16 | +jest.mock('expo-router', () => { |
| 17 | + const React = jest.requireActual<typeof import('react')>('react'); |
| 18 | + const { View } = jest.requireActual<typeof import('react-native')>('react-native'); |
| 19 | + |
| 20 | + function Tabs({ screenOptions, children }: { screenOptions: (input: { route: { name: string } }) => object; children: React.ReactNode }) { |
| 21 | + const resolvedOptions = screenOptions({ route: { name: 'remote' } }); |
| 22 | + return React.createElement(View, { testID: 'tabs', accessibilityValue: { text: JSON.stringify(resolvedOptions) } }, children); |
| 23 | + } |
| 24 | + |
| 25 | + function TabsScreen({ name, options }: { name: string; options: { title: string } }) { |
| 26 | + return React.createElement(View, { testID: `tab-${name}`, accessibilityLabel: options.title }); |
| 27 | + } |
| 28 | + |
| 29 | + Tabs.Screen = TabsScreen; |
| 30 | + |
| 31 | + return { Tabs }; |
| 32 | +}); |
| 33 | + |
| 34 | +const mockWindowDimensions = useWindowDimensions as jest.MockedFunction<typeof useWindowDimensions>; |
| 35 | +const mockSafeAreaInsets = useSafeAreaInsets as jest.MockedFunction<typeof useSafeAreaInsets>; |
| 36 | + |
| 37 | +describe('TabsLayout', () => { |
| 38 | + beforeEach(() => { |
| 39 | + mockWindowDimensions.mockReturnValue({ width: 390, height: 844, scale: 3, fontScale: 1 }); |
| 40 | + mockSafeAreaInsets.mockReturnValue({ top: 0, right: 0, bottom: 24, left: 0 }); |
| 41 | + }); |
| 42 | + |
| 43 | + it('applies font scale and the bottom inset to the explicit tab bar height', async () => { |
| 44 | + const view = await render(<TabsLayout />); |
| 45 | + expect(JSON.parse(view.getByTestId('tabs').props.accessibilityValue.text).tabBarStyle.height).toBe(88); |
| 46 | + |
| 47 | + mockWindowDimensions.mockReturnValue({ width: 844, height: 390, scale: 3, fontScale: 2 }); |
| 48 | + mockSafeAreaInsets.mockReturnValue({ top: 0, right: 0, bottom: 34, left: 0 }); |
| 49 | + await view.rerender(<TabsLayout />); |
| 50 | + |
| 51 | + expect(JSON.parse(view.getByTestId('tabs').props.accessibilityValue.text).tabBarStyle.height).toBe(106); |
| 52 | + }); |
| 53 | + |
| 54 | + it('renders the three primary destinations with unchanged labels', async () => { |
| 55 | + const view = await render(<TabsLayout />); |
| 56 | + expect(view.getByTestId('tab-index').props.accessibilityLabel).toBe('PCs'); |
| 57 | + expect(view.getByTestId('tab-remote').props.accessibilityLabel).toBe('Remote'); |
| 58 | + expect(view.getByTestId('tab-settings').props.accessibilityLabel).toBe('Settings'); |
| 59 | + }); |
| 60 | +}); |
| 61 | + |
0 commit comments