@samitouri / QOS-React / commits / 0072b59984

Improve scry() error message for bad first argument (#13351)

Dan Abramov committed Aug 9, 2018 at 15:05 UTC 0072b59984f0743b95664557c17904e8c0912ec5
2 files changed +92 -4
packages/react-dom/src/__tests__/ReactTestUtils-test.js
+56
@@ -281,6 +281,62 @@ describe('ReactTestUtils', () => {
281 expect(hrs.length).toBe(2);
282 });
283
284 + it('provides a clear error when passing invalid objects to scry', () => {
285 + // This is probably too relaxed but it's existing behavior.
286 + ReactTestUtils.findAllInRenderedTree(null, 'span');
287 + ReactTestUtils.findAllInRenderedTree(undefined, 'span');
288 + ReactTestUtils.findAllInRenderedTree('', 'span');
289 + ReactTestUtils.findAllInRenderedTree(0, 'span');
290 + ReactTestUtils.findAllInRenderedTree(false, 'span');
291 +
292 + expect(() => {
293 + ReactTestUtils.findAllInRenderedTree([], 'span');
294 + }).toThrow(
295 + 'findAllInRenderedTree(...): the first argument must be a React class instance. ' +
296 + 'Instead received: an array.',
297 + );
298 + expect(() => {
299 + ReactTestUtils.scryRenderedDOMComponentsWithClass(10, 'button');
300 + }).toThrow(
301 + 'scryRenderedDOMComponentsWithClass(...): the first argument must be a React class instance. ' +
302 + 'Instead received: 10.',
303 + );
304 + expect(() => {
305 + ReactTestUtils.findRenderedDOMComponentWithClass('hello', 'button');
306 + }).toThrow(
307 + 'findRenderedDOMComponentWithClass(...): the first argument must be a React class instance. ' +
308 + 'Instead received: hello.',
309 + );
310 + expect(() => {
311 + ReactTestUtils.scryRenderedDOMComponentsWithTag(
312 + {x: true, y: false},
313 + 'span',
314 + );
315 + }).toThrow(
316 + 'scryRenderedDOMComponentsWithTag(...): the first argument must be a React class instance. ' +
317 + 'Instead received: object with keys {x, y}.',
318 + );
319 + const div = document.createElement('div');
320 + expect(() => {
321 + ReactTestUtils.findRenderedDOMComponentWithTag(div, 'span');
322 + }).toThrow(
323 + 'findRenderedDOMComponentWithTag(...): the first argument must be a React class instance. ' +
324 + 'Instead received: a DOM node.',
325 + );
326 + expect(() => {
327 + ReactTestUtils.scryRenderedComponentsWithType(true, 'span');
328 + }).toThrow(
329 + 'scryRenderedComponentsWithType(...): the first argument must be a React class instance. ' +
330 + 'Instead received: true.',
331 + );
332 + expect(() => {
333 + ReactTestUtils.findRenderedComponentWithType(true, 'span');
334 + }).toThrow(
335 + 'findRenderedComponentWithType(...): the first argument must be a React class instance. ' +
336 + 'Instead received: true.',
337 + );
338 + });
339 +
340 describe('Simulate', () => {
341 it('should change the value of an input field', () => {
342 const obj = {
packages/react-dom/src/test-utils/ReactTestUtils.js
+36 -4
@@ -107,6 +107,35 @@ function findAllInRenderedFiberTreeInternal(fiber, test) {
107 }
108 }
109
110 +function validateClassInstance(inst, methodName) {
111 + if (!inst) {
112 + // This is probably too relaxed but it's existing behavior.
113 + return;
114 + }
115 + if (ReactInstanceMap.get(inst)) {
116 + // This is a public instance indeed.
117 + return;
118 + }
119 + let received;
120 + const stringified = '' + inst;
121 + if (Array.isArray(inst)) {
122 + received = 'an array';
123 + } else if (inst && inst.nodeType === 1 && inst.tagName) {
124 + received = 'a DOM node';
125 + } else if (stringified === '[object Object]') {
126 + received = 'object with keys {' + Object.keys(inst).join(', ') + '}';
127 + } else {
128 + received = stringified;
129 + }
130 + invariant(
131 + false,
132 + '%s(...): the first argument must be a React class instance. ' +
133 + 'Instead received: %s.',
134 + methodName,
135 + received,
136 + );
137 +}
138 +
139 /**
140 * Utilities for making it easy to test React components.
141 *
@@ -166,13 +195,10 @@ const ReactTestUtils = {
195 },
196
197 findAllInRenderedTree: function(inst, test) {
198 + validateClassInstance(inst, 'findAllInRenderedTree');
199 if (!inst) {
200 return [];
201 }
172 - invariant(
173 - ReactTestUtils.isCompositeComponent(inst),
174 - 'findAllInRenderedTree(...): instance must be a composite component',
175 - );
202 const internalInstance = ReactInstanceMap.get(inst);
203 return findAllInRenderedFiberTreeInternal(internalInstance, test);
204 },
@@ -183,6 +209,7 @@ const ReactTestUtils = {
209 * @return {array} an array of all the matches.
210 */
211 scryRenderedDOMComponentsWithClass: function(root, classNames) {
212 + validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
213 return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
214 if (ReactTestUtils.isDOMComponent(inst)) {
215 let className = inst.className;
@@ -215,6 +242,7 @@ const ReactTestUtils = {
242 * @return {!ReactDOMComponent} The one match.
243 */
244 findRenderedDOMComponentWithClass: function(root, className) {
245 + validateClassInstance(root, 'findRenderedDOMComponentWithClass');
246 const all = ReactTestUtils.scryRenderedDOMComponentsWithClass(
247 root,
248 className,
@@ -237,6 +265,7 @@ const ReactTestUtils = {
265 * @return {array} an array of all the matches.
266 */
267 scryRenderedDOMComponentsWithTag: function(root, tagName) {
268 + validateClassInstance(root, 'scryRenderedDOMComponentsWithTag');
269 return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
270 return (
271 ReactTestUtils.isDOMComponent(inst) &&
@@ -252,6 +281,7 @@ const ReactTestUtils = {
281 * @return {!ReactDOMComponent} The one match.
282 */
283 findRenderedDOMComponentWithTag: function(root, tagName) {
284 + validateClassInstance(root, 'findRenderedDOMComponentWithTag');
285 const all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
286 if (all.length !== 1) {
287 throw new Error(
@@ -270,6 +300,7 @@ const ReactTestUtils = {
300 * @return {array} an array of all the matches.
301 */
302 scryRenderedComponentsWithType: function(root, componentType) {
303 + validateClassInstance(root, 'scryRenderedComponentsWithType');
304 return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
305 return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
306 });
@@ -282,6 +313,7 @@ const ReactTestUtils = {
313 * @return {!ReactComponent} The one match.
314 */
315 findRenderedComponentWithType: function(root, componentType) {
316 + validateClassInstance(root, 'findRenderedComponentWithType');
317 const all = ReactTestUtils.scryRenderedComponentsWithType(
318 root,
319 componentType,