From 17c22f2f642b790fd45eec926bd9f042393fdc85 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 09:50:26 +0200 Subject: [PATCH 1/9] Basic within implementation and tests --- src/__tests__/within.test.js | 72 ++++++++++++++++++++++++++++++++++++ src/within.js | 12 ++++++ 2 files changed, 84 insertions(+) create mode 100644 src/__tests__/within.test.js create mode 100644 src/within.js diff --git a/src/__tests__/within.test.js b/src/__tests__/within.test.js new file mode 100644 index 000000000..9c0ce93cf --- /dev/null +++ b/src/__tests__/within.test.js @@ -0,0 +1,72 @@ +// @flow +import React from 'react'; +import { View, Text, TextInput } from 'react-native'; +import { render } from '..'; +import { within } from '../within'; + +test('within() exposes basic queries', () => { + const rootQueries = render( + + + Same Text + + + + Same Text + + + + ); + + expect(rootQueries.getAllByText('Same Text')).toHaveLength(2); + expect(rootQueries.getAllByDisplayValue('Same Value')).toHaveLength(2); + expect(rootQueries.getAllByPlaceholder('Same Placeholder')).toHaveLength(2); + + const firstQueries = within(rootQueries.getAllByTestId('first')[0]); + expect(firstQueries.getAllByText('Same Text')).toHaveLength(1); + expect(firstQueries.getByText('Same Text')).toBeTruthy(); + expect(firstQueries.queryAllByText('Same Text')).toHaveLength(1); + expect(firstQueries.queryByText('Same Text')).toBeTruthy(); + expect(firstQueries.getByDisplayValue('Same Value')).toBeTruthy(); + expect(firstQueries.getByPlaceholder('Same Placeholder')).toBeTruthy(); + + const secondQueries = within(rootQueries.getAllByTestId('second')[0]); + expect(secondQueries.getAllByText('Same Text')).toHaveLength(1); + expect(secondQueries.getByText('Same Text')).toBeTruthy(); + expect(secondQueries.queryAllByText('Same Text')).toHaveLength(1); + expect(secondQueries.queryByText('Same Text')).toBeTruthy(); + expect(secondQueries.getByDisplayValue('Same Value')).toBeTruthy(); + expect(secondQueries.getByPlaceholder('Same Placeholder')).toBeTruthy(); +}); + +test('within() exposes a11y queries', () => { + const rootQueries = render( + + + + + + + + + ); + + expect(rootQueries.getAllByA11yLabel('Same Label')).toHaveLength(2); + expect(rootQueries.getAllByA11yHint('Same Hint')).toHaveLength(2); + + const firstQueries = within(rootQueries.getAllByTestId('first')[0]); + expect(firstQueries.getByA11yLabel('Same Label')).toBeTruthy(); + expect(firstQueries.getByA11yHint('Same Hint')).toBeTruthy(); + + const secondQueries = within(rootQueries.getAllByTestId('second')[0]); + expect(secondQueries.getAllByA11yLabel('Same Label')).toHaveLength(1); + expect(secondQueries.getAllByA11yHint('Same Hint')).toHaveLength(1); +}); diff --git a/src/within.js b/src/within.js new file mode 100644 index 000000000..855f3879f --- /dev/null +++ b/src/within.js @@ -0,0 +1,12 @@ +// @flow +import { getByAPI } from './helpers/getByAPI'; +import { queryByAPI } from './helpers/queryByAPI'; +import a11yAPI from './helpers/a11yAPI'; + +export function within(instance: ReactTestInstance) { + return { + ...getByAPI(instance), + ...queryByAPI(instance), + ...a11yAPI(instance), + }; +} From e8284fd0ef5e4423cf6462a8b5404221af9637a8 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 10:20:48 +0200 Subject: [PATCH 2/9] Added typescript types & tests --- typings/__tests__/index.test.tsx | 179 ++++++++++++++++++++++++++----- typings/index.d.ts | 103 +++++++++++------- 2 files changed, 216 insertions(+), 66 deletions(-) diff --git a/typings/__tests__/index.test.tsx b/typings/__tests__/index.test.tsx index c4a5ad020..b12b2522b 100644 --- a/typings/__tests__/index.test.tsx +++ b/typings/__tests__/index.test.tsx @@ -9,6 +9,7 @@ import { debug, waitForElement, act, + within, } from '../..'; interface HasRequiredProp { @@ -60,9 +61,9 @@ const getAllByNameConstructor: Array = tree.getAllByName( View ); const getAllByType: Array = tree.getAllByType(View); -const getAllByTypeWithRequiredProps: Array< - ReactTestInstance -> = tree.getAllByType(ElementWithRequiredProps); +const getAllByTypeWithRequiredProps: Array = tree.getAllByType( + ElementWithRequiredProps +); const getAllByTextString: Array = tree.getAllByText( '' ); @@ -104,21 +105,21 @@ const queryAllByNameConstructor: Array = tree.queryAllByName( View ); const queryAllByType: Array = tree.queryAllByType(View); -const queryAllByTypeWithRequiredProps: Array< - ReactTestInstance -> = tree.queryAllByType(ElementWithRequiredProps); +const queryAllByTypeWithRequiredProps: Array = tree.queryAllByType( + ElementWithRequiredProps +); const queryAllByTextString: Array = tree.queryAllByText( 'View' ); const queryAllByTextRegExp: Array = tree.queryAllByText( /View/g ); -const queryAllByDisplayValueString: Array< - ReactTestInstance -> = tree.queryAllByDisplayValue('View'); -const queryAllByDisplayValueRegExp: Array< - ReactTestInstance -> = tree.queryAllByDisplayValue(/View/g); +const queryAllByDisplayValueString: Array = tree.queryAllByDisplayValue( + 'View' +); +const queryAllByDisplayValueRegExp: Array = tree.queryAllByDisplayValue( + /View/g +); // Accessibility queries const getByA11yLabel: ReactTestInstance = tree.getByA11yLabel('label'); @@ -146,33 +147,45 @@ const queryAllByA11yRole: Array = tree.queryAllByA11yRole( 'button' ); const getByA11yStates: ReactTestInstance = tree.getByA11yStates('selected'); -const getByA11yStatesArray: ReactTestInstance = tree.getByA11yStates(['selected']); +const getByA11yStatesArray: ReactTestInstance = tree.getByA11yStates([ + 'selected', +]); const getAllByA11yStates: Array = tree.getAllByA11yStates( 'selected' ); -const getAllByA11yStatesArray: Array< - ReactTestInstance -> = tree.getAllByA11yStates(['selected']); +const getAllByA11yStatesArray: Array = tree.getAllByA11yStates( + ['selected'] +); const queryByA11yStates: ReactTestInstance = tree.queryByA11yStates('selected'); const queryByA11yStatesArray: ReactTestInstance = tree.queryByA11yStates([ 'selected', ]); -const queryAllByA11yStates: Array< - ReactTestInstance -> = tree.queryAllByA11yStates('selected'); -const queryAllByA11yStatesArray: Array< - ReactTestInstance -> = tree.queryAllByA11yStates(['selected']); +const queryAllByA11yStates: Array = tree.queryAllByA11yStates( + 'selected' +); +const queryAllByA11yStatesArray: Array = tree.queryAllByA11yStates( + ['selected'] +); const getByA11yState: ReactTestInstance = tree.getByA11yState({ busy: true }); -const getAllByA11yState: Array = tree.getAllByA11yState({ busy: true }); -const queryByA11yState: ReactTestInstance = tree.queryByA11yState({ busy: true }); -const queryAllByA11yState: Array = tree.queryAllByA11yState({ busy: true }); +const getAllByA11yState: Array = tree.getAllByA11yState({ + busy: true, +}); +const queryByA11yState: ReactTestInstance = tree.queryByA11yState({ + busy: true, +}); +const queryAllByA11yState: Array = tree.queryAllByA11yState({ + busy: true, +}); const getByA11yValue: ReactTestInstance = tree.getByA11yValue({ min: 10 }); -const getAllByA11yValue: Array = tree.getAllByA11yValue({ min: 10 }); +const getAllByA11yValue: Array = tree.getAllByA11yValue({ + min: 10, +}); const queryByA11yValue: ReactTestInstance = tree.queryByA11yValue({ min: 10 }); -const queryAllByA11yValue: Array = tree.queryAllByA11yValue({ min: 10 }); +const queryAllByA11yValue: Array = tree.queryAllByA11yValue({ + min: 10, +}); const debugFn = tree.debug(); const debugFnWithMessage = tree.debug('my message'); @@ -217,3 +230,115 @@ const waitByAll: Promise> = waitForElement< act(() => { render(); }); + +// within API +const instance: ReactTestInstance = tree.getByText(''); +const withinGetByText: ReactTestInstance = within(instance).getByText('Test'); +const withinGetAllByText: ReactTestInstance[] = within(instance).getAllByText( + 'Test' +); +const withinGetByDisplayValue: ReactTestInstance = within( + instance +).getByDisplayValue('Test'); +const withinGetAllByDisplayValue: ReactTestInstance[] = within( + instance +).getAllByDisplayValue('Test'); +const withinGetByPlaceholder: ReactTestInstance = within( + instance +).getByPlaceholder('Test'); +const withinGetAllByPlaceholder: ReactTestInstance[] = within( + instance +).getAllByPlaceholder('Test'); +const withinGetByTestId: ReactTestInstance = within(instance).getByTestId( + 'Test' +); +const withinGetAllByTestId: ReactTestInstance[] = within( + instance +).getAllByTestId('Test'); + +const withinQueryByText: ReactTestInstance | null = within( + instance +).queryByText('Test'); +const withinQueryAllByText: ReactTestInstance[] = within( + instance +).queryAllByText('Test'); +const withinQueryByDisplayValue: ReactTestInstance | null = within( + instance +).queryByDisplayValue('Test'); +const withinQueryAllByDisplayValue: ReactTestInstance[] = within( + instance +).queryAllByDisplayValue('Test'); +const withinQueryByPlaceholder: ReactTestInstance | null = within( + instance +).queryByPlaceholder('Test'); +const withinQueryAllByPlaceholder: ReactTestInstance[] = within( + instance +).queryAllByPlaceholder('Test'); +const withinQueryByTestId: ReactTestInstance | null = within( + instance +).queryByTestId('Test'); +const withinQueryAllByTestId: ReactTestInstance[] = within( + instance +).queryAllByTestId('Test'); + +const withinGetByA11yLabel: ReactTestInstance = within(instance).getByA11yLabel( + 'Test' +); +const withinGetAllByA11yLabel: ReactTestInstance[] = within( + instance +).getAllByA11yLabel('Test'); +const withinGetByA11yHint: ReactTestInstance = within(instance).getByA11yHint( + 'Test' +); +const withinGetAllByA11yHint: ReactTestInstance[] = within( + instance +).getAllByA11yHint('Test'); +const withinGetByA11yRole: ReactTestInstance = within(instance).getByA11yRole( + 'button' +); +const withinGetAllByA11yRole: ReactTestInstance[] = within( + instance +).getAllByA11yRole('button'); +const withinGetByA11yState: ReactTestInstance = within( + instance +).getByA11yState({ busy: true }); +const withinGetAllByA11yState: ReactTestInstance[] = within( + instance +).getAllByA11yState({ busy: true }); +const withinGetByA11yValue: ReactTestInstance = within( + instance +).getByA11yValue({ min: 10 }); +const withinGetAllByA11yValue: ReactTestInstance[] = within( + instance +).getAllByA11yValue({ min: 10 }); + +const withinQueryByA11yLabel: ReactTestInstance | null = within( + instance +).queryByA11yLabel('Test'); +const withinQueryAllByA11yLabel: ReactTestInstance[] = within( + instance +).queryAllByA11yLabel('Test'); +const withinQueryByA11yHint: ReactTestInstance | null = within( + instance +).queryByA11yHint('Test'); +const withinQueryAllByA11yHint: ReactTestInstance[] = within( + instance +).queryAllByA11yHint('Test'); +const withinQueryByA11yRole: ReactTestInstance | null = within( + instance +).queryByA11yRole('button'); +const withinQueryAllByA11yRole: ReactTestInstance[] = within( + instance +).queryAllByA11yRole('button'); +const withinQueryByA11yState: ReactTestInstance | null = within( + instance +).queryByA11yState({ busy: true }); +const withinQueryAllByA11yState: ReactTestInstance[] = within( + instance +).queryAllByA11yState({ busy: true }); +const withinQueryByA11yValue: ReactTestInstance | null = within( + instance +).queryByA11yValue({ min: 10 }); +const withinQueryAllByA11yValue: ReactTestInstance[] = within( + instance +).queryAllByA11yValue({ min: 10 }); diff --git a/typings/index.d.ts b/typings/index.d.ts index 1c03ed410..fab528813 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,6 +1,11 @@ import * as React from 'react'; -import { AccessibilityState, AccessibilityStates, AccessibilityRole } from 'react-native'; +import { + AccessibilityState, + AccessibilityStates, + AccessibilityRole, +} from 'react-native'; import { ReactTestInstance, ReactTestRendererJSON } from 'react-test-renderer'; +import { ReactTestInstance } from 'react-test-renderer'; export interface GetByAPI { getByName: (name: React.ReactType | string) => ReactTestInstance; @@ -20,12 +25,15 @@ export interface GetByAPI { getAllByDisplayValue: (value: string | RegExp) => Array; getAllByProps: (props: Record) => Array; - // Unsafe aliases - UNSAFE_getByType:

