Skip to content

Commit 9488dcc

Browse files
feat: by role ts hints (#1596)
* feat: autocomplete * refactor: cleanup * chore: fix typos
1 parent 16b9e49 commit 9488dcc

22 files changed

+49
-42
lines changed

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"cSpell.words": ["Pressable", "RNTL"]
2+
"cSpell.words": ["Pressable", "RNTL", "Uncapitalize"]
33
}

src/fire-event.ts

+9-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { isHostElement } from './helpers/component-tree';
1111
import { isHostTextInput } from './helpers/host-component-names';
1212
import { isPointerEventEnabled } from './helpers/pointer-events';
1313
import { isTextInputEditable } from './helpers/text-input';
14+
import { StringWithAutocomplete } from './types';
1415

1516
type EventHandler = (...args: unknown[]) => unknown;
1617

@@ -105,20 +106,17 @@ function getEventHandlerName(eventName: string) {
105106
return `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
106107
}
107108

108-
// Allows any string but will provide autocomplete for type T
109-
type StringWithAutoComplete<T> = T | (string & Record<never, never>);
110-
111-
// String union type of keys of T that start with on, stripped from on
112-
type OnKeys<T> = keyof {
109+
// String union type of keys of T that start with on, stripped of 'on'
110+
type EventNameExtractor<T> = keyof {
113111
[K in keyof T as K extends `on${infer Rest}` ? Uncapitalize<Rest> : never]: T[K];
114112
};
115113

116-
type EventName = StringWithAutoComplete<
117-
| OnKeys<ViewProps>
118-
| OnKeys<TextProps>
119-
| OnKeys<TextInputProps>
120-
| OnKeys<PressableProps>
121-
| OnKeys<ScrollViewProps>
114+
type EventName = StringWithAutocomplete<
115+
| EventNameExtractor<ViewProps>
116+
| EventNameExtractor<TextProps>
117+
| EventNameExtractor<TextInputProps>
118+
| EventNameExtractor<PressableProps>
119+
| EventNameExtractor<ScrollViewProps>
122120
>;
123121

124122
function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) {

src/helpers/__tests__/accessiblity.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { View, Text, TextInput, Pressable, Switch, TouchableOpacity } from 'react-native';
33
import { render, isHiddenFromAccessibility, isInaccessible, screen } from '../..';
4-
import { isAccessibilityElement } from '../accessiblity';
4+
import { isAccessibilityElement } from '../accessibility';
55

66
describe('isHiddenFromAccessibility', () => {
77
test('returns false for accessible elements', () => {

src/helpers/accessiblity.ts src/helpers/accessibility.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const accessibilityStateKeys: (keyof AccessibilityState)[] = [
1616
'expanded',
1717
];
1818

19-
export const accessiblityValueKeys: (keyof AccessibilityValue)[] = ['min', 'max', 'now', 'text'];
19+
export const accessibilityValueKeys: (keyof AccessibilityValue)[] = ['min', 'max', 'now', 'text'];
2020

2121
export function isHiddenFromAccessibility(
2222
element: ReactTestInstance | null,

src/helpers/find-all.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { getConfig } from '../config';
3-
import { isHiddenFromAccessibility } from './accessiblity';
3+
import { isHiddenFromAccessibility } from './accessibility';
44
import { HostTestInstance, isHostElement } from './component-tree';
55

66
interface FindAllOptions {

src/helpers/matchers/match-accessibility-state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AccessibilityState } from 'react-native';
22
import { ReactTestInstance } from 'react-test-renderer';
3-
import { accessibilityStateKeys, getAccessibilityState } from '../accessiblity';
3+
import { accessibilityStateKeys, getAccessibilityState } from '../accessibility';
44

55
// This type is the same as AccessibilityState from `react-native` package
66
// It is re-declared here due to issues with migration from `@types/react-native` to

src/helpers/matchers/match-accessibility-value.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ReactTestInstance } from 'react-test-renderer';
2-
import { getAccessibilityValue } from '../accessiblity';
2+
import { getAccessibilityValue } from '../accessibility';
33
import { TextMatch } from '../../matches';
44
import { matchStringProp } from './match-string-prop';
55

src/helpers/matchers/match-label-text.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { matches, TextMatch, TextMatchOptions } from '../../matches';
3-
import { getAccessibilityLabel, getAccessibilityLabelledBy } from '../accessiblity';
3+
import { getAccessibilityLabel, getAccessibilityLabelledBy } from '../accessibility';
44
import { findAll } from '../find-all';
55
import { matchTextContent } from './match-text-content';
66

src/matchers/to-be-busy.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
3-
import { isElementBusy } from '../helpers/accessiblity';
3+
import { isElementBusy } from '../helpers/accessibility';
44
import { checkHostElement, formatElement } from './utils';
55

66
export function toBeBusy(this: jest.MatcherContext, element: ReactTestInstance) {

src/matchers/to-be-checked.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getAccessibilityCheckedState,
55
getAccessibilityRole,
66
isAccessibilityElement,
7-
} from '../helpers/accessiblity';
7+
} from '../helpers/accessibility';
88
import { ErrorWithStack } from '../helpers/errors';
99
import { checkHostElement, formatElement } from './utils';
1010

src/matchers/to-be-collapsed.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
3-
import { isElementCollapsed } from '../helpers/accessiblity';
3+
import { isElementCollapsed } from '../helpers/accessibility';
44
import { checkHostElement, formatElement } from './utils';
55

66
export function toBeCollapsed(this: jest.MatcherContext, element: ReactTestInstance) {

src/matchers/to-be-expanded.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
3-
import { isElementExpanded } from '../helpers/accessiblity';
3+
import { isElementExpanded } from '../helpers/accessibility';
44
import { checkHostElement, formatElement } from './utils';
55

66
export function toBeExpanded(this: jest.MatcherContext, element: ReactTestInstance) {

src/matchers/to-be-partially-checked.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getAccessibilityCheckedState,
55
getAccessibilityRole,
66
isAccessibilityElement,
7-
} from '../helpers/accessiblity';
7+
} from '../helpers/accessibility';
88
import { ErrorWithStack } from '../helpers/errors';
99
import { checkHostElement, formatElement } from './utils';
1010

src/matchers/to-be-selected.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
3-
import { isElementSelected } from '../helpers/accessiblity';
3+
import { isElementSelected } from '../helpers/accessibility';
44
import { checkHostElement, formatElement } from './utils';
55

66
export function toBeSelected(this: jest.MatcherContext, element: ReactTestInstance) {

src/matchers/to-be-visible.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
33
import { StyleSheet } from 'react-native';
4-
import { isHiddenFromAccessibility } from '../helpers/accessiblity';
4+
import { isHiddenFromAccessibility } from '../helpers/accessibility';
55
import { getHostParent } from '../helpers/component-tree';
66
import { isHostModal } from '../helpers/host-component-names';
77
import { checkHostElement, formatElement } from './utils';

src/matchers/to-have-accessibility-value.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint, stringify } from 'jest-matcher-utils';
3-
import { getAccessibilityValue } from '../helpers/accessiblity';
3+
import { getAccessibilityValue } from '../helpers/accessibility';
44
import {
55
AccessibilityValueMatcher,
66
matchAccessibilityValue,

src/matchers/to-have-accessible-name.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22
import { matcherHint } from 'jest-matcher-utils';
3-
import { getAccessibleName } from '../helpers/accessiblity';
3+
import { getAccessibleName } from '../helpers/accessibility';
44
import { TextMatch, TextMatchOptions, matches } from '../matches';
55
import { checkHostElement, formatMessage } from './utils';
66

src/pure.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export { default as waitForElementToBeRemoved } from './wait-for-element-to-be-r
77
export { within, getQueriesForElement } from './within';
88

99
export { configure, resetToDefaults } from './config';
10-
export { isHiddenFromAccessibility, isInaccessible } from './helpers/accessiblity';
10+
export { isHiddenFromAccessibility, isInaccessible } from './helpers/accessibility';
1111
export { getDefaultNormalizer } from './matches';
1212
export { renderHook } from './render-hook';
1313
export { screen } from './screen';

src/queries/accessibility-state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
2-
import { accessibilityStateKeys } from '../helpers/accessiblity';
2+
import { accessibilityStateKeys } from '../helpers/accessibility';
33
import { deprecateQueries } from '../helpers/deprecation';
44
import { findAll } from '../helpers/find-all';
55
import {

src/queries/accessibility-value.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
2-
import { accessiblityValueKeys } from '../helpers/accessiblity';
2+
import { accessibilityValueKeys } from '../helpers/accessibility';
33
import { deprecateQueries } from '../helpers/deprecation';
44
import { findAll } from '../helpers/find-all';
55
import {
@@ -27,7 +27,7 @@ const queryAllByA11yValue = (
2727
const formatQueryParams = (matcher: AccessibilityValueMatcher) => {
2828
const params: string[] = [];
2929

30-
accessiblityValueKeys.forEach((valueKey) => {
30+
accessibilityValueKeys.forEach((valueKey) => {
3131
if (matcher[valueKey] !== undefined) {
3232
params.push(`${valueKey} value: ${matcher[valueKey]}`);
3333
}

src/queries/role.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
2+
import type { AccessibilityRole, Role } from 'react-native';
23
import {
34
accessibilityStateKeys,
4-
accessiblityValueKeys,
5+
accessibilityValueKeys,
56
getAccessibilityRole,
67
isAccessibilityElement,
7-
} from '../helpers/accessiblity';
8+
} from '../helpers/accessibility';
89
import { findAll } from '../helpers/find-all';
910
import {
1011
AccessibilityStateMatcher,
@@ -16,6 +17,7 @@ import {
1617
} from '../helpers/matchers/match-accessibility-value';
1718
import { matchStringProp } from '../helpers/matchers/match-string-prop';
1819
import type { TextMatch } from '../matches';
20+
import { StringWithAutocomplete } from '../types';
1921
import { getQueriesForElement } from '../within';
2022
import { makeQueries } from './make-queries';
2123
import type {
@@ -28,7 +30,9 @@ import type {
2830
} from './make-queries';
2931
import { CommonQueryOptions } from './options';
3032

31-
type ByRoleOptions = CommonQueryOptions &
33+
export type ByRoleMatcher = StringWithAutocomplete<AccessibilityRole | Role> | RegExp;
34+
35+
export type ByRoleOptions = CommonQueryOptions &
3236
AccessibilityStateMatcher & {
3337
name?: TextMatch;
3438
value?: AccessibilityValueMatcher;
@@ -52,7 +56,9 @@ const matchAccessibilityValueIfNeeded = (
5256
return value != null ? matchAccessibilityValue(node, value) : true;
5357
};
5458

55-
const queryAllByRole = (instance: ReactTestInstance): QueryAllByQuery<TextMatch, ByRoleOptions> =>
59+
const queryAllByRole = (
60+
instance: ReactTestInstance,
61+
): QueryAllByQuery<ByRoleMatcher, ByRoleOptions> =>
5662
function queryAllByRoleFn(role, options) {
5763
return findAll(
5864
instance,
@@ -80,7 +86,7 @@ const formatQueryParams = (role: TextMatch, options: ByRoleOptions = {}) => {
8086
}
8187
});
8288

83-
accessiblityValueKeys.forEach((valueKey) => {
89+
accessibilityValueKeys.forEach((valueKey) => {
8490
if (options?.value?.[valueKey] !== undefined) {
8591
params.push(`${valueKey} value: ${options?.value?.[valueKey]}`);
8692
}
@@ -102,12 +108,12 @@ const { getBy, getAllBy, queryBy, queryAllBy, findBy, findAllBy } = makeQueries(
102108
);
103109

104110
export type ByRoleQueries = {
105-
getByRole: GetByQuery<TextMatch, ByRoleOptions>;
106-
getAllByRole: GetAllByQuery<TextMatch, ByRoleOptions>;
107-
queryByRole: QueryByQuery<TextMatch, ByRoleOptions>;
108-
queryAllByRole: QueryAllByQuery<TextMatch, ByRoleOptions>;
109-
findByRole: FindByQuery<TextMatch, ByRoleOptions>;
110-
findAllByRole: FindAllByQuery<TextMatch, ByRoleOptions>;
111+
getByRole: GetByQuery<ByRoleMatcher, ByRoleOptions>;
112+
getAllByRole: GetAllByQuery<ByRoleMatcher, ByRoleOptions>;
113+
queryByRole: QueryByQuery<ByRoleMatcher, ByRoleOptions>;
114+
queryAllByRole: QueryAllByQuery<ByRoleMatcher, ByRoleOptions>;
115+
findByRole: FindByQuery<ByRoleMatcher, ByRoleOptions>;
116+
findAllByRole: FindAllByQuery<ByRoleMatcher, ByRoleOptions>;
111117
};
112118

113119
export const bindByRoleQueries = (instance: ReactTestInstance): ByRoleQueries => ({

src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// TS autocomplete trick
2+
// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939
3+
export type StringWithAutocomplete<T> = T | (string & {});

0 commit comments

Comments
 (0)