Jest and React Native Testing Library patterns. Use when writing component tests.
Provides Jest and React Native Testing Library patterns for testing React Native components. Use when writing unit tests for components, hooks, or utilities that require mocking native modules.
/plugin marketplace add IvanTorresEdge/molcajete.ai/plugin install react-native@Molcajete.aiThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill covers testing React Native components with Jest and RNTL.
Use this skill when:
TEST BEHAVIOR - Test what users see and do, not implementation details.
npm install --save-dev @testing-library/react-native jest @types/jest
// jest.config.js
module.exports = {
preset: 'jest-expo',
setupFilesAfterEnv: ['@testing-library/react-native/extend-expect'],
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
},
collectCoverageFrom: [
'**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/coverage/**',
'!**/*.d.ts',
],
};
// components/__tests__/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { Button } from '../Button';
describe('Button', () => {
it('renders with text', () => {
render(<Button>Press me</Button>);
expect(screen.getByText('Press me')).toBeOnTheScreen();
});
it('calls onPress when pressed', () => {
const onPress = jest.fn();
render(<Button onPress={onPress}>Press me</Button>);
fireEvent.press(screen.getByText('Press me'));
expect(onPress).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
const onPress = jest.fn();
render(<Button onPress={onPress} disabled>Press me</Button>);
fireEvent.press(screen.getByText('Press me'));
expect(onPress).not.toHaveBeenCalled();
});
});
import { render, screen } from '@testing-library/react-native';
describe('AccessibleButton', () => {
it('has correct accessibility role', () => {
render(<Button accessibilityRole="button">Submit</Button>);
expect(screen.getByRole('button')).toBeOnTheScreen();
});
it('has accessibility label', () => {
render(
<Button accessibilityLabel="Submit form">
<Icon name="check" />
</Button>
);
expect(screen.getByLabelText('Submit form')).toBeOnTheScreen();
});
});
import { render, screen, waitFor } from '@testing-library/react-native';
describe('UserProfile', () => {
it('shows loading state initially', () => {
render(<UserProfile userId="123" />);
expect(screen.getByText('Loading...')).toBeOnTheScreen();
});
it('shows user data after loading', async () => {
render(<UserProfile userId="123" />);
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeOnTheScreen();
});
});
it('shows error on fetch failure', async () => {
server.use(
rest.get('/api/users/123', (req, res, ctx) => {
return res(ctx.status(500));
})
);
render(<UserProfile userId="123" />);
await waitFor(() => {
expect(screen.getByText('Error loading user')).toBeOnTheScreen();
});
});
});
import { render, screen, fireEvent, waitFor } from '@testing-library/react-native';
import { LoginForm } from '../LoginForm';
describe('LoginForm', () => {
it('shows validation errors for empty submission', async () => {
render(<LoginForm />);
fireEvent.press(screen.getByText('Sign In'));
await waitFor(() => {
expect(screen.getByText('Email is required')).toBeOnTheScreen();
});
});
it('submits with valid data', async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(
screen.getByPlaceholderText('Email'),
'test@example.com'
);
fireEvent.changeText(
screen.getByPlaceholderText('Password'),
'password123'
);
fireEvent.press(screen.getByText('Sign In'));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
});
});
import { render, screen, fireEvent } from '@testing-library/react-native';
describe('TodoList', () => {
const items = [
{ id: '1', text: 'Buy groceries' },
{ id: '2', text: 'Walk the dog' },
];
it('renders all items', () => {
render(<TodoList items={items} />);
expect(screen.getByText('Buy groceries')).toBeOnTheScreen();
expect(screen.getByText('Walk the dog')).toBeOnTheScreen();
});
it('calls onItemPress with correct item', () => {
const onItemPress = jest.fn();
render(<TodoList items={items} onItemPress={onItemPress} />);
fireEvent.press(screen.getByText('Buy groceries'));
expect(onItemPress).toHaveBeenCalledWith(items[0]);
});
});
// jest.setup.js
jest.mock('expo-secure-store', () => ({
getItemAsync: jest.fn(),
setItemAsync: jest.fn(),
deleteItemAsync: jest.fn(),
}));
jest.mock('expo-router', () => ({
useRouter: () => ({
push: jest.fn(),
replace: jest.fn(),
back: jest.fn(),
}),
useLocalSearchParams: () => ({}),
}));
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from '../useCounter';
describe('useCounter', () => {
it('starts with initial value', () => {
const { result } = renderHook(() => useCounter(10));
expect(result.current.count).toBe(10);
});
it('increments counter', () => {
const { result } = renderHook(() => useCounter(0));
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render } from '@testing-library/react-native';
function createWrapper() {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
},
});
return ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
describe('UserList', () => {
it('fetches and displays users', async () => {
render(<UserList />, { wrapper: createWrapper() });
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeOnTheScreen();
});
});
});
import { useAuthStore } from '../authStore';
describe('authStore', () => {
beforeEach(() => {
useAuthStore.setState({
user: null,
token: null,
isAuthenticated: false,
});
});
it('sets user on login', async () => {
await useAuthStore.getState().login('test@test.com', 'password');
expect(useAuthStore.getState().isAuthenticated).toBe(true);
expect(useAuthStore.getState().user).toBeDefined();
});
it('clears state on logout', async () => {
useAuthStore.setState({
user: { id: '1', email: 'test@test.com' },
isAuthenticated: true,
});
await useAuthStore.getState().logout();
expect(useAuthStore.getState().user).toBeNull();
expect(useAuthStore.getState().isAuthenticated).toBe(false);
});
});
// Element presence
expect(element).toBeOnTheScreen();
expect(element).not.toBeOnTheScreen();
// Text content
expect(element).toHaveTextContent('Hello');
// Accessibility
expect(element).toBeEnabled();
expect(element).toBeDisabled();
expect(element).toHaveAccessibilityValue({ text: '50%' });
// Style (with jest-native)
expect(element).toHaveStyle({ backgroundColor: 'red' });
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Run specific file
npm test -- Button.test.tsx
# Watch mode
npm test -- --watch
screen for queries instead of destructuring from rendergetByRole and getByLabelText for accessibilitywaitFor for async operationsThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.