Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: deprecate some a11y queries #1226

Merged
merged 11 commits into from
Jan 28, 2023
10 changes: 10 additions & 0 deletions src/helpers/__tests__/query-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getQueryPrefix } from '../query-name';

test('getQueryPrefix should return correct prefix', () => {
expect(getQueryPrefix('getByRole')).toBe('get');
expect(getQueryPrefix('getAllByText')).toEqual('getAll');
expect(getQueryPrefix('queryByTestId')).toEqual('query');
expect(getQueryPrefix('queryAllByPlaceholderText')).toEqual('queryAll');
expect(getQueryPrefix('findByHintText')).toEqual('find');
expect(getQueryPrefix('findAllByDisplayValue')).toEqual('findAll');
});
36 changes: 36 additions & 0 deletions src/helpers/deprecation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { getQueryPrefix } from './query-name';

export function deprecateAllQueries<Queries extends Record<string, any>>(
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
queriesObject: Queries,
recommendation: string
): Queries {
const result = {} as Queries;
Object.keys(queriesObject).forEach((queryName) => {
const queryFn = queriesObject[queryName];
// @ts-expect-error: generic typing is hard
result[queryName] = deprecateQuery(queryFn, queryName, recommendation);
});

return result;
}

export function deprecateQuery<QueryFn extends (...args: any) => any>(
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
queryFn: QueryFn,
queryName: string,
recommendation: string
): QueryFn {
const formattedRecommendation = recommendation.replace(
/{queryPrefix}/g,
getQueryPrefix(queryName)
);

// @ts-expect-error: generic typing is hard
const wrapper: QueryFn = (...args: any) => {
const errorMessage = `${queryName}(...) is deprecated.\n\n${formattedRecommendation}`;
// eslint-disable-next-line no-console
console.warn(errorMessage);
return queryFn(...args);
};

return wrapper;
}
4 changes: 4 additions & 0 deletions src/helpers/query-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function getQueryPrefix(queryName: string) {
const parts = queryName.split('By');
return parts[0];
}
101 changes: 101 additions & 0 deletions src/queries/__tests__/a11yState.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/* eslint-disable no-console */
import * as React from 'react';
import { View, Text, Pressable, TouchableOpacity } from 'react-native';
import { render } from '../..';

type ConsoleLogMock = jest.Mock<typeof console.log>;

beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {});
});

const TEXT_LABEL = 'cool text';

const Typography = ({ children, ...rest }: any) => {
Expand Down Expand Up @@ -251,3 +258,97 @@ test('byA11yState queries support hidden option', () => {
`"Unable to find an element with expanded state: false"`
);
});

