Skip to content

Adding getByTextWithType #569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/__tests__/__snapshots__/render.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ exports[`debug 1`] = `
>
Second Text
</Text>
<Text>
multiple
</Text>
<Text>
multiple
</Text>
<Text>
0
</Text>
Expand Down Expand Up @@ -118,6 +124,12 @@ exports[`debug changing component: bananaFresh button message should now be "fre
>
Second Text
</Text>
<Text>
multiple
</Text>
<Text>
multiple
</Text>
<Text>
0
</Text>
Expand Down Expand Up @@ -166,6 +178,12 @@ exports[`debug: shallow 1`] = `
>
Second Text
</Text>
<Text>
multiple
</Text>
<Text>
multiple
</Text>
<Text>
0
</Text>
Expand Down Expand Up @@ -216,6 +234,12 @@ exports[`debug: shallow with message 1`] = `
>
Second Text
</Text>
<Text>
multiple
</Text>
<Text>
multiple
</Text>
<Text>
0
</Text>
Expand Down Expand Up @@ -280,6 +304,12 @@ exports[`debug: with message 1`] = `
>
Second Text
</Text>
<Text>
multiple
</Text>
<Text>
multiple
</Text>
<Text>
0
</Text>
Expand Down
103 changes: 103 additions & 0 deletions src/__tests__/render.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import React from 'react';
import {
Button,
View,
Text,
TextInput,
Expand Down Expand Up @@ -73,6 +74,8 @@ class Banana extends React.Component<any, any> {
</MyButton>
<Text testID="duplicateText">First Text</Text>
<Text testID="duplicateText">Second Text</Text>
<Text>multiple</Text>
<Text>multiple</Text>
<Text>{test}</Text>
</View>
);
Expand Down Expand Up @@ -133,6 +136,9 @@ test('getByText, queryByText', () => {

expect(sameButton.props.children).toBe('not fresh');
expect(() => getByText('InExistent')).toThrow('No instances found');
expect(() => getByText('multiple')).toThrow(
'Expected 1 but found 2 instances'
);

const zeroText = getByText('0');

Expand Down Expand Up @@ -165,6 +171,103 @@ test('getByText, queryByText with children as Array', () => {
]);
});

const MyText = (props) => <Text {...props} />;

const Component = () => {
return (
<View>
<Button testID="b1" title="wow" />
<Button testID="b2" title="wow" />
<Button testID="b3" title="amazing" />
<Text testID="t1">wow</Text>
<Text testID="t2">amazing</Text>
<MyText testID="m1">wow</MyText>
<MyText testID="m2">amazing</MyText>
</View>
);
};

test('getByTextWithType', () => {
const { getByTextWithType, getByText, getAllByText } = render(<Component />);

// sanity checks
expect(getAllByText('wow')).toHaveLength(4);
expect(getAllByText('amazing')).toHaveLength(3);
expect(() => getByText('non-existent')).toThrow('No instances found');

// find Buttons
expect(getByTextWithType('amazing', Button).type).toBe(Button);
expect(getByTextWithType('amazing', Button).props.testID).toBe('b3');
expect(getByTextWithType('amazing', Button).props.title).toBe('amazing');

expect(() => getByTextWithType('wow', Button).type).toThrow(
'Expected 1 but found 2 instances'
);
expect(() => getByTextWithType('non-existent', Button)).toThrow(
'No instances found'
);

// Text is also inside a Button, so looking for a Text with "wow" returns 3 instances:
// the two Buttons plus the one Text
expect(() => getByTextWithType('wow', Text).type).toThrow(
'Expected 1 but found 4 instances'
);
expect(() => getByTextWithType('amazing', Text).type).toThrow(
'Expected 1 but found 3 instances'
);

// If we are using "higher level" component to look for our text, we can be more accurate
expect(getByTextWithType('amazing', MyText).type).toBe(MyText);
expect(getByTextWithType('amazing', MyText).props.testID).toBe('m2');
expect(getByTextWithType('amazing', MyText).props.children).toBe('amazing');
});

test('getAllByTextWithType', () => {
const { getAllByTextWithType } = render(<Component />);

// find Buttons
expect(getAllByTextWithType('wow', Button)).toHaveLength(2);
expect(getAllByTextWithType('wow', Button)[0].type).toBe(Button);
expect(getAllByTextWithType('wow', Button)[0].props.testID).toBe('b1');
expect(getAllByTextWithType('wow', Button)[0].props.title).toBe('wow');
expect(getAllByTextWithType('wow', Button)[1].type).toBe(Button);
expect(getAllByTextWithType('wow', Button)[1].props.testID).toBe('b2');
expect(getAllByTextWithType('wow', Button)[1].props.title).toBe('wow');

expect(getAllByTextWithType('amazing', Button)).toHaveLength(1);
expect(getAllByTextWithType('amazing', Button)[0].type).toBe(Button);
expect(getAllByTextWithType('amazing', Button)[0].props.testID).toBe('b3');
expect(getAllByTextWithType('amazing', Button)[0].props.title).toBe(
'amazing'
);

expect(() => getAllByTextWithType('non-existent', Button)).toThrow(
'No instances found'
);

// Text is also inside a Button, so looking for a Text with "wow" returns 3 instances:
// the two Buttons plus the one Text
expect(getAllByTextWithType('wow', Text)).toHaveLength(4);
expect(getAllByTextWithType('amazing', Text)).toHaveLength(3);
expect(getAllByTextWithType('amazing', Text)[0].props.children).toBe(
'amazing'
);
expect(getAllByTextWithType('amazing', Text)[1].props.children).toBe(
'amazing'
);
expect(getAllByTextWithType('amazing', Text)[2].props.children).toBe(
'amazing'
);

// If we are using "higher level" component to look for our text, we can be more accurate
expect(getAllByTextWithType('amazing', MyText)).toHaveLength(1);
expect(getAllByTextWithType('amazing', MyText)[0].type).toBe(MyText);
expect(getAllByTextWithType('amazing', MyText)[0].props.testID).toBe('m2');
expect(getAllByTextWithType('amazing', MyText)[0].props.children).toBe(
'amazing'
);
});

test('getAllByText, queryAllByText', () => {
const { getAllByText, queryAllByText } = render(<Banana />);
const buttons = getAllByText(/fresh/i);
Expand Down
69 changes: 69 additions & 0 deletions src/helpers/getByAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,42 @@ export const getByText = (instance: ReactTestInstance) =>
}
};

export const getByTextWithType = (instance: ReactTestInstance) =>
function getByTextWithTypeFn(
text: string | RegExp,
type: React.ComponentType<any>
) {
const nodes = instance.findAll((node) => getNodeByText(node, text));
let found = [];
for (const node of nodes) {
let n = node;
while (n !== null) {
if (n.type === type) {
found.push(n);
break;
}

n = n.parent;
}
}

if (found.length === 0) {
throw new ErrorWithStack(
`No instances found with text: "${String(text)}" with type: ${String(
type.name
)}`,
getByTextWithTypeFn
);
} else if (found.length > 1) {
throw new ErrorWithStack(
`Expected 1 but found ${found.length} instances`,
getByTextWithTypeFn
);
}

return found[0];
};

export const getByPlaceholderText = (instance: ReactTestInstance) =>
function getByPlaceholderTextFn(placeholder: string | RegExp) {
try {
Expand Down Expand Up @@ -160,6 +196,37 @@ export const getAllByText = (instance: ReactTestInstance) =>
return results;
};

export const getAllByTextWithType = (instance: ReactTestInstance) =>
function getAllByTextWithTypeFn(
text: string | RegExp,
type: React.ComponentType<any>
) {
const nodes = instance.findAll((node) => getNodeByText(node, text));
let found = [];
for (const node of nodes) {
let n = node;
while (n !== null) {
if (n.type === type) {
found.push(n);
break;
}

n = n.parent;
}
}

if (found.length === 0) {
throw new ErrorWithStack(
`No instances found with text: "${String(text)}" with type: ${String(
type.name
)}`,
getAllByTextWithTypeFn
);
}

return found;
};

export const getAllByPlaceholderText = (instance: ReactTestInstance) =>
function getAllByPlaceholderTextFn(placeholder: string | RegExp) {
const results = instance.findAll((node) =>
Expand Down Expand Up @@ -244,10 +311,12 @@ export const UNSAFE_getAllByProps = (instance: ReactTestInstance) =>

export const getByAPI = (instance: ReactTestInstance) => ({
getByText: getByText(instance),
getByTextWithType: getByTextWithType(instance),
getByPlaceholderText: getByPlaceholderText(instance),
getByDisplayValue: getByDisplayValue(instance),
getByTestId: getByTestId(instance),
getAllByText: getAllByText(instance),
getAllByTextWithType: getAllByTextWithType(instance),
getAllByPlaceholderText: getAllByPlaceholderText(instance),
getAllByDisplayValue: getAllByDisplayValue(instance),
getAllByTestId: getAllByTestId(instance),
Expand Down
1 change: 1 addition & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type FindAllReturn = Promise<ReactTestInstance[]>;

interface GetByAPI {
getByText: (text: string | RegExp) => ReactTestInstance;
getByTextWithType: <P>(text: string | RegExp, type: React.ComponentType<P>) => ReactTestInstance;
getByPlaceholderText: (placeholder: string | RegExp) => ReactTestInstance;
getByDisplayValue: (value: string | RegExp) => ReactTestInstance;
getByTestId: (testID: string | RegExp) => ReactTestInstance;
Expand Down