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
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
@@ -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>;
};
@@ -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);
@@ -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
@@ -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')
);
});

@@ -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);
@@ -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
@@ -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')
);
});

@@ -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);
@@ -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);
});
@@ -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
@@ -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();

@@ -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([]);
});

@@ -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(
@@ -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);
@@ -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([]);

@@ -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
);
@@ -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);
@@ -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
@@ -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);
});
10 changes: 7 additions & 3 deletions src/__tests__/render.test.js
Original file line number Diff line number Diff line change
@@ -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');

@@ -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);
@@ -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();
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);
@@ -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')) {
16 changes: 11 additions & 5 deletions src/helpers/getByAPI.js
Original file line number Diff line number Diff line change
@@ -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
);
}
};

@@ -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
);
}
};

10 changes: 8 additions & 2 deletions src/helpers/makeQuery.js
Original file line number Diff line number Diff line change
@@ -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
);
}
};

@@ -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;