test('*ByA11yState deprecation warnings', () => {
const mockCalls = (console.warn as ConsoleLogMock).mock.calls;
const view = render(<View accessibilityState={{ disabled: true }} />);

view.getByA11yState({ disabled: true });
expect(mockCalls[0][0]).toMatchInlineSnapshot(`
"getByA11yState(...) is deprecated.

Use getByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
`);

view.getAllByA11yState({ disabled: true });
expect(mockCalls[1][0]).toMatchInlineSnapshot(`
"getAllByA11yState(...) is deprecated.

Use getAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.queryByA11yState({ disabled: true });
expect(mockCalls[2][0]).toMatchInlineSnapshot(`
"queryByA11yState(...) is deprecated.

Use queryByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.queryAllByA11yState({ disabled: true });
expect(mockCalls[3][0]).toMatchInlineSnapshot(`
"queryAllByA11yState(...) is deprecated.

Use queryAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.findByA11yState({ disabled: true });
expect(mockCalls[4][0]).toMatchInlineSnapshot(`
"findByA11yState(...) is deprecated.

Use findByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.findAllByA11yState({ disabled: true });
expect(mockCalls[5][0]).toMatchInlineSnapshot(`
"findAllByA11yState(...) is deprecated.

Use findAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);
});

test('*ByAccessibilityState deprecation warnings', () => {
const mockCalls = (console.warn as ConsoleLogMock).mock.calls;
const view = render(<View accessibilityState={{ disabled: true }} />);

view.getByAccessibilityState({ disabled: true });
expect(mockCalls[0][0]).toMatchInlineSnapshot(`
"getByAccessibilityState(...) is deprecated.

Use getByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.getAllByAccessibilityState({ disabled: true });
expect(mockCalls[1][0]).toMatchInlineSnapshot(`
"getAllByAccessibilityState(...) is deprecated.

Use getAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.queryByAccessibilityState({ disabled: true });
expect(mockCalls[2][0]).toMatchInlineSnapshot(`
"queryByAccessibilityState(...) is deprecated.

Use queryByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.queryAllByAccessibilityState({ disabled: true });
expect(mockCalls[3][0]).toMatchInlineSnapshot(`
"queryAllByAccessibilityState(...) is deprecated.

Use queryAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.findByAccessibilityState({ disabled: true });
expect(mockCalls[4][0]).toMatchInlineSnapshot(`
"findByAccessibilityState(...) is deprecated.

Use findByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);

view.findAllByAccessibilityState({ disabled: true });
expect(mockCalls[5][0]).toMatchInlineSnapshot(`
"findAllByAccessibilityState(...) is deprecated.

Use findAllByRole(role, { disabled, selected, checked, busy, expanded }) query or expect(...).toHaveAccessibilityState(...) matcher instead."
`);
});
101 changes: 101 additions & 0 deletions src/queries/__tests__/a11yValue.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/* eslint-disable no-console */
import * as React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { render } from '../..';

type ConsoleLogMock = jest.Mock<typeof console.log>;

beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {});
});

const TEXT_LABEL = 'cool text';

const Typography = ({ children, ...rest }: any) => {
Expand Down Expand Up @@ -130,3 +137,97 @@ test('byA11yValue error messages', () => {
`"Unable to find an element with min value: 1, max value: 2, now value: 3, text value: /foo/i"`
);
});

test('*ByA11yValue deprecation warnings', () => {
const mockCalls = (console.warn as ConsoleLogMock).mock.calls;
const view = render(<View accessibilityValue={{ min: 10 }} />);

view.getByA11yValue({ min: 10 });
expect(mockCalls[0][0]).toMatchInlineSnapshot(`
"getByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or getByRole(role, { value: ... }) query instead."
`);

view.getAllByA11yValue({ min: 10 });
expect(mockCalls[1][0]).toMatchInlineSnapshot(`
"getAllByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or getAllByRole(role, { value: ... }) query instead."
`);

view.queryByA11yValue({ min: 10 });
expect(mockCalls[2][0]).toMatchInlineSnapshot(`
"queryByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or queryByRole(role, { value: ... }) query instead."
`);

view.queryAllByA11yValue({ min: 10 });
expect(mockCalls[3][0]).toMatchInlineSnapshot(`
"queryAllByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or queryAllByRole(role, { value: ... }) query instead."
`);

view.findByA11yValue({ min: 10 });
expect(mockCalls[4][0]).toMatchInlineSnapshot(`
"findByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or findByRole(role, { value: ... }) query instead."
`);

view.findAllByA11yValue({ min: 10 });
expect(mockCalls[5][0]).toMatchInlineSnapshot(`
"findAllByA11yValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or findAllByRole(role, { value: ... }) query instead."
`);
});

test('*ByAccessibilityValue deprecation warnings', () => {
const mockCalls = (console.warn as ConsoleLogMock).mock.calls;
const view = render(<View accessibilityValue={{ min: 10 }} />);

view.getByAccessibilityValue({ min: 10 });
expect(mockCalls[0][0]).toMatchInlineSnapshot(`
"getByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or getByRole(role, { value: ... }) query instead."
`);

view.getAllByAccessibilityValue({ min: 10 });
expect(mockCalls[1][0]).toMatchInlineSnapshot(`
"getAllByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or getAllByRole(role, { value: ... }) query instead."
`);

view.queryByAccessibilityValue({ min: 10 });
expect(mockCalls[2][0]).toMatchInlineSnapshot(`
"queryByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or queryByRole(role, { value: ... }) query instead."
`);

view.queryAllByAccessibilityValue({ min: 10 });
expect(mockCalls[3][0]).toMatchInlineSnapshot(`
"queryAllByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or queryAllByRole(role, { value: ... }) query instead."
`);

view.findByAccessibilityValue({ min: 10 });
expect(mockCalls[4][0]).toMatchInlineSnapshot(`
"findByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or findByRole(role, { value: ... }) query instead."
`);

view.findAllByAccessibilityValue({ min: 10 });
expect(mockCalls[5][0]).toMatchInlineSnapshot(`
"findAllByAccessibilityValue(...) is deprecated.

Use expect(...).toHaveAccessibilityValue(...) matcher or findAllByRole(role, { value: ... }) query instead."
`);
});
31 changes: 18 additions & 13 deletions src/queries/a11yState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ReactTestInstance } from 'react-test-renderer';
import { AccessibilityState } from 'react-native';
import { accessibilityStateKeys } from '../helpers/accessiblity';
import { deprecateAllQueries } from '../helpers/deprecation';
import { findAll } from '../helpers/findAll';
import { matchAccessibilityState } from '../helpers/matchers/accessibilityState';
import { makeQueries } from './makeQueries';
Expand Down Expand Up @@ -92,18 +93,22 @@ export const bindByA11yStateQueries = (
const findAllByA11yState = findAllBy(instance);

return {
getByA11yState,
getAllByA11yState,
queryByA11yState,
queryAllByA11yState,
findByA11yState,
findAllByA11yState,

getByAccessibilityState: getByA11yState,
getAllByAccessibilityState: getAllByA11yState,
queryByAccessibilityState: queryByA11yState,
queryAllByAccessibilityState: queryAllByA11yState,
findByAccessibilityState: findByA11yState,
findAllByAccessibilityState: findAllByA11yState,
...deprecateAllQueries(
{
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 expect(...).toHaveAccessibilityState(...) matcher instead.'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great refactor !

mdjastrzebski marked this conversation as resolved.
Show resolved Hide resolved
),
};
};
31 changes: 18 additions & 13 deletions src/queries/a11yValue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ReactTestInstance } from 'react-test-renderer';
import { accessiblityValueKeys } from '../helpers/accessiblity';
import { deprecateAllQueries } from '../helpers/deprecation';
import { findAll } from '../helpers/findAll';
import {
AccessibilityValueMatcher,
Expand Down Expand Up @@ -109,18 +110,22 @@ export const bindByA11yValueQueries = (
const findAllByA11yValue = findAllBy(instance);

return {
getByA11yValue,
getAllByA11yValue,
queryByA11yValue,
queryAllByA11yValue,
findByA11yValue,
findAllByA11yValue,

getByAccessibilityValue: getByA11yValue,
getAllByAccessibilityValue: getAllByA11yValue,
queryByAccessibilityValue: queryByA11yValue,
queryAllByAccessibilityValue: queryAllByA11yValue,
findByAccessibilityValue: findByA11yValue,
findAllByAccessibilityValue: findAllByA11yValue,
...deprecateAllQueries(
{
getByA11yValue,
getAllByA11yValue,
queryByA11yValue,
queryAllByA11yValue,
findByA11yValue,
findAllByA11yValue,
getByAccessibilityValue: getByA11yValue,
getAllByAccessibilityValue: getAllByA11yValue,
queryByAccessibilityValue: queryByA11yValue,
queryAllByAccessibilityValue: queryAllByA11yValue,
findByAccessibilityValue: findByA11yValue,
findAllByAccessibilityValue: findAllByA11yValue,
},
'Use expect(...).toHaveAccessibilityValue(...) matcher or {queryPrefix}ByRole(role, { value: ... }) query instead.'
),
};
};
4 changes: 2 additions & 2 deletions website/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ Use cases for scoped queries include:

## `query` APIs

Each of the get APIs listed in the render section above have a complimentary query API. The get APIs will throw errors if a proper node cannot be found. This is normally the desired effect. However, if you want to make an assertion that an element is not present in the hierarchy, then you can use the query API instead:
Each of the `getBy` APIs listed in the render section above have a complimentary `queryBy` API. The `getBy` APIs will throw errors if a proper node cannot be found. This is normally the desired effect. However, if you want to make an assertion that an element is not present in the hierarchy, then you can use the `queryBy` API instead:

```jsx
import { render, screen } from '@testing-library/react-native';
Expand All @@ -697,7 +697,7 @@ expect(submitButton).not.toBeOnTheScreen(); // it doesn't exist

## `queryAll` APIs

Each of the query APIs have a corresponding queryAll version that always returns an Array of matching nodes. getAll is the same but throws when the array has a length of 0.
Each of the query APIs have a corresponding `queryAll` version that always returns an array of matching nodes. `getAll` is the same but throws when the array has a length of 0.

```jsx
import { render } from '@testing-library/react-native';
Expand Down
Loading