(type: React.ComponentType

) => ReactTestInstance, - UNSAFE_getAllByType:

(type: React.ComponentType

) => Array, - UNSAFE_getByProps: (props: Record) => ReactTestInstance, - UNSAFE_getAllByProps: (props: Record) => Array, + UNSAFE_getByType:

(type: React.ComponentType

) => ReactTestInstance; + UNSAFE_getAllByType:

( + type: React.ComponentType

+ ) => Array; + UNSAFE_getByProps: (props: Record) => ReactTestInstance; + UNSAFE_getAllByProps: ( + props: Record + ) => Array; } export interface QueryByAPI { @@ -57,10 +65,16 @@ export interface QueryByAPI { ) => Array | []; // Unsafe aliases - UNSAFE_queryByType:

(type: React.ComponentType

) => ReactTestInstance | null, - UNSAFE_queryAllByType:

(type: React.ComponentType

) => Array | [], - UNSAFE_queryByProps: (props: Record) => ReactTestInstance | null, - UNSAFE_queryAllByProps: (props: Record) => Array | [], + UNSAFE_queryByType:

( + type: React.ComponentType

+ ) => ReactTestInstance | null; + UNSAFE_queryAllByType:

( + type: React.ComponentType

+ ) => Array | []; + UNSAFE_queryByProps: (props: Record) => ReactTestInstance | null; + UNSAFE_queryAllByProps: ( + props: Record + ) => Array | []; } type GetReturn = ReactTestInstance; @@ -70,48 +84,56 @@ type QueryAllReturn = Array | []; // Not yet available in DefinitelyTyped export type A11yValue = { - min?: number, - max?: number, - now?: number, - text?: string, + min?: number; + max?: number; + now?: number; + text?: string; }; type A11yAPI = { // Label - getByA11yLabel: (matcher: string | RegExp) => GetReturn, - getAllByA11yLabel: (matcher: string | RegExp) => GetAllReturn, - queryByA11yLabel: (matcher: string | RegExp) => QueryReturn, - queryAllByA11yLabel: (matcher: string | RegExp) => QueryAllReturn, + getByA11yLabel: (matcher: string | RegExp) => GetReturn; + getAllByA11yLabel: (matcher: string | RegExp) => GetAllReturn; + queryByA11yLabel: (matcher: string | RegExp) => QueryReturn; + queryAllByA11yLabel: (matcher: string | RegExp) => QueryAllReturn; // Hint - getByA11yHint: (matcher: string | RegExp) => GetReturn, - getAllByA11yHint: (matcher: string | RegExp) => GetAllReturn, - queryByA11yHint: (matcher: string | RegExp) => QueryReturn, - queryAllByA11yHint: (matcher: string | RegExp) => QueryAllReturn, + getByA11yHint: (matcher: string | RegExp) => GetReturn; + getAllByA11yHint: (matcher: string | RegExp) => GetAllReturn; + queryByA11yHint: (matcher: string | RegExp) => QueryReturn; + queryAllByA11yHint: (matcher: string | RegExp) => QueryAllReturn; // Role - getByA11yRole: (matcher: AccessibilityRole | RegExp) => GetReturn, - getAllByA11yRole: (matcher: AccessibilityRole | RegExp) => GetAllReturn, - queryByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryReturn, - queryAllByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryAllReturn, + getByA11yRole: (matcher: AccessibilityRole | RegExp) => GetReturn; + getAllByA11yRole: (matcher: AccessibilityRole | RegExp) => GetAllReturn; + queryByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryReturn; + queryAllByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryAllReturn; // States - getByA11yStates: (matcher: AccessibilityStates | Array) => GetReturn, - getAllByA11yStates: (matcher: AccessibilityStates | Array) => GetAllReturn, - queryByA11yStates: (matcher: AccessibilityStates | Array) => QueryReturn, - queryAllByA11yStates: (matcher: AccessibilityStates | Array) => QueryAllReturn, + getByA11yStates: ( + matcher: AccessibilityStates | Array + ) => GetReturn; + getAllByA11yStates: ( + matcher: AccessibilityStates | Array + ) => GetAllReturn; + queryByA11yStates: ( + matcher: AccessibilityStates | Array + ) => QueryReturn; + queryAllByA11yStates: ( + matcher: AccessibilityStates | Array + ) => QueryAllReturn; // State - getByA11yState: (matcher: AccessibilityState) => GetReturn, - getAllByA11yState: (matcher: AccessibilityState) => GetAllReturn, - queryByA11yState: (matcher: AccessibilityState) => QueryReturn, - queryAllByA11yState: (matcher: AccessibilityState) => QueryAllReturn, + getByA11yState: (matcher: AccessibilityState) => GetReturn; + getAllByA11yState: (matcher: AccessibilityState) => GetAllReturn; + queryByA11yState: (matcher: AccessibilityState) => QueryReturn; + queryAllByA11yState: (matcher: AccessibilityState) => QueryAllReturn; // Value - getByA11yValue: (matcher: A11yValue) => GetReturn, - getAllByA11yValue: (matcher: A11yValue) => GetAllReturn, - queryByA11yValue: (matcher: A11yValue) => QueryReturn, - queryAllByA11yValue: (matcher: A11yValue) => QueryAllReturn, + getByA11yValue: (matcher: A11yValue) => GetReturn; + getAllByA11yValue: (matcher: A11yValue) => GetAllReturn; + queryByA11yValue: (matcher: A11yValue) => QueryReturn; + queryAllByA11yValue: (matcher: A11yValue) => QueryAllReturn; }; export interface Thenable { @@ -123,7 +145,9 @@ export interface RenderOptions { createNodeMock?: (element: React.ReactElement) => any; } -export interface RenderAPI extends GetByAPI, QueryByAPI, A11yAPI { +type Queries = GetByAPI & QueryByAPI & A11yAPI; + +export interface RenderAPI extends Queries { update(nextElement: React.ReactElement): void; rerender(nextElement: React.ReactElement): void; unmount(nextElement?: React.ReactElement): void; @@ -175,3 +199,4 @@ export declare const debug: DebugAPI; export declare const fireEvent: FireEventAPI; export declare const waitForElement: WaitForElementFunction; export declare const act: (callback: () => void) => Thenable; +export declare const within: (instance: ReactTestInstance) => Queries; From 967163f6731992257ed9a3e965eea8a542ab9254 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 10:32:00 +0200 Subject: [PATCH 3/9] Removed duplicated import in index.d.ts --- typings/index.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index fab528813..394daad35 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -5,7 +5,6 @@ import { AccessibilityRole, } from 'react-native'; import { ReactTestInstance, ReactTestRendererJSON } from 'react-test-renderer'; -import { ReactTestInstance } from 'react-test-renderer'; export interface GetByAPI { getByName: (name: React.ReactType | string) => ReactTestInstance; From d679ca670ea225b5d88ba735fbef5b9193cb7545 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 11:03:43 +0200 Subject: [PATCH 4/9] Reversed prettier run on typings --- typings/__tests__/index.test.tsx | 66 +++++++++------------- typings/index.d.ts | 97 +++++++++++++------------------- 2 files changed, 65 insertions(+), 98 deletions(-) diff --git a/typings/__tests__/index.test.tsx b/typings/__tests__/index.test.tsx index b12b2522b..5e0db16f4 100644 --- a/typings/__tests__/index.test.tsx +++ b/typings/__tests__/index.test.tsx @@ -61,9 +61,9 @@ const getAllByNameConstructor: Array = tree.getAllByName( View ); const getAllByType: Array = tree.getAllByType(View); -const getAllByTypeWithRequiredProps: Array = tree.getAllByType( - ElementWithRequiredProps -); +const getAllByTypeWithRequiredProps: Array< + ReactTestInstance +> = tree.getAllByType(ElementWithRequiredProps); const getAllByTextString: Array = tree.getAllByText( '' ); @@ -105,21 +105,21 @@ const queryAllByNameConstructor: Array = tree.queryAllByName( View ); const queryAllByType: Array = tree.queryAllByType(View); -const queryAllByTypeWithRequiredProps: Array = tree.queryAllByType( - ElementWithRequiredProps -); +const queryAllByTypeWithRequiredProps: Array< + ReactTestInstance +> = tree.queryAllByType(ElementWithRequiredProps); const queryAllByTextString: Array = tree.queryAllByText( 'View' ); const queryAllByTextRegExp: Array = tree.queryAllByText( /View/g ); -const queryAllByDisplayValueString: Array = tree.queryAllByDisplayValue( - 'View' -); -const queryAllByDisplayValueRegExp: Array = tree.queryAllByDisplayValue( - /View/g -); +const queryAllByDisplayValueString: Array< + ReactTestInstance +> = tree.queryAllByDisplayValue('View'); +const queryAllByDisplayValueRegExp: Array< + ReactTestInstance +> = tree.queryAllByDisplayValue(/View/g); // Accessibility queries const getByA11yLabel: ReactTestInstance = tree.getByA11yLabel('label'); @@ -147,45 +147,33 @@ const queryAllByA11yRole: Array = tree.queryAllByA11yRole( 'button' ); const getByA11yStates: ReactTestInstance = tree.getByA11yStates('selected'); -const getByA11yStatesArray: ReactTestInstance = tree.getByA11yStates([ - 'selected', -]); +const getByA11yStatesArray: ReactTestInstance = tree.getByA11yStates(['selected']); const getAllByA11yStates: Array = tree.getAllByA11yStates( 'selected' ); -const getAllByA11yStatesArray: Array = tree.getAllByA11yStates( - ['selected'] -); +const getAllByA11yStatesArray: Array< + ReactTestInstance +> = tree.getAllByA11yStates(['selected']); const queryByA11yStates: ReactTestInstance = tree.queryByA11yStates('selected'); const queryByA11yStatesArray: ReactTestInstance = tree.queryByA11yStates([ 'selected', ]); -const queryAllByA11yStates: Array = tree.queryAllByA11yStates( - 'selected' -); -const queryAllByA11yStatesArray: Array = tree.queryAllByA11yStates( - ['selected'] -); +const queryAllByA11yStates: Array< + ReactTestInstance +> = tree.queryAllByA11yStates('selected'); +const queryAllByA11yStatesArray: Array< + ReactTestInstance +> = tree.queryAllByA11yStates(['selected']); const getByA11yState: ReactTestInstance = tree.getByA11yState({ busy: true }); -const getAllByA11yState: Array = tree.getAllByA11yState({ - busy: true, -}); -const queryByA11yState: ReactTestInstance = tree.queryByA11yState({ - busy: true, -}); -const queryAllByA11yState: Array = tree.queryAllByA11yState({ - busy: true, -}); +const getAllByA11yState: Array = tree.getAllByA11yState({ busy: true }); +const queryByA11yState: ReactTestInstance = tree.queryByA11yState({ busy: true }); +const queryAllByA11yState: Array = tree.queryAllByA11yState({ busy: true }); const getByA11yValue: ReactTestInstance = tree.getByA11yValue({ min: 10 }); -const getAllByA11yValue: Array = tree.getAllByA11yValue({ - min: 10, -}); +const getAllByA11yValue: Array = tree.getAllByA11yValue({ min: 10 }); const queryByA11yValue: ReactTestInstance = tree.queryByA11yValue({ min: 10 }); -const queryAllByA11yValue: Array = tree.queryAllByA11yValue({ - min: 10, -}); +const queryAllByA11yValue: Array = tree.queryAllByA11yValue({ min: 10 }); const debugFn = tree.debug(); const debugFnWithMessage = tree.debug('my message'); diff --git a/typings/index.d.ts b/typings/index.d.ts index 394daad35..4afc01665 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,9 +1,5 @@ import * as React from 'react'; -import { - AccessibilityState, - AccessibilityStates, - AccessibilityRole, -} from 'react-native'; +import { AccessibilityState, AccessibilityStates, AccessibilityRole } from 'react-native'; import { ReactTestInstance, ReactTestRendererJSON } from 'react-test-renderer'; export interface GetByAPI { @@ -24,15 +20,12 @@ export interface GetByAPI { getAllByDisplayValue: (value: string | RegExp) => Array; getAllByProps: (props: Record) => Array; + // Unsafe aliases - UNSAFE_getByType:

