Skip to content

Commit

Permalink
Improve scry() error message for bad first argument (#13351)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Aug 9, 2018
1 parent d59b993 commit 0072b59
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 4 deletions.
56 changes: 56 additions & 0 deletions packages/react-dom/src/__tests__/ReactTestUtils-test.js
Expand Up @@ -281,6 +281,62 @@ describe('ReactTestUtils', () => {
expect(hrs.length).toBe(2);
});

it('provides a clear error when passing invalid objects to scry', () => {
// This is probably too relaxed but it's existing behavior.
ReactTestUtils.findAllInRenderedTree(null, 'span');
ReactTestUtils.findAllInRenderedTree(undefined, 'span');
ReactTestUtils.findAllInRenderedTree('', 'span');
ReactTestUtils.findAllInRenderedTree(0, 'span');
ReactTestUtils.findAllInRenderedTree(false, 'span');

expect(() => {
ReactTestUtils.findAllInRenderedTree([], 'span');
}).toThrow(
'findAllInRenderedTree(...): the first argument must be a React class instance. ' +
'Instead received: an array.',
);
expect(() => {
ReactTestUtils.scryRenderedDOMComponentsWithClass(10, 'button');
}).toThrow(
'scryRenderedDOMComponentsWithClass(...): the first argument must be a React class instance. ' +
'Instead received: 10.',
);
expect(() => {
ReactTestUtils.findRenderedDOMComponentWithClass('hello', 'button');
}).toThrow(
'findRenderedDOMComponentWithClass(...): the first argument must be a React class instance. ' +
'Instead received: hello.',
);
expect(() => {
ReactTestUtils.scryRenderedDOMComponentsWithTag(
{x: true, y: false},
'span',
);
}).toThrow(
'scryRenderedDOMComponentsWithTag(...): the first argument must be a React class instance. ' +
'Instead received: object with keys {x, y}.',
);
const div = document.createElement('div');
expect(() => {
ReactTestUtils.findRenderedDOMComponentWithTag(div, 'span');
}).toThrow(
'findRenderedDOMComponentWithTag(...): the first argument must be a React class instance. ' +
'Instead received: a DOM node.',
);
expect(() => {
ReactTestUtils.scryRenderedComponentsWithType(true, 'span');
}).toThrow(
'scryRenderedComponentsWithType(...): the first argument must be a React class instance. ' +
'Instead received: true.',
);
expect(() => {
ReactTestUtils.findRenderedComponentWithType(true, 'span');
}).toThrow(
'findRenderedComponentWithType(...): the first argument must be a React class instance. ' +
'Instead received: true.',
);
});

describe('Simulate', () => {
it('should change the value of an input field', () => {
const obj = {
Expand Down
40 changes: 36 additions & 4 deletions packages/react-dom/src/test-utils/ReactTestUtils.js
Expand Up @@ -107,6 +107,35 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
}
}

function validateClassInstance(inst, methodName) {
if (!inst) {
// This is probably too relaxed but it's existing behavior.
return;
}
if (ReactInstanceMap.get(inst)) {
// This is a public instance indeed.
return;
}
let received;
const stringified = '' + inst;
if (Array.isArray(inst)) {
received = 'an array';
} else if (inst && inst.nodeType === 1 && inst.tagName) {
received = 'a DOM node';
} else if (stringified === '[object Object]') {
received = 'object with keys {' + Object.keys(inst).join(', ') + '}';
} else {
received = stringified;
}
invariant(
false,
'%s(...): the first argument must be a React class instance. ' +
'Instead received: %s.',
methodName,
received,
);
}

/**
* Utilities for making it easy to test React components.
*
Expand Down Expand Up @@ -166,13 +195,10 @@ const ReactTestUtils = {
},

findAllInRenderedTree: function(inst, test) {
validateClassInstance(inst, 'findAllInRenderedTree');
if (!inst) {
return [];
}
invariant(
ReactTestUtils.isCompositeComponent(inst),
'findAllInRenderedTree(...): instance must be a composite component',
);
const internalInstance = ReactInstanceMap.get(inst);
return findAllInRenderedFiberTreeInternal(internalInstance, test);
},
Expand All @@ -183,6 +209,7 @@ const ReactTestUtils = {
* @return {array} an array of all the matches.
*/
scryRenderedDOMComponentsWithClass: function(root, classNames) {
validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
if (ReactTestUtils.isDOMComponent(inst)) {
let className = inst.className;
Expand Down Expand Up @@ -215,6 +242,7 @@ const ReactTestUtils = {
* @return {!ReactDOMComponent} The one match.
*/
findRenderedDOMComponentWithClass: function(root, className) {
validateClassInstance(root, 'findRenderedDOMComponentWithClass');
const all = ReactTestUtils.scryRenderedDOMComponentsWithClass(
root,
className,
Expand All @@ -237,6 +265,7 @@ const ReactTestUtils = {
* @return {array} an array of all the matches.
*/
scryRenderedDOMComponentsWithTag: function(root, tagName) {
validateClassInstance(root, 'scryRenderedDOMComponentsWithTag');
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
return (
ReactTestUtils.isDOMComponent(inst) &&
Expand All @@ -252,6 +281,7 @@ const ReactTestUtils = {
* @return {!ReactDOMComponent} The one match.
*/
findRenderedDOMComponentWithTag: function(root, tagName) {
validateClassInstance(root, 'findRenderedDOMComponentWithTag');
const all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
if (all.length !== 1) {
throw new Error(
Expand All @@ -270,6 +300,7 @@ const ReactTestUtils = {
* @return {array} an array of all the matches.
*/
scryRenderedComponentsWithType: function(root, componentType) {
validateClassInstance(root, 'scryRenderedComponentsWithType');
return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
});
Expand All @@ -282,6 +313,7 @@ const ReactTestUtils = {
* @return {!ReactComponent} The one match.
*/
findRenderedComponentWithType: function(root, componentType) {
validateClassInstance(root, 'findRenderedComponentWithType');
const all = ReactTestUtils.scryRenderedComponentsWithType(
root,
componentType,
Expand Down

0 comments on commit 0072b59

Please sign in to comment.