react-native-web-testing

React Native Web - Testing

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-native-web-testing" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-react-native-web-testing

React Native Web - Testing

Comprehensive testing patterns for React Native Web applications using Jest and React Native Testing Library.

Key Concepts

React Native Testing Library

The standard testing library for React Native components:

import { render, screen, fireEvent } from '@testing-library/react-native'; import { Button } from './Button';

describe('Button', () => { it('calls onPress when pressed', () => { const onPress = jest.fn(); render(<Button title="Click me" onPress={onPress} />);

const button = screen.getByText('Click me');
fireEvent.press(button);

expect(onPress).toHaveBeenCalledTimes(1);

}); });

Jest Configuration

Configure Jest for React Native Web:

// jest.config.js module.exports = { preset: 'react-native', moduleNameMapper: { '^react-native$': 'react-native-web', }, transformIgnorePatterns: [ 'node_modules/(?!(react-native|@react-native|react-native-web)/)', ], setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], };

Testing Utilities

Common testing utilities and helpers:

import { render, RenderOptions } from '@testing-library/react-native'; import { ReactElement } from 'react'; import { ThemeProvider } from './theme';

interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> { theme?: Theme; }

export function renderWithProviders( ui: ReactElement, { theme = defaultTheme, ...options }: CustomRenderOptions = {} ) { return render( <ThemeProvider value={theme}> {ui} </ThemeProvider>, options ); }

Best Practices

Component Testing

✅ Test user interactions and behavior:

import { render, screen, fireEvent, waitFor } from '@testing-library/react-native'; import { LoginForm } from './LoginForm';

describe('LoginForm', () => { it('submits form with valid credentials', async () => { const onSubmit = jest.fn(); render(<LoginForm onSubmit={onSubmit} />);

const emailInput = screen.getByPlaceholderText('Email');
const passwordInput = screen.getByPlaceholderText('Password');
const submitButton = screen.getByText('Login');

fireEvent.changeText(emailInput, 'user@example.com');
fireEvent.changeText(passwordInput, 'password123');
fireEvent.press(submitButton);

await waitFor(() => {
  expect(onSubmit).toHaveBeenCalledWith({
    email: 'user@example.com',
    password: 'password123',
  });
});

});

it('shows error for invalid email', async () => { render(<LoginForm onSubmit={jest.fn()} />);

const emailInput = screen.getByPlaceholderText('Email');
const submitButton = screen.getByText('Login');

fireEvent.changeText(emailInput, 'invalid-email');
fireEvent.press(submitButton);

await waitFor(() => {
  expect(screen.getByText('Invalid email address')).toBeTruthy();
});

}); });

Async Testing

✅ Use waitFor for async operations:

import { render, screen, waitFor } from '@testing-library/react-native'; import { UserProfile } from './UserProfile';

describe('UserProfile', () => { it('loads and displays user data', async () => { const mockUser = { id: '1', name: 'John Doe', email: 'john@example.com' };

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve(mockUser),
  })
) as jest.Mock;

render(&#x3C;UserProfile userId="1" />);

// Check loading state
expect(screen.getByTestId('loading-indicator')).toBeTruthy();

// Wait for data to load
await waitFor(() => {
  expect(screen.getByText('John Doe')).toBeTruthy();
  expect(screen.getByText('john@example.com')).toBeTruthy();
});

expect(screen.queryByTestId('loading-indicator')).toBeNull();

}); });

Mocking Modules

✅ Mock navigation and other dependencies:

import { render, screen, fireEvent } from '@testing-library/react-native';

// Mock navigation const mockNavigate = jest.fn(); jest.mock('@react-navigation/native', () => ({ useNavigation: () => ({ navigate: mockNavigate, }), }));

describe('HomeScreen', () => { it('navigates to details on item press', () => { render(<HomeScreen />);

const item = screen.getByText('Item 1');
fireEvent.press(item);

expect(mockNavigate).toHaveBeenCalledWith('Details', { id: '1' });

}); });

Examples

Testing Custom Hooks

import { renderHook, act } from '@testing-library/react-hooks'; import { useCounter } from './useCounter';

describe('useCounter', () => { it('increments counter', () => { const { result } = renderHook(() => useCounter());

expect(result.current.count).toBe(0);

act(() => {
  result.current.increment();
});

expect(result.current.count).toBe(1);

});

it('decrements counter', () => { const { result } = renderHook(() => useCounter(5));

act(() => {
  result.current.decrement();
});

expect(result.current.count).toBe(4);

}); });

