diff --git a/examples/basic/__tests__/App.test.tsx b/examples/basic/__tests__/App.test.tsx index 671fb2071..ecf09a946 100644 --- a/examples/basic/__tests__/App.test.tsx +++ b/examples/basic/__tests__/App.test.tsx @@ -9,9 +9,11 @@ test('renders correctly', () => { // Idiom: no need to capture render output, as we will use `screen` for queries. render(); - // Idiom: `getByXxx` is a predicate by itself, but we will use it with `expect().toBeTruthy()` + // Idiom: `getBy*` queries are predicates by themselves, but we will use it with `expect().toBeTruthy()` // to clarify our intent. - expect(screen.getByText('Sign in to Example App')).toBeTruthy(); + expect( + screen.getByRole('header', { name: 'Sign in to Example App' }) + ).toBeTruthy(); }); /** @@ -23,30 +25,34 @@ test('User can sign in successully with correct credentials', async () => { // Idiom: no need to capture render output, as we will use `screen` for queries. render(); - // Idiom: `getByXxx` is a predicate by itself, but we will use it with `expect().toBeTruthy()` to - // clarify our intent. + // Idiom: `getBy*` queries are predicates by themselves, but we will use it with `expect().toBeTruthy()` + // to clarify our intent. // Note: `.toBeTruthy()` is the preferred matcher for checking that elements are present. - expect(screen.getByText('Sign in to Example App')).toBeTruthy(); - expect(screen.getByText('Username')).toBeTruthy(); - expect(screen.getByText('Password')).toBeTruthy(); + expect( + screen.getByRole('header', { name: 'Sign in to Example App' }) + ).toBeTruthy(); - // Hint: we can use `getByLabelText` to find our text inputs in accessibility-friendly way. + // Hint: we can use `getByLabelText` to find our text inputs using their labels. fireEvent.changeText(screen.getByLabelText('Username'), 'admin'); fireEvent.changeText(screen.getByLabelText('Password'), 'admin1'); - // Hint: we can use `getByText` to find our button by its text. - fireEvent.press(screen.getByText('Sign In')); + // Hint: we can use `getByRole` to find our button with given text. + fireEvent.press(screen.getByRole('button', { name: 'Sign In' })); - // Idiom: since pressing button triggers async operation we need to use `findBy` query to wait + // Idiom: since pressing button triggers async operation we need to use `findBy*` query to wait // for the action to complete. - // Hint: subsequent queries do not need to use `findBy`, because they are used after the async action + // Hint: subsequent queries do not need to use `findBy*`, because they are used after the async action // already finished - expect(await screen.findByText('Welcome admin!')).toBeTruthy(); - - // Idiom: use `queryByXxx` with `expect().toBeFalsy()` to assess that element is not present. - expect(screen.queryByText('Sign in to Example App')).toBeFalsy(); - expect(screen.queryByText('Username')).toBeFalsy(); - expect(screen.queryByText('Password')).toBeFalsy(); + expect( + await screen.findByRole('header', { name: 'Welcome admin!' }) + ).toBeTruthy(); + + // Idiom: use `queryBy*` with `expect().toBeFalsy()` to assess that element is not present. + expect( + screen.queryByRole('header', { name: 'Sign in to Example App' }) + ).toBeFalsy(); + expect(screen.queryByLabelText('Username')).toBeFalsy(); + expect(screen.queryByLabelText('Password')).toBeFalsy(); }); /** @@ -56,7 +62,7 @@ test('User can sign in successully with correct credentials', async () => { * that is not directly reflected in the UI. * * For this reason prefer quries that correspond to things directly observable by the user like: - * `getByText`, `getByLabelText`, `getByPlaceholderText, `getByDisplayValue`, `getByRole`, etc. + * `getByRole`, `getByText`, `getByLabelText`, `getByPlaceholderText, `getByDisplayValue`, etc. * over `getByTestId` which is not directly observable by the user. * * Note: that some times you will have to resort to `getByTestId`, but treat it as a last resort. @@ -64,22 +70,24 @@ test('User can sign in successully with correct credentials', async () => { test('User will see errors for incorrect credentials', async () => { render(); - expect(screen.getByText('Sign in to Example App')).toBeTruthy(); - expect(screen.getByText('Username')).toBeTruthy(); - expect(screen.getByText('Password')).toBeTruthy(); + expect( + screen.getByRole('header', { name: 'Sign in to Example App' }) + ).toBeTruthy(); fireEvent.changeText(screen.getByLabelText('Username'), 'admin'); fireEvent.changeText(screen.getByLabelText('Password'), 'qwerty123'); - fireEvent.press(screen.getByText('Sign In')); + fireEvent.press(screen.getByRole('button', { name: 'Sign In' })); // Hint: you can use custom Jest Native matcher to check text content. - expect(await screen.findByLabelText('Error')).toHaveTextContent( + expect(await screen.findByRole('alert')).toHaveTextContent( 'Incorrect username or password' ); - expect(screen.getByText('Sign in to Example App')).toBeTruthy(); - expect(screen.getByText('Username')).toBeTruthy(); - expect(screen.getByText('Password')).toBeTruthy(); + expect( + screen.getByRole('header', { name: 'Sign in to Example App' }) + ).toBeTruthy(); + expect(screen.getByLabelText('Username')).toBeTruthy(); + expect(screen.getByLabelText('Password')).toBeTruthy(); }); /** @@ -88,23 +96,25 @@ test('User will see errors for incorrect credentials', async () => { test('User can sign in after incorrect attempt', async () => { render(); - expect(screen.getByText('Sign in to Example App')).toBeTruthy(); - expect(screen.getByText('Username')).toBeTruthy(); - expect(screen.getByText('Password')).toBeTruthy(); + expect( + screen.getByRole('header', { name: 'Sign in to Example App' }) + ).toBeTruthy(); fireEvent.changeText(screen.getByLabelText('Username'), 'admin'); fireEvent.changeText(screen.getByLabelText('Password'), 'qwerty123'); - fireEvent.press(screen.getByText('Sign In')); + fireEvent.press(screen.getByRole('button', { name: 'Sign In' })); - expect(await screen.findByLabelText('Error')).toHaveTextContent( + expect(await screen.findByRole('alert')).toHaveTextContent( 'Incorrect username or password' ); fireEvent.changeText(screen.getByLabelText('Password'), 'admin1'); - fireEvent.press(screen.getByText('Sign In')); + fireEvent.press(screen.getByRole('button', { name: 'Sign In' })); expect(await screen.findByText('Welcome admin!')).toBeTruthy(); - expect(screen.queryByText('Sign in to Example App')).toBeFalsy(); - expect(screen.queryByText('Username')).toBeFalsy(); - expect(screen.queryByText('Password')).toBeFalsy(); + expect( + screen.queryByRole('header', { name: 'Sign in to Example App' }) + ).toBeFalsy(); + expect(screen.queryByLabelText('Username')).toBeFalsy(); + expect(screen.queryByLabelText('Password')).toBeFalsy(); }); diff --git a/examples/basic/components/Home.tsx b/examples/basic/components/Home.tsx index 74317d50d..d1cc588e6 100644 --- a/examples/basic/components/Home.tsx +++ b/examples/basic/components/Home.tsx @@ -8,7 +8,9 @@ type Props = { export function Home({ user }: Props) { return ( - Welcome {user}! + + Welcome {user}! + ); } diff --git a/examples/basic/components/LoginForm.tsx b/examples/basic/components/LoginForm.tsx index dc3961e48..191509ea1 100644 --- a/examples/basic/components/LoginForm.tsx +++ b/examples/basic/components/LoginForm.tsx @@ -4,7 +4,7 @@ import { View, Text, TextInput, - TouchableOpacity, + Pressable, ActivityIndicator, } from 'react-native'; @@ -34,7 +34,9 @@ export function LoginForm({ onLoginSuccess }: Props) { return ( - Sign in to Example App + + Sign in to Example App + Username {error && ( - + {error} )} - + {isLoading ? ( ) : ( Sign In )} - + ); } diff --git a/examples/basic/package.json b/examples/basic/package.json index 3a196bb25..95f620c48 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -6,25 +6,26 @@ "ios": "expo start --ios", "web": "expo start --web", "eject": "expo eject", - "test": "jest" + "test": "jest", + "typecheck": "tsc --noEmit" }, "dependencies": { - "expo": "^46.0.0", - "expo-status-bar": "~1.4.0", - "react": "18.0.0", - "react-dom": "18.0.0", - "react-native": "0.69.4", + "expo": "^47.0.0", + "expo-status-bar": "~1.4.2", + "react": "18.1.0", + "react-dom": "18.1.0", + "react-native": "0.70.5", "react-native-web": "~0.18.7" }, "devDependencies": { - "@babel/core": "^7.18.6", - "@testing-library/jest-native": "^5.0.0", - "@testing-library/react-native": "^11.0.0", - "@types/react": "~18.0.0", - "@types/react-native": "~0.69.1", - "jest": "^28.0.0", - "react-test-renderer": "18.0.0", - "typescript": "^4.6.3" + "@babel/core": "^7.19.3", + "@testing-library/jest-native": "^5.1.2", + "@testing-library/react-native": "^11.4.0", + "@types/react": "~18.0.24", + "@types/react-native": "~0.70.6", + "jest": "^29.3.0", + "react-test-renderer": "18.1.0", + "typescript": "^4.8.4" }, "private": true }