diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ec3a6626..22d0c9bc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: test: needs: [install-cache-deps] runs-on: ubuntu-latest - name: Test + name: Test (concurrent by default) steps: - name: Checkout uses: actions/checkout@v4 @@ -61,11 +61,10 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 - - test-concurrent: + test-legacy: needs: [install-cache-deps] runs-on: ubuntu-latest - name: Test (concurrent mode) + name: Test (legacy) steps: - name: Checkout uses: actions/checkout@v4 @@ -74,7 +73,7 @@ jobs: uses: ./.github/actions/setup-deps - name: Test in concurrent mode - run: CONCURRENT_MODE=1 yarn test:ci + run: CONCURRENT_MODE=0 yarn test:ci test-website: runs-on: ubuntu-latest diff --git a/README.md b/README.md index ac32f38ed..ad79d406c 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,7 @@ This library has a `peerDependencies` listing for `react-test-renderer`. Make su ### Additional Jest matchers -You can use the built-in Jest matchers by adding the following line to your `jest-setup.ts` file (configured using [`setupFilesAfterEnv`](https://jestjs.io/docs/configuration#setupfilesafterenv-array)): - -```ts -import '@testing-library/react-native/extend-expect'; -``` +You can use the built-in Jest matchers automatically by having any import from `@testing-library/react-native` in your test. ## Example diff --git a/examples/basic/.eslintignore b/examples/basic/.eslintignore new file mode 100644 index 000000000..bda2aca85 --- /dev/null +++ b/examples/basic/.eslintignore @@ -0,0 +1 @@ +jest-setup.ts diff --git a/examples/basic/components/AnimatedView.tsx b/examples/basic/components/AnimatedView.tsx index d2f2a2e12..7f42d0083 100644 --- a/examples/basic/components/AnimatedView.tsx +++ b/examples/basic/components/AnimatedView.tsx @@ -17,7 +17,7 @@ export function AnimatedView(props: AnimatedViewProps) { duration: props.fadeInDuration ?? 250, useNativeDriver: props.useNativeDriver ?? true, }).start(); - }, [fadeAnim]); + }, [fadeAnim, props.fadeInDuration, props.useNativeDriver]); return ( { it('should use native driver when useNativeDriver is true', async () => { render( - Test + Test , ); expect(screen.root).toHaveStyle({ opacity: 0 }); @@ -25,7 +27,7 @@ describe('AnimatedView', () => { it('should not use native driver when useNativeDriver is false', async () => { render( - Test + Test , ); expect(screen.root).toHaveStyle({ opacity: 0 }); diff --git a/examples/basic/jest-setup.ts b/examples/basic/jest-setup.ts index 7f63025d9..29505bd57 100644 --- a/examples/basic/jest-setup.ts +++ b/examples/basic/jest-setup.ts @@ -1,7 +1,9 @@ -/* eslint-disable no-undef, import/no-extraneous-dependencies */ +import { configure } from '@testing-library/react-native'; // Import built-in Jest matchers import '@testing-library/react-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'); + +configure({ concurrentRoot: true }); diff --git a/examples/basic/package.json b/examples/basic/package.json index 86dd9d991..eb0ea4447 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -20,7 +20,7 @@ }, "devDependencies": { "@babel/core": "^7.24.0", - "@testing-library/react-native": "^12.7.1", + "@testing-library/react-native": "^12.8.0", "@types/eslint": "^8.56.10", "@types/jest": "^29.5.12", "@types/react": "~18.2.79", diff --git a/examples/basic/yarn.lock b/examples/basic/yarn.lock index 69368e8e0..518d371ad 100644 --- a/examples/basic/yarn.lock +++ b/examples/basic/yarn.lock @@ -2463,9 +2463,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/react-native@npm:^12.7.1": - version: 12.7.1 - resolution: "@testing-library/react-native@npm:12.7.1" +"@testing-library/react-native@npm:^12.8.0": + version: 12.8.0 + resolution: "@testing-library/react-native@npm:12.8.0" dependencies: jest-matcher-utils: "npm:^29.7.0" pretty-format: "npm:^29.7.0" @@ -2478,7 +2478,7 @@ __metadata: peerDependenciesMeta: jest: optional: true - checksum: 10c0/caaa4bdf97834b307b72af05c447ce40a2ba2ff40b464050bc29535caadf81981ea2873668445e633fdb3d13efccb136ef0932d6d9f4736bc6f7f98be98088d4 + checksum: 10c0/216d40eefc3afa3259b37611213dcd6667cd0b8deb30521a8aaabe3afc15f07116ce64acba150f8c88b8e268df80639baf6bc38f05af1dbbae247e1d07639bde languageName: node linkType: hard @@ -8923,7 +8923,7 @@ __metadata: resolution: "root-workspace-0b6124@workspace:." dependencies: "@babel/core": "npm:^7.24.0" - "@testing-library/react-native": "npm:^12.7.1" + "@testing-library/react-native": "npm:^12.8.0" "@types/eslint": "npm:^8.56.10" "@types/jest": "npm:^29.5.12" "@types/react": "npm:~18.2.79" diff --git a/examples/cookbook/.eslintignore b/examples/cookbook/.eslintignore index 2b876e21d..91e57fb44 100644 --- a/examples/cookbook/.eslintignore +++ b/examples/cookbook/.eslintignore @@ -1 +1,2 @@ -test-utils.* \ No newline at end of file +jest-setup.ts +test-utils.* diff --git a/examples/cookbook/jest-setup.ts b/examples/cookbook/jest-setup.ts index d51605250..c66317303 100644 --- a/examples/cookbook/jest-setup.ts +++ b/examples/cookbook/jest-setup.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-undef, import/no-extraneous-dependencies */ +import { configure } from '@testing-library/react-native'; // Import built-in Jest matchers import '@testing-library/react-native/extend-expect'; @@ -9,7 +9,11 @@ jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); // Enable API mocking via Mock Service Worker (MSW) beforeAll(() => server.listen()); + // Reset any runtime request handlers we may add during the tests afterEach(() => server.resetHandlers()); + // Disable API mocking after the tests are done afterAll(() => server.close()); + +configure({ concurrentRoot: true }); diff --git a/examples/cookbook/package.json b/examples/cookbook/package.json index 8c2416cb2..3bb98797c 100644 --- a/examples/cookbook/package.json +++ b/examples/cookbook/package.json @@ -30,7 +30,7 @@ "devDependencies": { "@babel/core": "^7.20.0", "@expo/metro-runtime": "~3.2.3", - "@testing-library/react-native": "^12.7.2", + "@testing-library/react-native": "^12.8.0", "@types/eslint": "^8.56.10", "@types/jest": "^29.5.12", "@types/react": "~18.2.45", diff --git a/examples/cookbook/yarn.lock b/examples/cookbook/yarn.lock index acf5f5385..01a301ae3 100644 --- a/examples/cookbook/yarn.lock +++ b/examples/cookbook/yarn.lock @@ -2833,9 +2833,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/react-native@npm:^12.7.2": - version: 12.7.2 - resolution: "@testing-library/react-native@npm:12.7.2" +"@testing-library/react-native@npm:^12.8.0": + version: 12.8.0 + resolution: "@testing-library/react-native@npm:12.8.0" dependencies: jest-matcher-utils: "npm:^29.7.0" pretty-format: "npm:^29.7.0" @@ -2848,7 +2848,7 @@ __metadata: peerDependenciesMeta: jest: optional: true - checksum: 10c0/0e4e26bd211056646f8b5c80e9177efc90affe0ddc7e1a2c22742a4e6da7129ec1f9125c7d233adddeb27f429fb3eb91e3f3bfa9e77e176f042475574546b001 + checksum: 10c0/216d40eefc3afa3259b37611213dcd6667cd0b8deb30521a8aaabe3afc15f07116ce64acba150f8c88b8e268df80639baf6bc38f05af1dbbae247e1d07639bde languageName: node linkType: hard @@ -9762,7 +9762,7 @@ __metadata: dependencies: "@babel/core": "npm:^7.20.0" "@expo/metro-runtime": "npm:~3.2.3" - "@testing-library/react-native": "npm:^12.7.2" + "@testing-library/react-native": "npm:^12.8.0" "@types/eslint": "npm:^8.56.10" "@types/jest": "npm:^29.5.12" "@types/react": "npm:~18.2.45" diff --git a/extend-expect.d.ts b/extend-expect.d.ts deleted file mode 100644 index 14b2aff7c..000000000 --- a/extend-expect.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './build/matchers/extend-expect'; diff --git a/extend-expect.js b/extend-expect.js deleted file mode 100644 index 796915b5f..000000000 --- a/extend-expect.js +++ /dev/null @@ -1 +0,0 @@ -require('./build/matchers/extend-expect'); diff --git a/jest-setup.ts b/jest-setup.ts index 9ed60181d..868f7ba89 100644 --- a/jest-setup.ts +++ b/jest-setup.ts @@ -3,7 +3,7 @@ import './src/matchers/extend-expect'; beforeEach(() => { resetToDefaults(); - if (process.env.CONCURRENT_MODE === '1') { - configure({ concurrentRoot: true }); + if (process.env.CONCURRENT_MODE === '0') { + configure({ concurrentRoot: false }); } }); diff --git a/matchers.d.ts b/matchers.d.ts new file mode 100644 index 000000000..6abd59c4e --- /dev/null +++ b/matchers.d.ts @@ -0,0 +1 @@ +export * from './build/matchers'; diff --git a/matchers.js b/matchers.js new file mode 100644 index 000000000..dafd7cfb2 --- /dev/null +++ b/matchers.js @@ -0,0 +1,2 @@ +// makes it so people can import from '@testing-library/react-native/pure' +module.exports = require('./build/matchers'); diff --git a/package.json b/package.json index 227def117..d186d5be7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@testing-library/react-native", - "version": "12.8.0", + "version": "13.0.0-alpha.0", "description": "Simple and complete React Native testing utilities that encourage good testing practices.", "main": "build/index.js", "types": "build/index.d.ts", @@ -34,13 +34,14 @@ "build:js": "babel src --out-dir build --extensions \".js,.ts,.jsx,.tsx\" --source-maps --ignore \"**/__tests__/**\"", "build:ts": "tsc --build tsconfig.release.json", "build": "yarn clean && yarn build:js && yarn build:ts && yarn copy-flowtypes", - "release": "release-it" + "release": "release-it", + "release:alpha": "release-it --preRelease=alpha" }, "files": [ "build/", "jest-preset/", - "extend-expect.js", - "extend-expect.d.ts", + "matchers.js", + "matchers.d.ts", "pure.js", "pure.d.ts", "dont-cleanup-after-each.js", @@ -52,10 +53,10 @@ "redent": "^3.0.0" }, "peerDependencies": { - "jest": ">=28.0.0", - "react": ">=16.8.0", - "react-native": ">=0.59", - "react-test-renderer": ">=16.8.0" + "jest": ">=29.0.0", + "react": ">=18.2.0", + "react-native": ">=0.71", + "react-test-renderer": ">=18.2.0" }, "peerDependenciesMeta": { "jest": { @@ -94,5 +95,8 @@ "publishConfig": { "registry": "https://registry.npmjs.org" }, - "packageManager": "yarn@4.4.0" + "packageManager": "yarn@4.4.0", + "engines": { + "node": ">=18" + } } diff --git a/src/__tests__/__snapshots__/render-debug.test.tsx.snap b/src/__tests__/__snapshots__/render-debug.test.tsx.snap index 561b363ae..8e3c10779 100644 --- a/src/__tests__/__snapshots__/render-debug.test.tsx.snap +++ b/src/__tests__/__snapshots__/render-debug.test.tsx.snap @@ -367,106 +367,6 @@ exports[`debug: another custom message 1`] = ` " `; -exports[`debug: shallow 1`] = ` -" - - Is the banana fresh? - - - not fresh - - - - - - - Change freshness! - - - First Text - - - Second Text - - - 0 - -" -`; - -exports[`debug: shallow with message 1`] = ` -"my other custom message - - - - Is the banana fresh? - - - not fresh - - - - - - - Change freshness! - - - First Text - - - Second Text - - - 0 - -" -`; - exports[`debug: with message 1`] = ` "my custom message diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index b3d2a7ed1..d20f91707 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -16,7 +16,7 @@ test('configure() overrides existing config values', () => { asyncUtilTimeout: 5000, defaultDebugOptions: { message: 'debug message' }, defaultIncludeHiddenElements: false, - concurrentRoot: false, + concurrentRoot: true, }); }); diff --git a/src/__tests__/render-debug.test.tsx b/src/__tests__/render-debug.test.tsx index 9a57c8144..0b5bd462b 100644 --- a/src/__tests__/render-debug.test.tsx +++ b/src/__tests__/render-debug.test.tsx @@ -97,21 +97,19 @@ test('debug', () => { 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 = 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'); + expect(stripAnsi(mockCalls[2][0] + mockCalls[2][1])).toMatchSnapshot('another custom message'); 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.', - ]); + expect(mockWarnCalls[0]).toMatchInlineSnapshot(` + [ + "Using debug("message") is deprecated and will be removed in future release, please use debug({ message: "message" }) instead.", + ] + `); }); test('debug changing component', () => { diff --git a/src/__tests__/screen.test.tsx b/src/__tests__/screen.test.tsx index b22e92522..d5e5183ca 100644 --- a/src/__tests__/screen.test.tsx +++ b/src/__tests__/screen.test.tsx @@ -55,6 +55,5 @@ test('screen throws without render', () => { expect(() => screen.root).toThrow('`render` method has not been called'); expect(() => screen.UNSAFE_root).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'); expect(() => screen.getByText('Mt. Everest')).toThrow('`render` method has not been called'); }); diff --git a/src/act.ts b/src/act.ts index 5c44ca358..940df677f 100644 --- a/src/act.ts +++ b/src/act.ts @@ -1,9 +1,10 @@ // This file and the act() implementation is sourced from react-testing-library -// https://github.com/testing-library/react-testing-library/blob/c80809a956b0b9f3289c4a6fa8b5e8cc72d6ef6d/src/act-compat.js +// https://github.com/testing-library/react-testing-library/blob/3dcd8a9649e25054c0e650d95fca2317b7008576/types/index.d.ts +import * as React from 'react'; import { act as reactTestRendererAct } from 'react-test-renderer'; -import { checkReactVersionAtLeast } from './react-versions'; -type ReactAct = typeof reactTestRendererAct; +const reactAct = typeof React.act === 'function' ? React.act : reactTestRendererAct; +type ReactAct = 0 extends 1 & typeof React.act ? typeof reactTestRendererAct : typeof React.act; // See https://github.com/reactwg/react-18/discussions/102 for more context on global.IS_REACT_ACT_ENVIRONMENT declare global { @@ -23,19 +24,13 @@ function withGlobalActEnvironment(actImplementation: ReactAct) { const previousActEnvironment = getIsReactActEnvironment(); setIsReactActEnvironment(true); - // this code is riddled with eslint disabling comments because this doesn't use real promises but eslint thinks we do try { // The return value of `act` is always a thenable. let callbackNeedsToBeAwaited = false; const actResult = actImplementation(() => { const result = callback(); - if ( - result !== null && - typeof result === 'object' && - // @ts-expect-error this should be a promise or thenable - // eslint-disable-next-line promise/prefer-await-to-then - typeof result.then === 'function' - ) { + // @ts-expect-error TS is too strict here + if (result !== null && typeof result === 'object' && typeof result.then === 'function') { callbackNeedsToBeAwaited = true; } return result; @@ -45,15 +40,17 @@ function withGlobalActEnvironment(actImplementation: ReactAct) { const thenable = actResult; return { then: (resolve: (value: never) => never, reject: (value: never) => never) => { - // eslint-disable-next-line + // eslint-disable-next-line promise/catch-or-return, promise/prefer-await-to-then thenable.then( // eslint-disable-next-line promise/always-return (returnValue) => { setIsReactActEnvironment(previousActEnvironment); + // @ts-expect-error resolve(returnValue); }, (error) => { setIsReactActEnvironment(previousActEnvironment); + // @ts-expect-error reject(error); }, ); @@ -72,9 +69,8 @@ function withGlobalActEnvironment(actImplementation: ReactAct) { }; } -const act: ReactAct = checkReactVersionAtLeast(18, 0) - ? (withGlobalActEnvironment(reactTestRendererAct) as ReactAct) - : reactTestRendererAct; +// @ts-expect-error +const act = withGlobalActEnvironment(reactAct) as ReactAct; export default act; export { setIsReactActEnvironment as setReactActEnvironment, getIsReactActEnvironment }; diff --git a/src/config.ts b/src/config.ts index 388933cdd..742963376 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { DebugOptions } from './helpers/debug-deep'; +import { DebugOptions } from './helpers/debug'; /** * Global configuration options for React Native Testing Library. @@ -15,8 +15,8 @@ export type Config = { defaultDebugOptions?: Partial; /** - * Set to `true` to enable concurrent rendering. - * Otherwise `render` will default to legacy synchronous rendering. + * Set to `false` to disable concurrent rendering. + * Otherwise `render` will default to concurrent rendering. */ concurrentRoot: boolean; }; @@ -43,7 +43,7 @@ export type InternalConfig = Config & { const defaultConfig: InternalConfig = { asyncUtilTimeout: 1000, defaultIncludeHiddenElements: false, - concurrentRoot: false, + concurrentRoot: true, }; let config = { ...defaultConfig }; diff --git a/src/flush-micro-tasks.ts b/src/flush-micro-tasks.ts index 08295740b..4e5fac764 100644 --- a/src/flush-micro-tasks.ts +++ b/src/flush-micro-tasks.ts @@ -3,28 +3,3 @@ import { setImmediate } from './helpers/timers'; export function flushMicroTasks() { return new Promise((resolve) => setImmediate(resolve)); } - -/** - * @deprecated To be removed in the next major release. - */ -type Thenable = { then: (callback: () => T) => unknown }; - -/** - * This legacy implementation of `flushMicroTasks` is used for compatibility with - * older versions of React Native (pre 0.71) which uses Promise polyfil. - * - * For users with older version of React Native there is a workaround of using our own - * Jest preset instead the `react-native` one, but requiring such change would be a - * breaking change for existing users. - * - * @deprecated To be removed in the next major release. - */ -export function flushMicroTasksLegacy(): Thenable { - return { - // using "thenable" instead of a Promise, because otherwise it breaks when - // using "modern" fake timers - then(resolve) { - setImmediate(resolve); - }, - }; -} diff --git a/src/helpers/debug-shallow.ts b/src/helpers/debug-shallow.ts deleted file mode 100644 index 510a1f402..000000000 --- a/src/helpers/debug-shallow.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import type { ReactTestInstance } from 'react-test-renderer'; -import { shallowInternal } from '../shallow'; -import format from './format'; - -/** - * Log pretty-printed shallow test component instance - */ -export default function debugShallow( - instance: ReactTestInstance | React.ReactElement, - message?: string, -) { - const { output } = shallowInternal(instance); - - if (message) { - // eslint-disable-next-line no-console - console.log(`${message}\n\n`, format(output)); - } else { - // eslint-disable-next-line no-console - console.log(format(output)); - } -} diff --git a/src/helpers/debug-deep.ts b/src/helpers/debug.ts similarity index 95% rename from src/helpers/debug-deep.ts rename to src/helpers/debug.ts index 0450330e9..14ced11af 100644 --- a/src/helpers/debug-deep.ts +++ b/src/helpers/debug.ts @@ -8,7 +8,7 @@ export type DebugOptions = { /** * Log pretty-printed deep test component instance */ -export default function debugDeep( +export function debug( instance: ReactTestRendererJSON | ReactTestRendererJSON[], options?: DebugOptions | string, ) { diff --git a/src/helpers/wrap-async.ts b/src/helpers/wrap-async.ts index c22a1df5e..a80d86156 100644 --- a/src/helpers/wrap-async.ts +++ b/src/helpers/wrap-async.ts @@ -1,8 +1,7 @@ /* istanbul ignore file */ -import act, { getIsReactActEnvironment, setReactActEnvironment } from '../act'; -import { flushMicroTasksLegacy } from '../flush-micro-tasks'; -import { checkReactVersionAtLeast } from '../react-versions'; +import { getIsReactActEnvironment, setReactActEnvironment } from '../act'; +import { flushMicroTasks } from '../flush-micro-tasks'; /** * Run given async callback with temporarily disabled `act` environment and flushes microtasks queue. @@ -11,30 +10,15 @@ import { checkReactVersionAtLeast } from '../react-versions'; * @returns Result of the callback */ export async function wrapAsync(callback: () => Promise): Promise { - if (checkReactVersionAtLeast(18, 0)) { - const previousActEnvironment = getIsReactActEnvironment(); - setReactActEnvironment(false); + const previousActEnvironment = getIsReactActEnvironment(); + setReactActEnvironment(false); - try { - const result = await callback(); - // Flush the microtask queue before restoring the `act` environment - await flushMicroTasksLegacy(); - return result; - } finally { - setReactActEnvironment(previousActEnvironment); - } + try { + const result = await callback(); + // Flush the microtask queue before restoring the `act` environment + await flushMicroTasks(); + return result; + } finally { + setReactActEnvironment(previousActEnvironment); } - - if (!checkReactVersionAtLeast(16, 9)) { - return callback(); - } - - // Wrapping with act for react version 16.9 to 17.x - let result: Result; - await act(async () => { - result = await callback(); - }); - - // Either we have result or `callback` threw error - return result!; } diff --git a/src/index.ts b/src/index.ts index 5c867106f..8b2ab83d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { cleanup } from './pure'; -import { flushMicroTasksLegacy } from './flush-micro-tasks'; +import { flushMicroTasks } from './flush-micro-tasks'; import { getIsReactActEnvironment, setReactActEnvironment } from './act'; +import './matchers/extend-expect'; if (!process?.env?.RNTL_SKIP_AUTO_CLEANUP) { // If we're running in a test runner that supports afterEach @@ -11,7 +12,7 @@ if (!process?.env?.RNTL_SKIP_AUTO_CLEANUP) { if (typeof afterEach === 'function') { // eslint-disable-next-line no-undef afterEach(async () => { - await flushMicroTasksLegacy(); + await flushMicroTasks(); cleanup(); }); } diff --git a/src/matchers/__tests__/to-be-checked.test.tsx b/src/matchers/__tests__/to-be-checked.test.tsx index 6cf432b85..2b674c627 100644 --- a/src/matchers/__tests__/to-be-checked.test.tsx +++ b/src/matchers/__tests__/to-be-checked.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { type AccessibilityRole, Switch, View } from 'react-native'; -import render from '../../render'; -import { screen } from '../../screen'; +import { render, screen } from '../..'; function renderViewsWithRole(role: AccessibilityRole) { render( diff --git a/src/matchers/__tests__/to-be-partially-checked.test.tsx b/src/matchers/__tests__/to-be-partially-checked.test.tsx index 03ab58290..dd84e0cb7 100644 --- a/src/matchers/__tests__/to-be-partially-checked.test.tsx +++ b/src/matchers/__tests__/to-be-partially-checked.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { type AccessibilityRole, View } from 'react-native'; -import render from '../../render'; -import { screen } from '../../screen'; +import { render, screen } from '../..'; function renderViewsWithRole(role: AccessibilityRole) { return render( diff --git a/src/queries/__tests__/accessibility-state.test.tsx b/src/queries/__tests__/accessibility-state.test.tsx deleted file mode 100644 index 26a2b61de..000000000 --- a/src/queries/__tests__/accessibility-state.test.tsx +++ /dev/null @@ -1,535 +0,0 @@ -/* eslint-disable no-console */ -import * as React from 'react'; -import { View, Text, Pressable, TouchableOpacity } from 'react-native'; -import { render, screen } from '../..'; - -type ConsoleLogMock = jest.Mock; - -beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); -}); - -const TEXT_LABEL = 'cool text'; - -const Typography = ({ children, ...rest }: any) => { - return {children}; -}; - -const Button = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); - -const Section = () => ( - <> - Title - - -); - -test('getByA11yState, queryByA11yState, findByA11yState', async () => { - render(
); - - expect(screen.getByA11yState({ selected: true }).props.accessibilityState).toEqual({ - selected: true, - expanded: false, - }); - expect(screen.queryByA11yState({ selected: true })?.props.accessibilityState).toEqual({ - selected: true, - expanded: false, - }); - - expect(() => screen.getByA11yState({ disabled: true })).toThrow( - 'Unable to find an element with disabled state: true', - ); - expect(screen.queryByA11yState({ disabled: true })).toEqual(null); - - expect(() => screen.getByA11yState({ expanded: false })).toThrow( - 'Found multiple elements with expanded state: false', - ); - expect(() => screen.queryByA11yState({ expanded: false })).toThrow( - 'Found multiple elements with expanded state: false', - ); - - const asyncButton = await screen.findByA11yState({ selected: true }); - expect(asyncButton.props.accessibilityState).toEqual({ - selected: true, - expanded: false, - }); - await expect(screen.findByA11yState({ disabled: true })).rejects.toThrow( - 'Unable to find an element with disabled state: true', - ); - await expect(screen.findByA11yState({ expanded: false })).rejects.toThrow( - 'Found multiple elements with expanded state: false', - ); -}); - -test('getAllByA11yState, queryAllByA11yState, findAllByA11yState', async () => { - render(
); - - expect(screen.getAllByA11yState({ selected: true })).toHaveLength(1); - expect(screen.queryAllByA11yState({ selected: true })).toHaveLength(1); - - expect(() => screen.getAllByA11yState({ disabled: true })).toThrow( - 'Unable to find an element with disabled state: true', - ); - expect(screen.queryAllByA11yState({ disabled: true })).toEqual([]); - - expect(screen.getAllByA11yState({ expanded: false })).toHaveLength(2); - expect(screen.queryAllByA11yState({ expanded: false })).toHaveLength(2); - - await expect(screen.findAllByA11yState({ selected: true })).resolves.toHaveLength(1); - await expect(screen.findAllByA11yState({ disabled: true })).rejects.toThrow( - 'Unable to find an element with disabled state: true', - ); - await expect(screen.findAllByA11yState({ expanded: false })).resolves.toHaveLength(2); -}); - -describe('checked state matching', () => { - it('handles true', () => { - render(); - - expect(screen.getByA11yState({ checked: true })).toBeTruthy(); - expect(screen.queryByA11yState({ checked: 'mixed' })).toBeFalsy(); - expect(screen.queryByA11yState({ checked: false })).toBeFalsy(); - }); - - it('handles mixed', () => { - render(); - - expect(screen.getByA11yState({ checked: 'mixed' })).toBeTruthy(); - expect(screen.queryByA11yState({ checked: true })).toBeFalsy(); - expect(screen.queryByA11yState({ checked: false })).toBeFalsy(); - }); - - it('handles false', () => { - render(); - - expect(screen.getByA11yState({ checked: false })).toBeTruthy(); - expect(screen.queryByA11yState({ checked: true })).toBeFalsy(); - expect(screen.queryByA11yState({ checked: 'mixed' })).toBeFalsy(); - }); - - it('handles default', () => { - render(); - - expect(screen.queryByA11yState({ checked: false })).toBeFalsy(); - expect(screen.queryByA11yState({ checked: true })).toBeFalsy(); - expect(screen.queryByA11yState({ checked: 'mixed' })).toBeFalsy(); - }); -}); - -describe('expanded state matching', () => { - it('handles true', () => { - render(); - - expect(screen.getByA11yState({ expanded: true })).toBeTruthy(); - expect(screen.queryByA11yState({ expanded: false })).toBeFalsy(); - }); - - it('handles false', () => { - render(); - - expect(screen.getByA11yState({ expanded: false })).toBeTruthy(); - expect(screen.queryByA11yState({ expanded: true })).toBeFalsy(); - }); - - it('handles default', () => { - render(); - - expect(screen.queryByA11yState({ expanded: false })).toBeFalsy(); - expect(screen.queryByA11yState({ expanded: true })).toBeFalsy(); - }); -}); - -describe('disabled state matching', () => { - it('handles true', () => { - render(); - - expect(screen.getByA11yState({ disabled: true })).toBeTruthy(); - expect(screen.queryByA11yState({ disabled: false })).toBeFalsy(); - }); - - it('handles false', () => { - render(); - - expect(screen.getByA11yState({ disabled: false })).toBeTruthy(); - expect(screen.queryByA11yState({ disabled: true })).toBeFalsy(); - }); - - it('handles default', () => { - render(); - - expect(screen.getByA11yState({ disabled: false })).toBeTruthy(); - expect(screen.queryByA11yState({ disabled: true })).toBeFalsy(); - }); -}); - -describe('busy state matching', () => { - it('handles true', () => { - render(); - - expect(screen.getByA11yState({ busy: true })).toBeTruthy(); - expect(screen.queryByA11yState({ busy: false })).toBeFalsy(); - }); - - it('handles false', () => { - render(); - - expect(screen.getByA11yState({ busy: false })).toBeTruthy(); - expect(screen.queryByA11yState({ busy: true })).toBeFalsy(); - }); - - it('handles default', () => { - render(); - - expect(screen.getByA11yState({ busy: false })).toBeTruthy(); - expect(screen.queryByA11yState({ busy: true })).toBeFalsy(); - }); -}); - -describe('selected state matching', () => { - it('handles true', () => { - render(); - - expect(screen.getByA11yState({ selected: true })).toBeTruthy(); - expect(screen.queryByA11yState({ selected: false })).toBeFalsy(); - }); - - it('handles false', () => { - render(); - - expect(screen.getByA11yState({ selected: false })).toBeTruthy(); - expect(screen.queryByA11yState({ selected: true })).toBeFalsy(); - }); - - it('handles default', () => { - render(); - - expect(screen.getByA11yState({ selected: false })).toBeTruthy(); - expect(screen.queryByA11yState({ selected: true })).toBeFalsy(); - }); -}); - -test('*ByA11yState on Pressable with "disabled" prop', () => { - render(); - expect(screen.getByA11yState({ disabled: true })).toBeTruthy(); - expect(screen.queryByA11yState({ disabled: false })).toBeFalsy(); -}); - -test('*ByA11yState on TouchableOpacity with "disabled" prop', () => { - render(); - expect(screen.getByA11yState({ disabled: true })).toBeTruthy(); - expect(screen.queryByA11yState({ disabled: false })).toBeFalsy(); -}); - -test('byA11yState queries support hidden option', () => { - render( - - Hidden from accessibility - , - ); - - expect(screen.getByA11yState({ expanded: false }, { includeHiddenElements: true })).toBeTruthy(); - - expect(screen.queryByA11yState({ expanded: false })).toBeFalsy(); - expect( - screen.queryByA11yState({ expanded: false }, { includeHiddenElements: false }), - ).toBeFalsy(); - expect(() => screen.getByA11yState({ expanded: false }, { includeHiddenElements: false })) - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with expanded state: false - - - - Hidden from accessibility - - " - `); -}); - -test('*ByA11yState deprecation warnings', async () => { - const mockCalls = (console.warn as ConsoleLogMock).mock.calls; - render(); - - screen.getByA11yState({ disabled: true }); - expect(mockCalls[0][0]).toMatchInlineSnapshot(` - "getByA11yState(...) is deprecated and will be removed in the future. - - Use getByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.getAllByA11yState({ disabled: true }); - expect(mockCalls[1][0]).toMatchInlineSnapshot(` - "getAllByA11yState(...) is deprecated and will be removed in the future. - - Use getAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.queryByA11yState({ disabled: true }); - expect(mockCalls[2][0]).toMatchInlineSnapshot(` - "queryByA11yState(...) is deprecated and will be removed in the future. - - Use queryByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.queryAllByA11yState({ disabled: true }); - expect(mockCalls[3][0]).toMatchInlineSnapshot(` - "queryAllByA11yState(...) is deprecated and will be removed in the future. - - Use queryAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - await screen.findByA11yState({ disabled: true }); - expect(mockCalls[4][0]).toMatchInlineSnapshot(` - "findByA11yState(...) is deprecated and will be removed in the future. - - Use findByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - await screen.findAllByA11yState({ disabled: true }); - expect(mockCalls[5][0]).toMatchInlineSnapshot(` - "findAllByA11yState(...) is deprecated and will be removed in the future. - - Use findAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); -}); - -test('*ByAccessibilityState deprecation warnings', async () => { - const mockCalls = (console.warn as ConsoleLogMock).mock.calls; - render(); - - screen.getByAccessibilityState({ disabled: true }); - expect(mockCalls[0][0]).toMatchInlineSnapshot(` - "getByAccessibilityState(...) is deprecated and will be removed in the future. - - Use getByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.getAllByAccessibilityState({ disabled: true }); - expect(mockCalls[1][0]).toMatchInlineSnapshot(` - "getAllByAccessibilityState(...) is deprecated and will be removed in the future. - - Use getAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.queryByAccessibilityState({ disabled: true }); - expect(mockCalls[2][0]).toMatchInlineSnapshot(` - "queryByAccessibilityState(...) is deprecated and will be removed in the future. - - Use queryByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - screen.queryAllByAccessibilityState({ disabled: true }); - expect(mockCalls[3][0]).toMatchInlineSnapshot(` - "queryAllByAccessibilityState(...) is deprecated and will be removed in the future. - - Use queryAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - await screen.findByAccessibilityState({ disabled: true }); - expect(mockCalls[4][0]).toMatchInlineSnapshot(` - "findByAccessibilityState(...) is deprecated and will be removed in the future. - - Use findByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); - - await screen.findAllByAccessibilityState({ disabled: true }); - expect(mockCalls[5][0]).toMatchInlineSnapshot(` - "findAllByAccessibilityState(...) is deprecated and will be removed in the future. - - Use findAllByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead." - `); -}); - -test('error message renders the element tree, preserving only helpful props', async () => { - render( - null}> - Some text - , - ); - - expect(() => screen.getByA11yState({ checked: true })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with checked state: true - - - Some text - " - `); - - expect(() => screen.getAllByA11yState({ checked: true })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with checked state: true - - - Some text - " - `); - - await expect(screen.findByA11yState({ checked: true })).rejects - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with checked state: true - - - Some text - " - `); - - await expect(screen.findAllByA11yState({ checked: true })).rejects - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with checked state: true - - - Some text - " - `); -}); - -describe('aria-disabled prop', () => { - test('supports aria-disabled={true} prop', () => { - render(); - expect(screen.getByAccessibilityState({ disabled: true })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ disabled: false })).toBeNull(); - }); - - test('supports aria-disabled={false} prop', () => { - render(); - expect(screen.getByAccessibilityState({ disabled: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ disabled: true })).toBeNull(); - }); - - test('supports default aria-disabled prop', () => { - render(); - expect(screen.getByAccessibilityState({ disabled: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ disabled: true })).toBeNull(); - }); -}); - -describe('aria-selected prop', () => { - test('supports aria-selected={true} prop', () => { - render(); - expect(screen.getByAccessibilityState({ selected: true })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ selected: false })).toBeNull(); - }); - - test('supports aria-selected={false} prop', () => { - render(); - expect(screen.getByAccessibilityState({ selected: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ selected: true })).toBeNull(); - }); - - test('supports default aria-selected prop', () => { - render(); - expect(screen.getByAccessibilityState({ selected: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ selected: true })).toBeNull(); - }); -}); - -describe('aria-checked prop', () => { - test('supports aria-checked={true} prop', () => { - render(); - expect(screen.getByAccessibilityState({ checked: true })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ checked: false })).toBeNull(); - expect(screen.queryByAccessibilityState({ checked: 'mixed' })).toBeNull(); - }); - - test('supports aria-checked={false} prop', () => { - render(); - expect(screen.getByAccessibilityState({ checked: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ checked: true })).toBeNull(); - expect(screen.queryByAccessibilityState({ checked: 'mixed' })).toBeNull(); - }); - - test('supports aria-checked="mixed" prop', () => { - render(); - expect(screen.getByAccessibilityState({ checked: 'mixed' })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ checked: true })).toBeNull(); - expect(screen.queryByAccessibilityState({ checked: false })).toBeNull(); - }); - - test('supports default aria-checked prop', () => { - render(); - expect(screen.getByAccessibilityState({})).toBeTruthy(); - expect(screen.queryByAccessibilityState({ checked: true })).toBeNull(); - expect(screen.queryByAccessibilityState({ checked: false })).toBeNull(); - expect(screen.queryByAccessibilityState({ checked: 'mixed' })).toBeNull(); - }); -}); - -describe('aria-busy prop', () => { - test('supports aria-busy={true} prop', () => { - render(); - expect(screen.getByAccessibilityState({ busy: true })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ busy: false })).toBeNull(); - }); - - test('supports aria-busy={false} prop', () => { - render(); - expect(screen.getByAccessibilityState({ busy: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ busy: true })).toBeNull(); - }); - - test('supports default aria-busy prop', () => { - render(); - expect(screen.getByAccessibilityState({ busy: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ busy: true })).toBeNull(); - }); -}); - -describe('aria-expanded prop', () => { - test('supports aria-expanded={true} prop', () => { - render(); - expect(screen.getByAccessibilityState({ expanded: true })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ expanded: false })).toBeNull(); - }); - - test('supports aria-expanded={false} prop', () => { - render(); - expect(screen.getByAccessibilityState({ expanded: false })).toBeTruthy(); - expect(screen.queryByAccessibilityState({ expanded: true })).toBeNull(); - }); - - test('supports default aria-expanded prop', () => { - render(); - render(); - expect(screen.getByAccessibilityState({})).toBeTruthy(); - expect(screen.queryByAccessibilityState({ expanded: true })).toBeNull(); - expect(screen.queryByAccessibilityState({ expanded: false })).toBeNull(); - }); -}); diff --git a/src/queries/__tests__/accessibility-value.test.tsx b/src/queries/__tests__/accessibility-value.test.tsx deleted file mode 100644 index 9e07ab41c..000000000 --- a/src/queries/__tests__/accessibility-value.test.tsx +++ /dev/null @@ -1,319 +0,0 @@ -/* eslint-disable no-console */ -import * as React from 'react'; -import { Text, TouchableOpacity, View } from 'react-native'; -import { render, screen } from '../..'; - -type ConsoleLogMock = jest.Mock; - -beforeEach(() => { - jest.spyOn(console, 'warn').mockImplementation(() => {}); -}); - -const TEXT_LABEL = 'cool text'; - -const Typography = ({ children, ...rest }: any) => { - return {children}; -}; - -const Button = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); - -const Section = () => ( - <> - Title - - -); - -test('getByA11yValue, queryByA11yValue, findByA11yValue', async () => { - render(
); - - expect(screen.getByA11yValue({ min: 40 }).props.accessibilityValue).toEqual({ - min: 40, - max: 60, - }); - expect(screen.queryByA11yValue({ min: 40 })?.props.accessibilityValue).toEqual({ - min: 40, - max: 60, - }); - - expect(() => screen.getByA11yValue({ min: 50 })).toThrow( - 'Unable to find an element with min value: 50', - ); - expect(screen.queryByA11yValue({ min: 50 })).toEqual(null); - - expect(() => screen.getByA11yValue({ max: 60 })).toThrow( - 'Found multiple elements with max value: 60', - ); - expect(() => screen.queryByA11yValue({ max: 60 })).toThrow( - 'Found multiple elements with max value: 60', - ); - - const asyncElement = await screen.findByA11yValue({ min: 40 }); - expect(asyncElement.props.accessibilityValue).toEqual({ - min: 40, - max: 60, - }); - await expect(screen.findByA11yValue({ min: 50 })).rejects.toThrow( - 'Unable to find an element with min value: 50', - ); - await expect(screen.findByA11yValue({ max: 60 })).rejects.toThrow( - 'Found multiple elements with max value: 60', - ); -}); - -test('getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue', async () => { - render(
); - - expect(screen.getAllByA11yValue({ min: 40 })).toHaveLength(1); - expect(screen.queryAllByA11yValue({ min: 40 })).toHaveLength(1); - - expect(() => screen.getAllByA11yValue({ min: 50 })).toThrow( - 'Unable to find an element with min value: 50', - ); - expect(screen.queryAllByA11yValue({ min: 50 })).toEqual([]); - - expect(screen.queryAllByA11yValue({ max: 60 })).toHaveLength(2); - expect(screen.getAllByA11yValue({ max: 60 })).toHaveLength(2); - - await expect(screen.findAllByA11yValue({ min: 40 })).resolves.toHaveLength(1); - await expect(screen.findAllByA11yValue({ min: 50 })).rejects.toThrow( - 'Unable to find an element with min value: 50', - ); - await expect(screen.findAllByA11yValue({ max: 60 })).resolves.toHaveLength(2); -}); - -test('byA11yValue queries support hidden option', () => { - render( - - Hidden from accessibility - , - ); - - expect(screen.getByA11yValue({ max: 10 }, { includeHiddenElements: true })).toBeTruthy(); - - expect(screen.queryByA11yValue({ max: 10 })).toBeFalsy(); - expect(screen.queryByA11yValue({ max: 10 }, { includeHiddenElements: false })).toBeFalsy(); - expect(() => screen.getByA11yValue({ max: 10 }, { includeHiddenElements: false })) - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with max value: 10 - - - Hidden from accessibility - " - `); -}); - -test('byA11yValue error messages', () => { - render(); - expect(() => screen.getByA11yValue({ min: 10, max: 10 })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 10, max value: 10 - - " - `); - expect(() => screen.getByA11yValue({ max: 20, now: 5 })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with max value: 20, now value: 5 - - " - `); - expect(() => screen.getByA11yValue({ min: 1, max: 2, now: 3 })) - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1, max value: 2, now value: 3 - - " - `); - expect(() => screen.getByA11yValue({ min: 1, max: 2, now: 3, text: /foo/i })) - .toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1, max value: 2, now value: 3, text value: /foo/i - - " - `); -}); - -test('*ByA11yValue deprecation warnings', async () => { - const mockCalls = (console.warn as ConsoleLogMock).mock.calls; - render(); - - screen.getByA11yValue({ min: 10 }); - expect(mockCalls[0][0]).toMatchInlineSnapshot(` - "getByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or getByRole(role, { value: ... }) query instead." - `); - - screen.getAllByA11yValue({ min: 10 }); - expect(mockCalls[1][0]).toMatchInlineSnapshot(` - "getAllByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or getAllByRole(role, { value: ... }) query instead." - `); - - screen.queryByA11yValue({ min: 10 }); - expect(mockCalls[2][0]).toMatchInlineSnapshot(` - "queryByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or queryByRole(role, { value: ... }) query instead." - `); - - screen.queryAllByA11yValue({ min: 10 }); - expect(mockCalls[3][0]).toMatchInlineSnapshot(` - "queryAllByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or queryAllByRole(role, { value: ... }) query instead." - `); - - await screen.findByA11yValue({ min: 10 }); - expect(mockCalls[4][0]).toMatchInlineSnapshot(` - "findByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or findByRole(role, { value: ... }) query instead." - `); - - await screen.findAllByA11yValue({ min: 10 }); - expect(mockCalls[5][0]).toMatchInlineSnapshot(` - "findAllByA11yValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or findAllByRole(role, { value: ... }) query instead." - `); -}); - -test('*ByAccessibilityValue deprecation warnings', async () => { - const mockCalls = (console.warn as ConsoleLogMock).mock.calls; - render(); - - screen.getByAccessibilityValue({ min: 10 }); - expect(mockCalls[0][0]).toMatchInlineSnapshot(` - "getByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or getByRole(role, { value: ... }) query instead." - `); - - screen.getAllByAccessibilityValue({ min: 10 }); - expect(mockCalls[1][0]).toMatchInlineSnapshot(` - "getAllByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or getAllByRole(role, { value: ... }) query instead." - `); - - screen.queryByAccessibilityValue({ min: 10 }); - expect(mockCalls[2][0]).toMatchInlineSnapshot(` - "queryByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or queryByRole(role, { value: ... }) query instead." - `); - - screen.queryAllByAccessibilityValue({ min: 10 }); - expect(mockCalls[3][0]).toMatchInlineSnapshot(` - "queryAllByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or queryAllByRole(role, { value: ... }) query instead." - `); - - await screen.findByAccessibilityValue({ min: 10 }); - expect(mockCalls[4][0]).toMatchInlineSnapshot(` - "findByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or findByRole(role, { value: ... }) query instead." - `); - - await screen.findAllByAccessibilityValue({ min: 10 }); - expect(mockCalls[5][0]).toMatchInlineSnapshot(` - "findAllByAccessibilityValue(...) is deprecated and will be removed in the future. - - Use toHaveAccessibilityValue(...) built-in Jest matcher or findAllByRole(role, { value: ... }) query instead." - `); -}); - -test('error message renders the element tree, preserving only helpful props', async () => { - render(); - - expect(() => screen.getByA11yValue({ min: 1 })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1 - - " - `); - - expect(() => screen.getAllByA11yValue({ min: 1 })).toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1 - - " - `); - - await expect(screen.findByA11yValue({ min: 1 })).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1 - - " - `); - - await expect(screen.findAllByA11yValue({ min: 1 })).rejects.toThrowErrorMatchingInlineSnapshot(` - "Unable to find an element with min value: 1 - - " - `); -}); - -describe('getByAccessibilityValue supports "aria-*" props', () => { - test('supports "aria-valuemax"', () => { - render(); - expect(screen.getByAccessibilityValue({ max: 10 })).toBeTruthy(); - }); - - test('supports "aria-valuemin"', () => { - render(); - expect(screen.getByAccessibilityValue({ min: 20 })).toBeTruthy(); - }); - - test('supports "aria-valuenow"', () => { - render(); - expect(screen.getByAccessibilityValue({ now: 30 })).toBeTruthy(); - }); - - test('supports "aria-valuetext"', () => { - render(); - expect(screen.getByAccessibilityValue({ text: 'Hello World' })).toBeTruthy(); - expect(screen.getByAccessibilityValue({ text: /hello/i })).toBeTruthy(); - }); - - test('supports multiple "aria-value*" props', () => { - render(); - expect(screen.getByAccessibilityValue({ now: 50, min: 0, max: 100 })).toBeTruthy(); - }); -}); diff --git a/src/queries/accessibility-state.ts b/src/queries/accessibility-state.ts deleted file mode 100644 index b38df4a7a..000000000 --- a/src/queries/accessibility-state.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { ReactTestInstance } from 'react-test-renderer'; -import { accessibilityStateKeys } from '../helpers/accessibility'; -import { deprecateQueries } from '../helpers/deprecation'; -import { findAll } from '../helpers/find-all'; -import { - AccessibilityStateMatcher, - matchAccessibilityState, -} from '../helpers/matchers/match-accessibility-state'; -import { makeQueries } from './make-queries'; -import type { - FindAllByQuery, - FindByQuery, - GetAllByQuery, - GetByQuery, - QueryAllByQuery, - QueryByQuery, -} from './make-queries'; -import { CommonQueryOptions } from './options'; - -const queryAllByA11yState = ( - instance: ReactTestInstance, -): QueryAllByQuery => - function queryAllByA11yStateFn(matcher, queryOptions) { - return findAll(instance, (node) => matchAccessibilityState(node, matcher), queryOptions); - }; - -const buildErrorMessage = (state: AccessibilityStateMatcher = {}) => { - const errors: string[] = []; - - accessibilityStateKeys.forEach((stateKey) => { - if (state[stateKey] !== undefined) { - errors.push(`${stateKey} state: ${state[stateKey]}`); - } - }); - - return errors.join(', '); -}; - -const getMultipleError = (state: AccessibilityStateMatcher) => - `Found multiple elements with ${buildErrorMessage(state)}`; - -const getMissingError = (state: AccessibilityStateMatcher) => - `Unable to find an element with ${buildErrorMessage(state)}`; - -const { getBy, getAllBy, queryBy, queryAllBy, findBy, findAllBy } = makeQueries( - queryAllByA11yState, - getMissingError, - getMultipleError, -); - -export type ByA11yStateQueries = { - getByA11yState: GetByQuery; - getAllByA11yState: GetAllByQuery; - queryByA11yState: QueryByQuery; - queryAllByA11yState: QueryAllByQuery; - findByA11yState: FindByQuery; - findAllByA11yState: FindAllByQuery; - - getByAccessibilityState: GetByQuery; - getAllByAccessibilityState: GetAllByQuery; - queryByAccessibilityState: QueryByQuery; - queryAllByAccessibilityState: QueryAllByQuery; - findByAccessibilityState: FindByQuery; - findAllByAccessibilityState: FindAllByQuery; -}; - -export const bindByA11yStateQueries = (instance: ReactTestInstance): ByA11yStateQueries => { - const getByA11yState = getBy(instance); - const getAllByA11yState = getAllBy(instance); - const queryByA11yState = queryBy(instance); - const queryAllByA11yState = queryAllBy(instance); - const findByA11yState = findBy(instance); - const findAllByA11yState = findAllBy(instance); - - return { - ...deprecateQueries( - { - getByA11yState, - getAllByA11yState, - queryByA11yState, - queryAllByA11yState, - findByA11yState, - findAllByA11yState, - getByAccessibilityState: getByA11yState, - getAllByAccessibilityState: getAllByA11yState, - queryByAccessibilityState: queryByA11yState, - queryAllByAccessibilityState: queryAllByA11yState, - findByAccessibilityState: findByA11yState, - findAllByAccessibilityState: findAllByA11yState, - }, - 'Use {queryPrefix}ByRole(role, { disabled, selected, checked, busy, expanded }) query or built-in Jest matchers: toBeDisabled(), toBeSelected(), toBeChecked(), toBeBusy(), and toBeExpanded() instead.', - ), - }; -}; diff --git a/src/queries/accessibility-value.ts b/src/queries/accessibility-value.ts deleted file mode 100644 index 25d4d5361..000000000 --- a/src/queries/accessibility-value.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { ReactTestInstance } from 'react-test-renderer'; -import { accessibilityValueKeys } from '../helpers/accessibility'; -import { deprecateQueries } from '../helpers/deprecation'; -import { findAll } from '../helpers/find-all'; -import { - AccessibilityValueMatcher, - matchAccessibilityValue, -} from '../helpers/matchers/match-accessibility-value'; -import { makeQueries } from './make-queries'; -import type { - FindAllByQuery, - FindByQuery, - GetAllByQuery, - GetByQuery, - QueryAllByQuery, - QueryByQuery, -} from './make-queries'; -import { CommonQueryOptions } from './options'; - -const queryAllByA11yValue = ( - instance: ReactTestInstance, -): QueryAllByQuery => - function queryAllByA11yValueFn(value, queryOptions) { - return findAll(instance, (node) => matchAccessibilityValue(node, value), queryOptions); - }; - -const formatQueryParams = (matcher: AccessibilityValueMatcher) => { - const params: string[] = []; - - accessibilityValueKeys.forEach((valueKey) => { - if (matcher[valueKey] !== undefined) { - params.push(`${valueKey} value: ${matcher[valueKey]}`); - } - }); - - return params.join(', '); -}; - -const getMultipleError = (matcher: AccessibilityValueMatcher) => - `Found multiple elements with ${formatQueryParams(matcher)}`; - -const getMissingError = (matcher: AccessibilityValueMatcher) => - `Unable to find an element with ${formatQueryParams(matcher)}`; - -const { getBy, getAllBy, queryBy, queryAllBy, findBy, findAllBy } = makeQueries( - queryAllByA11yValue, - getMissingError, - getMultipleError, -); - -export type ByA11yValueQueries = { - getByA11yValue: GetByQuery; - getAllByA11yValue: GetAllByQuery; - queryByA11yValue: QueryByQuery; - queryAllByA11yValue: QueryAllByQuery; - findByA11yValue: FindByQuery; - findAllByA11yValue: FindAllByQuery; - - getByAccessibilityValue: GetByQuery; - getAllByAccessibilityValue: GetAllByQuery; - queryByAccessibilityValue: QueryByQuery; - queryAllByAccessibilityValue: QueryAllByQuery; - findByAccessibilityValue: FindByQuery; - findAllByAccessibilityValue: FindAllByQuery; -}; - -export const bindByA11yValueQueries = (instance: ReactTestInstance): ByA11yValueQueries => { - const getByA11yValue = getBy(instance); - const getAllByA11yValue = getAllBy(instance); - const queryByA11yValue = queryBy(instance); - const queryAllByA11yValue = queryAllBy(instance); - const findByA11yValue = findBy(instance); - const findAllByA11yValue = findAllBy(instance); - - return { - ...deprecateQueries( - { - getByA11yValue, - getAllByA11yValue, - queryByA11yValue, - queryAllByA11yValue, - findByA11yValue, - findAllByA11yValue, - getByAccessibilityValue: getByA11yValue, - getAllByAccessibilityValue: getAllByA11yValue, - queryByAccessibilityValue: queryByA11yValue, - queryAllByAccessibilityValue: queryAllByA11yValue, - findByAccessibilityValue: findByA11yValue, - findAllByAccessibilityValue: findAllByA11yValue, - }, - 'Use toHaveAccessibilityValue(...) built-in Jest matcher or {queryPrefix}ByRole(role, { value: ... }) query instead.', - ), - }; -}; diff --git a/src/render.tsx b/src/render.tsx index e4a6e22e4..7727130c2 100644 --- a/src/render.tsx +++ b/src/render.tsx @@ -9,8 +9,7 @@ import act from './act'; import { addToCleanupQueue } from './cleanup'; import { getConfig } from './config'; import { getHostChildren } from './helpers/component-tree'; -import debugDeep, { DebugOptions } from './helpers/debug-deep'; -import debugShallow from './helpers/debug-shallow'; +import { debug, DebugOptions } from './helpers/debug'; import { configureHostComponentNamesIfNeeded } from './helpers/host-component-names'; import { validateStringsRenderedWithinText } from './helpers/string-validation'; import { renderWithAct } from './render-act'; @@ -25,10 +24,10 @@ export interface RenderOptions { wrapper?: React.ComponentType; /** - * Set to `true` to enable concurrent rendering. - * Otherwise `render` will default to legacy synchronous rendering. + * Set to `false` to disable concurrent rendering. + * Otherwise `render` will default to concurrent rendering. */ - concurrentRoot?: boolean | undefined; + concurrentRoot?: boolean; createNodeMock?: (element: React.ReactElement) => unknown; unstable_validateStringsRenderedWithinText?: boolean; @@ -129,7 +128,7 @@ function buildRenderResult( unmount, rerender: update, // alias for `update` toJSON: renderer.toJSON, - debug: debug(instance, renderer), + debug: makeDebug(instance, renderer), get root(): ReactTestInstance { return getHostChildren(instance)[0]; }, @@ -164,12 +163,9 @@ function updateWithAct( }; } -export interface DebugFunction { - (options?: DebugOptions | string): void; - shallow: (message?: string) => void; -} +export type DebugFunction = (options?: DebugOptions | string) => void; -function debug(instance: ReactTestInstance, renderer: ReactTestRenderer): DebugFunction { +function makeDebug(instance: ReactTestInstance, renderer: ReactTestRenderer): DebugFunction { function debugImpl(options?: DebugOptions | string) { const { defaultDebugOptions } = getConfig(); const debugOptions = @@ -180,15 +176,14 @@ function debug(instance: ReactTestInstance, renderer: ReactTestRenderer): DebugF if (typeof options === 'string') { // eslint-disable-next-line no-console console.warn( - 'Using debug("message") is deprecated and will be removed in future release, please use debug({ message; "message" }) instead.', + 'Using debug("message") is deprecated and will be removed in future release, please use debug({ message: "message" }) instead.', ); } const json = renderer.toJSON(); if (json) { - return debugDeep(json, debugOptions); + return debug(json, debugOptions); } } - debugImpl.shallow = (message?: string) => debugShallow(instance, message); return debugImpl; } diff --git a/src/screen.ts b/src/screen.ts index 1fcbd3e2a..2d85d7c35 100644 --- a/src/screen.ts +++ b/src/screen.ts @@ -10,7 +10,6 @@ const notImplemented = () => { const notImplementedDebug = () => { throw new Error(SCREEN_ERROR); }; -notImplementedDebug.shallow = notImplemented; interface Screen extends RenderResult { isDetached?: boolean; @@ -59,30 +58,6 @@ const defaultScreen: Screen = { queryAllByRole: notImplemented, findByRole: notImplemented, findAllByRole: notImplemented, - getByA11yState: notImplemented, - getAllByA11yState: notImplemented, - queryByA11yState: notImplemented, - queryAllByA11yState: notImplemented, - findByA11yState: notImplemented, - findAllByA11yState: notImplemented, - getByAccessibilityState: notImplemented, - getAllByAccessibilityState: notImplemented, - queryByAccessibilityState: notImplemented, - queryAllByAccessibilityState: notImplemented, - findByAccessibilityState: notImplemented, - findAllByAccessibilityState: notImplemented, - getByA11yValue: notImplemented, - getAllByA11yValue: notImplemented, - queryByA11yValue: notImplemented, - queryAllByA11yValue: notImplemented, - findByA11yValue: notImplemented, - findAllByA11yValue: notImplemented, - getByAccessibilityValue: notImplemented, - getAllByAccessibilityValue: notImplemented, - queryByAccessibilityValue: notImplemented, - queryAllByAccessibilityValue: notImplemented, - findByAccessibilityValue: notImplemented, - findAllByAccessibilityValue: notImplemented, UNSAFE_getByProps: notImplemented, UNSAFE_getAllByProps: notImplemented, UNSAFE_queryByProps: notImplemented, diff --git a/src/shallow.ts b/src/shallow.ts deleted file mode 100644 index 90e030ffe..000000000 --- a/src/shallow.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import { ReactTestInstance } from 'react-test-renderer'; -import ShallowRenderer from 'react-test-renderer/shallow'; // eslint-disable-line import/no-extraneous-dependencies - -/** - * Renders test component shallowly using react-test-renderer/shallow - */ -export function shallowInternal(instance: ReactTestInstance | React.ReactElement): { - output: any; -} { - const renderer = new (ShallowRenderer as any)(); - - renderer.render(React.createElement(instance.type, instance.props)); - - return { - output: renderer.getRenderOutput(), - }; -} diff --git a/src/within.ts b/src/within.ts index 59db8bf1a..0e5873007 100644 --- a/src/within.ts +++ b/src/within.ts @@ -6,8 +6,6 @@ import { bindByPlaceholderTextQueries } from './queries/placeholder-text'; import { bindByLabelTextQueries } from './queries/label-text'; import { bindByHintTextQueries } from './queries/hint-text'; import { bindByRoleQueries } from './queries/role'; -import { bindByA11yStateQueries } from './queries/accessibility-state'; -import { bindByA11yValueQueries } from './queries/accessibility-value'; import { bindUnsafeByTypeQueries } from './queries/unsafe-type'; import { bindUnsafeByPropsQueries } from './queries/unsafe-props'; @@ -20,8 +18,6 @@ export function within(instance: ReactTestInstance) { ...bindByLabelTextQueries(instance), ...bindByHintTextQueries(instance), ...bindByRoleQueries(instance), - ...bindByA11yStateQueries(instance), - ...bindByA11yValueQueries(instance), ...bindUnsafeByTypeQueries(instance), ...bindUnsafeByPropsQueries(instance), }; diff --git a/typings/index.flow.js b/typings/index.flow.js index eb05d9f24..df25313d7 100644 --- a/typings/index.flow.js +++ b/typings/index.flow.js @@ -55,10 +55,10 @@ declare type A11yRole = declare type A11yState = {| disabled?: boolean, - selected?: boolean, - checked?: boolean | 'mixed', - busy?: boolean, - expanded?: boolean, + selected ?: boolean, + checked ?: boolean | 'mixed', + busy ?: boolean, + expanded ?: boolean, |}; declare type A11yValue = { @@ -258,38 +258,6 @@ interface A11yAPI { queryOptions?: ByRoleOptions, waitForOptions?: WaitForOptions ) => FindAllReturn; - - // State - getByA11yState: (matcher: A11yState, options?: CommonQueryOptions) => GetReturn; - getAllByA11yState: (matcher: A11yState, options?: CommonQueryOptions) => GetAllReturn; - queryByA11yState: (matcher: A11yState, options?: CommonQueryOptions) => QueryReturn; - queryAllByA11yState: (matcher: A11yState, options?: CommonQueryOptions) => QueryAllReturn; - findByA11yState: ( - matcher: A11yState, - queryOptions?: CommonQueryOptions, - waitForOptions?: WaitForOptions - ) => FindReturn; - findAllByA11yState: ( - matcher: A11yState, - queryOptions?: CommonQueryOptions, - waitForOptions?: WaitForOptions - ) => FindAllReturn; - - // Value - getByA11yValue: (matcher: A11yValue, options?: CommonQueryOptions) => GetReturn; - getAllByA11yValue: (matcher: A11yValue, options?: CommonQueryOptions) => GetAllReturn; - queryByA11yValue: (matcher: A11yValue, options?: CommonQueryOptions) => QueryReturn; - queryAllByA11yValue: (matcher: A11yValue, options?: CommonQueryOptions) => QueryAllReturn; - findByA11yValue: ( - matcher: A11yValue, - queryOptions?: CommonQueryOptions, - waitForOptions?: WaitForOptions - ) => FindReturn; - findAllByA11yValue: ( - matcher: A11yValue, - queryOptions?: CommonQueryOptions, - waitForOptions?: WaitForOptions - ) => FindAllReturn; } interface Thenable { @@ -308,7 +276,6 @@ type DebugOptions = { type Debug = { (options?: DebugOptions | string): void, - shallow: (message?: string) => void, }; type Queries = ByTextQueries & diff --git a/website/docs/12.x/docs/api/misc/async.mdx b/website/docs/12.x/docs/api/misc/async.mdx index 5ae3cf1c8..47b040629 100644 --- a/website/docs/12.x/docs/api/misc/async.mdx +++ b/website/docs/12.x/docs/api/misc/async.mdx @@ -53,12 +53,6 @@ Avoiding side effects in `expectation` callback can be partially enforced with t It is also recommended to have a [single assertion per each `waitFor`](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback) for more consistency and faster failing tests. If you want to make several assertions, then they should be in seperate `waitFor` calls. In many cases you won't actually need to wrap the second assertion in `waitFor` since the first one will do the waiting required for asynchronous change to happen. -### Using a React Native version < 0.71 with Jest fake timers - -:::caution -When using a version of React Native < 0.71 and modern fake timers (the default for `Jest` >= 27), `waitFor` won't work (it will always timeout even if `expectation()` doesn't throw) unless you use the custom [@testing-library/react-native preset](https://github.com/callstack/react-native-testing-library#custom-jest-preset). -::: - `waitFor` checks whether Jest fake timers are enabled and adapts its behavior in such case. The following snippet is a simplified version of how it behaves when fake timers are enabled: ```tsx @@ -96,10 +90,6 @@ await waitFor(() => { }, 10000); ``` -:::info -In order to properly use `waitFor` you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.61 (which comes with React >=16.9.0). -::: - :::note If you receive warnings related to `act()` function consult our [Undestanding Act](docs/advanced/understanding-act.md) function document. ::: @@ -129,10 +119,6 @@ This method expects that the element is initially present in the render tree and You can use any of `getBy`, `getAllBy`, `queryBy` and `queryAllBy` queries for `expectation` parameter. -:::info -In order to properly use `waitForElementToBeRemoved` you need at least React >=16.9.0 (featuring async `act`) or React Native >=0.61 (which comes with React >=16.9.0). -::: - :::note If you receive warnings related to `act()` function consult our [Undestanding Act](docs/advanced/understanding-act.md) function document. ::: diff --git a/website/docs/12.x/docs/api/queries.mdx b/website/docs/12.x/docs/api/queries.mdx index 85df7e0ac..232ffab5f 100644 --- a/website/docs/12.x/docs/api/queries.mdx +++ b/website/docs/12.x/docs/api/queries.mdx @@ -383,115 +383,6 @@ const element = screen.getByTestId('unique-id'); In the spirit of [the guiding principles](https://testing-library.com/docs/guiding-principles), it is recommended to use this only after the other queries don't work for your use case. Using `testID` attributes do not resemble how your software is used and should be avoided if possible. However, they are particularly useful for end-to-end testing on real devices, e.g. using Detox and it's an encouraged technique to use there. Learn more from the blog post ["Making your UI tests resilient to change"](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change). ::: -### `*ByA11yState`, `ByAccessibilityState` (deprecated) {#by-accessibility-state} - -:::caution -This query has been marked deprecated, as is typically too general to give meaningful results. Therefore, it's better to use one of following options: - -- [`*ByRole`](#by-role) query with relevant state options: `disabled`, `selected`, `checked`, `expanded` and `busy` -- use built-in Jest matchers to check the state of element found using some other query: - - enabled state: [`toBeEnabled()` / `toBeDisabled()`](docs/api/jest-matchers#tobeenabled) - - checked state: [`toBeChecked()` / `toBePartiallyChecked()`](docs/api/jest-matchers#tobechecked) - - selected state: [`toBeSelected()`](docs/api/jest-matchers#tobeselected) - - expanded state: [`toBeExpanded()` / `toBeCollapsed()`](docs/api/jest-matchers#tobeexpanded) - - busy state: [`toBeBusy()`](docs/api/jest-matchers#tobebusy) - -::: - -> getByA11yState, getAllByA11yState, queryByA11yState, queryAllByA11yState, findByA11yState, findAllByA11yState -> getByAccessibilityState, getAllByAccessibilityState, queryByAccessibilityState, queryAllByAccessibilityState, findByAccessibilityState, findAllByAccessibilityState - -```ts -getByA11yState( - state: { - disabled?: boolean, - selected?: boolean, - checked?: boolean | 'mixed', - busy?: boolean, - expanded?: boolean, - }, - options?: { - includeHiddenElements?: boolean; - }, -): ReactTestInstance; -``` - -Returns a `ReactTestInstance` with matching `accessibilityState` prop or ARIA state props: `aria-disabled`, `aria-selected`, `aria-checked`, `aria-busy`, and `aria-expanded`. - -```jsx -import { render, screen } from '@testing-library/react-native'; - -render(); -const element = screen.getByA11yState({ disabled: true }); -``` - -:::note - -#### Default state for: `disabled`, `selected`, and `busy` keys - -Passing `false` matcher value will match both elements with explicit `false` state value and without explicit state value. - -For instance, `getByA11yState({ disabled: false })` will match elements with following props: - -- `accessibilityState={{ disabled: false, ... }}` -- no `disabled` key under `accessibilityState` prop, e.g. `accessibilityState={{}}` -- no `accessibilityState` prop at all - -#### Default state for: `checked` and `expanded` keys - -Passing `false` matcher value will only match elements with explicit `false` state value. - -For instance, `getByA11yState({ checked: false })` will only match elements with: - -- `accessibilityState={{ checked: false, ... }}` - -but will not match elements with following props: - -- no `checked` key under `accessibilityState` prop, e.g. `accessibilityState={{}}` -- no `accessibilityState` prop at all - -The difference in handling default values is made to reflect observed accessibility behaviour on iOS and Android platforms. -::: - -### `*ByA11yValue`, `*ByAccessibilityValue` (deprecated) {#by-accessibility-value} - -:::caution -This query has been marked deprecated, as is typically too general to give meaningful results. Therefore, it's better to use one of following options: - -- [`toHaveAccessibilityValue()`](docs/api/jest-matchers#tohaveaccessibilityvalue) Jest matcher to check the state of element found using some other query -- [`*ByRole`](#by-role) query with `value` option - -::: - -> getByA11yValue, getAllByA11yValue, queryByA11yValue, queryAllByA11yValue, findByA11yValue, findAllByA11yValue -> getByAccessibilityValue, getAllByAccessibilityValue, queryByAccessibilityValue, queryAllByAccessibilityValue, findByAccessibilityValue, findAllByAccessibilityValue - -```ts -getByA11yValue( - value: { - min?: number; - max?: number; - now?: number; - text?: TextMatch; - }, - options?: { - includeHiddenElements?: boolean; - }, -): ReactTestInstance; -``` - -Returns a host element with matching accessibility value based on `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` & `accessibilityValue` props. Only value entires provided to the query will be used to match elements. Element might have additional accessibility value entries and still be matched. - -When querying by `text` entry a string or regex might be used. - -```jsx -import { render, screen } from '@testing-library/react-native'; - -render(); -const element = screen.getByA11yValue({ now: 25 }); -const element2 = screen.getByA11yValue({ text: /25/ }); -``` - ### Common options Usually query first argument can be a **string** or a **regex**. All queries take at least the [`hidden`](#hidden-option) option as an optionnal second argument and some queries accept more options which change string matching behaviour. See [TextMatch](#textmatch) for more info. diff --git a/website/docs/12.x/docs/migration/v13.mdx b/website/docs/12.x/docs/migration/v13.mdx new file mode 100644 index 000000000..bcf81c721 --- /dev/null +++ b/website/docs/12.x/docs/migration/v13.mdx @@ -0,0 +1,68 @@ +# Migration to 13.0 + +Migration to React Native Testing Library version 13 from version 12.x. + +# Breaking changes + +## Supported React and React Native versions + +This version supports only React 19 and corresponding React Native versions. If you use React 18 or 19, please use latest of v12 versions. + +[Note: at the moment there is no React Native for React 19, and React 19 is still in beta, so we use React 18.3 for the time being]. + +## Removed deprecated \*ByAccessibilityState queries + +This deprecated query has been removed as is typically too general to give meaningful results. Use one of the following options: + +- [`*ByRole`](#by-role) query with relevant state options: `disabled`, `selected`, `checked`, `expanded` and `busy` +- use built-in Jest matchers to check the state of element found using some other query: + - enabled state: [`toBeEnabled()` / `toBeDisabled()`](docs/api/jest-matchers#tobeenabled) + - checked state: [`toBeChecked()` / `toBePartiallyChecked()`](docs/api/jest-matchers#tobechecked) + - selected state: [`toBeSelected()`](docs/api/jest-matchers#tobeselected) + - expanded state: [`toBeExpanded()` / `toBeCollapsed()`](docs/api/jest-matchers#tobeexpanded) + - busy state: [`toBeBusy()`](docs/api/jest-matchers#tobebusy) + +```ts +// Replace this +const view = screen.getByAccessibilityState({ disabled: true }); + +// with this (getByRole query) +const view = screen.getByRole('', { disabled: true }); + +// or this (Jest matcher) +const view = screen.getBy*(...); // Find the element using any query: *ByRole, *ByText, *ByTestId +expect(view).toBeDisabled(); // Assert its accessibility state +``` + +## Removed deprecated \*ByAccessibilityValue queries + +This deprecated query has been removed as is typically too general to give meaningful results. Use one of the following options: + +- [`toHaveAccessibilityValue()`](docs/api/jest-matchers#tohaveaccessibilityvalue) Jest matcher to check the state of element found using some other query +- [`*ByRole`](#by-role) query with `value` option + +```ts +// Replace this +const view = screen.getByAccessibilityValue({ now: 50, min: 0, max: 50 }); + +// with this (getByRole query) +const view = screen.getByRole('', { value: { now: 50, min: 0, max: 50 } }); + +// or this (Jest matcher) +const view = screen.getBy*(...); // Find the element using any query: *ByRole, *ByText, *ByTestId +expect(view).toHaveAccessibilityValue({ now: 50, min: 0, max: 50 }); // Assert its accessibility value +``` + +## Removed `debug.shallow` + +For a time being we didn't support shallow rendering. Now we are removing the last remains of it: `debug.shallow()`. If you are interested in shallow rendering see [here](docs/migration/previous/v2#removed-global-shallow-function). + +# Other changes + +## Updated `flushMicroTasks` internal method + +This should not break any tests. + +## Full Changelog + +https://github.com/callstack/react-native-testing-library/compare/v12.5.2...v13.0.0 diff --git a/yarn.lock b/yarn.lock index 49026c23c..10a58669a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2603,10 +2603,10 @@ __metadata: strip-ansi: "npm:^6.0.1" typescript: "npm:^5.5.4" peerDependencies: - jest: ">=28.0.0" - react: ">=16.8.0" - react-native: ">=0.59" - react-test-renderer: ">=16.8.0" + jest: ">=29.0.0" + react: ">=18.2.0" + react-native: ">=0.71" + react-test-renderer: ">=18.2.0" peerDependenciesMeta: jest: optional: true