diff --git a/examples/basic/jest-setup.js b/examples/basic/jest-setup.js index 708f3fc4a..af6ed0772 100644 --- a/examples/basic/jest-setup.js +++ b/examples/basic/jest-setup.js @@ -1,13 +1,7 @@ /* eslint-disable no-undef, import/no-extraneous-dependencies */ -import { configure } from '@testing-library/react-native'; // Import Jest Native matchers import '@testing-library/jest-native/extend-expect'; // Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); - -// Enable excluding hidden elements from the queries by default -configure({ - defaultIncludeHiddenElements: false, -}); diff --git a/examples/react-navigation/README.md b/examples/react-navigation/README.md index 89c573e18..3e39f9641 100644 --- a/examples/react-navigation/README.md +++ b/examples/react-navigation/README.md @@ -8,8 +8,6 @@ There are two types of recommeded tests: 1. Tests operating on navigator level - these use `renderNavigator` helper to render a navigator component used in the app. It is useful when you want to test a scenario that includes multiple screens. 2. Tests operating on single screen level - these use regular `render` helper but require refactoring screen components into `Screen` and `ScreenContent` components. Where `Screen` receives React Navigation props and/or uses hooks like `useNavigation` while `ScreenContent` does not have a direct relation to React Navigation API but gets props from `Screen` and calls relevant callbacks to trigger navigation. -> Note that this example applies `includeHiddenElements: false` by default, so all queries will ignore elements on the hidden screens, e.g. inactive tabs or screens present in stack navigators. This option is enabled in `jest-setup.js` file, using `defaultIncludeHiddenElements: false` option to `configure` function. - ## Non-recommended tests There also exists another popular type of screen level tests, where users mock React Navigation objects like `navigation`, `route` and/or hooks like `useNavigation`, etc. We don't recommend this way of testing. **Mocking internal parts of the libraries is effectively testing implementation details, which goes against the Testing Library's [Guiding Principles](https://testing-library.com/docs/guiding-principles/)**. diff --git a/examples/react-navigation/jest-setup.js b/examples/react-navigation/jest-setup.js index e8edcee19..914a6bbc5 100644 --- a/examples/react-navigation/jest-setup.js +++ b/examples/react-navigation/jest-setup.js @@ -1,5 +1,4 @@ /* eslint-disable no-undef, import/no-extraneous-dependencies */ -import { configure } from '@testing-library/react-native'; // Import Jest Native matchers import '@testing-library/jest-native/extend-expect'; @@ -10,8 +9,3 @@ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); // Setup Reanimated mocking for Drawer navigation global.ReanimatedDataMock = { now: () => Date.now() }; require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests(); - -// Enable excluding hidden elements from the queries by default -configure({ - defaultIncludeHiddenElements: false, -}); diff --git a/package.json b/package.json index fca5a71c6..9904cf519 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@testing-library/react-native", - "version": "11.5.4", + "version": "12.0.0-rc.2", "description": "Simple and complete React Native testing utilities that encourage good testing practices.", "main": "build/index.js", "types": "build/index.d.ts", @@ -10,6 +10,12 @@ }, "homepage": "https://callstack.github.io/react-native-testing-library", "author": "Michał Pierzchała ", + "contributors": [ + "Maciej Jastrzębski (https://github.com/mdjastrzebski)", + "Augustin Le Fèvre (https://github.com/AugustinLF)", + "Pierre Zimmermann (https://github.com/pierrezimmermannbam)", + "MattAgn (https://github.com/MattAgn)" + ], "license": "MIT", "private": false, "keywords": [ diff --git a/src/__tests__/__snapshots__/render.breaking.test.tsx.snap b/src/__tests__/__snapshots__/render.breaking.test.tsx.snap deleted file mode 100644 index 018ae7d63..000000000 --- a/src/__tests__/__snapshots__/render.breaking.test.tsx.snap +++ /dev/null @@ -1,39 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`toJSON renders host output 1`] = ` - - - press me - - -`; diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 03de47adc..e1798a84c 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -6,9 +6,8 @@ import { } from '../config'; test('getConfig() returns existing configuration', () => { - expect(getConfig().useBreakingChanges).toEqual(false); expect(getConfig().asyncUtilTimeout).toEqual(1000); - expect(getConfig().defaultIncludeHiddenElements).toEqual(true); + expect(getConfig().defaultIncludeHiddenElements).toEqual(false); }); test('configure() overrides existing config values', () => { @@ -17,37 +16,36 @@ test('configure() overrides existing config values', () => { expect(getConfig()).toEqual({ asyncUtilTimeout: 5000, defaultDebugOptions: { message: 'debug message' }, - defaultIncludeHiddenElements: true, - useBreakingChanges: false, + defaultIncludeHiddenElements: false, }); }); test('resetToDefaults() resets config to defaults', () => { configure({ asyncUtilTimeout: 5000, - defaultIncludeHiddenElements: false, + defaultIncludeHiddenElements: true, }); expect(getConfig().asyncUtilTimeout).toEqual(5000); - expect(getConfig().defaultIncludeHiddenElements).toEqual(false); + expect(getConfig().defaultIncludeHiddenElements).toEqual(true); resetToDefaults(); expect(getConfig().asyncUtilTimeout).toEqual(1000); - expect(getConfig().defaultIncludeHiddenElements).toEqual(true); + expect(getConfig().defaultIncludeHiddenElements).toEqual(false); }); test('resetToDefaults() resets internal config to defaults', () => { configureInternal({ - useBreakingChanges: true, + hostComponentNames: { text: 'A', textInput: 'A' }, }); - expect(getConfig().useBreakingChanges).toEqual(true); + expect(getConfig().hostComponentNames).toEqual({ text: 'A', textInput: 'A' }); resetToDefaults(); - expect(getConfig().useBreakingChanges).toEqual(false); + expect(getConfig().hostComponentNames).toBe(undefined); }); test('configure handles alias option defaultHidden', () => { - expect(getConfig().defaultIncludeHiddenElements).toEqual(true); - - configure({ defaultHidden: false }); expect(getConfig().defaultIncludeHiddenElements).toEqual(false); + + configure({ defaultHidden: true }); + expect(getConfig().defaultIncludeHiddenElements).toEqual(true); }); diff --git a/src/__tests__/render.breaking.test.tsx b/src/__tests__/render.breaking.test.tsx deleted file mode 100644 index ac6492664..000000000 --- a/src/__tests__/render.breaking.test.tsx +++ /dev/null @@ -1,247 +0,0 @@ -/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */ - -/* eslint-disable no-console */ -import * as React from 'react'; -import { View, Text, TextInput, Pressable } from 'react-native'; -import { render, screen, fireEvent, RenderAPI } from '..'; -import { configureInternal } from '../config'; - -beforeEach(() => { - configureInternal({ useBreakingChanges: true }); -}); - -const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; -const PLACEHOLDER_CHEF = 'Who inspected freshness?'; -const INPUT_FRESHNESS = 'Custom Freshie'; -const INPUT_CHEF = 'I inspected freshie'; -const DEFAULT_INPUT_CHEF = 'What did you inspect?'; -const DEFAULT_INPUT_CUSTOMER = 'What banana?'; - -class MyButton extends React.Component { - render() { - return ( - - {this.props.children} - - ); - } -} - -class Banana extends React.Component { - state = { - fresh: false, - }; - - componentDidUpdate() { - if (this.props.onUpdate) { - this.props.onUpdate(); - } - } - - componentWillUnmount() { - if (this.props.onUnmount) { - this.props.onUnmount(); - } - } - - changeFresh = () => { - this.setState((state) => ({ - fresh: !state.fresh, - })); - }; - - render() { - const test = 0; - return ( - - Is the banana fresh? - - {this.state.fresh ? 'fresh' : 'not fresh'} - - - - - - - Change freshness! - - First Text - Second Text - {test} - - ); - } -} - -test('UNSAFE_getAllByType, UNSAFE_queryAllByType', () => { - const { UNSAFE_getAllByType, UNSAFE_queryAllByType } = render(); - const [text, status, button] = 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(UNSAFE_queryAllByType(Text)[1]).toBe(status); - expect(UNSAFE_queryAllByType(InExistent)).toHaveLength(0); -}); - -test('UNSAFE_getByProps, UNSAFE_queryByProps', () => { - const { UNSAFE_getByProps, UNSAFE_queryByProps } = render(); - const primaryType = UNSAFE_getByProps({ type: 'primary' }); - - expect(primaryType.props.children).toBe('Change freshness!'); - expect(() => UNSAFE_getByProps({ type: 'inexistent' })).toThrow( - 'No instances found' - ); - - expect(UNSAFE_queryByProps({ type: 'primary' })).toBe(primaryType); - expect(UNSAFE_queryByProps({ type: 'inexistent' })).toBeNull(); -}); - -test('UNSAFE_getAllByProp, UNSAFE_queryAllByProps', () => { - const { UNSAFE_getAllByProps, UNSAFE_queryAllByProps } = render(); - const primaryTypes = UNSAFE_getAllByProps({ type: 'primary' }); - - expect(primaryTypes).toHaveLength(1); - expect(() => UNSAFE_getAllByProps({ type: 'inexistent' })).toThrow( - 'No instances found' - ); - - expect(UNSAFE_queryAllByProps({ type: 'primary' })).toEqual(primaryTypes); - expect(UNSAFE_queryAllByProps({ type: 'inexistent' })).toHaveLength(0); -}); - -test('update', () => { - const fn = jest.fn(); - const { getByText, update, rerender } = render(); - - fireEvent.press(getByText('Change freshness!')); - - update(); - rerender(); - - expect(fn).toHaveBeenCalledTimes(3); -}); - -test('unmount', () => { - const fn = jest.fn(); - const { unmount } = render(); - unmount(); - expect(fn).toHaveBeenCalled(); -}); - -test('unmount should handle cleanup functions', () => { - const cleanup = jest.fn(); - const Component = () => { - React.useEffect(() => cleanup); - return null; - }; - - const { unmount } = render(); - - unmount(); - - expect(cleanup).toHaveBeenCalledTimes(1); -}); - -test('toJSON renders host output', () => { - const { toJSON } = render(press me); - expect(toJSON()).toMatchSnapshot(); -}); - -test('renders options.wrapper around node', () => { - type WrapperComponentProps = { children: React.ReactNode }; - const WrapperComponent = ({ children }: WrapperComponentProps) => ( - {children} - ); - - const { toJSON, getByTestId } = render(, { - wrapper: WrapperComponent, - }); - - expect(getByTestId('wrapper')).toBeTruthy(); - expect(toJSON()).toMatchInlineSnapshot(` - - - - `); -}); - -test('renders options.wrapper around updated node', () => { - type WrapperComponentProps = { children: React.ReactNode }; - const WrapperComponent = ({ children }: WrapperComponentProps) => ( - {children} - ); - - const { toJSON, getByTestId, rerender } = render(, { - wrapper: WrapperComponent, - }); - - rerender( - - ); - - expect(getByTestId('wrapper')).toBeTruthy(); - expect(toJSON()).toMatchInlineSnapshot(` - - - - `); -}); - -test('returns host root', () => { - const { root } = render(); - - expect(root).toBeDefined(); - expect(root.type).toBe('View'); - expect(root.props.testID).toBe('inner'); -}); - -test('returns composite UNSAFE_root', () => { - const { UNSAFE_root } = render(); - - expect(UNSAFE_root).toBeDefined(); - expect(UNSAFE_root.type).toBe(View); - expect(UNSAFE_root.props.testID).toBe('inner'); -}); - -test('container displays deprecation', () => { - const view = render(); - - expect(() => view.container).toThrowErrorMatchingInlineSnapshot(` - "'container' property has been renamed to 'UNSAFE_root'. - - Consider using 'root' property which returns root host element." - `); - expect(() => screen.container).toThrowErrorMatchingInlineSnapshot(` - "'container' property has been renamed to 'UNSAFE_root'. - - Consider using 'root' property which returns root host element." - `); -}); - -test('RenderAPI type', () => { - render() as RenderAPI; - expect(true).toBeTruthy(); -}); diff --git a/src/__tests__/render.test.tsx b/src/__tests__/render.test.tsx index 088d476bd..61010e565 100644 --- a/src/__tests__/render.test.tsx +++ b/src/__tests__/render.test.tsx @@ -1,11 +1,7 @@ /* eslint-disable no-console */ import * as React from 'react'; -import { View, Text, TextInput, Pressable, SafeAreaView } from 'react-native'; -import { render, fireEvent, RenderAPI } from '..'; - -beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); -}); +import { View, Text, TextInput, Pressable } from 'react-native'; +import { render, screen, fireEvent, RenderAPI } from '..'; const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; const PLACEHOLDER_CHEF = 'Who inspected freshness?'; @@ -154,7 +150,6 @@ test('unmount should handle cleanup functions', () => { test('toJSON renders host output', () => { const { toJSON } = render(press me); - expect(toJSON()).toMatchSnapshot(); }); @@ -224,31 +219,19 @@ test('returns composite UNSAFE_root', () => { expect(UNSAFE_root.props.testID).toBe('inner'); }); -test('returns container', () => { - const { container } = render(); +test('container displays deprecation', () => { + const view = render(); - expect(container).toBeDefined(); - // `View` composite component is returned. This behavior will break if we - // start returning only host components. - expect(container.type).toBe(View); - expect(container.props.testID).toBe('inner'); -}); - -test('returns wrapper component as container', () => { - type WrapperComponentProps = { children: React.ReactNode }; - const WrapperComponent = ({ children }: WrapperComponentProps) => ( - {children} - ); + expect(() => (view as any).container).toThrowErrorMatchingInlineSnapshot(` + "'container' property has been renamed to 'UNSAFE_root'. - const { container } = render(, { - wrapper: WrapperComponent, - }); + Consider using 'root' property which returns root host element." + `); + expect(() => (screen as any).container).toThrowErrorMatchingInlineSnapshot(` + "'container' property has been renamed to 'UNSAFE_root'. - expect(container).toBeDefined(); - // `WrapperComponent` composite component is returned with no testID passed to - // it. This behavior will break if we start returning only host components. - expect(container.type).toBe(WrapperComponent); - expect(container.props.testID).not.toBeDefined(); + Consider using 'root' property which returns root host element." + `); }); test('RenderAPI type', () => { @@ -261,5 +244,4 @@ test('returned output can be spread using rest operator', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { rerender, ...rest } = render(); expect(rest).toBeTruthy(); - expect(console.warn).not.toHaveBeenCalled(); }); diff --git a/src/__tests__/screen.test.tsx b/src/__tests__/screen.test.tsx index f13685a51..198214308 100644 --- a/src/__tests__/screen.test.tsx +++ b/src/__tests__/screen.test.tsx @@ -56,7 +56,6 @@ test('screen throws without render', () => { expect(() => screen.UNSAFE_root).toThrow( '`render` method has not been called' ); - expect(() => screen.container).toThrow('`render` method has not been called'); expect(() => screen.debug()).toThrow('`render` method has not been called'); expect(() => screen.debug.shallow()).toThrow( '`render` method has not been called' diff --git a/src/config.ts b/src/config.ts index 732206e38..3528391e3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -26,17 +26,13 @@ export type HostComponentNames = { }; export type InternalConfig = Config & { - /** Whether to use breaking changes intended for next major version release. */ - useBreakingChanges: boolean; - /** Names for key React Native host components. */ hostComponentNames?: HostComponentNames; }; const defaultConfig: InternalConfig = { - useBreakingChanges: false, asyncUtilTimeout: 1000, - defaultIncludeHiddenElements: true, + defaultIncludeHiddenElements: false, }; let config = { ...defaultConfig }; diff --git a/src/helpers/__tests__/accessiblity.test.tsx b/src/helpers/__tests__/accessiblity.test.tsx index eeee51610..d47ebd6a2 100644 --- a/src/helpers/__tests__/accessiblity.test.tsx +++ b/src/helpers/__tests__/accessiblity.test.tsx @@ -14,19 +14,25 @@ describe('isHiddenFromAccessibility', () => { test('returns false for accessible elements', () => { expect( isHiddenFromAccessibility( - render().getByTestId('subject') + render().getByTestId('subject', { + includeHiddenElements: true, + }) ) ).toBe(false); expect( isHiddenFromAccessibility( - render(Hello).getByTestId('subject') + render(Hello).getByTestId('subject', { + includeHiddenElements: true, + }) ) ).toBe(false); expect( isHiddenFromAccessibility( - render().getByTestId('subject') + render().getByTestId('subject', { + includeHiddenElements: true, + }) ) ).toBe(false); }); @@ -37,7 +43,13 @@ describe('isHiddenFromAccessibility', () => { test('detects elements with accessibilityElementsHidden prop', () => { const view = render(); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects nested elements with accessibilityElementsHidden prop', () => { @@ -46,7 +58,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects deeply nested elements with accessibilityElementsHidden prop', () => { @@ -59,14 +77,26 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects elements with importantForAccessibility="no-hide-descendants" prop', () => { const view = render( ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects nested elements with importantForAccessibility="no-hide-descendants" prop', () => { @@ -75,12 +105,24 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects elements with display=none', () => { const view = render(); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects nested elements with display=none', () => { @@ -89,7 +131,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects deeply nested elements with display=none', () => { @@ -102,7 +150,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects elements with display=none with complex style', () => { @@ -116,12 +170,24 @@ describe('isHiddenFromAccessibility', () => { ]} /> ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('is not trigged by opacity = 0', () => { const view = render(); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(false); }); test('detects siblings of element with accessibilityViewIsModal prop', () => { @@ -131,7 +197,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('detects deeply nested siblings of element with accessibilityViewIsModal prop', () => { @@ -145,7 +217,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(true); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(true); }); test('is not triggered for element with accessibilityViewIsModal prop', () => { @@ -154,7 +232,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(false); }); test('is not triggered for child of element with accessibilityViewIsModal prop', () => { @@ -165,7 +249,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(false); }); test('is not triggered for descendent of element with accessibilityViewIsModal prop', () => { @@ -180,7 +270,13 @@ describe('isHiddenFromAccessibility', () => { ); - expect(isHiddenFromAccessibility(view.getByTestId('subject'))).toBe(false); + expect( + isHiddenFromAccessibility( + view.getByTestId('subject', { + includeHiddenElements: true, + }) + ) + ).toBe(false); }); test('has isInaccessible alias', () => { diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index 86a99ae70..91a9bc246 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -59,6 +59,11 @@ export function isHiddenFromAccessibility( export const isInaccessible = isHiddenFromAccessibility; function isSubtreeInaccessible(element: ReactTestInstance): boolean { + // Null props can happen for React.Fragments + if (element.props == null) { + return false; + } + // iOS: accessibilityElementsHidden // See: https://reactnative.dev/docs/accessibility#accessibilityelementshidden-ios if (element.props.accessibilityElementsHidden) { diff --git a/src/queries/__tests__/a11yState.test.tsx b/src/queries/__tests__/a11yState.test.tsx index bb50ae2fa..41ed4b7c0 100644 --- a/src/queries/__tests__/a11yState.test.tsx +++ b/src/queries/__tests__/a11yState.test.tsx @@ -244,11 +244,11 @@ test('byA11yState queries support hidden option', () => { ); - expect(getByA11yState({ expanded: false })).toBeTruthy(); expect( getByA11yState({ expanded: false }, { includeHiddenElements: true }) ).toBeTruthy(); + expect(queryByA11yState({ expanded: false })).toBeFalsy(); expect( queryByA11yState({ expanded: false }, { includeHiddenElements: false }) ).toBeFalsy(); diff --git a/src/queries/__tests__/a11yValue.test.tsx b/src/queries/__tests__/a11yValue.test.tsx index c713fc38d..a3f6fb74e 100644 --- a/src/queries/__tests__/a11yValue.test.tsx +++ b/src/queries/__tests__/a11yValue.test.tsx @@ -99,11 +99,11 @@ test('byA11yValue queries support hidden option', () => { ); - expect(getByA11yValue({ max: 10 })).toBeTruthy(); expect( getByA11yValue({ max: 10 }, { includeHiddenElements: true }) ).toBeTruthy(); + expect(queryByA11yValue({ max: 10 })).toBeFalsy(); expect( queryByA11yValue({ max: 10 }, { includeHiddenElements: false }) ).toBeFalsy(); diff --git a/src/queries/__tests__/displayValue.breaking.test.tsx b/src/queries/__tests__/displayValue.breaking.test.tsx deleted file mode 100644 index 01949d375..000000000 --- a/src/queries/__tests__/displayValue.breaking.test.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */ - -import * as React from 'react'; -import { View, TextInput } from 'react-native'; - -import { render } from '../..'; -import { configureInternal } from '../../config'; - -const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; -const PLACEHOLDER_CHEF = 'Who inspected freshness?'; -const INPUT_FRESHNESS = 'Custom Freshie'; -const INPUT_CHEF = 'I inspected freshie'; -const DEFAULT_INPUT_CHEF = 'What did you inspect?'; -const DEFAULT_INPUT_CUSTOMER = 'What banana?'; - -beforeEach(() => configureInternal({ useBreakingChanges: true })); - -const Banana = () => ( - - - - - - -); - -test('getByDisplayValue, queryByDisplayValue', () => { - const { getByDisplayValue, queryByDisplayValue } = render(); - const input = getByDisplayValue(/custom/i); - - expect(input.props.value).toBe(INPUT_FRESHNESS); - - const sameInput = getByDisplayValue(INPUT_FRESHNESS); - - expect(sameInput.props.value).toBe(INPUT_FRESHNESS); - expect(() => getByDisplayValue('no value')).toThrow( - 'Unable to find an element with displayValue: no value' - ); - - expect(queryByDisplayValue(/custom/i)).toBe(input); - expect(queryByDisplayValue('no value')).toBeNull(); - expect(() => queryByDisplayValue(/fresh/i)).toThrow( - 'Found multiple elements with display value: /fresh/i' - ); -}); - -test('getByDisplayValue, queryByDisplayValue get element by default value only when value is undefined', () => { - const { getByDisplayValue, queryByDisplayValue } = render(); - expect(() => - getByDisplayValue(DEFAULT_INPUT_CHEF) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with displayValue: What did you inspect?"` - ); - expect(queryByDisplayValue(DEFAULT_INPUT_CHEF)).toBeNull(); - - expect(() => getByDisplayValue('hello')).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with displayValue: hello"` - ); - expect(queryByDisplayValue('hello')).toBeNull(); - - expect(getByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy(); - expect(queryByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy(); -}); - -test('getAllByDisplayValue, queryAllByDisplayValue', () => { - const { getAllByDisplayValue, queryAllByDisplayValue } = render(); - const inputs = getAllByDisplayValue(/fresh/i); - - expect(inputs).toHaveLength(2); - expect(() => getAllByDisplayValue('no value')).toThrow( - 'Unable to find an element with displayValue: no value' - ); - - expect(queryAllByDisplayValue(/fresh/i)).toEqual(inputs); - expect(queryAllByDisplayValue('no value')).toHaveLength(0); -}); - -test('findBy queries work asynchronously', async () => { - const options = { timeout: 10 }; // Short timeout so that this test runs quickly - const { rerender, findByDisplayValue, findAllByDisplayValue } = render( - - ); - - await expect( - findByDisplayValue('Display Value', {}, options) - ).rejects.toBeTruthy(); - await expect( - findAllByDisplayValue('Display Value', {}, options) - ).rejects.toBeTruthy(); - - setTimeout( - () => - rerender( - - - - ), - 20 - ); - - await expect(findByDisplayValue('Display Value')).resolves.toBeTruthy(); - await expect(findAllByDisplayValue('Display Value')).resolves.toHaveLength(1); -}, 20000); - -test('byDisplayValue queries support hidden option', () => { - const { getByDisplayValue, queryByDisplayValue } = render( - - ); - - expect(getByDisplayValue('hidden')).toBeTruthy(); - expect( - getByDisplayValue('hidden', { includeHiddenElements: true }) - ).toBeTruthy(); - - expect( - queryByDisplayValue('hidden', { includeHiddenElements: false }) - ).toBeFalsy(); - expect(() => - getByDisplayValue('hidden', { includeHiddenElements: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with displayValue: hidden"` - ); -}); - -test('byDisplayValue should return host component', () => { - const { getByDisplayValue } = render(); - - expect(getByDisplayValue('value').type).toBe('TextInput'); -}); diff --git a/src/queries/__tests__/displayValue.test.tsx b/src/queries/__tests__/displayValue.test.tsx index e8f351c2b..c1c7f3713 100644 --- a/src/queries/__tests__/displayValue.test.tsx +++ b/src/queries/__tests__/displayValue.test.tsx @@ -50,10 +50,16 @@ test('getByDisplayValue, queryByDisplayValue', () => { test('getByDisplayValue, queryByDisplayValue get element by default value only when value is undefined', () => { const { getByDisplayValue, queryByDisplayValue } = render(); - expect(() => getByDisplayValue(DEFAULT_INPUT_CHEF)).toThrow(); + expect(() => + getByDisplayValue(DEFAULT_INPUT_CHEF) + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to find an element with displayValue: What did you inspect?"` + ); expect(queryByDisplayValue(DEFAULT_INPUT_CHEF)).toBeNull(); - expect(() => getByDisplayValue('hello')).toThrow(); + expect(() => getByDisplayValue('hello')).toThrowErrorMatchingInlineSnapshot( + `"Unable to find an element with displayValue: hello"` + ); expect(queryByDisplayValue('hello')).toBeNull(); expect(getByDisplayValue(DEFAULT_INPUT_CUSTOMER)).toBeTruthy(); @@ -105,11 +111,11 @@ test('byDisplayValue queries support hidden option', () => { ); - expect(getByDisplayValue('hidden')).toBeTruthy(); expect( getByDisplayValue('hidden', { includeHiddenElements: true }) ).toBeTruthy(); + expect(queryByDisplayValue('hidden')).toBeFalsy(); expect( queryByDisplayValue('hidden', { includeHiddenElements: false }) ).toBeFalsy(); @@ -120,8 +126,8 @@ test('byDisplayValue queries support hidden option', () => { ); }); -test('byDisplayValue should return composite TextInput', () => { +test('byDisplayValue should return host component', () => { const { getByDisplayValue } = render(); - expect(getByDisplayValue('value').type).toBe(TextInput); + expect(getByDisplayValue('value').type).toBe('TextInput'); }); diff --git a/src/queries/__tests__/hintText.test.tsx b/src/queries/__tests__/hintText.test.tsx index 4adc5bb33..d97427e3c 100644 --- a/src/queries/__tests__/hintText.test.tsx +++ b/src/queries/__tests__/hintText.test.tsx @@ -114,9 +114,9 @@ test('byHintText queries support hidden option', () => { ); - expect(getByHintText('hidden')).toBeTruthy(); expect(getByHintText('hidden', { includeHiddenElements: true })).toBeTruthy(); + expect(queryByHintText('hidden')).toBeFalsy(); expect( queryByHintText('hidden', { includeHiddenElements: false }) ).toBeFalsy(); diff --git a/src/queries/__tests__/labelText.test.tsx b/src/queries/__tests__/labelText.test.tsx index 0a05c3716..da6de4ec4 100644 --- a/src/queries/__tests__/labelText.test.tsx +++ b/src/queries/__tests__/labelText.test.tsx @@ -151,11 +151,11 @@ test('byLabelText queries support hidden option', () => { ); - expect(getByLabelText('hidden')).toBeTruthy(); expect( getByLabelText('hidden', { includeHiddenElements: true }) ).toBeTruthy(); + expect(queryByLabelText('hidden')).toBeFalsy(); expect( queryByLabelText('hidden', { includeHiddenElements: false }) ).toBeFalsy(); diff --git a/src/queries/__tests__/placeholderText.breaking.test.tsx b/src/queries/__tests__/placeholderText.breaking.test.tsx deleted file mode 100644 index ab2c82264..000000000 --- a/src/queries/__tests__/placeholderText.breaking.test.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/** This is a copy of regular tests with `useBreakingChanges` flag turned on. */ - -import * as React from 'react'; -import { View, TextInput } from 'react-native'; -import { render } from '../..'; -import { configureInternal } from '../../config'; - -const PLACEHOLDER_FRESHNESS = 'Add custom freshness'; -const PLACEHOLDER_CHEF = 'Who inspected freshness?'; -const INPUT_FRESHNESS = 'Custom Freshie'; -const INPUT_CHEF = 'I inspected freshie'; -const DEFAULT_INPUT_CHEF = 'What did you inspect?'; - -beforeEach(() => { - configureInternal({ useBreakingChanges: true }); -}); - -const Banana = () => ( - - - - -); - -test('getByPlaceholderText, queryByPlaceholderText', () => { - const { getByPlaceholderText, queryByPlaceholderText } = render(); - const input = getByPlaceholderText(/custom/i); - - expect(input.props.placeholder).toBe(PLACEHOLDER_FRESHNESS); - - const sameInput = getByPlaceholderText(PLACEHOLDER_FRESHNESS); - - expect(sameInput.props.placeholder).toBe(PLACEHOLDER_FRESHNESS); - expect(() => getByPlaceholderText('no placeholder')).toThrow( - 'Unable to find an element with placeholder: no placeholder' - ); - - expect(queryByPlaceholderText(/add/i)).toBe(input); - expect(queryByPlaceholderText('no placeholder')).toBeNull(); - expect(() => queryByPlaceholderText(/fresh/)).toThrow( - 'Found multiple elements with placeholder: /fresh/ ' - ); -}); - -test('getAllByPlaceholderText, queryAllByPlaceholderText', () => { - const { getAllByPlaceholderText, queryAllByPlaceholderText } = render( - - ); - const inputs = getAllByPlaceholderText(/fresh/i); - - expect(inputs).toHaveLength(2); - expect(() => getAllByPlaceholderText('no placeholder')).toThrow( - 'Unable to find an element with placeholder: no placeholder' - ); - - expect(queryAllByPlaceholderText(/fresh/i)).toEqual(inputs); - expect(queryAllByPlaceholderText('no placeholder')).toHaveLength(0); -}); - -test('byPlaceholderText queries support hidden option', () => { - const { getByPlaceholderText, queryByPlaceholderText } = render( - - ); - - expect(getByPlaceholderText('hidden')).toBeTruthy(); - expect( - getByPlaceholderText('hidden', { includeHiddenElements: true }) - ).toBeTruthy(); - - expect( - queryByPlaceholderText('hidden', { includeHiddenElements: false }) - ).toBeFalsy(); - expect(() => - getByPlaceholderText('hidden', { includeHiddenElements: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with placeholder: hidden"` - ); -}); - -test('byPlaceHolderText should return host component', () => { - const { getByPlaceholderText } = render( - - ); - - expect(getByPlaceholderText('placeholder').type).toBe('TextInput'); -}); diff --git a/src/queries/__tests__/placeholderText.test.tsx b/src/queries/__tests__/placeholderText.test.tsx index c16cf1f59..276a301bb 100644 --- a/src/queries/__tests__/placeholderText.test.tsx +++ b/src/queries/__tests__/placeholderText.test.tsx @@ -64,11 +64,11 @@ test('byPlaceholderText queries support hidden option', () => { ); - expect(getByPlaceholderText('hidden')).toBeTruthy(); expect( getByPlaceholderText('hidden', { includeHiddenElements: true }) ).toBeTruthy(); + expect(queryByPlaceholderText('hidden')).toBeFalsy(); expect( queryByPlaceholderText('hidden', { includeHiddenElements: false }) ).toBeFalsy(); @@ -79,10 +79,10 @@ test('byPlaceholderText queries support hidden option', () => { ); }); -test('byPlaceHolderText should return composite component', () => { +test('byPlaceHolderText should return host component', () => { const { getByPlaceholderText } = render( ); - expect(getByPlaceholderText('placeholder').type).toBe(TextInput); + expect(getByPlaceholderText('placeholder').type).toBe('TextInput'); }); diff --git a/src/queries/__tests__/role.breaking.test.tsx b/src/queries/__tests__/role.breaking.test.tsx deleted file mode 100644 index dbca753ed..000000000 --- a/src/queries/__tests__/role.breaking.test.tsx +++ /dev/null @@ -1,769 +0,0 @@ -import * as React from 'react'; -import { - TouchableOpacity, - TouchableWithoutFeedback, - Text, - View, - Pressable, - Button as RNButton, -} from 'react-native'; -import { render } from '../..'; -import { configureInternal } from '../../config'; - -beforeEach(() => { - configureInternal({ useBreakingChanges: true }); -}); - -const TEXT_LABEL = 'cool text'; - -// Little hack to make all the methods happy with type -const NO_MATCHES_TEXT: any = 'not-existent-element'; - -const getMultipleInstancesFoundMessage = (value: string) => { - return `Found multiple elements with role: "${value}"`; -}; - -const getNoInstancesFoundMessage = (value: string) => { - return `Unable to find an element with role: "${value}"`; -}; - -const Typography = ({ children, ...rest }: any) => { - return {children}; -}; - -const Button = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); - -const Section = () => ( - <> - Title - - -); - -test('getByRole, queryByRole, findByRole', async () => { - const { getByRole, queryByRole, findByRole } = render(
); - - expect(getByRole('button').props.accessibilityRole).toEqual('button'); - const button = queryByRole(/button/g); - expect(button?.props.accessibilityRole).toEqual('button'); - - expect(() => getByRole(NO_MATCHES_TEXT)).toThrow( - getNoInstancesFoundMessage(NO_MATCHES_TEXT) - ); - - expect(queryByRole(NO_MATCHES_TEXT)).toBeNull(); - - expect(() => getByRole('link')).toThrow( - getMultipleInstancesFoundMessage('link') - ); - expect(() => queryByRole('link')).toThrow( - getMultipleInstancesFoundMessage('link') - ); - - const asyncButton = await findByRole('button'); - expect(asyncButton.props.accessibilityRole).toEqual('button'); - await expect(findByRole(NO_MATCHES_TEXT)).rejects.toThrow( - getNoInstancesFoundMessage(NO_MATCHES_TEXT) - ); - await expect(findByRole('link')).rejects.toThrow( - getMultipleInstancesFoundMessage('link') - ); -}); - -test('getAllByRole, queryAllByRole, findAllByRole', async () => { - const { getAllByRole, queryAllByRole, findAllByRole } = render(
); - - expect(getAllByRole('link')).toHaveLength(2); - expect(queryAllByRole(/ink/g)).toHaveLength(2); - - expect(() => getAllByRole(NO_MATCHES_TEXT)).toThrow( - getNoInstancesFoundMessage(NO_MATCHES_TEXT) - ); - expect(queryAllByRole(NO_MATCHES_TEXT)).toEqual([]); - - await expect(findAllByRole('link')).resolves.toHaveLength(2); - await expect(findAllByRole(NO_MATCHES_TEXT)).rejects.toThrow( - getNoInstancesFoundMessage(NO_MATCHES_TEXT) - ); -}); - -describe('supports name option', () => { - test('returns an element that has the corresponding role and a children with the name', () => { - const { getByRole } = render( - - Save - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('button', { name: 'Save' }).props.testID).toBe( - 'target-button' - ); - }); - - test('returns an element that has the corresponding role when several children include the name', () => { - const { getByRole } = render( - - Save - Save - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('button', { name: 'Save' }).props.testID).toBe( - 'target-button' - ); - }); - - test('returns an element that has the corresponding role and a children with a matching accessibilityLabel', () => { - const { getByRole } = render( - - - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('button', { name: 'Save' }).props.testID).toBe( - 'target-button' - ); - }); - - test('returns an element that has the corresponding role and a matching accessibilityLabel', () => { - const { getByRole } = render( - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('button', { name: 'Save' }).props.testID).toBe( - 'target-button' - ); - }); - - test('returns an element when the direct child is text', () => { - const { getByRole, getByTestId } = render( - - About - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('header', { name: 'About' })).toBe( - getByTestId('target-header') - ); - expect(getByRole('header', { name: 'About' }).props.testID).toBe( - 'target-header' - ); - }); - - test('returns an element with nested Text as children', () => { - const { getByRole, getByTestId } = render( - - About - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('header', { name: 'About' })).toBe(getByTestId('parent')); - expect(getByRole('header', { name: 'About' }).props.testID).toBe('parent'); - }); - - test('returns a header with an accessibilityLabel', () => { - const { getByRole, getByTestId } = render( - - ); - - // assert on the testId to be sure that the returned element is the one with the accessibilityRole - expect(getByRole('header', { name: 'About' })).toBe( - getByTestId('target-header') - ); - expect(getByRole('header', { name: 'About' }).props.testID).toBe( - 'target-header' - ); - }); -}); - -describe('supports accessibility states', () => { - describe('disabled', () => { - test('returns a disabled element when required', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { disabled: true })).toBeTruthy(); - expect(queryByRole('button', { disabled: false })).toBe(null); - }); - - test('returns the correct element when only one matches all the requirements', () => { - const { getByRole } = render( - <> - - Save - - - Save - - - ); - - expect( - getByRole('button', { name: 'Save', disabled: true }).props.testID - ).toBe('correct'); - }); - - test('returns an implicitly enabled element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { disabled: false })).toBeTruthy(); - expect(queryByRole('button', { disabled: true })).toBe(null); - }); - - test('returns an explicitly enabled element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { disabled: false })).toBeTruthy(); - expect(queryByRole('button', { disabled: true })).toBe(null); - }); - - test('does not return disabled elements when querying for non disabled', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('button', { disabled: false })).toBe(null); - }); - - test('returns elements using the built-in disabled prop', () => { - const { getByRole } = render( - <> - - Pressable - - - - - TouchableWithoutFeedback - - - {}} title="RNButton" /> - - ); - - expect( - getByRole('button', { name: 'Pressable', disabled: true }) - ).toBeTruthy(); - - expect( - getByRole('button', { - name: 'TouchableWithoutFeedback', - disabled: true, - }) - ).toBeTruthy(); - - expect( - getByRole('button', { name: 'RNButton', disabled: true }) - ).toBeTruthy(); - }); - }); - - describe('selected', () => { - test('returns a selected element when required', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('tab', { selected: true })).toBeTruthy(); - expect(queryByRole('tab', { selected: false })).toBe(null); - }); - - test('returns the correct element when only one matches all the requirements', () => { - const { getByRole } = render( - <> - - Save - - - Save - - - ); - - expect( - getByRole('tab', { name: 'Save', selected: true }).props.testID - ).toBe('correct'); - }); - - test('returns an implicitly non selected element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('tab', { selected: false })).toBeTruthy(); - expect(queryByRole('tab', { selected: true })).toBe(null); - }); - - test('returns an explicitly non selected element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('tab', { selected: false })).toBeTruthy(); - expect(queryByRole('tab', { selected: true })).toBe(null); - }); - - test('does not return selected elements when querying for non selected', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('tab', { selected: false })).toBe(null); - }); - }); - - describe('checked', () => { - test('returns a checked element when required', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('checkbox', { checked: true })).toBeTruthy(); - expect(queryByRole('checkbox', { checked: false })).toBe(null); - expect(queryByRole('checkbox', { checked: 'mixed' })).toBe(null); - }); - - it('returns `mixed` checkboxes', () => { - const { queryByRole, getByRole } = render( - - ); - - expect(getByRole('checkbox', { checked: 'mixed' })).toBeTruthy(); - expect(queryByRole('checkbox', { checked: true })).toBe(null); - expect(queryByRole('checkbox', { checked: false })).toBe(null); - }); - - it('does not return mixed checkboxes when querying for checked: true', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('checkbox', { checked: false })).toBe(null); - }); - - test('returns the correct element when only one matches all the requirements', () => { - const { getByRole } = render( - <> - - Save - - - Save - - - ); - - expect( - getByRole('checkbox', { name: 'Save', checked: true }).props.testID - ).toBe('correct'); - }); - - test('does not return return as non checked an element with checked: undefined', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('checkbox', { checked: false })).toBe(null); - }); - - test('returns an explicitly non checked element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('checkbox', { checked: false })).toBeTruthy(); - expect(queryByRole('checkbox', { checked: true })).toBe(null); - }); - - test('does not return checked elements when querying for non checked', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('checkbox', { checked: false })).toBe(null); - }); - - test('does not return mixed elements when querying for non checked', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('checkbox', { checked: false })).toBe(null); - }); - }); - - describe('busy', () => { - test('returns a busy element when required', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { busy: true })).toBeTruthy(); - expect(queryByRole('button', { busy: false })).toBe(null); - }); - - test('returns the correct element when only one matches all the requirements', () => { - const { getByRole } = render( - <> - - Save - - - Save - - - ); - - expect( - getByRole('button', { name: 'Save', busy: true }).props.testID - ).toBe('correct'); - }); - - test('returns an implicitly non busy element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { busy: false })).toBeTruthy(); - expect(queryByRole('button', { busy: true })).toBe(null); - }); - - test('returns an explicitly non busy element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { busy: false })).toBeTruthy(); - expect(queryByRole('button', { busy: true })).toBe(null); - }); - - test('does not return busy elements when querying for non busy', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('button', { selected: false })).toBe(null); - }); - }); - - describe('expanded', () => { - test('returns a expanded element when required', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { expanded: true })).toBeTruthy(); - expect(queryByRole('button', { expanded: false })).toBe(null); - }); - - test('returns the correct element when only one matches all the requirements', () => { - const { getByRole } = render( - <> - - Save - - - Save - - - ); - - expect( - getByRole('button', { name: 'Save', expanded: true }).props.testID - ).toBe('correct'); - }); - - test('does not return return as non expanded an element with expanded: undefined', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('button', { expanded: false })).toBe(null); - }); - - test('returns an explicitly non expanded element', () => { - const { getByRole, queryByRole } = render( - - ); - - expect(getByRole('button', { expanded: false })).toBeTruthy(); - expect(queryByRole('button', { expanded: true })).toBe(null); - }); - - test('does not return expanded elements when querying for non expanded', () => { - const { queryByRole } = render( - - ); - - expect(queryByRole('button', { expanded: false })).toBe(null); - }); - }); - - test('ignores non queried accessibilityState', () => { - const { getByRole, queryByRole } = render( - - Save - - ); - - expect( - getByRole('button', { - name: 'Save', - disabled: true, - }) - ).toBeTruthy(); - expect( - queryByRole('button', { - name: 'Save', - disabled: false, - }) - ).toBe(null); - }); - - test('matches an element combining all the options', () => { - const { getByRole } = render( - - Save - - ); - - expect( - getByRole('button', { - name: 'Save', - disabled: true, - selected: true, - checked: true, - busy: true, - expanded: true, - }) - ).toBeTruthy(); - }); -}); - -describe('error messages', () => { - test('gives a descriptive error message when querying with a role', () => { - const { getByRole } = render(); - - expect(() => getByRole('button')).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button""` - ); - }); - - test('gives a descriptive error message when querying with a role and a name', () => { - const { getByRole } = render(); - - expect(() => - getByRole('button', { name: 'Save' }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button", name: "Save""` - ); - }); - - test('gives a descriptive error message when querying with a role, a name and accessibility state', () => { - const { getByRole } = render(); - - expect(() => - getByRole('button', { name: 'Save', disabled: true }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button", name: "Save", disabled state: true"` - ); - }); - - test('gives a descriptive error message when querying with a role, a name and several accessibility state', () => { - const { getByRole } = render(); - - expect(() => - getByRole('button', { name: 'Save', disabled: true, selected: true }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button", name: "Save", disabled state: true, selected state: true"` - ); - }); - - test('gives a descriptive error message when querying with a role and an accessibility state', () => { - const { getByRole } = render(); - - expect(() => - getByRole('button', { disabled: true }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button", disabled state: true"` - ); - }); - - test('gives a descriptive error message when querying with a role and an accessibility value', () => { - const { getByRole } = render(); - - expect(() => - getByRole('adjustable', { value: { min: 1 } }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "adjustable", min value: 1"` - ); - - expect(() => - getByRole('adjustable', { - value: { min: 1, max: 2, now: 1, text: /hello/ }, - }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "adjustable", min value: 1, max value: 2, now value: 1, text value: /hello/"` - ); - }); -}); - -test('byRole queries support hidden option', () => { - const { getByRole, queryByRole } = render( - - Hidden from accessibility - - ); - - expect(getByRole('button')).toBeTruthy(); - expect(getByRole('button', { includeHiddenElements: true })).toBeTruthy(); - - expect(queryByRole('button', { includeHiddenElements: false })).toBeFalsy(); - expect(() => - getByRole('button', { includeHiddenElements: false }) - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to find an element with role: "button""` - ); -}); - -describe('matches only accessible elements', () => { - test('matches elements with accessible={true}', () => { - const { queryByRole } = render( - - Action - - ); - expect(queryByRole('menu', { name: 'Action' })).toBeTruthy(); - }); - - test('ignores elements with accessible={false}', () => { - const { queryByRole } = render( - - Action - - ); - expect(queryByRole('button', { name: 'Action' })).toBeFalsy(); - }); - - test('ignores elements with accessible={undefined} and that are implicitely not accessible', () => { - const { queryByRole } = render( - - Action - - ); - expect(queryByRole('menu', { name: 'Action' })).toBeFalsy(); - }); -}); diff --git a/src/queries/__tests__/role.test.tsx b/src/queries/__tests__/role.test.tsx index 1b377ede0..f953061d6 100644 --- a/src/queries/__tests__/role.test.tsx +++ b/src/queries/__tests__/role.test.tsx @@ -723,9 +723,9 @@ test('byRole queries support hidden option', () => { ); - expect(getByRole('button')).toBeTruthy(); expect(getByRole('button', { includeHiddenElements: true })).toBeTruthy(); + expect(queryByRole('button')).toBeFalsy(); expect(queryByRole('button', { includeHiddenElements: false })).toBeFalsy(); expect(() => getByRole('button', { includeHiddenElements: false }) @@ -734,11 +734,31 @@ test('byRole queries support hidden option', () => { ); }); -test('does not take accessible prop into account', () => { - const { getByRole } = render( - - Action - - ); - expect(getByRole('button', { name: 'Action' })).toBeTruthy(); +describe('matches only accessible elements', () => { + test('matches elements with accessible={true}', () => { + const { queryByRole } = render( + + Action + + ); + expect(queryByRole('menu', { name: 'Action' })).toBeTruthy(); + }); + + test('ignores elements with accessible={false}', () => { + const { queryByRole } = render( + + Action + + ); + expect(queryByRole('button', { name: 'Action' })).toBeFalsy(); + }); + + test('ignores elements with accessible={undefined} and that are implicitely not accessible', () => { + const { queryByRole } = render( + + Action + + ); + expect(queryByRole('menu', { name: 'Action' })).toBeFalsy(); + }); }); diff --git a/src/queries/__tests__/testId.test.tsx b/src/queries/__tests__/testId.test.tsx index f064d411c..ae615ea86 100644 --- a/src/queries/__tests__/testId.test.tsx +++ b/src/queries/__tests__/testId.test.tsx @@ -140,9 +140,9 @@ test('byTestId queries support hidden option', () => { ); - expect(getByTestId('hidden')).toBeTruthy(); expect(getByTestId('hidden', { includeHiddenElements: true })).toBeTruthy(); + expect(queryByTestId('hidden')).toBeFalsy(); expect(queryByTestId('hidden', { includeHiddenElements: false })).toBeFalsy(); expect(() => getByTestId('hidden', { includeHiddenElements: false }) diff --git a/src/queries/__tests__/text.breaking.test.tsx b/src/queries/__tests__/text.breaking.test.tsx deleted file mode 100644 index eb243edd1..000000000 --- a/src/queries/__tests__/text.breaking.test.tsx +++ /dev/null @@ -1,514 +0,0 @@ -import * as React from 'react'; -import { - View, - Text, - TouchableOpacity, - Image, - Button, - TextInput, -} from 'react-native'; -import { render, getDefaultNormalizer, within } from '../..'; -import { configureInternal } from '../../config'; - -beforeEach(() => { - configureInternal({ useBreakingChanges: true }); -}); - -test('byText matches simple text', () => { - const { getByText } = render(Hello World); - expect(getByText('Hello World').props.testID).toBe('text'); -}); - -test('byText matches inner nested text', () => { - const { getByText } = render( - - Hello World - - ); - expect(getByText('Hello World').props.testID).toBe('inner'); -}); - -test('byText matches accross multiple texts', () => { - const { getByText } = render( - - Hello World - - ); - expect(getByText('Hello World').props.testID).toBe('outer'); -}); - -type MyButtonProps = { - children: React.ReactNode; - onPress: () => void; -}; -const MyButton = ({ children, onPress }: MyButtonProps) => ( - - {children} - -); - -const Banana = () => { - const test = 0; - return ( - - Is the banana fresh? - not fresh - - Change freshness! - First Text - Second Text - {test} - - ); -}; - -type ChildrenProps = { children: React.ReactNode }; - -test('getByText, queryByText', () => { - const { getByText, queryByText } = render(); - const button = getByText(/change/i); - - expect(button.props.children).toBe('Change freshness!'); - - const sameButton = getByText('not fresh'); - - expect(sameButton.props.children).toBe('not fresh'); - expect(() => getByText('InExistent')).toThrow( - 'Unable to find an element with text: InExistent' - ); - - const zeroText = getByText('0'); - - expect(queryByText(/change/i)).toBe(button); - expect(queryByText('InExistent')).toBeNull(); - expect(() => queryByText(/fresh/)).toThrow( - 'Found multiple elements with text: /fresh/' - ); - expect(queryByText('0')).toBe(zeroText); -}); - -test('getByText, queryByText with children as Array', () => { - type BananaCounterProps = { numBananas: number }; - const BananaCounter = ({ numBananas }: BananaCounterProps) => ( - There are {numBananas} bananas in the bunch - ); - - const BananaStore = () => ( - - - - - - ); - - const { getByText } = render(); - - const threeBananaBunch = getByText('There are 3 bananas in the bunch'); - expect(threeBananaBunch.props.children).toEqual([ - 'There are ', - 3, - ' bananas in the bunch', - ]); -}); - -test('getAllByText, queryAllByText', () => { - const { getAllByText, queryAllByText } = render(); - const buttons = getAllByText(/fresh/i); - - expect(buttons).toHaveLength(3); - expect(() => getAllByText('InExistent')).toThrow( - 'Unable to find an element with text: InExistent' - ); - - expect(queryAllByText(/fresh/i)).toEqual(buttons); - expect(queryAllByText('InExistent')).toHaveLength(0); -}); - -test('findByText queries work asynchronously', async () => { - const options = { timeout: 10 }; // Short timeout so that this test runs quickly - const { rerender, findByText, findAllByText } = render(); - await expect(findByText('Some Text', {}, options)).rejects.toBeTruthy(); - await expect(findAllByText('Some Text', {}, options)).rejects.toBeTruthy(); - - setTimeout( - () => - rerender( - - Some Text - - ), - 20 - ); - - await expect(findByText('Some Text')).resolves.toBeTruthy(); - await expect(findAllByText('Some Text')).resolves.toHaveLength(1); -}, 20000); - -test('getByText works properly with custom text component', () => { - function BoldText({ children }: ChildrenProps) { - return {children}; - } - - expect( - render( - - Hello - - ).getByText('Hello') - ).toBeTruthy(); -}); - -test('getByText works properly with custom text container', () => { - function MyText({ children }: ChildrenProps) { - return {children}; - } - function BoldText({ children }: ChildrenProps) { - return {children}; - } - - expect( - render( - - Hello - - ).getByText('Hello') - ).toBeTruthy(); -}); - -test('queryByText nested in at start', () => { - expect( - render( - - - Hello - - ).queryByText('Hello') - ).toBeTruthy(); -}); - -test('queryByText nested in at end', () => { - expect( - render( - - Hello - - - ).queryByText('Hello') - ).toBeTruthy(); -}); - -test('queryByText nested in in middle', () => { - expect( - render( - - Hello - - World - - ).queryByText('HelloWorld') - ).toBeTruthy(); -}); - -test('queryByText not found', () => { - expect( - render( - - Hello - - - ).queryByText('SomethingElse') - ).toBeFalsy(); -}); - -test('*ByText matches text across multiple nested Text', () => { - const { getByText } = render( - - Hello{' '} - - World - !{true} - - - ); - - expect(getByText('Hello World!')).toBeTruthy(); -}); - -test('queryByText with nested Text components return the closest Text', () => { - const NestedTexts = () => ( - - My text - - ); - - const { queryByText } = render(); - expect(queryByText('My text', { exact: false })?.props.nativeID).toBe('2'); -}); - -test('queryByText with nested Text components each with text return the lowest one', () => { - const NestedTexts = () => ( - - bob - My text - - ); - - const { queryByText } = render(); - - expect(queryByText('My text', { exact: false })?.props.nativeID).toBe('2'); -}); - -test('queryByText nested deep in ', () => { - const CustomText = ({ children }: ChildrenProps) => { - return {children}; - }; - - expect( - render( - - Hello World! - - ).getByText('Hello World!') - ).toBeTruthy(); -}); - -test('queryByText with nested Text components: not-exact text match returns the most deeply nested common component', () => { - const { queryByText: queryByTextFirstCase } = render( - - bob - My - text - - ); - - const { queryByText: queryByTextSecondCase } = render( - - bob - My text for test - - ); - - expect(queryByTextFirstCase('My text')).toBe(null); - expect( - queryByTextSecondCase('My text', { exact: false })?.props.nativeID - ).toBe('2'); -}); - -test('queryAllByText does not match several times the same text', () => { - const allMatched = render( - - Start - This is a long text - - ).queryAllByText('long text', { exact: false }); - expect(allMatched.length).toBe(1); - expect(allMatched[0].props.nativeID).toBe('2'); -}); - -test('queryAllByText matches all the matching nodes', () => { - const allMatched = render( - - Start - This is a long text - This is another long text - - ).queryAllByText('long text', { exact: false }); - expect(allMatched.length).toBe(2); - expect(allMatched.map((node) => node.props.nativeID)).toEqual(['2', '3']); -}); - -describe('supports TextMatch options', () => { - test('getByText, getAllByText', () => { - const { getByText, getAllByText } = render( - - Text and details -