Skip to content

Commit 367787f

Browse files
committed
docs: add comments
1 parent 853e2ca commit 367787f

File tree

8 files changed

+842
-43
lines changed

8 files changed

+842
-43
lines changed

examples/cookbook/basics-tutorial-react-strict-dom/4-events-rsd.test.tsx

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,37 @@
1+
/**
2+
* ========================================
3+
* React Native Testing Library Tutorial
4+
* Chapter 4: Testing Events
5+
* ========================================
6+
*
7+
* This chapter covers how to simulate and test user interactions in React Native
8+
* applications using React Native Testing Library's userEvent API.
9+
*
10+
* Key concepts covered:
11+
* - Setting up userEvent for realistic user interactions
12+
* - Testing button press events
13+
* - Testing text input events
14+
* - Testing form submissions
15+
* - Testing state changes triggered by events
16+
* - Best practices for event testing
17+
*
18+
* Note: This example uses React Strict DOM (react-strict-dom) which provides
19+
* HTML-like components that work across React Native and web platforms.
20+
*/
21+
122
import * as React from 'react';
223
import { html } from 'react-strict-dom';
324
import { render, screen, userEvent } from '@testing-library/react-native';
425

26+
/**
27+
* ========================================
28+
* Example 1: Basic Button Press Events
29+
* ========================================
30+
*
31+
* This example demonstrates how to test button press events that trigger
32+
* state changes in a component.
33+
*/
34+
535
function Counter() {
636
const [count, setCount] = React.useState(0);
737

@@ -15,19 +45,53 @@ function Counter() {
1545
);
1646
}
1747

48+
/**
49+
* Testing button press events:
50+
*
51+
* 1. Setup userEvent - creates a user session for realistic interactions
52+
* 2. Find the button element using queries
53+
* 3. Simulate the press event using user.press()
54+
* 4. Assert the expected state change occurred
55+
*
56+
* Key points:
57+
* - userEvent.setup() creates a user session for the test
58+
* - user.press() simulates a realistic button press (including focus, press, release)
59+
* - Always use await when calling userEvent methods (they return promises)
60+
* - Test both the initial state and the state after the event
61+
*/
1862
test('Counter should increment the count when the button is pressed', async () => {
63+
// Setup userEvent - this creates a user session for the test
1964
const user = userEvent.setup();
2065

66+
// Render the component
2167
render(<Counter />);
68+
69+
// Verify initial state
2270
expect(screen.getByText('0')).toBeOnTheScreen();
2371

72+
// Find the button element using role and accessible name
2473
const button = screen.getByRole('button', { name: 'Increment' });
2574
expect(button).toBeOnTheScreen();
2675

76+
// Simulate a button press event
2777
await user.press(button);
78+
79+
// Verify the state change occurred
2880
expect(screen.getByText('1')).toBeOnTheScreen();
2981
});
3082

83+
/**
84+
* ========================================
85+
* Example 2: Text Input and Form Events
86+
* ========================================
87+
*
88+
* This example demonstrates testing more complex user interactions including:
89+
* - Text input events
90+
* - Form submission events
91+
* - Conditional rendering based on events
92+
* - Error handling scenarios
93+
*/
94+
3195
function LoginForm() {
3296
const [email, setEmail] = React.useState('');
3397
const [password, setPassword] = React.useState('');
@@ -69,24 +133,90 @@ function LoginForm() {
69133
);
70134
}
71135

136+
/**
137+
* Testing text input events and successful form submission:
138+
*
139+
* This test demonstrates:
140+
* - Using user.type() to simulate realistic text input
141+
* - Finding input elements by placeholder text
142+
* - Testing form submission with valid data
143+
* - Verifying conditional rendering after successful submission
144+
*
145+
* Key points:
146+
* - user.type() simulates realistic typing (including focus, keystrokes, blur)
147+
* - Always await user.type() calls
148+
* - Use placeholder text, labels, or roles to find input elements
149+
* - Test the complete user flow from input to submission to result
150+
*/
72151
test('should login with valid credentials', async () => {
73152
const user = userEvent.setup();
74153
render(<LoginForm />);
75154

155+
// Simulate typing in the email field
76156
await user.type(screen.getByPlaceholderText('Email'), '[email protected]');
157+
158+
// Simulate typing in the password field
77159
await user.type(screen.getByPlaceholderText('Password'), 'password');
160+
161+
// Simulate clicking the login button
78162
await user.press(screen.getByRole('button', { name: 'Login' }));
79163

164+
// Verify successful login redirects to success page
80165
expect(screen.getByRole('heading', { name: 'Login successful' })).toBeOnTheScreen();
81166
});
82167

