diff --git a/.vscode/settings.json b/.vscode/settings.json index d2fc91bb1..0e9990b8a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "cSpell.words": ["Pressable", "RNTL"] + "cSpell.words": ["Pressable", "RNTL", "Uncapitalize"] } diff --git a/src/fire-event.ts b/src/fire-event.ts index c2be5a6c5..6859857ff 100644 --- a/src/fire-event.ts +++ b/src/fire-event.ts @@ -11,6 +11,7 @@ import { isHostElement } from './helpers/component-tree'; import { isHostTextInput } from './helpers/host-component-names'; import { isPointerEventEnabled } from './helpers/pointer-events'; import { isTextInputEditable } from './helpers/text-input'; +import { StringWithAutocomplete } from './types'; type EventHandler = (...args: unknown[]) => unknown; @@ -105,20 +106,17 @@ function getEventHandlerName(eventName: string) { return `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`; } -// Allows any string but will provide autocomplete for type T -type StringWithAutoComplete = T | (string & Record); - -// String union type of keys of T that start with on, stripped from on -type OnKeys = keyof { +// String union type of keys of T that start with on, stripped of 'on' +type EventNameExtractor = keyof { [K in keyof T as K extends `on${infer Rest}` ? Uncapitalize : never]: T[K]; }; -type EventName = StringWithAutoComplete< - | OnKeys - | OnKeys - | OnKeys - | OnKeys - | OnKeys +type EventName = StringWithAutocomplete< + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor >; function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) { diff --git a/src/helpers/__tests__/accessiblity.test.tsx b/src/helpers/__tests__/accessiblity.test.tsx index b692b9ac7..cbfd021c4 100644 --- a/src/helpers/__tests__/accessiblity.test.tsx +++ b/src/helpers/__tests__/accessiblity.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { View, Text, TextInput, Pressable, Switch, TouchableOpacity } from 'react-native'; import { render, isHiddenFromAccessibility, isInaccessible, screen } from '../..'; -import { isAccessibilityElement } from '../accessiblity'; +import { isAccessibilityElement } from '../accessibility'; describe('isHiddenFromAccessibility', () => { test('returns false for accessible elements', () => { diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessibility.ts similarity index 98% rename from src/helpers/accessiblity.ts rename to src/helpers/accessibility.ts index 4058ea934..5e031761f 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessibility.ts @@ -16,7 +16,7 @@ export const accessibilityStateKeys: (keyof AccessibilityState)[] = [ 'expanded', ]; -export const accessiblityValueKeys: (keyof AccessibilityValue)[] = ['min', 'max', 'now', 'text']; +export const accessibilityValueKeys: (keyof AccessibilityValue)[] = ['min', 'max', 'now', 'text']; export function isHiddenFromAccessibility( element: ReactTestInstance | null, diff --git a/src/helpers/find-all.ts b/src/helpers/find-all.ts index ce7cb90f7..ffd62f936 100644 --- a/src/helpers/find-all.ts +++ b/src/helpers/find-all.ts @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { getConfig } from '../config'; -import { isHiddenFromAccessibility } from './accessiblity'; +import { isHiddenFromAccessibility } from './accessibility'; import { HostTestInstance, isHostElement } from './component-tree'; interface FindAllOptions { diff --git a/src/helpers/matchers/match-accessibility-state.ts b/src/helpers/matchers/match-accessibility-state.ts index 43968c8ce..17ba1d305 100644 --- a/src/helpers/matchers/match-accessibility-state.ts +++ b/src/helpers/matchers/match-accessibility-state.ts @@ -1,6 +1,6 @@ import { AccessibilityState } from 'react-native'; import { ReactTestInstance } from 'react-test-renderer'; -import { accessibilityStateKeys, getAccessibilityState } from '../accessiblity'; +import { accessibilityStateKeys, getAccessibilityState } from '../accessibility'; // This type is the same as AccessibilityState from `react-native` package // It is re-declared here due to issues with migration from `@types/react-native` to diff --git a/src/helpers/matchers/match-accessibility-value.ts b/src/helpers/matchers/match-accessibility-value.ts index 3fecfd441..bd93b63df 100644 --- a/src/helpers/matchers/match-accessibility-value.ts +++ b/src/helpers/matchers/match-accessibility-value.ts @@ -1,5 +1,5 @@ import { ReactTestInstance } from 'react-test-renderer'; -import { getAccessibilityValue } from '../accessiblity'; +import { getAccessibilityValue } from '../accessibility'; import { TextMatch } from '../../matches'; import { matchStringProp } from './match-string-prop'; diff --git a/src/helpers/matchers/match-label-text.ts b/src/helpers/matchers/match-label-text.ts index 94d6fa01c..aca584afd 100644 --- a/src/helpers/matchers/match-label-text.ts +++ b/src/helpers/matchers/match-label-text.ts @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { matches, TextMatch, TextMatchOptions } from '../../matches'; -import { getAccessibilityLabel, getAccessibilityLabelledBy } from '../accessiblity'; +import { getAccessibilityLabel, getAccessibilityLabelledBy } from '../accessibility'; import { findAll } from '../find-all'; import { matchTextContent } from './match-text-content'; diff --git a/src/matchers/to-be-busy.tsx b/src/matchers/to-be-busy.tsx index 06e2fe5eb..6bc01f5f5 100644 --- a/src/matchers/to-be-busy.tsx +++ b/src/matchers/to-be-busy.tsx @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; -import { isElementBusy } from '../helpers/accessiblity'; +import { isElementBusy } from '../helpers/accessibility'; import { checkHostElement, formatElement } from './utils'; export function toBeBusy(this: jest.MatcherContext, element: ReactTestInstance) { diff --git a/src/matchers/to-be-checked.tsx b/src/matchers/to-be-checked.tsx index 2d9ca66d8..ddb0c96dd 100644 --- a/src/matchers/to-be-checked.tsx +++ b/src/matchers/to-be-checked.tsx @@ -4,7 +4,7 @@ import { getAccessibilityCheckedState, getAccessibilityRole, isAccessibilityElement, -} from '../helpers/accessiblity'; +} from '../helpers/accessibility'; import { ErrorWithStack } from '../helpers/errors'; import { checkHostElement, formatElement } from './utils'; diff --git a/src/matchers/to-be-collapsed.tsx b/src/matchers/to-be-collapsed.tsx index 9fdd7786b..251fa8b9e 100644 --- a/src/matchers/to-be-collapsed.tsx +++ b/src/matchers/to-be-collapsed.tsx @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; -import { isElementCollapsed } from '../helpers/accessiblity'; +import { isElementCollapsed } from '../helpers/accessibility'; import { checkHostElement, formatElement } from './utils'; export function toBeCollapsed(this: jest.MatcherContext, element: ReactTestInstance) { diff --git a/src/matchers/to-be-expanded.tsx b/src/matchers/to-be-expanded.tsx index 0152bf911..2393a7531 100644 --- a/src/matchers/to-be-expanded.tsx +++ b/src/matchers/to-be-expanded.tsx @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; -import { isElementExpanded } from '../helpers/accessiblity'; +import { isElementExpanded } from '../helpers/accessibility'; import { checkHostElement, formatElement } from './utils'; export function toBeExpanded(this: jest.MatcherContext, element: ReactTestInstance) { diff --git a/src/matchers/to-be-partially-checked.tsx b/src/matchers/to-be-partially-checked.tsx index 530568d97..3e0c97172 100644 --- a/src/matchers/to-be-partially-checked.tsx +++ b/src/matchers/to-be-partially-checked.tsx @@ -4,7 +4,7 @@ import { getAccessibilityCheckedState, getAccessibilityRole, isAccessibilityElement, -} from '../helpers/accessiblity'; +} from '../helpers/accessibility'; import { ErrorWithStack } from '../helpers/errors'; import { checkHostElement, formatElement } from './utils'; diff --git a/src/matchers/to-be-selected.ts b/src/matchers/to-be-selected.ts index 5f265b855..a73e0a42a 100644 --- a/src/matchers/to-be-selected.ts +++ b/src/matchers/to-be-selected.ts @@ -1,6 +1,6 @@ import { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; -import { isElementSelected } from '../helpers/accessiblity'; +import { isElementSelected } from '../helpers/accessibility'; import { checkHostElement, formatElement } from './utils'; export function toBeSelected(this: jest.MatcherContext, element: ReactTestInstance) { diff --git a/src/matchers/to-be-visible.tsx b/src/matchers/to-be-visible.tsx index b19bc6023..5d992be99 100644 --- a/src/matchers/to-be-visible.tsx +++ b/src/matchers/to-be-visible.tsx @@ -1,7 +1,7 @@ import type { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; import { StyleSheet } from 'react-native'; -import { isHiddenFromAccessibility } from '../helpers/accessiblity'; +import { isHiddenFromAccessibility } from '../helpers/accessibility'; import { getHostParent } from '../helpers/component-tree'; import { isHostModal } from '../helpers/host-component-names'; import { checkHostElement, formatElement } from './utils'; diff --git a/src/matchers/to-have-accessibility-value.tsx b/src/matchers/to-have-accessibility-value.tsx index f4cfbb004..4a9a2badf 100644 --- a/src/matchers/to-have-accessibility-value.tsx +++ b/src/matchers/to-have-accessibility-value.tsx @@ -1,6 +1,6 @@ import type { ReactTestInstance } from 'react-test-renderer'; import { matcherHint, stringify } from 'jest-matcher-utils'; -import { getAccessibilityValue } from '../helpers/accessiblity'; +import { getAccessibilityValue } from '../helpers/accessibility'; import { AccessibilityValueMatcher, matchAccessibilityValue, diff --git a/src/matchers/to-have-accessible-name.tsx b/src/matchers/to-have-accessible-name.tsx index 426bb6182..3a38382d2 100644 --- a/src/matchers/to-have-accessible-name.tsx +++ b/src/matchers/to-have-accessible-name.tsx @@ -1,6 +1,6 @@ import type { ReactTestInstance } from 'react-test-renderer'; import { matcherHint } from 'jest-matcher-utils'; -import { getAccessibleName } from '../helpers/accessiblity'; +import { getAccessibleName } from '../helpers/accessibility'; import { TextMatch, TextMatchOptions, matches } from '../matches'; import { checkHostElement, formatMessage } from './utils'; diff --git a/src/pure.ts b/src/pure.ts index b3690fd9c..f4aa4f7a0 100644 --- a/src/pure.ts +++ b/src/pure.ts @@ -7,7 +7,7 @@ export { default as waitForElementToBeRemoved } from './wait-for-element-to-be-r export { within, getQueriesForElement } from './within'; export { configure, resetToDefaults } from './config'; -export { isHiddenFromAccessibility, isInaccessible } from './helpers/accessiblity'; +export { isHiddenFromAccessibility, isInaccessible } from './helpers/accessibility'; export { getDefaultNormalizer } from './matches'; export { renderHook } from './render-hook'; export { screen } from './screen'; diff --git a/src/queries/accessibility-state.ts b/src/queries/accessibility-state.ts index c5aa8fdfe..b38df4a7a 100644 --- a/src/queries/accessibility-state.ts +++ b/src/queries/accessibility-state.ts @@ -1,5 +1,5 @@ import type { ReactTestInstance } from 'react-test-renderer'; -import { accessibilityStateKeys } from '../helpers/accessiblity'; +import { accessibilityStateKeys } from '../helpers/accessibility'; import { deprecateQueries } from '../helpers/deprecation'; import { findAll } from '../helpers/find-all'; import { diff --git a/src/queries/accessibility-value.ts b/src/queries/accessibility-value.ts index 246ab1167..25d4d5361 100644 --- a/src/queries/accessibility-value.ts +++ b/src/queries/accessibility-value.ts @@ -1,5 +1,5 @@ import type { ReactTestInstance } from 'react-test-renderer'; -import { accessiblityValueKeys } from '../helpers/accessiblity'; +import { accessibilityValueKeys } from '../helpers/accessibility'; import { deprecateQueries } from '../helpers/deprecation'; import { findAll } from '../helpers/find-all'; import { @@ -27,7 +27,7 @@ const queryAllByA11yValue = ( const formatQueryParams = (matcher: AccessibilityValueMatcher) => { const params: string[] = []; - accessiblityValueKeys.forEach((valueKey) => { + accessibilityValueKeys.forEach((valueKey) => { if (matcher[valueKey] !== undefined) { params.push(`${valueKey} value: ${matcher[valueKey]}`); } diff --git a/src/queries/role.ts b/src/queries/role.ts index 55b4f7a3d..23eccc9f9 100644 --- a/src/queries/role.ts +++ b/src/queries/role.ts @@ -1,10 +1,11 @@ import type { ReactTestInstance } from 'react-test-renderer'; +import type { AccessibilityRole, Role } from 'react-native'; import { accessibilityStateKeys, - accessiblityValueKeys, + accessibilityValueKeys, getAccessibilityRole, isAccessibilityElement, -} from '../helpers/accessiblity'; +} from '../helpers/accessibility'; import { findAll } from '../helpers/find-all'; import { AccessibilityStateMatcher, @@ -16,6 +17,7 @@ import { } from '../helpers/matchers/match-accessibility-value'; import { matchStringProp } from '../helpers/matchers/match-string-prop'; import type { TextMatch } from '../matches'; +import { StringWithAutocomplete } from '../types'; import { getQueriesForElement } from '../within'; import { makeQueries } from './make-queries'; import type { @@ -28,7 +30,9 @@ import type { } from './make-queries'; import { CommonQueryOptions } from './options'; -type ByRoleOptions = CommonQueryOptions & +export type ByRoleMatcher = StringWithAutocomplete | RegExp; + +export type ByRoleOptions = CommonQueryOptions & AccessibilityStateMatcher & { name?: TextMatch; value?: AccessibilityValueMatcher; @@ -52,7 +56,9 @@ const matchAccessibilityValueIfNeeded = ( return value != null ? matchAccessibilityValue(node, value) : true; }; -const queryAllByRole = (instance: ReactTestInstance): QueryAllByQuery => +const queryAllByRole = ( + instance: ReactTestInstance, +): QueryAllByQuery => function queryAllByRoleFn(role, options) { return findAll( instance, @@ -80,7 +86,7 @@ const formatQueryParams = (role: TextMatch, options: ByRoleOptions = {}) => { } }); - accessiblityValueKeys.forEach((valueKey) => { + accessibilityValueKeys.forEach((valueKey) => { if (options?.value?.[valueKey] !== undefined) { params.push(`${valueKey} value: ${options?.value?.[valueKey]}`); } @@ -102,12 +108,12 @@ const { getBy, getAllBy, queryBy, queryAllBy, findBy, findAllBy } = makeQueries( ); export type ByRoleQueries = { - getByRole: GetByQuery; - getAllByRole: GetAllByQuery; - queryByRole: QueryByQuery; - queryAllByRole: QueryAllByQuery; - findByRole: FindByQuery; - findAllByRole: FindAllByQuery; + getByRole: GetByQuery; + getAllByRole: GetAllByQuery; + queryByRole: QueryByQuery; + queryAllByRole: QueryAllByQuery; + findByRole: FindByQuery; + findAllByRole: FindAllByQuery; }; export const bindByRoleQueries = (instance: ReactTestInstance): ByRoleQueries => ({ diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 000000000..77df0fb34 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,3 @@ +// TS autocomplete trick +// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 +export type StringWithAutocomplete = T | (string & {});