Skip to content

feat: add the value expected in getBy error messages #550

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

Merged
Merged
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
96 changes: 73 additions & 23 deletions src/__tests__/a11yAPI.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ const TEXT_LABEL = 'cool text';
const TEXT_HINT = 'static text';
// Little hack to make all the methods happy with type
const NO_MATCHES_TEXT: any = 'not-existent-element';
const NO_INSTANCES_FOUND = 'No instances found';
const FOUND_TWO_INSTANCES = 'Expected 1 but found 2 instances';

const getNoInstancesFoundMessage = (
name: string,
value: string = NO_MATCHES_TEXT,
includeQuotes: boolean = true
) => {
const quote = includeQuotes ? '"' : '';
return `No instances found with ${name} ${quote}${value}${quote}`;
};

const Typography = ({ children, ...rest }: any) => {
return <Text {...rest}>{children}</Text>;
};
Expand Down Expand Up @@ -73,7 +81,9 @@ test('getByA11yLabel, queryByA11yLabel, findByA11yLabel', async () => {
const button = queryByA11yLabel(/button/g);
expect(button && button.props.accessibilityLabel).toEqual(BUTTON_LABEL);

expect(() => getByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yLabel(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityLabel')
);
expect(queryByA11yLabel(NO_MATCHES_TEXT)).toBeNull();

expect(() => getByA11yLabel(TEXT_LABEL)).toThrow(FOUND_TWO_INSTANCES);
Expand All @@ -83,7 +93,7 @@ test('getByA11yLabel, queryByA11yLabel, findByA11yLabel', async () => {
expect(asyncButton.props.accessibilityLabel).toEqual(BUTTON_LABEL);
await expect(
findByA11yLabel(NO_MATCHES_TEXT, waitForOptions)
).rejects.toThrow(NO_INSTANCES_FOUND);
).rejects.toThrow(getNoInstancesFoundMessage('accessibilityLabel'));

await expect(findByA11yLabel(TEXT_LABEL, waitForOptions)).rejects.toThrow(
FOUND_TWO_INSTANCES
Expand All @@ -98,12 +108,14 @@ test('getAllByA11yLabel, queryAllByA11yLabel, findAllByA11yLabel', async () => {
expect(getAllByA11yLabel(TEXT_LABEL)).toHaveLength(2);
expect(queryAllByA11yLabel(/cool/g)).toHaveLength(3);

expect(() => getAllByA11yLabel(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getAllByA11yLabel(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityLabel')
);
expect(queryAllByA11yLabel(NO_MATCHES_TEXT)).toEqual([]);

await expect(findAllByA11yLabel(TEXT_LABEL)).resolves.toHaveLength(2);
await expect(findAllByA11yLabel(NO_MATCHES_TEXT)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityLabel')
);
});

Expand All @@ -118,7 +130,9 @@ test('getByA11yHint, queryByA11yHint, findByA11yHint', async () => {
const button = queryByA11yHint(/button/g);
expect(button && button.props.accessibilityHint).toEqual(BUTTON_HINT);

expect(() => getByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yHint(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityHint')
);
expect(queryByA11yHint(NO_MATCHES_TEXT)).toBeNull();

expect(() => getByA11yHint(TEXT_HINT)).toThrow(FOUND_TWO_INSTANCES);
Expand All @@ -127,7 +141,7 @@ test('getByA11yHint, queryByA11yHint, findByA11yHint', async () => {
const asyncButton = await findByA11yHint(BUTTON_HINT);
expect(asyncButton.props.accessibilityHint).toEqual(BUTTON_HINT);
await expect(findByA11yHint(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityHint')
);
await expect(findByA11yHint(TEXT_HINT, waitForOptions)).rejects.toThrow(
FOUND_TWO_INSTANCES
Expand All @@ -142,12 +156,14 @@ test('getAllByA11yHint, queryAllByA11yHint, findAllByA11yHint', async () => {
expect(getAllByA11yHint(TEXT_HINT)).toHaveLength(2);
expect(queryAllByA11yHint(/static/g)).toHaveLength(2);

expect(() => getAllByA11yHint(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getAllByA11yHint(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityHint')
);
expect(queryAllByA11yHint(NO_MATCHES_TEXT)).toEqual([]);

await expect(findAllByA11yHint(TEXT_HINT)).resolves.toHaveLength(2);
await expect(findAllByA11yHint(NO_MATCHES_TEXT)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityHint')
);
});

Expand All @@ -160,7 +176,9 @@ test('getByA11yRole, queryByA11yRole, findByA11yRole', async () => {
const button = queryByA11yRole(/button/g);
expect(button && button.props.accessibilityRole).toEqual('button');

expect(() => getByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yRole(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityRole')
);
expect(queryByA11yRole(NO_MATCHES_TEXT)).toBeNull();

expect(() => getByA11yRole('link')).toThrow(FOUND_TWO_INSTANCES);
Expand All @@ -169,7 +187,7 @@ test('getByA11yRole, queryByA11yRole, findByA11yRole', async () => {
const asyncButton = await findByA11yRole('button');
expect(asyncButton.props.accessibilityRole).toEqual('button');
await expect(findByA11yRole(NO_MATCHES_TEXT, waitForOptions)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityRole')
);
await expect(findByA11yRole('link')).rejects.toThrow(FOUND_TWO_INSTANCES);
});
Expand All @@ -182,13 +200,15 @@ test('getAllByA11yRole, queryAllByA11yRole, findAllByA11yRole', async () => {
expect(getAllByA11yRole('link')).toHaveLength(2);
expect(queryAllByA11yRole(/ink/g)).toHaveLength(2);

expect(() => getAllByA11yRole(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getAllByA11yRole(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityRole')
);
expect(queryAllByA11yRole(NO_MATCHES_TEXT)).toEqual([]);

await expect(findAllByA11yRole('link')).resolves.toHaveLength(2);
await expect(
findAllByA11yRole(NO_MATCHES_TEXT, waitForOptions)
).rejects.toThrow(NO_INSTANCES_FOUND);
).rejects.toThrow(getNoInstancesFoundMessage('accessibilityRole'));
});

// TODO: accessibilityStates was removed from RN 0.62
Expand All @@ -209,7 +229,9 @@ test.skip('getByA11yStates, queryByA11yStates', () => {
disabledSelected && disabledSelected.props.accessibilityStates
).toEqual(['selected', 'disabled']);

expect(() => getByA11yStates(NO_MATCHES_TEXT)).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yStates(NO_MATCHES_TEXT)).toThrow(
getNoInstancesFoundMessage('accessibilityStates')
);
expect(queryByA11yStates(NO_MATCHES_TEXT)).toBeNull();
expect(queryByA11yStates([])).toBeNull();

Expand All @@ -224,7 +246,9 @@ test.skip('getAllByA11yStates, queryAllByA11yStates', () => {
expect(getAllByA11yStates('selected')).toHaveLength(3);
expect(queryAllByA11yStates(['selected'])).toHaveLength(3);

expect(() => getAllByA11yStates([])).toThrow(NO_INSTANCES_FOUND);
expect(() => getAllByA11yStates([])).toThrow(
getNoInstancesFoundMessage('accessibilityStates')
);
expect(queryAllByA11yStates(NO_MATCHES_TEXT)).toEqual([]);
});

Expand All @@ -244,7 +268,13 @@ test('getByA11yState, queryByA11yState, findByA11yState', async () => {
expanded: false,
});

expect(() => getByA11yState({ disabled: true })).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yState({ disabled: true })).toThrow(
getNoInstancesFoundMessage(
'accessibilityState',
'{"disabled": true}',
false
)
);
expect(queryByA11yState({ disabled: true })).toEqual(null);

expect(() => getByA11yState({ expanded: false })).toThrow(
Expand All @@ -261,7 +291,13 @@ test('getByA11yState, queryByA11yState, findByA11yState', async () => {
});
await expect(
findByA11yState({ disabled: true }, waitForOptions)
).rejects.toThrow(NO_INSTANCES_FOUND);
).rejects.toThrow(
getNoInstancesFoundMessage(
'accessibilityState',
'{"disabled": true}',
false
)
);
await expect(
findByA11yState({ expanded: false }, waitForOptions)
).rejects.toThrow(FOUND_TWO_INSTANCES);
Expand All @@ -276,7 +312,11 @@ test('getAllByA11yState, queryAllByA11yState, findAllByA11yState', async () => {
expect(queryAllByA11yState({ selected: true })).toHaveLength(1);

expect(() => getAllByA11yState({ disabled: true })).toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage(
'accessibilityState',
'{"disabled": true}',
false
)
);
expect(queryAllByA11yState({ disabled: true })).toEqual([]);

Expand All @@ -286,7 +326,13 @@ test('getAllByA11yState, queryAllByA11yState, findAllByA11yState', async () => {
await expect(findAllByA11yState({ selected: true })).resolves.toHaveLength(1);
await expect(
findAllByA11yState({ disabled: true }, waitForOptions)
).rejects.toThrow(NO_INSTANCES_FOUND);
).rejects.toThrow(
getNoInstancesFoundMessage(
'accessibilityState',
'{"disabled": true}',
false
)
);
await expect(findAllByA11yState({ expanded: false })).resolves.toHaveLength(
2
);
Expand All @@ -306,7 +352,9 @@ test('getByA11yValue, queryByA11yValue, findByA11yValue', async () => {
max: 60,
});

expect(() => getByA11yValue({ min: 50 })).toThrow(NO_INSTANCES_FOUND);
expect(() => getByA11yValue({ min: 50 })).toThrow(
getNoInstancesFoundMessage('accessibilityValue', '{"min": 50}', false)
);
expect(queryByA11yValue({ min: 50 })).toEqual(null);

expect(() => getByA11yValue({ max: 60 })).toThrow(FOUND_TWO_INSTANCES);
Expand All @@ -318,7 +366,7 @@ test('getByA11yValue, queryByA11yValue, findByA11yValue', async () => {
max: 60,
});
await expect(findByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityValue', '{"min": 50}', false)
);
await expect(findByA11yValue({ max: 60 }, waitForOptions)).rejects.toThrow(
FOUND_TWO_INSTANCES
Expand All @@ -333,15 +381,17 @@ test('getAllByA11yValue, queryAllByA11yValue, findAllByA11yValue', async () => {
expect(getAllByA11yValue({ min: 40 })).toHaveLength(1);
expect(queryAllByA11yValue({ min: 40 })).toHaveLength(1);

expect(() => getAllByA11yValue({ min: 50 })).toThrow(NO_INSTANCES_FOUND);
expect(() => getAllByA11yValue({ min: 50 })).toThrow(
getNoInstancesFoundMessage('accessibilityValue', '{"min": 50}', false)
);
expect(queryAllByA11yValue({ min: 50 })).toEqual([]);

expect(queryAllByA11yValue({ max: 60 })).toHaveLength(2);
expect(getAllByA11yValue({ max: 60 })).toHaveLength(2);

await expect(findAllByA11yValue({ min: 40 })).resolves.toHaveLength(1);
await expect(findAllByA11yValue({ min: 50 }, waitForOptions)).rejects.toThrow(
NO_INSTANCES_FOUND
getNoInstancesFoundMessage('accessibilityValue', '{"min": 50}', false)
);
await expect(findAllByA11yValue({ max: 60 })).resolves.toHaveLength(2);
});
Expand Down
10 changes: 7 additions & 3 deletions src/__tests__/render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ test('getByText, queryByText', () => {
const sameButton = getByText('not fresh');

expect(sameButton.props.children).toBe('not fresh');
expect(() => getByText('InExistent')).toThrow('No instances found');
expect(() => getByText('InExistent')).toThrow(
'No instances found with text "InExistent"'
);

const zeroText = getByText('0');

Expand Down Expand Up @@ -186,7 +188,7 @@ test('getByPlaceholderText, queryByPlaceholderText', () => {

expect(sameInput.props.placeholder).toBe(PLACEHOLDER_FRESHNESS);
expect(() => getByPlaceholderText('no placeholder')).toThrow(
'No instances found'
'No instances found with placeholder "no placeholder"'
);

expect(queryByPlaceholderText(/add/i)).toBe(input);
Expand Down Expand Up @@ -220,7 +222,9 @@ test('getByDisplayValue, queryByDisplayValue', () => {
const sameInput = getByDisplayValue(INPUT_FRESHNESS);

expect(sameInput.props.value).toBe(INPUT_FRESHNESS);
expect(() => getByDisplayValue('no value')).toThrow('No instances found');
expect(() => getByDisplayValue('no value')).toThrow(
'No instances found with display value "no value"'
);

expect(queryByDisplayValue(/custom/i)).toBe(input);
expect(queryByDisplayValue('no value')).toBeNull();
Expand Down
19 changes: 17 additions & 2 deletions src/helpers/errors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// @flow
import prettyFormat from 'pretty-format';

export class ErrorWithStack extends Error {
constructor(message: ?string, callsite: Function) {
super(message);
Expand All @@ -13,9 +15,22 @@ export const createLibraryNotSupportedError = (error: Error) =>
`Currently the only supported library to search by text is "react-native".\n\n${error.message}`
);

export const prepareErrorMessage = (error: Error) =>
export const prepareErrorMessage = (
error: Error,
name: ?string,
value: ?mixed
) => {
// Strip info about custom predicate
error.message.replace(/ matching custom predicate[^]*/gm, '');
let errorMessage = error.message.replace(
/ matching custom predicate[^]*/gm,
''
);

if (name && value) {
errorMessage += ` with ${name} ${prettyFormat(value, { min: true })}`;
}
return errorMessage;
};

export const createQueryByError = (error: Error, callsite: Function) => {
if (error.message.includes('No instances found')) {
Expand Down
16 changes: 11 additions & 5 deletions src/helpers/getByAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ export const getByText = (instance: ReactTestInstance) =>
try {
return instance.find((node) => getNodeByText(node, text));
} catch (error) {
throw new ErrorWithStack(prepareErrorMessage(error), getByTextFn);
throw new ErrorWithStack(
prepareErrorMessage(error, 'text', text),
getByTextFn
);
}
};

Expand All @@ -112,20 +115,23 @@ export const getByPlaceholderText = (instance: ReactTestInstance) =>
);
} catch (error) {
throw new ErrorWithStack(
prepareErrorMessage(error),
prepareErrorMessage(error, 'placeholder', placeholder),
getByPlaceholderTextFn
);
}
};

export const getByDisplayValue = (instance: ReactTestInstance) =>
function getByDisplayValueFn(placeholder: string | RegExp) {
function getByDisplayValueFn(displayValue: string | RegExp) {
try {
return instance.find((node) =>
getTextInputNodeByDisplayValue(node, placeholder)
getTextInputNodeByDisplayValue(node, displayValue)
);
} catch (error) {
throw new ErrorWithStack(prepareErrorMessage(error), getByDisplayValueFn);
throw new ErrorWithStack(
prepareErrorMessage(error, 'display value', displayValue),
getByDisplayValueFn
);
}
};

Expand Down
10 changes: 8 additions & 2 deletions src/helpers/makeQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ const makeQuery = <P: mixed, M: mixed>(
(node) => isNodeValid(node) && matcherFn(node.props[name], matcher)
);
} catch (error) {
throw new ErrorWithStack(prepareErrorMessage(error), getBy);
throw new ErrorWithStack(
prepareErrorMessage(error, name, matcher),
getBy
);
}
};

Expand All @@ -47,7 +50,10 @@ const makeQuery = <P: mixed, M: mixed>(
);

if (results.length === 0) {
throw new ErrorWithStack('No instances found', getAllBy);
throw new ErrorWithStack(
prepareErrorMessage(new Error('No instances found'), name, matcher),
getAllBy
);
}

return results;
Expand Down