(type: React.ComponentType

) => ReactTestInstance; - UNSAFE_getAllByType:

( - type: React.ComponentType

- ) => Array; - UNSAFE_getByProps: (props: Record) => ReactTestInstance; - UNSAFE_getAllByProps: ( - props: Record - ) => Array; + UNSAFE_getByType:

(type: React.ComponentType

) => ReactTestInstance, + UNSAFE_getAllByType:

(type: React.ComponentType

) => Array, + UNSAFE_getByProps: (props: Record) => ReactTestInstance, + UNSAFE_getAllByProps: (props: Record) => Array, } export interface QueryByAPI { @@ -64,16 +57,10 @@ export interface QueryByAPI { ) => Array | []; // Unsafe aliases - UNSAFE_queryByType:

( - type: React.ComponentType

- ) => ReactTestInstance | null; - UNSAFE_queryAllByType:

( - type: React.ComponentType

- ) => Array | []; - UNSAFE_queryByProps: (props: Record) => ReactTestInstance | null; - UNSAFE_queryAllByProps: ( - props: Record - ) => Array | []; + UNSAFE_queryByType:

(type: React.ComponentType

) => ReactTestInstance | null, + UNSAFE_queryAllByType:

(type: React.ComponentType

) => Array | [], + UNSAFE_queryByProps: (props: Record) => ReactTestInstance | null, + UNSAFE_queryAllByProps: (props: Record) => Array | [], } type GetReturn = ReactTestInstance; @@ -83,56 +70,48 @@ type QueryAllReturn = Array | []; // Not yet available in DefinitelyTyped export type A11yValue = { - min?: number; - max?: number; - now?: number; - text?: string; + min?: number, + max?: number, + now?: number, + text?: string, }; type A11yAPI = { // Label - getByA11yLabel: (matcher: string | RegExp) => GetReturn; - getAllByA11yLabel: (matcher: string | RegExp) => GetAllReturn; - queryByA11yLabel: (matcher: string | RegExp) => QueryReturn; - queryAllByA11yLabel: (matcher: string | RegExp) => QueryAllReturn; + getByA11yLabel: (matcher: string | RegExp) => GetReturn, + getAllByA11yLabel: (matcher: string | RegExp) => GetAllReturn, + queryByA11yLabel: (matcher: string | RegExp) => QueryReturn, + queryAllByA11yLabel: (matcher: string | RegExp) => QueryAllReturn, // Hint - getByA11yHint: (matcher: string | RegExp) => GetReturn; - getAllByA11yHint: (matcher: string | RegExp) => GetAllReturn; - queryByA11yHint: (matcher: string | RegExp) => QueryReturn; - queryAllByA11yHint: (matcher: string | RegExp) => QueryAllReturn; + getByA11yHint: (matcher: string | RegExp) => GetReturn, + getAllByA11yHint: (matcher: string | RegExp) => GetAllReturn, + queryByA11yHint: (matcher: string | RegExp) => QueryReturn, + queryAllByA11yHint: (matcher: string | RegExp) => QueryAllReturn, // Role - getByA11yRole: (matcher: AccessibilityRole | RegExp) => GetReturn; - getAllByA11yRole: (matcher: AccessibilityRole | RegExp) => GetAllReturn; - queryByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryReturn; - queryAllByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryAllReturn; + getByA11yRole: (matcher: AccessibilityRole | RegExp) => GetReturn, + getAllByA11yRole: (matcher: AccessibilityRole | RegExp) => GetAllReturn, + queryByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryReturn, + queryAllByA11yRole: (matcher: AccessibilityRole | RegExp) => QueryAllReturn, // States - getByA11yStates: ( - matcher: AccessibilityStates | Array - ) => GetReturn; - getAllByA11yStates: ( - matcher: AccessibilityStates | Array - ) => GetAllReturn; - queryByA11yStates: ( - matcher: AccessibilityStates | Array - ) => QueryReturn; - queryAllByA11yStates: ( - matcher: AccessibilityStates | Array - ) => QueryAllReturn; + getByA11yStates: (matcher: AccessibilityStates | Array) => GetReturn, + getAllByA11yStates: (matcher: AccessibilityStates | Array) => GetAllReturn, + queryByA11yStates: (matcher: AccessibilityStates | Array) => QueryReturn, + queryAllByA11yStates: (matcher: AccessibilityStates | Array) => QueryAllReturn, // State - getByA11yState: (matcher: AccessibilityState) => GetReturn; - getAllByA11yState: (matcher: AccessibilityState) => GetAllReturn; - queryByA11yState: (matcher: AccessibilityState) => QueryReturn; - queryAllByA11yState: (matcher: AccessibilityState) => QueryAllReturn; + getByA11yState: (matcher: AccessibilityState) => GetReturn, + getAllByA11yState: (matcher: AccessibilityState) => GetAllReturn, + queryByA11yState: (matcher: AccessibilityState) => QueryReturn, + queryAllByA11yState: (matcher: AccessibilityState) => QueryAllReturn, // Value - getByA11yValue: (matcher: A11yValue) => GetReturn; - getAllByA11yValue: (matcher: A11yValue) => GetAllReturn; - queryByA11yValue: (matcher: A11yValue) => QueryReturn; - queryAllByA11yValue: (matcher: A11yValue) => QueryAllReturn; + getByA11yValue: (matcher: A11yValue) => GetReturn, + getAllByA11yValue: (matcher: A11yValue) => GetAllReturn, + queryByA11yValue: (matcher: A11yValue) => QueryReturn, + queryAllByA11yValue: (matcher: A11yValue) => QueryAllReturn, }; export interface Thenable { From 4bdf8f0ec8500529ddbeacdf834180715b20dbee Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 11:09:06 +0200 Subject: [PATCH 5/9] Fixed package level exports for `within` operator --- src/__tests__/within.test.js | 3 +-- src/index.js | 2 ++ src/within.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/__tests__/within.test.js b/src/__tests__/within.test.js index 9c0ce93cf..d44d8431a 100644 --- a/src/__tests__/within.test.js +++ b/src/__tests__/within.test.js @@ -1,8 +1,7 @@ // @flow import React from 'react'; import { View, Text, TextInput } from 'react-native'; -import { render } from '..'; -import { within } from '../within'; +import { render, within } from '..'; test('within() exposes basic queries', () => { const rootQueries = render( diff --git a/src/index.js b/src/index.js index f4263745c..2e7ac9f20 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import flushMicrotasksQueue from './flushMicrotasksQueue'; import render from './render'; import shallow from './shallow'; import waitForElement from './waitForElement'; +import within from './within'; export { act }; export { cleanup }; @@ -16,3 +17,4 @@ export { flushMicrotasksQueue }; export { render }; export { shallow }; export { waitForElement }; +export { within }; diff --git a/src/within.js b/src/within.js index 855f3879f..a7dd00b40 100644 --- a/src/within.js +++ b/src/within.js @@ -3,7 +3,7 @@ import { getByAPI } from './helpers/getByAPI'; import { queryByAPI } from './helpers/queryByAPI'; import a11yAPI from './helpers/a11yAPI'; -export function within(instance: ReactTestInstance) { +export default function within(instance: ReactTestInstance) { return { ...getByAPI(instance), ...queryByAPI(instance), From 79c2c6d02279a5bb8f184057e28cb1f87d83414e Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 11:25:26 +0200 Subject: [PATCH 6/9] Replaced getByTestId queries with getByA11yHint queries in unit tests for `within` --- src/__tests__/within.test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/__tests__/within.test.js b/src/__tests__/within.test.js index d44d8431a..97bf5252b 100644 --- a/src/__tests__/within.test.js +++ b/src/__tests__/within.test.js @@ -6,11 +6,11 @@ import { render, within } from '..'; test('within() exposes basic queries', () => { const rootQueries = render( - + Same Text - + Same Text @@ -21,7 +21,7 @@ test('within() exposes basic queries', () => { expect(rootQueries.getAllByDisplayValue('Same Value')).toHaveLength(2); expect(rootQueries.getAllByPlaceholder('Same Placeholder')).toHaveLength(2); - const firstQueries = within(rootQueries.getAllByTestId('first')[0]); + const firstQueries = within(rootQueries.getByA11yHint('first')); expect(firstQueries.getAllByText('Same Text')).toHaveLength(1); expect(firstQueries.getByText('Same Text')).toBeTruthy(); expect(firstQueries.queryAllByText('Same Text')).toHaveLength(1); @@ -29,7 +29,7 @@ test('within() exposes basic queries', () => { expect(firstQueries.getByDisplayValue('Same Value')).toBeTruthy(); expect(firstQueries.getByPlaceholder('Same Placeholder')).toBeTruthy(); - const secondQueries = within(rootQueries.getAllByTestId('second')[0]); + const secondQueries = within(rootQueries.getByA11yHint('second')); expect(secondQueries.getAllByText('Same Text')).toHaveLength(1); expect(secondQueries.getByText('Same Text')).toBeTruthy(); expect(secondQueries.queryAllByText('Same Text')).toHaveLength(1); @@ -41,14 +41,14 @@ test('within() exposes basic queries', () => { test('within() exposes a11y queries', () => { const rootQueries = render( - + - + { expect(rootQueries.getAllByA11yLabel('Same Label')).toHaveLength(2); expect(rootQueries.getAllByA11yHint('Same Hint')).toHaveLength(2); - const firstQueries = within(rootQueries.getAllByTestId('first')[0]); + const firstQueries = within(rootQueries.getByA11yHint('first')); expect(firstQueries.getByA11yLabel('Same Label')).toBeTruthy(); expect(firstQueries.getByA11yHint('Same Hint')).toBeTruthy(); - const secondQueries = within(rootQueries.getAllByTestId('second')[0]); + const secondQueries = within(rootQueries.getByA11yHint('second')); expect(secondQueries.getAllByA11yLabel('Same Label')).toHaveLength(1); expect(secondQueries.getAllByA11yHint('Same Hint')).toHaveLength(1); }); From 98e57d0c19d7a9743d6d89536dc9090d34dc742e Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 12:19:02 +0200 Subject: [PATCH 7/9] Added `within` section to API.md --- website/docs/API.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/website/docs/API.md b/website/docs/API.md index 8cf47a27c..20899309b 100644 --- a/website/docs/API.md +++ b/website/docs/API.md @@ -341,6 +341,30 @@ test('waiting for an Banana to be ready', async () => { If you're using Jest's [Timer Mocks](https://jestjs.io/docs/en/timer-mocks#docsNav), remember not to use `async/await` syntax as it will stall your tests. +## `within` + +- [`Example code`](https://github.com/callstack/react-native-testing-library/blob/master/src/__tests__/within.test.js) + +Defined as: + +```jsx +function within(instance: ReactTestInstance): Queries +``` + +Perform [queries](./Queries.md) scoped to given element. + +```jsx +const detailsScreen = within(getByA11yHint('Details Screen')); +expect(detailsScreen.getByText('Some Text')).toBeTruthy(); +expect(detailsScreen.getByDisplayValue('Some Value')).toBeTruthy(); +expect(detailsScreen.getByA11yLabel('Some Label')).toBeTruthy(); +expect(detailsScreen.getByA11yHint('Some Label')).toBeTruthy(); +``` + +Use cases for scoped queries include: +* queries scoped to a single item inside a FlatList containing many items +* queries scoped to a single screen in tests involving screen transitions (e.g. with react-navigation) + ## `debug` - [`Example code`](https://github.com/callstack/react-native-testing-library/blob/master/src/__tests__/debug.test.js) From c731da337a3f5c090291db83f12d2d1380d10b51 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Thu, 14 May 2020 12:22:38 +0200 Subject: [PATCH 8/9] Updated README.md regarding `within` operator --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6373816d4..b95b05d41 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ The [public API](https://callstack.github.io/react-native-testing-library/docs/a - [`render`](https://callstack.github.io/react-native-testing-library/docs/api#render) – deeply renders given React element and returns helpers to query the output components. - [`fireEvent`](https://callstack.github.io/react-native-testing-library/docs/api#fireevent) - invokes named event handler on the element. - [`waitForElement`](https://callstack.github.io/react-native-testing-library/docs/api#waitforelement) - waits for non-deterministic periods of time until your element appears or times out. +- [`within`](https://callstack.github.io/react-native-testing-library/docs/api#within) - creates a queries object scoped for given element. - [`flushMicrotasksQueue`](https://callstack.github.io/react-native-testing-library/docs/api#flushmicrotasksqueue) - waits for microtasks queue to flush. **Note to users who are more familiar with `react-testing-library`:** This API does not expose `cleanup` because it doesn't interact with the DOM. There's nothing to clean up. From 4b3ec798e07d87b1fe8a0f64f28077d0959eee8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 14 May 2020 12:48:13 +0200 Subject: [PATCH 9/9] Update website/docs/API.md --- website/docs/API.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/docs/API.md b/website/docs/API.md index 20899309b..f3037d779 100644 --- a/website/docs/API.md +++ b/website/docs/API.md @@ -353,6 +353,10 @@ function within(instance: ReactTestInstance): Queries Perform [queries](./Queries.md) scoped to given element. +:::note +Please note that additional `render` specific operations like `update`, `unmount`, `debug`, `toJSON` are _not_ included. +::: + ```jsx const detailsScreen = within(getByA11yHint('Details Screen')); expect(detailsScreen.getByText('Some Text')).toBeTruthy();