Testing with Context

import { render, screen } from '@testing-library/react-native'; import { AuthProvider } from './auth-context'; import { ProtectedScreen } from './ProtectedScreen';

describe('ProtectedScreen', () => { it('shows content when authenticated', () => { const mockUser = { id: '1', name: 'John' };

render(
  &#x3C;AuthProvider initialUser={mockUser}>
    &#x3C;ProtectedScreen />
  &#x3C;/AuthProvider>
);

expect(screen.getByText('Welcome, John')).toBeTruthy();

});

it('shows login prompt when not authenticated', () => { render( <AuthProvider initialUser={null}> <ProtectedScreen /> </AuthProvider> );

expect(screen.getByText('Please log in')).toBeTruthy();

}); });

Snapshot Testing

import { render } from '@testing-library/react-native'; import { Card } from './Card';

describe('Card', () => { it('matches snapshot', () => { const { toJSON } = render( <Card title="Test Card" description="Test description" /> );

expect(toJSON()).toMatchSnapshot();

}); });

Integration Testing

import { render, screen, fireEvent, waitFor } from '@testing-library/react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { HomeScreen } from './HomeScreen'; import { DetailsScreen } from './DetailsScreen';

const Stack = createNativeStackNavigator();

function TestApp() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Navigator> </NavigationContainer> ); }

describe('Navigation Flow', () => { it('navigates from home to details', async () => { render(<TestApp />);

// On Home screen
expect(screen.getByText('Home Screen')).toBeTruthy();

// Navigate to Details
const item = screen.getByText('View Details');
fireEvent.press(item);

// Wait for Details screen
await waitFor(() => {
  expect(screen.getByText('Details Screen')).toBeTruthy();
});

}); });

Common Patterns

Testing Forms

describe('ContactForm', () => { it('validates all fields before submit', async () => { const onSubmit = jest.fn(); render(<ContactForm onSubmit={onSubmit} />);

const submitButton = screen.getByText('Submit');
fireEvent.press(submitButton);

await waitFor(() => {
  expect(screen.getByText('Name is required')).toBeTruthy();
  expect(screen.getByText('Email is required')).toBeTruthy();
  expect(onSubmit).not.toHaveBeenCalled();
});

}); });

Testing Lists

describe('ItemsList', () => { it('renders all items', () => { const items = [ { id: '1', title: 'Item 1' }, { id: '2', title: 'Item 2' }, { id: '3', title: 'Item 3' }, ];

render(&#x3C;ItemsList items={items} />);

items.forEach(item => {
  expect(screen.getByText(item.title)).toBeTruthy();
});

});

it('handles empty state', () => { render(<ItemsList items={[]} />); expect(screen.getByText('No items found')).toBeTruthy(); }); });

Testing Accessibility

describe('Button accessibility', () => { it('has correct accessibility props', () => { render(<Button title="Submit" onPress={jest.fn()} />);

const button = screen.getByRole('button');
expect(button).toHaveAccessibilityState({ disabled: false });
expect(button).toHaveAccessibilityHint('Submits the form');

});

it('is disabled when loading', () => { render(<Button title="Submit" onPress={jest.fn()} loading />);

const button = screen.getByRole('button');
expect(button).toHaveAccessibilityState({ disabled: true, busy: true });

}); });

Anti-Patterns

❌ Don't test implementation details:

// Bad - testing internal state expect(component.state.count).toBe(5);

// Good - test observable behavior expect(screen.getByText('Count: 5')).toBeTruthy();

❌ Don't use querySelector or DOM methods:

// Bad const element = container.querySelector('.button');

// Good const button = screen.getByRole('button');

❌ Don't create overly coupled tests:

// Bad - too specific expect(screen.getByText('Submit')).toHaveStyle({ backgroundColor: '#007AFF', paddingHorizontal: 16 });

// Good - test behavior const button = screen.getByText('Submit'); expect(button).toBeTruthy(); fireEvent.press(button); expect(mockSubmit).toHaveBeenCalled();

❌ Don't forget to clean up:

// Bad afterEach(() => { // No cleanup });

// Good afterEach(() => { jest.clearAllMocks(); cleanup(); });

Related Skills

  • react-native-web-core: Core React Native Web concepts

  • react-native-web-navigation: Testing navigation flows

  • react-native-web-performance: Performance testing

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review