From erne-universal
Guides test-driven development workflow for React Native using Jest, React Native Testing Library, and Detox in Red-Green-Refactor cycle. For new features, bug fixes, refactoring.
npx claudepluginhub jubakitiashvili/everything-react-native-expoThis skill uses the workspace's default tool permissions.
You are executing a test-driven development workflow for React Native. Follow the Red-Green-Refactor cycle strictly.
Generates platform-native social content for X, LinkedIn, TikTok, YouTube, newsletters from source material like articles, demos, docs, or notes. Adapts voice and format per platform.
Interactively installs Everything Claude Code skills and rules to user-level (~/.claude) or project-level (.claude) directories, verifies paths, and optimizes files. Activate on 'configure ecc' or setup requests.
Reorganizes X and LinkedIn networks: review-first pruning of low-value follows, priority-based add/follow recommendations, and drafts warm outreach in user's voice.
You are executing a test-driven development workflow for React Native. Follow the Red-Green-Refactor cycle strictly.
Invoke this skill when:
Before writing any implementation code, write a test that describes the expected behavior:
Component test (React Native Testing Library):
import { render, screen, fireEvent } from '@testing-library/react-native';
import { LoginForm } from '../LoginForm';
describe('LoginForm', () => {
it('disables submit when fields are empty', () => {
render(<LoginForm onSubmit={jest.fn()} />);
const submitButton = screen.getByRole('button', { name: /sign in/i });
expect(submitButton).toBeDisabled();
});
it('calls onSubmit with email and password', () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(screen.getByPlaceholderText(/email/i), 'user@test.com');
fireEvent.changeText(screen.getByPlaceholderText(/password/i), 'secret123');
fireEvent.press(screen.getByRole('button', { name: /sign in/i }));
expect(onSubmit).toHaveBeenCalledWith({
email: 'user@test.com',
password: 'secret123',
});
});
it('shows error message on failed login', async () => {
const onSubmit = jest.fn().mockRejectedValue(new Error('Invalid credentials'));
render(<LoginForm onSubmit={onSubmit} />);
fireEvent.changeText(screen.getByPlaceholderText(/email/i), 'user@test.com');
fireEvent.changeText(screen.getByPlaceholderText(/password/i), 'wrong');
fireEvent.press(screen.getByRole('button', { name: /sign in/i }));
expect(await screen.findByText(/invalid credentials/i)).toBeTruthy();
});
});
Run the test. It MUST fail (red).
Write the minimum code to make the test pass. Do NOT add anything extra:
export function LoginForm({ onSubmit }: LoginFormProps) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const handleSubmit = async () => {
try {
await onSubmit({ email, password });
} catch (e) {
setError(e instanceof Error ? e.message : 'Unknown error');
}
};
return (
<View>
<TextInput placeholder="Email" value={email} onChangeText={setEmail} />
<TextInput placeholder="Password" value={password} onChangeText={setPassword} secureTextEntry />
<Pressable
onPress={handleSubmit}
disabled={!email || !password}
accessibilityRole="button"
accessibilityLabel="Sign in"
>
<Text>Sign In</Text>
</Pressable>
{error && <Text>{error}</Text>}
</View>
);
}
Run tests again. All MUST pass (green).
Now improve the code without changing behavior:
useLoginForm)Run tests after every change. They MUST stay green.
| Layer | Tool | What to Test |
|---|---|---|
| Unit | Jest | Pure functions, hooks, utilities |
| Component | RNTL | Component rendering, user interactions |
| Integration | RNTL | Multiple components working together |
| E2E | Detox | Full user flows on real app |
Tests live next to their source:
src/features/auth/
LoginForm.tsx
__tests__/
LoginForm.test.tsx
hooks/
useLoginForm.ts
__tests__/
useLoginForm.test.ts
screen queries — prefer getByRole, getByText, getByPlaceholderText