From baa68a3bde27322dc5d577b6dcb58f0188876eba Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Mon, 29 Apr 2024 22:12:07 +0200 Subject: [PATCH 1/3] feat: autocomplete --- .vscode/settings.json | 2 +- src/fire-event.ts | 11 +++++------ src/queries/role.ts | 19 ++++++++++++------- 3 files changed, 18 insertions(+), 14 deletions(-) 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..82160a9ca 100644 --- a/src/fire-event.ts +++ b/src/fire-event.ts @@ -105,21 +105,20 @@ 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 +// String union type of keys of T that start with on, stripped of 'on' type OnKeys = keyof { [K in keyof T as K extends `on${infer Rest}` ? Uncapitalize : never]: T[K]; }; -type EventName = StringWithAutoComplete< +// TS autocomplete trick +// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 +type EventName = | OnKeys | OnKeys | OnKeys | OnKeys | OnKeys ->; + | (string & {}); function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) { const handler = findEventHandler(element, eventName); diff --git a/src/queries/role.ts b/src/queries/role.ts index 55b4f7a3d..ca54158ac 100644 --- a/src/queries/role.ts +++ b/src/queries/role.ts @@ -1,4 +1,5 @@ import type { ReactTestInstance } from 'react-test-renderer'; +import type { AccessibilityRole, Role } from 'react-native'; import { accessibilityStateKeys, accessiblityValueKeys, @@ -28,6 +29,10 @@ import type { } from './make-queries'; import { CommonQueryOptions } from './options'; +// TS autocomplete trick +// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 +export type RoleMatcher = AccessibilityRole | Role | (string & {}) | RegExp; + type ByRoleOptions = CommonQueryOptions & AccessibilityStateMatcher & { name?: TextMatch; @@ -52,7 +57,7 @@ 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, @@ -102,12 +107,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 => ({ From 32b18670bd7e291c716075add83e608389c08269 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Mon, 29 Apr 2024 22:17:18 +0200 Subject: [PATCH 2/3] refactor: cleanup --- src/fire-event.ts | 19 +++++++++---------- src/queries/role.ts | 7 +++---- src/types.ts | 3 +++ 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 src/types.ts diff --git a/src/fire-event.ts b/src/fire-event.ts index 82160a9ca..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; @@ -106,19 +107,17 @@ function getEventHandlerName(eventName: string) { } // String union type of keys of T that start with on, stripped of 'on' -type OnKeys = keyof { +type EventNameExtractor = keyof { [K in keyof T as K extends `on${infer Rest}` ? Uncapitalize : never]: T[K]; }; -// TS autocomplete trick -// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 -type EventName = - | OnKeys - | OnKeys - | OnKeys - | OnKeys - | OnKeys - | (string & {}); +type EventName = StringWithAutocomplete< + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor + | EventNameExtractor +>; function fireEvent(element: ReactTestInstance, eventName: EventName, ...data: unknown[]) { const handler = findEventHandler(element, eventName); diff --git a/src/queries/role.ts b/src/queries/role.ts index ca54158ac..c595d2409 100644 --- a/src/queries/role.ts +++ b/src/queries/role.ts @@ -17,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 { @@ -29,11 +30,9 @@ import type { } from './make-queries'; import { CommonQueryOptions } from './options'; -// TS autocomplete trick -// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939 -export type RoleMatcher = AccessibilityRole | Role | (string & {}) | RegExp; +export type RoleMatcher = StringWithAutocomplete | RegExp; -type ByRoleOptions = CommonQueryOptions & +export type ByRoleOptions = CommonQueryOptions & AccessibilityStateMatcher & { name?: TextMatch; value?: AccessibilityValueMatcher; 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 & {}); From 99c3702afcce82213b33e5d4ccd5afb4ef019645 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Mon, 29 Apr 2024 22:22:22 +0200 Subject: [PATCH 3/3] chore: fix typos --- src/helpers/__tests__/accessiblity.test.tsx | 2 +- .../{accessiblity.ts => accessibility.ts} | 2 +- src/helpers/find-all.ts | 2 +- .../matchers/match-accessibility-state.ts | 2 +- .../matchers/match-accessibility-value.ts | 2 +- src/helpers/matchers/match-label-text.ts | 2 +- src/matchers/to-be-busy.tsx | 2 +- src/matchers/to-be-checked.tsx | 2 +- src/matchers/to-be-collapsed.tsx | 2 +- src/matchers/to-be-expanded.tsx | 2 +- src/matchers/to-be-partially-checked.tsx | 2 +- src/matchers/to-be-selected.ts | 2 +- src/matchers/to-be-visible.tsx | 2 +- src/matchers/to-have-accessibility-value.tsx | 2 +- src/matchers/to-have-accessible-name.tsx | 2 +- src/pure.ts | 2 +- src/queries/accessibility-state.ts | 2 +- src/queries/accessibility-value.ts | 4 ++-- src/queries/role.ts | 24 ++++++++++--------- 19 files changed, 32 insertions(+), 30 deletions(-) rename src/helpers/{accessiblity.ts => accessibility.ts} (98%) 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 c595d2409..23eccc9f9 100644 --- a/src/queries/role.ts +++ b/src/queries/role.ts @@ -2,10 +2,10 @@ 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, @@ -30,7 +30,7 @@ import type { } from './make-queries'; import { CommonQueryOptions } from './options'; -export type RoleMatcher = StringWithAutocomplete | RegExp; +export type ByRoleMatcher = StringWithAutocomplete | RegExp; export type ByRoleOptions = CommonQueryOptions & AccessibilityStateMatcher & { @@ -56,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, @@ -84,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]}`); } @@ -106,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 => ({