From fead652013d1dae5542a4e80b57e4563f6946503 Mon Sep 17 00:00:00 2001 From: Scotty Waggoner Date: Wed, 3 Jul 2019 01:17:32 -0700 Subject: [PATCH] feat: add `getAllByTestId` and `queryAllByTestId` queries (#198) * feat: add getAllByTestId and queryAllByTestId queries * Add test for getAllByTestId and queryAllByTestId - Filter out intermediate components in getAllByTestId. testIDs may be passed down to child components and appearing multiple times in the render tree. Some React Native components render multiple anyways. is an example. * Update TypeScript typings * Fix flow types * Update TS typings test --- .../__snapshots__/render.test.js.snap | 50 +++++++++++++++++++ src/__tests__/render.test.js | 23 ++++++++- src/helpers/getByAPI.js | 16 ++++++ src/helpers/queryByAPI.js | 12 +++++ typings/__tests__/index.test.tsx | 4 ++ typings/index.d.ts | 2 + 6 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/__tests__/__snapshots__/render.test.js.snap b/src/__tests__/__snapshots__/render.test.js.snap index 29c1bfa1..9839db4d 100644 --- a/src/__tests__/__snapshots__/render.test.js.snap +++ b/src/__tests__/__snapshots__/render.test.js.snap @@ -45,6 +45,16 @@ exports[`debug 1`] = ` Change freshness! + + First Text + + + Second Text + " `; @@ -93,6 +103,16 @@ exports[`debug changing component: bananaFresh button message should now be "fre Change freshness! + + First Text + + + Second Text + " `; @@ -128,6 +148,16 @@ exports[`debug: shallow 1`] = ` > Change freshness! + + First Text + + + Second Text + " `; @@ -165,6 +195,16 @@ exports[`debug: shallow with message 1`] = ` > Change freshness! + + First Text + + + Second Text + " `; @@ -215,6 +255,16 @@ exports[`debug: with message 1`] = ` Change freshness! + + First Text + + + Second Text + " `; diff --git a/src/__tests__/render.test.js b/src/__tests__/render.test.js index 8879b90e..372e19fd 100644 --- a/src/__tests__/render.test.js +++ b/src/__tests__/render.test.js @@ -71,6 +71,8 @@ class Banana extends React.Component<*, *> { + First Text + Second Text ); } @@ -87,6 +89,25 @@ test('getByTestId, queryByTestId', () => { expect(queryByTestId('InExistent')).toBeNull(); }); +test('getAllByTestId, queryAllByTestId', () => { + const { getAllByTestId, queryAllByTestId } = render(); + const textElements = getAllByTestId('duplicateText'); + + expect(textElements.length).toBe(2); + expect(textElements[0].props.children).toBe('First Text'); + expect(textElements[1].props.children).toBe('Second Text'); + expect(() => getAllByTestId('nonExistentTestId')).toThrow( + 'No instances found' + ); + + const queriedTextElements = queryAllByTestId('duplicateText'); + + expect(queriedTextElements.length).toBe(2); + expect(queriedTextElements[0]).toBe(textElements[0]); + expect(queriedTextElements[1]).toBe(textElements[1]); + expect(queryAllByTestId('nonExistentTestId')).toHaveLength(0); +}); + test('getByName, queryByName', () => { const { getByTestId, getByName, queryByName } = render(); const bananaFresh = getByTestId('bananaFresh'); @@ -101,7 +122,7 @@ test('getByName, queryByName', () => { expect(bananaFresh.props.children).toBe('not fresh'); expect(() => getByName('InExistent')).toThrow('No instances found'); - expect(() => getByName(Text)).toThrow('Expected 1 but found 3'); + expect(() => getByName(Text)).toThrow('Expected 1 but found 5'); expect(queryByName('Button')).toBe(button); expect(queryByName('InExistent')).toBeNull(); diff --git a/src/helpers/getByAPI.js b/src/helpers/getByAPI.js index c0a219c8..0bf99d16 100644 --- a/src/helpers/getByAPI.js +++ b/src/helpers/getByAPI.js @@ -212,6 +212,21 @@ export const getAllByProps = (instance: ReactTestInstance) => return results; }; +export const getAllByTestId = (instance: ReactTestInstance) => + function getAllByTestIdFn(testID: string): ReactTestInstance[] { + const results = instance + .findAllByProps({ testID }) + .filter(element => typeof element.type === 'string'); + + if (results.length === 0) { + throw new ErrorWithStack( + `No instances found with testID: ${String(testID)}`, + getAllByTestIdFn + ); + } + return results; + }; + export const getByAPI = (instance: ReactTestInstance) => ({ getByTestId: getByTestId(instance), getByName: getByName(instance), @@ -220,6 +235,7 @@ export const getByAPI = (instance: ReactTestInstance) => ({ getByPlaceholder: getByPlaceholder(instance), getByDisplayValue: getByDisplayValue(instance), getByProps: getByProps(instance), + getAllByTestId: getAllByTestId(instance), getAllByName: getAllByName(instance), getAllByType: getAllByType(instance), getAllByText: getAllByText(instance), diff --git a/src/helpers/queryByAPI.js b/src/helpers/queryByAPI.js index 95c6c5c0..33c146ad 100644 --- a/src/helpers/queryByAPI.js +++ b/src/helpers/queryByAPI.js @@ -8,6 +8,7 @@ import { getByPlaceholder, getByDisplayValue, getByProps, + getAllByTestId, getAllByName, getAllByType, getAllByText, @@ -142,6 +143,16 @@ export const queryAllByProps = (instance: ReactTestInstance) => (props: { } }; +export const queryAllByTestId = (instance: ReactTestInstance) => ( + testID: string +) => { + try { + return getAllByTestId(instance)(testID); + } catch (error) { + return []; + } +}; + export const queryByAPI = (instance: ReactTestInstance) => ({ queryByTestId: queryByTestId(instance), queryByName: queryByName(instance), @@ -150,6 +161,7 @@ export const queryByAPI = (instance: ReactTestInstance) => ({ queryByPlaceholder: queryByPlaceholder(instance), queryByDisplayValue: queryByDisplayValue(instance), queryByProps: queryByProps(instance), + queryAllByTestId: queryAllByTestId(instance), queryAllByName: queryAllByName(instance), queryAllByType: queryAllByType(instance), queryAllByText: queryAllByText(instance), diff --git a/typings/__tests__/index.test.tsx b/typings/__tests__/index.test.tsx index c06112a8..2ea7d1ec 100644 --- a/typings/__tests__/index.test.tsx +++ b/typings/__tests__/index.test.tsx @@ -54,6 +54,7 @@ const getByDisplayValueRegExp: ReactTestInstance = tree.getByDisplayValue( ); const getByProps: ReactTestInstance = tree.getByProps({ value: 2 }); const getByTestId: ReactTestInstance = tree.getByTestId('test-id'); +const getAllByTestId: ReactTestInstance[] = tree.getAllByTestId('test-id'); const getAllByNameString: Array = tree.getAllByName('View'); const getAllByNameConstructor: Array = tree.getAllByName( View @@ -93,6 +94,9 @@ const queryByDisplayValueRegExp: ReactTestInstance | null = tree.queryByDisplayV ); const queryByProps: ReactTestInstance | null = tree.queryByProps({ value: 2 }); const queryByTestId: ReactTestInstance | null = tree.queryByTestId('test-id'); +const queryAllByTestId: ReactTestInstance[] | null = tree.queryAllByTestId( + 'test-id' +); const queryAllByNameString: Array = tree.queryAllByName( 'View' ); diff --git a/typings/index.d.ts b/typings/index.d.ts index 83846033..d78bd884 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -9,6 +9,7 @@ export interface GetByAPI { getByDisplayValue: (value: string | RegExp) => ReactTestInstance; getByProps: (props: Record) => ReactTestInstance; getByTestId: (testID: string) => ReactTestInstance; + getAllByTestId: (testID: string) => Array; getAllByName: (name: React.ReactType | string) => Array; getAllByType:

(type: React.ComponentType

) => Array; getAllByText: (text: string | RegExp) => Array; @@ -29,6 +30,7 @@ export interface QueryByAPI { queryByDisplayValue: (value: string | RegExp) => ReactTestInstance | null; queryByProps: (props: Record) => ReactTestInstance | null; queryByTestId: (testID: string) => ReactTestInstance | null; + queryAllByTestId: (testID: string) => Array | null; queryAllByName: ( name: React.ReactType | string ) => Array | [];