From 4e148bdb0573fcb86686482c3af1d1e02e1cc937 Mon Sep 17 00:00:00 2001 From: Steve Galili Date: Mon, 11 Mar 2024 22:46:07 +0100 Subject: [PATCH] chore: tweak docs and examples to match recommended practices 2024 (use screen object for queries) (#1561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: tweak docs and examples to match modern usage of lib. Use screen object for queries (pt.1) Use screen object for queries (pt.2) update render docs to modern screen API and fix lint * refactor: code review tweaks --------- Co-authored-by: Maciej Jastrzębski --- README.md | 28 +- .../__snapshots__/render-debug.test.tsx.snap | 6 +- src/__tests__/act.test.tsx | 12 +- src/__tests__/fire-event-textInput.test.tsx | 18 +- src/__tests__/fire-event.test.tsx | 104 +++-- src/__tests__/host-text-nesting.test.tsx | 30 +- src/__tests__/questionsBoard.test.tsx | 35 +- src/__tests__/react-native-api.test.tsx | 44 +- src/__tests__/render-debug.test.tsx | 89 ++-- .../render-string-validation.test.tsx | 10 +- src/__tests__/render.test.tsx | 91 ++-- .../wait-for-element-to-be-removed.test.tsx | 16 +- src/__tests__/wait-for.test.tsx | 54 +-- src/helpers/__tests__/accessiblity.test.tsx | 126 +++--- src/helpers/__tests__/component-tree.test.tsx | 102 ++--- src/helpers/__tests__/text-content.test.tsx | 22 +- .../__tests__/to-be-disabled.test.tsx | 16 +- src/matchers/__tests__/to-have-style.test.tsx | 12 +- src/matchers/__tests__/utils.test.tsx | 8 +- .../__tests__/accessibility-state.test.tsx | 242 +++++------ .../__tests__/accessibility-value.test.tsx | 119 +++--- src/queries/__tests__/display-value.test.tsx | 78 ++-- src/queries/__tests__/hint-text.test.tsx | 74 ++-- src/queries/__tests__/label-text.test.tsx | 100 ++--- src/queries/__tests__/make-queries.test.tsx | 46 +- .../__tests__/placeholder-text.test.tsx | 54 ++- src/queries/__tests__/role-value.test.tsx | 62 +-- src/queries/__tests__/role.test.tsx | 392 +++++++++--------- src/queries/__tests__/test-id.test.tsx | 88 ++-- src/queries/__tests__/text.test.tsx | 189 ++++----- src/user-event/__tests__/clear.test.tsx | 14 +- .../scroll/__tests__/scroll-to.test.tsx | 2 +- .../type/__tests__/type-managed.test.tsx | 8 +- src/user-event/type/__tests__/type.test.tsx | 16 +- .../utils/__tests__/dispatch-event.test.tsx | 8 +- website/docs/UnderstandingAct.md | 24 +- 36 files changed, 1181 insertions(+), 1158 deletions(-) diff --git a/README.md b/README.md index c0e2aa50..ff6292d3 100644 --- a/README.md +++ b/README.md @@ -88,21 +88,29 @@ flow-typed install react-test-renderer import { render, screen, fireEvent } from '@testing-library/react-native'; import { QuestionsBoard } from '../QuestionsBoard'; -test('form submits two answers', () => { - const allQuestions = ['q1', 'q2']; - const mockFn = jest.fn(); +// It is recommended to use userEvent with fake timers +// Some events involve duration so your tests may take a long time to run. +jest.useFakeTimers(); - render(); +test('form submits two answers', async () => { + const questions = ['q1', 'q2']; + const onSubmit = jest.fn(); + + const user = userEvent.setup(); + render(); const answerInputs = screen.getAllByLabelText('answer input'); - fireEvent.changeText(answerInputs[0], 'a1'); - fireEvent.changeText(answerInputs[1], 'a2'); - fireEvent.press(screen.getByText('Submit')); + // simulates the user focusing on TextInput and typing text one char at a time + await user.type(answerInputs[0], 'a1'); + await user.type(answerInputs[1], 'a2'); - expect(mockFn).toBeCalledWith({ - 1: { q: 'q1', a: 'a1' }, - 2: { q: 'q2', a: 'a2' }, + // simulates the user pressing on any pressable element + await user.press(screen.getByRole('button', { name: 'Submit' })); + + expect(onSubmit).toHaveBeenCalledWith({ + '1': { q: 'q1', a: 'a1' }, + '2': { q: 'q2', a: 'a2' }, }); }); ``` diff --git a/src/__tests__/__snapshots__/render-debug.test.tsx.snap b/src/__tests__/__snapshots__/render-debug.test.tsx.snap index 088df781..25106295 100644 --- a/src/__tests__/__snapshots__/render-debug.test.tsx.snap +++ b/src/__tests__/__snapshots__/render-debug.test.tsx.snap @@ -58,6 +58,7 @@ exports[`debug 1`] = ` onResponderTerminate={[Function onResponderTerminate]} onResponderTerminationRequest={[Function onResponderTerminationRequest]} onStartShouldSetResponder={[Function onStartShouldSetResponder]} + role="button" > Change freshness! @@ -137,6 +138,7 @@ exports[`debug changing component: bananaFresh button message should now be "fre onResponderTerminate={[Function onResponderTerminate]} onResponderTerminationRequest={[Function onResponderTerminationRequest]} onStartShouldSetResponder={[Function onStartShouldSetResponder]} + role="button" > Change freshness! @@ -343,6 +345,7 @@ exports[`debug: another custom message 1`] = ` onResponderTerminate={[Function onResponderTerminate]} onResponderTerminationRequest={[Function onResponderTerminationRequest]} onStartShouldSetResponder={[Function onStartShouldSetResponder]} + role="button" > Change freshness! @@ -394,7 +397,6 @@ exports[`debug: shallow 1`] = ` /> Change freshness! @@ -446,7 +448,6 @@ exports[`debug: shallow with message 1`] = ` /> Change freshness! @@ -526,6 +527,7 @@ exports[`debug: with message 1`] = ` onResponderTerminate={[Function onResponderTerminate]} onResponderTerminationRequest={[Function onResponderTerminationRequest]} onStartShouldSetResponder={[Function onStartShouldSetResponder]} + role="button" > Change freshness! diff --git a/src/__tests__/act.test.tsx b/src/__tests__/act.test.tsx index b5f58ef7..379eecc4 100644 --- a/src/__tests__/act.test.tsx +++ b/src/__tests__/act.test.tsx @@ -1,8 +1,6 @@ import * as React from 'react'; import { Text } from 'react-native'; -import act from '../act'; -import render from '../render'; -import fireEvent from '../fire-event'; +import { act, fireEvent, render, screen } from '../'; type UseEffectProps = { callback(): void }; const UseEffect = ({ callback }: UseEffectProps) => { @@ -26,15 +24,15 @@ test('render should trigger useEffect', () => { test('update should trigger useEffect', () => { const effectCallback = jest.fn(); - const { update } = render(); - update(); + render(); + screen.update(); expect(effectCallback).toHaveBeenCalledTimes(2); }); test('fireEvent should trigger useState', () => { - const { getByText } = render(); - const counter = getByText(/Total count/i); + render(); + const counter = screen.getByText(/Total count/i); expect(counter.props.children).toEqual('Total count: 0'); fireEvent.press(counter); diff --git a/src/__tests__/fire-event-textInput.test.tsx b/src/__tests__/fire-event-textInput.test.tsx index 6f017f3c..d7bd823a 100644 --- a/src/__tests__/fire-event-textInput.test.tsx +++ b/src/__tests__/fire-event-textInput.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Text, TextInput, TextInputProps } from 'react-native'; -import { render, fireEvent } from '..'; +import { render, fireEvent, screen } from '..'; function WrappedTextInput(props: TextInputProps) { return ; @@ -18,7 +18,7 @@ test('should fire only non-touch-related events on non-editable TextInput', () = const onSubmitEditing = jest.fn(); const onLayout = jest.fn(); - const view = render( + render( ); - const subject = view.getByTestId('subject'); + const subject = screen.getByTestId('subject'); fireEvent(subject, 'focus'); fireEvent.changeText(subject, 'Text'); fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } }); @@ -47,7 +47,7 @@ test('should fire only non-touch-related events on non-editable TextInput with n const onSubmitEditing = jest.fn(); const onLayout = jest.fn(); - const view = render( + render( ); - const subject = view.getByText('Nested Text'); + const subject = screen.getByText('Nested Text'); fireEvent(subject, 'focus'); fireEvent(subject, 'onFocus'); fireEvent.changeText(subject, 'Text'); @@ -98,7 +98,7 @@ test('should fire only non-touch-related events on non-editable wrapped TextInpu const onSubmitEditing = jest.fn(); const onLayout = jest.fn(); - const view = render( + render( ); - const subject = view.getByTestId('subject'); + const subject = screen.getByTestId('subject'); fireEvent(subject, 'focus'); fireEvent.changeText(subject, 'Text'); fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } }); @@ -130,7 +130,7 @@ test('should fire only non-touch-related events on non-editable double wrapped T const onSubmitEditing = jest.fn(); const onLayout = jest.fn(); - const view = render( + render( ); - const subject = view.getByTestId('subject'); + const subject = screen.getByTestId('subject'); fireEvent(subject, 'focus'); fireEvent.changeText(subject, 'Text'); fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } }); diff --git a/src/__tests__/fire-event.test.tsx b/src/__tests__/fire-event.test.tsx index fd71f998..fc0f6aa2 100644 --- a/src/__tests__/fire-event.test.tsx +++ b/src/__tests__/fire-event.test.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; import { - View, - TouchableOpacity, PanResponder, Pressable, - Text, ScrollView, + Text, TextInput, + TouchableOpacity, + View, } from 'react-native'; -import { render, fireEvent } from '..'; +import { fireEvent, render, screen } from '..'; type OnPressComponentProps = { onPress: () => void; @@ -58,9 +58,9 @@ const CustomEventComponentWithCustomName = ({ describe('fireEvent', () => { test('should invoke specified event', () => { const onPressMock = jest.fn(); - const { getByText } = render(); + render(); - fireEvent(getByText('Press me'), 'press'); + fireEvent(screen.getByText('Press me'), 'press'); expect(onPressMock).toHaveBeenCalled(); }); @@ -68,19 +68,19 @@ describe('fireEvent', () => { test('should invoke specified event on parent element', () => { const onPressMock = jest.fn(); const text = 'New press text'; - const { getByText } = render(); + render(); - fireEvent(getByText(text), 'press'); + fireEvent(screen.getByText(text), 'press'); expect(onPressMock).toHaveBeenCalled(); }); test('should not fire if the press handler is not passed to children', () => { const onPressMock = jest.fn(); - const { getByText } = render( + render( // TODO: this functionality is buggy, i.e. it will fail if we wrap this component with a View. ); - fireEvent(getByText('Without event'), 'press'); + fireEvent(screen.getByText('Without event'), 'press'); expect(onPressMock).not.toHaveBeenCalled(); }); @@ -88,13 +88,13 @@ describe('fireEvent', () => { const handlerMock = jest.fn(); const EVENT_DATA = 'event data'; - const { getByText } = render( + render( ); - fireEvent(getByText('Custom event component'), 'customEvent', EVENT_DATA); + fireEvent(screen.getByText('Custom event component'), 'customEvent', EVENT_DATA); expect(handlerMock).toHaveBeenCalledWith(EVENT_DATA); }); @@ -109,9 +109,9 @@ test('fireEvent.press', () => { pageY: 30, }, }; - const { getByText } = render(); + render(); - fireEvent.press(getByText(text), eventData); + fireEvent.press(screen.getByText(text), eventData); expect(onPressMock).toHaveBeenCalledWith(eventData); }); @@ -126,13 +126,13 @@ test('fireEvent.scroll', () => { }, }; - const { getByText } = render( + render( XD ); - fireEvent.scroll(getByText('XD'), eventData); + fireEvent.scroll(screen.getByText('XD'), eventData); expect(onScrollMock).toHaveBeenCalledWith(eventData); }); @@ -141,13 +141,13 @@ test('fireEvent.changeText', () => { const onChangeTextMock = jest.fn(); const CHANGE_TEXT = 'content'; - const { getByPlaceholderText } = render( + render( ); - fireEvent.changeText(getByPlaceholderText('Customer placeholder'), CHANGE_TEXT); + fireEvent.changeText(screen.getByPlaceholderText('Customer placeholder'), CHANGE_TEXT); expect(onChangeTextMock).toHaveBeenCalledWith(CHANGE_TEXT); }); @@ -155,9 +155,9 @@ test('fireEvent.changeText', () => { test('custom component with custom event name', () => { const handlePress = jest.fn(); - const { getByText } = render(); + render(); - fireEvent(getByText('Custom component'), 'handlePress'); + fireEvent(screen.getByText('Custom component'), 'handlePress'); expect(handlePress).toHaveBeenCalled(); }); @@ -165,16 +165,16 @@ test('custom component with custom event name', () => { test('event with multiple handler parameters', () => { const handlePress = jest.fn(); - const { getByText } = render(); + render(); - fireEvent(getByText('Custom component'), 'handlePress', 'param1', 'param2'); + fireEvent(screen.getByText('Custom component'), 'handlePress', 'param1', 'param2'); expect(handlePress).toHaveBeenCalledWith('param1', 'param2'); }); test('should not fire on disabled TouchableOpacity', () => { const handlePress = jest.fn(); - const screen = render( + render( Trigger @@ -188,7 +188,7 @@ test('should not fire on disabled TouchableOpacity', () => { test('should not fire on disabled Pressable', () => { const handlePress = jest.fn(); - const screen = render( + render( Trigger @@ -202,7 +202,7 @@ test('should not fire on disabled Pressable', () => { test('should not fire inside View with pointerEvents="none"', () => { const onPress = jest.fn(); - const screen = render( + render( Trigger @@ -217,7 +217,7 @@ test('should not fire inside View with pointerEvents="none"', () => { test('should not fire inside View with pointerEvents="box-only"', () => { const onPress = jest.fn(); - const screen = render( + render( Trigger @@ -232,7 +232,7 @@ test('should not fire inside View with pointerEvents="box-only"', () => { test('should fire inside View with pointerEvents="box-none"', () => { const onPress = jest.fn(); - const screen = render( + render( Trigger @@ -247,7 +247,7 @@ test('should fire inside View with pointerEvents="box-none"', () => { test('should fire inside View with pointerEvents="auto"', () => { const onPress = jest.fn(); - const screen = render( + render( Trigger @@ -262,7 +262,7 @@ test('should fire inside View with pointerEvents="auto"', () => { test('should not fire deeply inside View with pointerEvents="box-only"', () => { const onPress = jest.fn(); - const screen = render( + render( @@ -279,9 +279,7 @@ test('should not fire deeply inside View with pointerEvents="box-only"', () => { test('should fire non-pointer events inside View with pointerEvents="box-none"', () => { const onTouchStart = jest.fn(); - const screen = render( - - ); + render(); fireEvent(screen.getByTestId('view'), 'touchStart'); expect(onTouchStart).toHaveBeenCalled(); @@ -289,7 +287,7 @@ test('should fire non-pointer events inside View with pointerEvents="box-none"', test('should fire non-touch events inside View with pointerEvents="box-none"', () => { const onLayout = jest.fn(); - const screen = render(); + render(); fireEvent(screen.getByTestId('view'), 'layout'); expect(onLayout).toHaveBeenCalled(); @@ -299,9 +297,7 @@ test('should fire non-touch events inside View with pointerEvents="box-none"', ( // the 'press' event on host View rendered by pressable. test('should fire on Pressable with pointerEvents="box-only', () => { const onPress = jest.fn(); - const screen = render( - - ); + render(); fireEvent.press(screen.getByTestId('pressable')); expect(onPress).toHaveBeenCalled(); @@ -310,7 +306,7 @@ test('should fire on Pressable with pointerEvents="box-only', () => { test('should pass event up on disabled TouchableOpacity', () => { const handleInnerPress = jest.fn(); const handleOuterPress = jest.fn(); - const screen = render( + render( Inner Trigger @@ -326,7 +322,7 @@ test('should pass event up on disabled TouchableOpacity', () => { test('should pass event up on disabled Pressable', () => { const handleInnerPress = jest.fn(); const handleOuterPress = jest.fn(); - const screen = render( + render( Inner Trigger @@ -353,7 +349,7 @@ const TestComponent = ({ onPress }: TestComponentProps) => { test('is not fooled by non-native disabled prop', () => { const handlePress = jest.fn(); - const screen = render(); + render(); fireEvent.press(screen.getByText('Trigger Test')); expect(handlePress).toHaveBeenCalledTimes(1); @@ -363,6 +359,7 @@ type TestChildTouchableComponentProps = { onPress: () => void; someProp: boolean; }; + function TestChildTouchableComponent({ onPress, someProp }: TestChildTouchableComponentProps) { return ( @@ -376,7 +373,7 @@ function TestChildTouchableComponent({ onPress, someProp }: TestChildTouchableCo test('is not fooled by non-responder wrapping host elements', () => { const handlePress = jest.fn(); - const screen = render( + render( @@ -387,6 +384,7 @@ test('is not fooled by non-responder wrapping host elements', () => { }); type TestDraggableComponentProps = { onDrag: () => void }; + function TestDraggableComponent({ onDrag }: TestDraggableComponentProps) { const responderHandlers = PanResponder.create({ onMoveShouldSetPanResponder: (_evt, _gestureState) => true, @@ -403,7 +401,7 @@ function TestDraggableComponent({ onDrag }: TestDraggableComponentProps) { test('has only onMove', () => { const handleDrag = jest.fn(); - const screen = render(); + render(); fireEvent(screen.getByText('Trigger'), 'responderMove', { touchHistory: { mostRecentTimeStamp: '2', touchBank: [] }, @@ -416,41 +414,33 @@ test('has only onMove', () => { describe('native events', () => { test('triggers onScrollBeginDrag', () => { const onScrollBeginDragSpy = jest.fn(); - const { getByTestId } = render( - - ); + render(); - fireEvent(getByTestId('test-id'), 'onScrollBeginDrag'); + fireEvent(screen.getByTestId('test-id'), 'onScrollBeginDrag'); expect(onScrollBeginDragSpy).toHaveBeenCalled(); }); test('triggers onScrollEndDrag', () => { const onScrollEndDragSpy = jest.fn(); - const { getByTestId } = render( - - ); + render(); - fireEvent(getByTestId('test-id'), 'onScrollEndDrag'); + fireEvent(screen.getByTestId('test-id'), 'onScrollEndDrag'); expect(onScrollEndDragSpy).toHaveBeenCalled(); }); test('triggers onMomentumScrollBegin', () => { const onMomentumScrollBeginSpy = jest.fn(); - const { getByTestId } = render( - - ); + render(); - fireEvent(getByTestId('test-id'), 'onMomentumScrollBegin'); + fireEvent(screen.getByTestId('test-id'), 'onMomentumScrollBegin'); expect(onMomentumScrollBeginSpy).toHaveBeenCalled(); }); test('triggers onMomentumScrollEnd', () => { const onMomentumScrollEndSpy = jest.fn(); - const { getByTestId } = render( - - ); + render(); - fireEvent(getByTestId('test-id'), 'onMomentumScrollEnd'); + fireEvent(screen.getByTestId('test-id'), 'onMomentumScrollEnd'); expect(onMomentumScrollEndSpy).toHaveBeenCalled(); }); }); diff --git a/src/__tests__/host-text-nesting.test.tsx b/src/__tests__/host-text-nesting.test.tsx index c32b8d8e..f9a4c1c9 100644 --- a/src/__tests__/host-text-nesting.test.tsx +++ b/src/__tests__/host-text-nesting.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Text, Pressable, View } from 'react-native'; -import { render, within } from '../pure'; +import { render, within, screen } from '../pure'; /** * Our queries interact differently with composite and host elements, and some specific cases require us @@ -12,18 +12,18 @@ import { render, within } from '../pure'; */ describe('nested text handling', () => { test('within same node', () => { - const view = render(Hello); - expect(within(view.getByTestId('subject')).getByText('Hello')).toBeTruthy(); + render(Hello); + expect(within(screen.getByTestId('subject')).getByText('Hello')).toBeTruthy(); }); test('role with direct text children', () => { - const view = render(About); + render(About); - expect(view.getByRole('header', { name: 'About' })).toBeTruthy(); + expect(screen.getByRole('header', { name: 'About' })).toBeTruthy(); }); test('nested text with child with role', () => { - const view = render( + render( About @@ -31,11 +31,11 @@ describe('nested text handling', () => { ); - expect(view.getByRole('header', { name: 'About' }).props.testID).toBe('child'); + expect(screen.getByRole('header', { name: 'About' }).props.testID).toBe('child'); }); test('pressable within View, with text child', () => { - const view = render( + render( Save @@ -43,11 +43,11 @@ describe('nested text handling', () => { ); - expect(view.getByRole('button', { name: 'Save' }).props.testID).toBe('pressable'); + expect(screen.getByRole('button', { name: 'Save' }).props.testID).toBe('pressable'); }); test('pressable within View, with text child within view', () => { - const view = render( + render( @@ -57,21 +57,21 @@ describe('nested text handling', () => { ); - expect(view.getByRole('button', { name: 'Save' }).props.testID).toBe('pressable'); + expect(screen.getByRole('button', { name: 'Save' }).props.testID).toBe('pressable'); }); test('Text within pressable', () => { - const view = render( + render( Save ); - expect(view.getByText('Save').props.testID).toBe('text'); + expect(screen.getByText('Save').props.testID).toBe('text'); }); test('Text within view within pressable', () => { - const view = render( + render( Save @@ -79,6 +79,6 @@ describe('nested text handling', () => { ); - expect(view.getByText('Save').props.testID).toBe('text'); + expect(screen.getByText('Save').props.testID).toBe('text'); }); }); diff --git a/src/__tests__/questionsBoard.test.tsx b/src/__tests__/questionsBoard.test.tsx index 7958e589..5776ebb4 100644 --- a/src/__tests__/questionsBoard.test.tsx +++ b/src/__tests__/questionsBoard.test.tsx @@ -1,11 +1,14 @@ import * as React from 'react'; -import { View, TouchableOpacity, Text, ScrollView, TextInput } from 'react-native'; -import { render, fireEvent } from '..'; +import { Pressable, ScrollView, Text, TextInput, View } from 'react-native'; +import { render, screen, userEvent } from '..'; type QuestionsBoardProps = { questions: string[]; onSubmit: (obj: {}) => void; }; + +jest.useFakeTimers(); + function QuestionsBoard({ questions, onSubmit }: QuestionsBoardProps) { const [data, setData] = React.useState({}); @@ -28,28 +31,30 @@ function QuestionsBoard({ questions, onSubmit }: QuestionsBoardProps) { ); })} - onSubmit(data)}> + onSubmit(data)}> Submit - + ); } -test('form submits two answers', () => { - const allQuestions = ['q1', 'q2']; - const mockFn = jest.fn(); +test('form submits two answers', async () => { + const questions = ['q1', 'q2']; + const onSubmit = jest.fn(); - const { getAllByLabelText, getByText } = render( - - ); + const user = userEvent.setup(); + render(); - const answerInputs = getAllByLabelText('answer input'); + const answerInputs = screen.getAllByLabelText('answer input'); - fireEvent.changeText(answerInputs[0], 'a1'); - fireEvent.changeText(answerInputs[1], 'a2'); - fireEvent.press(getByText('Submit')); + // simulates the user focusing on TextInput and typing text one char at a time + await user.type(answerInputs[0], 'a1'); + await user.type(answerInputs[1], 'a2'); - expect(mockFn).toHaveBeenCalledWith({ + // simulates the user pressing on any pressable element + await user.press(screen.getByRole('button', { name: 'Submit' })); + + expect(onSubmit).toHaveBeenCalledWith({ '1': { q: 'q1', a: 'a1' }, '2': { q: 'q2', a: 'a2' }, }); diff --git a/src/__tests__/react-native-api.test.tsx b/src/__tests__/react-native-api.test.tsx index 6972b275..ab1bfcb7 100644 --- a/src/__tests__/react-native-api.test.tsx +++ b/src/__tests__/react-native-api.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { View, Text, TextInput, Switch, ScrollView, FlatList } from 'react-native'; -import { render } from '..'; +import { FlatList, ScrollView, Switch, Text, TextInput, View } from 'react-native'; +import { render, screen } from '..'; /** * Tests in this file are intended to give us an proactive warning that React Native behavior has @@ -8,9 +8,9 @@ import { render } from '..'; */ test('React Native API assumption: renders single host element', () => { - const view = render(); + render(); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` @@ -18,9 +18,9 @@ test('React Native API assumption: renders single host element', () => { }); test('React Native API assumption: renders single host element', () => { - const view = render(Hello); + render(Hello); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` @@ -30,7 +30,7 @@ test('React Native API assumption: renders single host element', () => { }); test('React Native API assumption: nested renders single host element', () => { - const view = render( + render( Before Hello @@ -40,7 +40,7 @@ test('React Native API assumption: nested renders single host element', ( ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` @@ -64,7 +64,7 @@ test('React Native API assumption: nested renders single host element', ( }); test('React Native API assumption: renders single host element', () => { - const view = render( + render( renders single host element', () /> ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` renders single host element', () }); test('React Native API assumption: with nested Text renders single host element', () => { - const view = render( + render( Hello ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` with nested Text renders single h }); test('React Native API assumption: renders single host element', () => { - const view = render(); + render(); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` renders single host element', () => }); test('React Native API assumption: aria-* props render on host View', () => { - const view = render( + render( { /> ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` { }); test('React Native API assumption: aria-* props render on host Text', () => { - const view = render( + render( { /> ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` { }); test('React Native API assumption: aria-* props render on host TextInput', () => { - const view = render( + render( /> ); - expect(view.toJSON()).toMatchInlineSnapshot(` + expect(screen.toJSON()).toMatchInlineSnapshot(` }); test('ScrollView renders correctly', () => { - const screen = render( + render( @@ -288,7 +288,7 @@ test('ScrollView renders correctly', () => { }); test('FlatList renders correctly', () => { - const screen = render( + render( {item}} /> ); diff --git a/src/__tests__/render-debug.test.tsx b/src/__tests__/render-debug.test.tsx index a9f92778..988d491f 100644 --- a/src/__tests__/render-debug.test.tsx +++ b/src/__tests__/render-debug.test.tsx @@ -1,10 +1,8 @@ /* eslint-disable no-console */ import * as React from 'react'; -import { View, Text, TextInput, Pressable } from 'react-native'; +import { Pressable, Text, TextInput, View } from 'react-native'; import stripAnsi from 'strip-ansi'; -import { render, fireEvent, configure } from '..'; - -type ConsoleLogMock = jest.Mock>; +import { configure, fireEvent, render, screen } from '..'; const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; const PLACEHOLDER_CHEF = 'Who inspected freshness?'; @@ -30,14 +28,17 @@ afterEach(() => { jest.restoreAllMocks(); }); -class MyButton extends React.Component { - render() { - return ( - - {this.props.children} - - ); - } +interface MyButtonProps { + onPress: () => void; + children: string; +} + +function MyButton(props: MyButtonProps) { + return ( + + {props.children} + + ); } class Banana extends React.Component { @@ -82,9 +83,7 @@ class Banana extends React.Component { /> - - Change freshness! - + Change freshness! First Text Second Text {test} @@ -94,50 +93,50 @@ class Banana extends React.Component { } test('debug', () => { - const { debug } = render(); + render(); - debug(); - debug('my custom message'); - debug.shallow(); - debug.shallow('my other custom message'); - debug({ message: 'another custom message' }); + screen.debug(); + screen.debug('my custom message'); + screen.debug.shallow(); + screen.debug.shallow('my other custom message'); + screen.debug({ message: 'another custom message' }); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); expect(stripAnsi(mockCalls[1][0] + mockCalls[1][1])).toMatchSnapshot('with message'); expect(stripAnsi(mockCalls[2][0])).toMatchSnapshot('shallow'); expect(stripAnsi(mockCalls[3][0] + mockCalls[3][1])).toMatchSnapshot('shallow with message'); expect(stripAnsi(mockCalls[4][0] + mockCalls[4][1])).toMatchSnapshot('another custom message'); - const mockWarnCalls = (console.warn as any as ConsoleLogMock).mock.calls; + const mockWarnCalls = jest.mocked(console.warn).mock.calls; expect(mockWarnCalls[0]).toEqual([ 'Using debug("message") is deprecated and will be removed in future release, please use debug({ message; "message" }) instead.', ]); }); test('debug changing component', () => { - const { UNSAFE_getByProps, debug } = render(); - fireEvent.press(UNSAFE_getByProps({ type: 'primary' })); + render(); + fireEvent.press(screen.getByRole('button', { name: 'Change freshness!' })); - debug(); + screen.debug(); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot( 'bananaFresh button message should now be "fresh"' ); }); test('debug with only children prop', () => { - const { debug } = render(); - debug({ mapProps: () => ({}) }); + render(); + screen.debug({ mapProps: () => ({}) }); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); test('debug with only prop whose value is bananaChef', () => { - const { debug } = render(); - debug({ + render(); + screen.debug({ mapProps: (props) => { const filterProps: Record = {}; Object.keys(props).forEach((key) => { @@ -149,51 +148,51 @@ test('debug with only prop whose value is bananaChef', () => { }, }); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); test('debug with only props from TextInput components', () => { - const { debug } = render(); - debug({ + render(); + screen.debug({ mapProps: (props, node) => (node.type === 'TextInput' ? props : {}), }); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); test('debug should use debugOptions from config when no option is specified', () => { configure({ defaultDebugOptions: { mapProps: () => ({}) } }); - const { debug } = render( + render( hello ); - debug(); + screen.debug(); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); test('filtering out props through mapProps option should not modify component', () => { - const { debug, getByTestId } = render(); - debug({ mapProps: () => ({}) }); + render(); + screen.debug({ mapProps: () => ({}) }); - expect(getByTestId('viewTestID')).toBeTruthy(); + expect(screen.getByTestId('viewTestID')).toBeTruthy(); }); test('debug should use given options over config debugOptions', () => { configure({ defaultDebugOptions: { mapProps: () => ({}) } }); - const { debug } = render( + render( hello ); - debug({ mapProps: (props) => props }); + screen.debug({ mapProps: (props) => props }); - const mockCalls = (console.log as any as ConsoleLogMock).mock.calls; + const mockCalls = jest.mocked(console.log).mock.calls; expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot(); }); diff --git a/src/__tests__/render-string-validation.test.tsx b/src/__tests__/render-string-validation.test.tsx index a0be4267..1ff6b2c2 100644 --- a/src/__tests__/render-string-validation.test.tsx +++ b/src/__tests__/render-string-validation.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { View, Text, Pressable } from 'react-native'; -import { render, fireEvent } from '..'; +import { render, fireEvent, screen } from '..'; // eslint-disable-next-line no-console const originalConsoleError = console.error; @@ -34,11 +34,11 @@ test('should throw when rendering a string outside a text component', () => { }); test('should throw an error when rerendering with text outside of Text component', () => { - const { rerender } = render(, { + render(, { unstable_validateStringsRenderedWithinText: true, }); - expect(() => rerender(hello)).toThrow( + expect(() => screen.rerender(hello)).toThrow( `${VALIDATION_ERROR}. Detected attempt to render "hello" string within a component.` ); }); @@ -58,11 +58,11 @@ const InvalidTextAfterPress = () => { }; test('should throw an error when strings are rendered outside Text', () => { - const { getByText } = render(, { + render(, { unstable_validateStringsRenderedWithinText: true, }); - expect(() => fireEvent.press(getByText('Show text'))).toThrow( + expect(() => fireEvent.press(screen.getByText('Show text'))).toThrow( `${VALIDATION_ERROR}. Detected attempt to render "text rendered outside text component" string within a component.` ); }); diff --git a/src/__tests__/render.test.tsx b/src/__tests__/render.test.tsx index e6ca0369..0c09b390 100644 --- a/src/__tests__/render.test.tsx +++ b/src/__tests__/render.test.tsx @@ -1,8 +1,8 @@ /* eslint-disable no-console */ import * as React from 'react'; -import { View, Text, TextInput, Pressable } from 'react-native'; +import { Pressable, Text, TextInput, View } from 'react-native'; import { getConfig, resetToDefaults } from '../config'; -import { render, screen, fireEvent, RenderAPI } from '..'; +import { fireEvent, render, RenderAPI, screen } from '..'; const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; const PLACEHOLDER_CHEF = 'Who inspected freshness?'; @@ -75,57 +75,57 @@ class Banana extends React.Component { } test('UNSAFE_getAllByType, UNSAFE_queryAllByType', () => { - const { UNSAFE_getAllByType, UNSAFE_queryAllByType } = render(); - const [text, status, button] = UNSAFE_getAllByType(Text); + render(); + const [text, status, button] = screen.UNSAFE_getAllByType(Text); const InExistent = () => null; expect(text.props.children).toBe('Is the banana fresh?'); expect(status.props.children).toBe('not fresh'); expect(button.props.children).toBe('Change freshness!'); - expect(() => UNSAFE_getAllByType(InExistent)).toThrow('No instances found'); + expect(() => screen.UNSAFE_getAllByType(InExistent)).toThrow('No instances found'); - expect(UNSAFE_queryAllByType(Text)[1]).toBe(status); - expect(UNSAFE_queryAllByType(InExistent)).toHaveLength(0); + expect(screen.UNSAFE_queryAllByType(Text)[1]).toBe(status); + expect(screen.UNSAFE_queryAllByType(InExistent)).toHaveLength(0); }); test('UNSAFE_getByProps, UNSAFE_queryByProps', () => { - const { UNSAFE_getByProps, UNSAFE_queryByProps } = render(); - const primaryType = UNSAFE_getByProps({ type: 'primary' }); + render(); + const primaryType = screen.UNSAFE_getByProps({ type: 'primary' }); expect(primaryType.props.children).toBe('Change freshness!'); - expect(() => UNSAFE_getByProps({ type: 'inexistent' })).toThrow('No instances found'); + expect(() => screen.UNSAFE_getByProps({ type: 'inexistent' })).toThrow('No instances found'); - expect(UNSAFE_queryByProps({ type: 'primary' })).toBe(primaryType); - expect(UNSAFE_queryByProps({ type: 'inexistent' })).toBeNull(); + expect(screen.UNSAFE_queryByProps({ type: 'primary' })).toBe(primaryType); + expect(screen.UNSAFE_queryByProps({ type: 'inexistent' })).toBeNull(); }); test('UNSAFE_getAllByProp, UNSAFE_queryAllByProps', () => { - const { UNSAFE_getAllByProps, UNSAFE_queryAllByProps } = render(); - const primaryTypes = UNSAFE_getAllByProps({ type: 'primary' }); + render(); + const primaryTypes = screen.UNSAFE_getAllByProps({ type: 'primary' }); expect(primaryTypes).toHaveLength(1); - expect(() => UNSAFE_getAllByProps({ type: 'inexistent' })).toThrow('No instances found'); + expect(() => screen.UNSAFE_getAllByProps({ type: 'inexistent' })).toThrow('No instances found'); - expect(UNSAFE_queryAllByProps({ type: 'primary' })).toEqual(primaryTypes); - expect(UNSAFE_queryAllByProps({ type: 'inexistent' })).toHaveLength(0); + expect(screen.UNSAFE_queryAllByProps({ type: 'primary' })).toEqual(primaryTypes); + expect(screen.UNSAFE_queryAllByProps({ type: 'inexistent' })).toHaveLength(0); }); test('update', () => { const fn = jest.fn(); - const { getByText, update, rerender } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); + fireEvent.press(screen.getByText('Change freshness!')); - update(); - rerender(); + screen.update(); + screen.rerender(); expect(fn).toHaveBeenCalledTimes(3); }); test('unmount', () => { const fn = jest.fn(); - const { unmount } = render(); - unmount(); + render(); + screen.unmount(); expect(fn).toHaveBeenCalled(); }); @@ -136,16 +136,16 @@ test('unmount should handle cleanup functions', () => { return null; }; - const { unmount } = render(); + render(); - unmount(); + screen.unmount(); expect(cleanup).toHaveBeenCalledTimes(1); }); test('toJSON renders host output', () => { - const { toJSON } = render(press me); - expect(toJSON()).toMatchSnapshot(); + render(press me); + expect(screen.toJSON()).toMatchSnapshot(); }); test('renders options.wrapper around node', () => { @@ -154,12 +154,12 @@ test('renders options.wrapper around node', () => { {children} ); - const { toJSON, getByTestId } = render(, { + render(, { wrapper: WrapperComponent, }); - expect(getByTestId('wrapper')).toBeTruthy(); - expect(toJSON()).toMatchInlineSnapshot(` + expect(screen.getByTestId('wrapper')).toBeTruthy(); + expect(screen.toJSON()).toMatchInlineSnapshot(` @@ -176,14 +176,14 @@ test('renders options.wrapper around updated node', () => { {children} ); - const { toJSON, getByTestId, rerender } = render(, { + render(, { wrapper: WrapperComponent, }); - rerender(); + screen.rerender(); - expect(getByTestId('wrapper')).toBeTruthy(); - expect(toJSON()).toMatchInlineSnapshot(` + expect(screen.getByTestId('wrapper')).toBeTruthy(); + expect(screen.toJSON()).toMatchInlineSnapshot(` @@ -197,29 +197,24 @@ test('renders options.wrapper around updated node', () => { }); test('returns host root', () => { - const { root } = render(); + render(); - expect(root).toBeDefined(); - expect(root.type).toBe('View'); - expect(root.props.testID).toBe('inner'); + expect(screen.root).toBeDefined(); + expect(screen.root.type).toBe('View'); + expect(screen.root.props.testID).toBe('inner'); }); test('returns composite UNSAFE_root', () => { - const { UNSAFE_root } = render(); + render(); - expect(UNSAFE_root).toBeDefined(); - expect(UNSAFE_root.type).toBe(View); - expect(UNSAFE_root.props.testID).toBe('inner'); + expect(screen.UNSAFE_root).toBeDefined(); + expect(screen.UNSAFE_root.type).toBe(View); + expect(screen.UNSAFE_root.props.testID).toBe('inner'); }); test('container displays deprecation', () => { - const view = render(); + render(); - expect(() => (view as any).container).toThrowErrorMatchingInlineSnapshot(` - "'container' property has been renamed to 'UNSAFE_root'. - - Consider using 'root' property which returns root host element." - `); expect(() => (screen as any).container).toThrowErrorMatchingInlineSnapshot(` "'container' property has been renamed to 'UNSAFE_root'. diff --git a/src/__tests__/wait-for-element-to-be-removed.test.tsx b/src/__tests__/wait-for-element-to-be-removed.test.tsx index dd09f79a..09586a2a 100644 --- a/src/__tests__/wait-for-element-to-be-removed.test.tsx +++ b/src/__tests__/wait-for-element-to-be-removed.test.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { View, Text, TouchableOpacity } from 'react-native'; -import { render, fireEvent, waitForElementToBeRemoved } from '..'; +import { Text, TouchableOpacity, View } from 'react-native'; +import { fireEvent, render, screen, waitForElementToBeRemoved } from '..'; const TestSetup = ({ shouldUseDelay = true }) => { const [isAdded, setIsAdded] = useState(true); @@ -29,7 +29,7 @@ afterEach(() => { }); test('waits when using getBy query', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); const element = screen.getByText('Observed Element'); @@ -41,7 +41,7 @@ test('waits when using getBy query', async () => { }); test('waits when using getAllBy query', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); const elements = screen.getAllByText('Observed Element'); @@ -53,7 +53,7 @@ test('waits when using getAllBy query', async () => { }); test('waits when using queryBy query', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); const element = screen.getByText('Observed Element'); @@ -65,7 +65,7 @@ test('waits when using queryBy query', async () => { }); test('waits when using queryAllBy query', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); const elements = screen.getAllByText('Observed Element'); @@ -77,7 +77,7 @@ test('waits when using queryAllBy query', async () => { }); test('checks if elements exist at start', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); expect(screen.queryByText('Observed Element')).toBeNull(); @@ -90,7 +90,7 @@ test('checks if elements exist at start', async () => { }); test('waits until timeout', async () => { - const screen = render(); + render(); fireEvent.press(screen.getByText('Remove Element')); expect(screen.getByText('Observed Element')).toBeTruthy(); diff --git a/src/__tests__/wait-for.test.tsx b/src/__tests__/wait-for.test.tsx index c10c317b..a42b991d 100644 --- a/src/__tests__/wait-for.test.tsx +++ b/src/__tests__/wait-for.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Text, TouchableOpacity, View, Pressable } from 'react-native'; -import { fireEvent, render, waitFor, configure } from '..'; +import { fireEvent, render, waitFor, configure, screen } from '..'; class Banana extends React.Component { changeFresh = () => { @@ -37,51 +37,51 @@ afterEach(() => { }); test('waits for element until it stops throwing', async () => { - const { getByText, queryByText } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); + fireEvent.press(screen.getByText('Change freshness!')); - expect(queryByText('Fresh')).toBeNull(); + expect(screen.queryByText('Fresh')).toBeNull(); - const freshBananaText = await waitFor(() => getByText('Fresh')); + const freshBananaText = await waitFor(() => screen.getByText('Fresh')); expect(freshBananaText.props.children).toBe('Fresh'); }); test('waits for element until timeout is met', async () => { - const { getByText } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); + fireEvent.press(screen.getByText('Change freshness!')); - await expect(waitFor(() => getByText('Fresh'), { timeout: 100 })).rejects.toThrow(); + await expect(waitFor(() => screen.getByText('Fresh'), { timeout: 100 })).rejects.toThrow(); // Async action ends after 300ms and we only waited 100ms, so we need to wait // for the remaining async actions to finish - await waitFor(() => getByText('Fresh')); + await waitFor(() => screen.getByText('Fresh')); }); test('waitFor defaults to asyncWaitTimeout config option', async () => { configure({ asyncUtilTimeout: 100 }); - const { getByText } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); - await expect(waitFor(() => getByText('Fresh'))).rejects.toThrow(); + fireEvent.press(screen.getByText('Change freshness!')); + await expect(waitFor(() => screen.getByText('Fresh'))).rejects.toThrow(); // Async action ends after 300ms and we only waited 100ms, so we need to wait // for the remaining async actions to finish - await waitFor(() => getByText('Fresh'), { timeout: 1000 }); + await waitFor(() => screen.getByText('Fresh'), { timeout: 1000 }); }); test('waitFor timeout option takes precendence over `asyncWaitTimeout` config option', async () => { configure({ asyncUtilTimeout: 2000 }); - const { getByText } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); - await expect(waitFor(() => getByText('Fresh'), { timeout: 100 })).rejects.toThrow(); + fireEvent.press(screen.getByText('Change freshness!')); + await expect(waitFor(() => screen.getByText('Fresh'), { timeout: 100 })).rejects.toThrow(); // Async action ends after 300ms and we only waited 100ms, so we need to wait // for the remaining async actions to finish - await waitFor(() => getByText('Fresh')); + await waitFor(() => screen.getByText('Fresh')); }); test('waits for element with custom interval', async () => { @@ -124,9 +124,9 @@ const Comp = ({ onPress }: { onPress: () => void }) => { test('waits for async event with fireEvent', async () => { const spy = jest.fn(); - const { getByText } = render(); + render(); - fireEvent.press(getByText('Trigger')); + fireEvent.press(screen.getByText('Trigger')); await waitFor(() => { expect(spy).toHaveBeenCalled(); @@ -137,13 +137,13 @@ test.each([false, true])( 'waits for element until it stops throwing using fake timers (legacyFakeTimers = %s)', async (legacyFakeTimers) => { jest.useFakeTimers({ legacyFakeTimers }); - const { getByText, queryByText } = render(); + render(); - fireEvent.press(getByText('Change freshness!')); - expect(queryByText('Fresh')).toBeNull(); + fireEvent.press(screen.getByText('Change freshness!')); + expect(screen.queryByText('Fresh')).toBeNull(); jest.advanceTimersByTime(300); - const freshBananaText = await waitFor(() => getByText('Fresh')); + const freshBananaText = await waitFor(() => screen.getByText('Fresh')); expect(freshBananaText.props.children).toBe('Fresh'); } @@ -293,18 +293,18 @@ test.each([ } const onPress = jest.fn(); - const view = render(); + render(); // Required: this `waitFor` will succeed on first check, because the "root" view is there // since the initial mount. - await waitFor(() => view.getByTestId('root')); + await waitFor(() => screen.getByTestId('root')); // This `waitFor` will also succeed on first check, because the promise that sets the // `color` state to "red" resolves right after the previous `await waitFor` statement. - await waitFor(() => view.getByText('red')); + await waitFor(() => screen.getByText('red')); // Check that the `onPress` callback is called with the already-updated value of `syncedColor`. - fireEvent.press(view.getByText('Trigger')); + fireEvent.press(screen.getByText('Trigger')); expect(onPress).toHaveBeenCalledWith('red'); } ); diff --git a/src/helpers/__tests__/accessiblity.test.tsx b/src/helpers/__tests__/accessiblity.test.tsx index 502d6e3b..a2efc577 100644 --- a/src/helpers/__tests__/accessiblity.test.tsx +++ b/src/helpers/__tests__/accessiblity.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, TextInput, Pressable, Switch, TouchableOpacity } from 'react-native'; -import { render, isHiddenFromAccessibility, isInaccessible } from '../..'; +import { render, isHiddenFromAccessibility, isInaccessible, screen } from '../..'; import { isAccessibilityElement } from '../accessiblity'; describe('isHiddenFromAccessibility', () => { @@ -35,10 +35,10 @@ describe('isHiddenFromAccessibility', () => { }); test('detects elements with aria-hidden prop', () => { - const view = render(); + render(); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -46,14 +46,14 @@ describe('isHiddenFromAccessibility', () => { }); test('detects nested elements with aria-hidden prop', () => { - const view = render( + render( ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -61,10 +61,10 @@ describe('isHiddenFromAccessibility', () => { }); test('detects elements with accessibilityElementsHidden prop', () => { - const view = render(); + render(); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -72,14 +72,14 @@ describe('isHiddenFromAccessibility', () => { }); test('detects nested elements with accessibilityElementsHidden prop', () => { - const view = render( + render( ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -87,7 +87,7 @@ describe('isHiddenFromAccessibility', () => { }); test('detects deeply nested elements with accessibilityElementsHidden prop', () => { - const view = render( + render( @@ -98,7 +98,7 @@ describe('isHiddenFromAccessibility', () => { ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -106,10 +106,10 @@ describe('isHiddenFromAccessibility', () => { }); test('detects elements with importantForAccessibility="no-hide-descendants" prop', () => { - const view = render(); + render(); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -117,14 +117,14 @@ describe('isHiddenFromAccessibility', () => { }); test('detects nested elements with importantForAccessibility="no-hide-descendants" prop', () => { - const view = render( + render( ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -132,10 +132,10 @@ describe('isHiddenFromAccessibility', () => { }); test('detects elements with display=none', () => { - const view = render(); + render(); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -143,14 +143,14 @@ describe('isHiddenFromAccessibility', () => { }); test('detects nested elements with display=none', () => { - const view = render( + render( ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -158,7 +158,7 @@ describe('isHiddenFromAccessibility', () => { }); test('detects deeply nested elements with display=none', () => { - const view = render( + render( @@ -169,7 +169,7 @@ describe('isHiddenFromAccessibility', () => { ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -177,7 +177,7 @@ describe('isHiddenFromAccessibility', () => { }); test('detects elements with display=none with complex style', () => { - const view = render( + render( { ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -193,10 +193,10 @@ describe('isHiddenFromAccessibility', () => { }); test('is not trigged by opacity = 0', () => { - const view = render(); + render(); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -204,7 +204,7 @@ describe('isHiddenFromAccessibility', () => { }); test('detects siblings of element with accessibilityViewIsModal prop', () => { - const view = render( + render( @@ -212,7 +212,7 @@ describe('isHiddenFromAccessibility', () => { ); expect( isHiddenFromAccessibility( - view.getByTestId('subject', { + screen.getByTestId('subject', { includeHiddenElements: true, }) ) @@ -220,7 +220,7 @@ describe('isHiddenFromAccessibility', () => { }); test('detects deeply nested siblings of element with accessibilityViewIsModal prop', () => { - const view = render( + render( @@ -231,38 +231,38 @@ describe('isHiddenFromAccessibility', () => { ); expect( - isHiddenFromAccessibility(view.getByTestId('subject', { includeHiddenElements: true })) + isHiddenFromAccessibility(screen.getByTestId('subject', { includeHiddenElements: true })) ).toBe(true); }); test('detects siblings of element with "aria-modal" prop', () => { - const view = render( + render( ); expect( - isHiddenFromAccessibility(view.getByTestId('subject', { includeHiddenElements: true })) + isHiddenFromAccessibility(screen.getByTestId('subject', { includeHiddenElements: true })) ).toBe(true); }); test('is not triggered for element with accessibilityViewIsModal prop', () => { - const view = render(); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + render(); + expect(isHiddenFromAccessibility(screen.getByTestId('subject'))).toBe(false); }); test('is not triggered for child of element with accessibilityViewIsModal prop', () => { - const view = render( + render( ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect(isHiddenFromAccessibility(screen.getByTestId('subject'))).toBe(false); }); test('is not triggered for descendent of element with accessibilityViewIsModal prop', () => { - const view = render( + render( @@ -271,7 +271,7 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect(isHiddenFromAccessibility(screen.getByTestId('subject'))).toBe(false); }); test('has isInaccessible alias', () => { @@ -280,39 +280,39 @@ describe('isHiddenFromAccessibility', () => { }); test('is not triggered for element with "aria-modal" prop', () => { - const view = render(); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + render(); + expect(isHiddenFromAccessibility(screen.getByTestId('subject'))).toBe(false); }); describe('isAccessibilityElement', () => { test('matches View component properly', () => { - const { getByTestId } = render( + render( ); - expect(isAccessibilityElement(getByTestId('default'))).toBeFalsy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('matches TextInput component properly', () => { - const { getByTestId } = render( + render( ); - expect(isAccessibilityElement(getByTestId('default'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('matches Text component properly', () => { - const { getByTestId } = render( + render( Default @@ -323,48 +323,48 @@ describe('isAccessibilityElement', () => { ); - expect(isAccessibilityElement(getByTestId('default'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('matches Switch component properly', () => { - const { getByTestId } = render( + render( ); - expect(isAccessibilityElement(getByTestId('default'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('matches Pressable component properly', () => { - const { getByTestId } = render( + render( ); - expect(isAccessibilityElement(getByTestId('default'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('matches TouchableOpacity component properly', () => { - const { getByTestId } = render( + render( ); - expect(isAccessibilityElement(getByTestId('default'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('true'))).toBeTruthy(); - expect(isAccessibilityElement(getByTestId('false'))).toBeFalsy(); + expect(isAccessibilityElement(screen.getByTestId('default'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('true'))).toBeTruthy(); + expect(isAccessibilityElement(screen.getByTestId('false'))).toBeFalsy(); }); test('returns false when given null', () => { diff --git a/src/helpers/__tests__/component-tree.test.tsx b/src/helpers/__tests__/component-tree.test.tsx index 32d2290f..c749ea6f 100644 --- a/src/helpers/__tests__/component-tree.test.tsx +++ b/src/helpers/__tests__/component-tree.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { View, Text, TextInput } from 'react-native'; -import { render } from '../..'; +import { Text, TextInput, View } from 'react-native'; +import { render, screen } from '../..'; import { getHostChildren, getHostParent, @@ -25,7 +25,7 @@ function MultipleHostChildren() { describe('getHostParent()', () => { it('returns host parent for host component', () => { - const view = render( + render( @@ -34,11 +34,11 @@ describe('getHostParent()', () => { ); - const hostParent = getHostParent(view.getByTestId('subject')); - expect(hostParent).toBe(view.getByTestId('parent')); + const hostParent = getHostParent(screen.getByTestId('subject')); + expect(hostParent).toBe(screen.getByTestId('parent')); const hostGrandparent = getHostParent(hostParent); - expect(hostGrandparent).toBe(view.getByTestId('grandparent')); + expect(hostGrandparent).toBe(screen.getByTestId('grandparent')); expect(getHostParent(hostGrandparent)).toBe(null); }); @@ -48,22 +48,22 @@ describe('getHostParent()', () => { }); it('returns host parent for composite component', () => { - const view = render( + render( ); - const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren); + const compositeComponent = screen.UNSAFE_getByType(MultipleHostChildren); const hostParent = getHostParent(compositeComponent); - expect(hostParent).toBe(view.getByTestId('parent')); + expect(hostParent).toBe(screen.getByTestId('parent')); }); }); describe('getHostChildren()', () => { it('returns host children for host component', () => { - const view = render( + render( @@ -72,21 +72,21 @@ describe('getHostChildren()', () => { ); - const hostSubject = view.getByTestId('subject'); + const hostSubject = screen.getByTestId('subject'); expect(getHostChildren(hostSubject)).toEqual([]); - const hostSibling = view.getByTestId('sibling'); + const hostSibling = screen.getByTestId('sibling'); expect(getHostChildren(hostSibling)).toEqual([]); - const hostParent = view.getByTestId('parent'); + const hostParent = screen.getByTestId('parent'); expect(getHostChildren(hostParent)).toEqual([hostSubject, hostSibling]); - const hostGrandparent = view.getByTestId('grandparent'); + const hostGrandparent = screen.getByTestId('grandparent'); expect(getHostChildren(hostGrandparent)).toEqual([hostParent]); }); it('returns host children for composite component', () => { - const view = render( + render( @@ -94,19 +94,19 @@ describe('getHostChildren()', () => { ); - expect(getHostChildren(view.getByTestId('parent'))).toEqual([ - view.getByTestId('child1'), - view.getByTestId('child2'), - view.getByTestId('child3'), - view.getByTestId('subject'), - view.getByTestId('sibling'), + expect(getHostChildren(screen.getByTestId('parent'))).toEqual([ + screen.getByTestId('child1'), + screen.getByTestId('child2'), + screen.getByTestId('child3'), + screen.getByTestId('subject'), + screen.getByTestId('sibling'), ]); }); }); describe('getHostSelves()', () => { it('returns passed element for host components', () => { - const view = render( + render( @@ -115,21 +115,21 @@ describe('getHostSelves()', () => { ); - const hostSubject = view.getByTestId('subject'); + const hostSubject = screen.getByTestId('subject'); expect(getHostSelves(hostSubject)).toEqual([hostSubject]); - const hostSibling = view.getByTestId('sibling'); + const hostSibling = screen.getByTestId('sibling'); expect(getHostSelves(hostSibling)).toEqual([hostSibling]); - const hostParent = view.getByTestId('parent'); + const hostParent = screen.getByTestId('parent'); expect(getHostSelves(hostParent)).toEqual([hostParent]); - const hostGrandparent = view.getByTestId('grandparent'); + const hostGrandparent = screen.getByTestId('grandparent'); expect(getHostSelves(hostGrandparent)).toEqual([hostGrandparent]); }); test('returns single host element for React Native composite components', () => { - const view = render( + render( Text { ); - const compositeText = view.getByText('Text'); - const hostText = view.getByTestId('text'); + const compositeText = screen.getByText('Text'); + const hostText = screen.getByTestId('text'); expect(getHostSelves(compositeText)).toEqual([hostText]); - const compositeTextInputByValue = view.getByDisplayValue('TextInputValue'); - const compositeTextInputByPlaceholder = view.getByPlaceholderText('TextInputPlaceholder'); + const compositeTextInputByValue = screen.getByDisplayValue('TextInputValue'); + const compositeTextInputByPlaceholder = screen.getByPlaceholderText('TextInputPlaceholder'); - const hostTextInput = view.getByTestId('textInput'); + const hostTextInput = screen.getByTestId('textInput'); expect(getHostSelves(compositeTextInputByValue)).toEqual([hostTextInput]); expect(getHostSelves(compositeTextInputByPlaceholder)).toEqual([hostTextInput]); }); test('returns host children for custom composite components', () => { - const view = render( + render( @@ -161,20 +161,20 @@ describe('getHostSelves()', () => { ); - const zeroCompositeComponent = view.UNSAFE_getByType(ZeroHostChildren); + const zeroCompositeComponent = screen.UNSAFE_getByType(ZeroHostChildren); expect(getHostSelves(zeroCompositeComponent)).toEqual([]); - const multipleCompositeComponent = view.UNSAFE_getByType(MultipleHostChildren); - const hostChild1 = view.getByTestId('child1'); - const hostChild2 = view.getByTestId('child2'); - const hostChild3 = view.getByTestId('child3'); + const multipleCompositeComponent = screen.UNSAFE_getByType(MultipleHostChildren); + const hostChild1 = screen.getByTestId('child1'); + const hostChild2 = screen.getByTestId('child2'); + const hostChild3 = screen.getByTestId('child3'); expect(getHostSelves(multipleCompositeComponent)).toEqual([hostChild1, hostChild2, hostChild3]); }); }); describe('getHostSiblings()', () => { it('returns host siblings for host component', () => { - const view = render( + render( @@ -185,18 +185,18 @@ describe('getHostSiblings()', () => { ); - const hostSiblings = getHostSiblings(view.getByTestId('subject')); + const hostSiblings = getHostSiblings(screen.getByTestId('subject')); expect(hostSiblings).toEqual([ - view.getByTestId('siblingBefore'), - view.getByTestId('siblingAfter'), - view.getByTestId('child1'), - view.getByTestId('child2'), - view.getByTestId('child3'), + screen.getByTestId('siblingBefore'), + screen.getByTestId('siblingAfter'), + screen.getByTestId('child1'), + screen.getByTestId('child2'), + screen.getByTestId('child3'), ]); }); it('returns host siblings for composite component', () => { - const view = render( + render( @@ -207,19 +207,19 @@ describe('getHostSiblings()', () => { ); - const compositeComponent = view.UNSAFE_getByType(MultipleHostChildren); + const compositeComponent = screen.UNSAFE_getByType(MultipleHostChildren); const hostSiblings = getHostSiblings(compositeComponent); expect(hostSiblings).toEqual([ - view.getByTestId('siblingBefore'), - view.getByTestId('subject'), - view.getByTestId('siblingAfter'), + screen.getByTestId('siblingBefore'), + screen.getByTestId('subject'), + screen.getByTestId('siblingAfter'), ]); }); }); describe('getUnsafeRootElement()', () => { it('returns UNSAFE_root for mounted view', () => { - const screen = render( + render( diff --git a/src/helpers/__tests__/text-content.test.tsx b/src/helpers/__tests__/text-content.test.tsx index 76a9bd56..1c8a3f8b 100644 --- a/src/helpers/__tests__/text-content.test.tsx +++ b/src/helpers/__tests__/text-content.test.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; import { Text } from 'react-native'; -import render from '../../render'; +import { render, screen } from '../../'; import { getTextContent } from '../text-content'; test('getTextContent with simple content', () => { - const view = render(Hello world); - expect(getTextContent(view.root)).toBe('Hello world'); + render(Hello world); + expect(getTextContent(screen.root)).toBe('Hello world'); }); test('getTextContent with null element', () => { @@ -13,37 +13,37 @@ test('getTextContent with null element', () => { }); test('getTextContent with single nested content', () => { - const view = render( + render( Hello world ); - expect(getTextContent(view.root)).toBe('Hello world'); + expect(getTextContent(screen.root)).toBe('Hello world'); }); test('getTextContent with multiple nested content', () => { - const view = render( + render( Hello world ); - expect(getTextContent(view.root)).toBe('Hello world'); + expect(getTextContent(screen.root)).toBe('Hello world'); }); test('getTextContent with multiple number content', () => { - const view = render( + render( Hello world {100} ); - expect(getTextContent(view.root)).toBe('Hello world 100'); + expect(getTextContent(screen.root)).toBe('Hello world 100'); }); test('getTextContent with multiple boolean content', () => { - const view = render( + render( Hello{false} {true}world ); - expect(getTextContent(view.root)).toBe('Hello world'); + expect(getTextContent(screen.root)).toBe('Hello world'); }); diff --git a/src/matchers/__tests__/to-be-disabled.test.tsx b/src/matchers/__tests__/to-be-disabled.test.tsx index 2a8ba44a..36554d7a 100644 --- a/src/matchers/__tests__/to-be-disabled.test.tsx +++ b/src/matchers/__tests__/to-be-disabled.test.tsx @@ -10,11 +10,11 @@ import { Text, View, } from 'react-native'; -import { render } from '../..'; +import { render, screen } from '../..'; import '../extend-expect'; test('toBeDisabled()/toBeEnabled() supports basic case', () => { - const screen = render( + render( @@ -88,7 +88,7 @@ test('toBeDisabled()/toBeEnabled() supports basic case', () => { }); test('toBeDisabled()/toBeEnabled() supports Pressable with "disabled" prop', () => { - const screen = render( + render( Button @@ -158,7 +158,7 @@ test.each([ ['TouchableWithoutFeedback', TouchableWithoutFeedback], ['TouchableNativeFeedback', TouchableNativeFeedback], ] as const)('toBeDisabled()/toBeEnabled() supports %s with "disabled" prop', (_, Component) => { - const screen = render( + render( // @ts-expect-error disabled prop is not available on all Touchables Button @@ -190,7 +190,7 @@ test.each([ ] as const)( 'toBeDisabled()/toBeEnabled() supports %s with "aria-disabled" prop', (_, Component) => { - const screen = render( + render( // @ts-expect-error too generic for typescript Hello @@ -217,7 +217,7 @@ test.each([ ] as const)( 'toBeDisabled()/toBeEnabled() supports %s with "accessibilityState.disabled" prop', (_, Component) => { - const screen = render( + render( // @ts-expect-error disabled prop is not available on all Touchables Hello @@ -233,7 +233,7 @@ test.each([ ); test('toBeDisabled()/toBeEnabled() supports "editable" prop on TextInput', () => { - const screen = render( + render( @@ -251,7 +251,7 @@ test('toBeDisabled()/toBeEnabled() supports "editable" prop on TextInput', () => }); test('toBeDisabled()/toBeEnabled() supports "disabled" prop on Button', () => { - const screen = render( + render(