168+
/**
169+
* Testing error scenarios:
170+
*
171+
* This test demonstrates:
172+
* - Testing error handling with invalid inputs
173+
* - Verifying error messages are displayed correctly
174+
* - Using role="alert" for error messages (accessibility best practice)
175+
*
176+
* Key points:
177+
* - Always test both success and error scenarios
178+
* - Use role="alert" for error messages to ensure accessibility
179+
* - Test that error messages have appropriate accessible names
180+
* - Verify error states don't accidentally trigger success states
181+
*/
83182
test('should show error message with invalid credentials', async () => {
84183
const user = userEvent.setup();
85184
render(<LoginForm />);
86185

186+
// Enter valid email but invalid password
87187
await user.type(screen.getByPlaceholderText('Email'), '[email protected]');
88188
await user.type(screen.getByPlaceholderText('Password'), 'wrong-password');
189+
190+
// Attempt to login
89191
await user.press(screen.getByRole('button', { name: 'Login' }));
90192

193+
// Verify error message is displayed
91194
expect(screen.getByRole('alert', { name: 'Invalid credentials' })).toBeOnTheScreen();
92195
});
196+
197+
/**
198+
* ========================================
199+
* Best Practices for Event Testing
200+
* ========================================
201+
*
202+
* 1. Always use userEvent.setup() to create a user session
203+
* 2. Always await userEvent method calls (they return promises)
204+
* 3. Use realistic user interactions (user.press, user.type) over fireEvent
205+
* 4. Test both success and error scenarios
206+
* 5. Find elements using accessible queries (role, label, placeholder)
207+
* 6. Test the complete user flow, not just individual events
208+
* 7. Verify state changes and side effects after events
209+
* 8. Use role="alert" for error messages and test them appropriately
210+
*
211+
* ========================================
212+
* Common userEvent Methods
213+
* ========================================
214+
*
215+
* - user.press(element) - Simulates pressing a button or touchable element
216+
* - user.type(element, text) - Simulates typing text into an input
217+
* - user.clear(element) - Clears text from an input
218+
* - user.selectText(element, options) - Selects text in an input
219+
* - user.scroll(element, options) - Simulates scrolling gestures
220+
*
221+
* For more advanced event testing, see the userEvent documentation.
222+
*/
Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1+
/*
2+
* React Native Testing Library - Chapter 1: Basics
3+
*
4+
* This tutorial introduces the fundamentals of testing React Native components
5+
* using React Native Testing Library (RNTL). You'll learn how to render components,
6+
* query elements, and write basic assertions.
7+
*
8+
* What is React Native Testing Library?
9+
* - A testing utility that helps you test React Native components in a way that
10+
* resembles how your users interact with your app
11+
* - Built on top of Jest and React Test Renderer
12+
* - Encourages testing behavior rather than implementation details
13+
*/
14+
115
import * as React from 'react';
216
import { Text, View } from 'react-native';
17+
18+
// Import the essential testing utilities from React Native Testing Library
319
import { render, screen } from '@testing-library/react-native';
20+
// - render: Creates a virtual representation of your component for testing
21+
// - screen: Provides convenient methods to query rendered elements
422

