From 4642563acbca4cf9bba82000346baa7594d7b835 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 10 Jul 2025 16:30:50 +0200 Subject: [PATCH 1/7] docs: examples --- examples/basic/tutorial/1-basics.test.tsx | 29 +++++++ .../2-queries/1-query-variants.test.tsx | 50 ++++++++++++ .../2-queries/2-query-predicates.test.tsx | 76 +++++++++++++++++++ examples/basic/tutorial/4-events.test.tsx | 42 ++++++++++ .../basic/tutorial/x-screen-object.test.tsx | 30 ++++++++ 5 files changed, 227 insertions(+) create mode 100644 examples/basic/tutorial/1-basics.test.tsx create mode 100644 examples/basic/tutorial/2-queries/1-query-variants.test.tsx create mode 100644 examples/basic/tutorial/2-queries/2-query-predicates.test.tsx create mode 100644 examples/basic/tutorial/4-events.test.tsx create mode 100644 examples/basic/tutorial/x-screen-object.test.tsx diff --git a/examples/basic/tutorial/1-basics.test.tsx b/examples/basic/tutorial/1-basics.test.tsx new file mode 100644 index 00000000..f4aad000 --- /dev/null +++ b/examples/basic/tutorial/1-basics.test.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { render, screen } from '@testing-library/react-native'; + +function Greeting({ name = 'World' }) { + return ( + + Hello, {name}! + + ); +} + +describe('Greeting', () => { + it('should render', () => { + // Arrange + render(); + + // Assert` + expect(screen.getByText('Hello, World!')).toBeOnTheScreen(); + }); + + it('should render with the correct name', () => { + // Arrange + render(); + + // Assert + expect(screen.getByText('Hello, John!')).toBeOnTheScreen(); + }); +}); diff --git a/examples/basic/tutorial/2-queries/1-query-variants.test.tsx b/examples/basic/tutorial/2-queries/1-query-variants.test.tsx new file mode 100644 index 00000000..a536d972 --- /dev/null +++ b/examples/basic/tutorial/2-queries/1-query-variants.test.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { render, screen } from '@testing-library/react-native'; + +test('showcase query variants', () => { + render( + + Item 1 + Item 2 + , + ); + + // Use getBy* queries to find a single element matching given predicate + expect(screen.getByText('Item 1')).toBeOnTheScreen(); + + // Use getAllBy* queries to find all elements matching given predicate (note the use of a regex) + expect(screen.getAllByText(/Item/)).toHaveLength(2); + + // Use queryBy* to look for an element that you expect does not exist + expect(screen.queryByText('Item 3')).not.toBeOnTheScreen(); +}); + +function LazyText({ content }: { content: string }) { + const [isLoaded, setIsLoaded] = React.useState(false); + + // Simulate async loading operation + React.useEffect(() => { + sleep(100); + setIsLoaded(true); + }, []); + + return {isLoaded ? content : 'Loading...'}; +} + +test('showcase async query variants', async () => { + render( + + + + , + ); + + // Use findBy* to wait for an element to appear + expect(await screen.findByText('Lazy Item 1')).toBeOnTheScreen(); +}); + +// Simulate async operation +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/examples/basic/tutorial/2-queries/2-query-predicates.test.tsx b/examples/basic/tutorial/2-queries/2-query-predicates.test.tsx new file mode 100644 index 00000000..73990ff6 --- /dev/null +++ b/examples/basic/tutorial/2-queries/2-query-predicates.test.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { Pressable, Switch, Text, TextInput, View } from 'react-native'; +import { render, screen } from '@testing-library/react-native'; + +test('query by semantic role: *ByRole (highly recommended)', () => { + render( + + Heading Text + + + Button 1 + + + + + Alert Text + + + Menu Item 1 + Menu Item 2 + + , + ); + + expect(screen.getByRole('heading', { name: 'Heading Text' })).toBeOnTheScreen(); + + expect(screen.getByRole('button', { name: 'Button 1' })).toBeOnTheScreen(); + expect(screen.getByRole('switch', { name: 'Switch 1' })).toBeOnTheScreen(); + expect(screen.getByRole('alert', { name: 'Alert Text' })).toBeOnTheScreen(); + + expect(screen.getByRole('menu')).toBeOnTheScreen(); + expect(screen.getAllByRole('menuitem')).toHaveLength(2); +}); + +test('querying TextInput elements', () => { + render( + + + , + ); + + // Option 1: Query by a11y label + expect(screen.getByLabelText('Text Label')).toHaveDisplayValue('Hello'); + + // Option 2: Query by placeholder text + expect(screen.getByPlaceholderText('Enter Text...')).toHaveDisplayValue('Hello'); + + // Option 3: Query by display value + expect(screen.getByDisplayValue('Hello')).toBeOnTheScreen(); +}); + +test('other accessible queries', () => { + render( + + Text content + + + + , + ); + + expect(screen.getByText('Text content')).toBeOnTheScreen(); + expect(screen.getByLabelText('ARIA Label')).toBeOnTheScreen(); + expect(screen.getByLabelText('Accessibility Label')).toBeOnTheScreen(); + expect(screen.getByHintText('Accessibility Hint')).toBeOnTheScreen(); +}); + +test('escape hatch: *ByTestId (use as a last resort)', () => { + render( + + Text 1 + , + ); + + expect(screen.getByTestId('Text 1')).toBeOnTheScreen(); +}); diff --git a/examples/basic/tutorial/4-events.test.tsx b/examples/basic/tutorial/4-events.test.tsx new file mode 100644 index 00000000..1a49a48d --- /dev/null +++ b/examples/basic/tutorial/4-events.test.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { Text, View, Button } from 'react-native'; +import { fireEvent, render, screen, userEvent } from '@testing-library/react-native'; + +function Counter() { + const [count, setCount] = React.useState(0); + + return ( + + {count} +