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(