23+
/*
24+
* Example Component: Greeting
25+
*
26+
* This is a simple React Native component that we'll use to demonstrate
27+
* basic testing concepts. It renders a greeting message with an optional name.
28+
*/
529
function Greeting({ name = 'World' }) {
630
return (
731
<View>
@@ -10,20 +34,43 @@ function Greeting({ name = 'World' }) {
1034
);
1135
}
1236

37+
/*
38+
* Test Suite: Greeting Component
39+
*
40+
* A test suite groups related tests together. We use Jest's describe() function
41+
* to create a test suite for our Greeting component.
42+
*/
1343
describe('Greeting', () => {
44+
/*
45+
* Test Case 1: Basic Rendering
46+
*
47+
* This test verifies that our component renders correctly with default props.
48+
* It follows the Arrange-Act-Assert pattern (though "Act" is implicit in the render).
49+
*/
1450
it('should render', () => {
15-
// Arrange
51+
// Arrange: Set up the test by rendering the component
52+
// The render() function creates a virtual DOM representation of your component
1653
render(<Greeting />);
1754

18-
// Assert`
55+
// Assert: Verify the expected behavior
56+
// screen.getByText() queries for an element containing the specified text
57+
// toBeOnTheScreen() is a custom matcher that checks if the element is rendered
1958
expect(screen.getByText('Hello, World!')).toBeOnTheScreen();
2059
});
2160

61+
/*
62+
* Test Case 2: Testing with Props
63+
*
64+
* This test demonstrates how to test component behavior when props change.
65+
* It shows how the same component can render differently based on input.
66+
*/
2267
it('should render with the correct name', () => {
23-
// Arrange
68+
// Arrange: Render the component with specific props
69+
// We're passing a custom name prop to test dynamic content
2470
render(<Greeting name="John" />);
2571

26-
// Assert
72+
// Assert: Verify that the component renders with the provided prop
73+
// The text should now include "John" instead of the default "World"
2774
expect(screen.getByText('Hello, John!')).toBeOnTheScreen();
2875
});
2976
});
Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,65 @@
1+
/**
2+
* React Native Testing Library Tutorial - Chapter 2.1: Query Variants
3+
*
4+
* This tutorial demonstrates the three main query variants in React Native Testing Library:
5+
* - getBy*: Finds a single element, throws error if not found or multiple found
6+
* - queryBy*: Finds a single element, returns null if not found (no error)
7+
* - findBy*: Async version of getBy*, waits for element to appear
8+
*
9+
* Each variant also has an "All" version (getAllBy*, queryAllBy*, findAllBy*)
10+
* that returns arrays of elements instead of single elements.
11+
*/
12+
113
import * as React from 'react';
214
import { Text, View } from 'react-native';
315
import { render, screen } from '@testing-library/react-native';
416

17+
/**
18+
* TEST 1: Basic Query Variants
19+
*
20+
* This test demonstrates the fundamental differences between getBy*, getAllBy*,
21+
* and queryBy* queries for synchronous DOM queries.
22+
*/
523
test('showcase query variants', () => {
24+
// Render a simple component with multiple text elements
625
render(
726
<View>
827
<Text>Item 1</Text>
928
<Text>Item 2</Text>
1029
</View>,
1130
);
1231

13-
// Use getBy* queries to find a single element matching given predicate
32+
// ✅ getBy* queries: Use when you expect exactly ONE element to be found
33+
// - Throws an error if the element is not found
34+
// - Throws an error if multiple elements are found
35+
// - Perfect for assertions where the element MUST exist
1436
expect(screen.getByText('Item 1')).toBeOnTheScreen();
1537

16-
// Use getAllBy* queries to find all elements matching given predicate (note the use of a regex)
38+
// ✅ getAllBy* queries: Use when you expect MULTIPLE elements to be found
39+
// - Returns an array of all matching elements
40+
// - Throws an error if NO elements are found
41+
// - Perfect for testing multiple matching elements
1742
expect(screen.getAllByText(/Item/)).toHaveLength(2);
1843

19-
// Use queryBy* to look for an element that you expect does not exist
44+
// ✅ queryBy* queries: Use when you expect an element to NOT exist
45+
// - Returns null if the element is not found (no error thrown)
46+
// - Returns the element if found (like getBy*)
47+
// - Perfect for testing absence of elements
2048
expect(screen.queryByText('Item 3')).not.toBeOnTheScreen();
2149
});
2250

51+
/**
52+
* LazyText Component - Simulates Async Loading
53+
*
54+
* This component demonstrates a common pattern in React Native apps:
55+
* - Initially shows a loading state
56+
* - After async operation completes, shows the actual content
57+
* - Useful for testing async query variants
58+
*/
2359
function LazyText({ content }: { content: string }) {
2460
const [isLoaded, setIsLoaded] = React.useState(false);
2561

26-
// Simulate async loading operation
62+
// Simulate async loading operation (API call, data fetching, etc.)
2763
React.useEffect(() => {
2864
sleep(100);
2965
setIsLoaded(true);
@@ -32,19 +68,56 @@ function LazyText({ content }: { content: string }) {
3268
return <Text>{isLoaded ? content : 'Loading...'}</Text>;
3369
}
3470

71+
/**
72+
* TEST 2: Async Query Variants
73+
*
74+
* This test demonstrates findBy* queries, which are essential for testing
75+
* components that load content asynchronously.
76+
*/
3577
test('showcase async query variants', async () => {
78+
// Render components that will load content asynchronously
3679
render(
3780
<View>
3881
<LazyText content="Lazy Item 1" />
3982
<LazyText content="Lazy Item 2" />
4083
</View>,
4184
);
4285

43-
// Use findBy* to wait for an element to appear
86+
// ✅ findBy* queries: Use when you need to WAIT for an element to appear
87+
// - Returns a Promise that resolves when the element is found
88+
// - Automatically retries until element appears (with timeout)
89+
// - Perfect for async operations like API calls, animations, etc.
90+
// - Default timeout is 1000ms (configurable)
4491
expect(await screen.findByText('Lazy Item 1')).toBeOnTheScreen();
92+
93+
// ❌ DON'T use getBy* for async content - this would fail:
94+
// expect(screen.getByText('Lazy Item 1')).toBeOnTheScreen(); // Error!
95+
96+
// ❌ DON'T use queryBy* for async content - this would return null:
97+
// expect(screen.queryByText('Lazy Item 1')).toBeOnTheScreen(); // Fails!
4598
});
4699

47-
// Simulate async operation
100+
/**
101+
* Query Variants Summary:
102+
*
103+
* +----------+------------+------------+------------+
104+
* | Variant | Single | Multiple | Use Case |
105+
* +----------+------------+------------+------------+
106+
* | getBy* | getByText | getAllBy* | Element must exist (sync) |
107+
* | queryBy* | queryByText| queryAllBy*| Element may not exist |
108+
* | findBy* | findByText | findAllBy* | Element appears async |
109+
* +----------+------------+------------+------------+
110+
*
111+
* When to use each:
112+
* - getBy*: "I know this element exists right now"
113+
* - queryBy*: "This element might not exist, and that's okay"
114+
* - findBy*: "This element will exist soon (after async operation)"
115+
*/
116+
117+
/**
118+
* Utility function to simulate async operations
119+
* In real apps, this might be API calls, database queries, etc.
120+
*/
48121
function sleep(ms: number) {
49122
return new Promise((resolve) => setTimeout(resolve, ms));
50123
}

0 commit comments

Comments
 (0)