Update additional tests to use .toWarnDev() matcher (#11952)
* Migrated several additional tests to use new .toWarnDev() matcher * Migrated ReactDOMComponent-test to use .toWarnDev() matcher Note this test previous had some hacky logic to verify errors were reported against unique line numbers. Since the new matcher doesn't suppor this, I replaced this check with an equivalent (I think) comparison of unique DOM elements (eg div -> span) * Updated several additional tests to use the new .toWarnDev() matcher * Updated many more tests to use .toWarnDev()
Brian Vaughn committed
Jan 3, 2018 at 10:08 UTC
a442d9bc082878ccf66872b1eeed3465927801b6
19 files changed
+1410
-2243
packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.internal.js
-4
@@ -277,15 +277,11 @@ describe('ReactBrowserEventEmitter', () => {
277
putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD));
278
putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT));
279
putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT));
280
- spyOnDev(console, 'error');
280
ReactTestUtils.Simulate.click(CHILD);
281
expect(idCallOrder.length).toBe(3);
282
expect(idCallOrder[0]).toBe(CHILD);
283
expect(idCallOrder[1]).toBe(PARENT);
284
expect(idCallOrder[2]).toBe(GRANDPARENT);
286
- if (__DEV__) {
287
- expect(console.error.calls.count()).toEqual(0);
288
- }
285
});
286
287
/**
packages/react-dom/src/__tests__/ReactComponent-test.js
+54
-75
@@ -46,7 +46,6 @@ describe('ReactComponent', () => {
46
});
47
48
it('should throw (in dev) when children are mutated during render', () => {
49
- spyOnDev(console, 'error');
49
function Wrapper(props) {
50
props.children[1] = <p key={1} />; // Mutation is illegal
51
return <div>{props.children}</div>;
@@ -73,8 +72,6 @@ describe('ReactComponent', () => {
72
});
73
74
it('should throw (in dev) when children are mutated during update', () => {
76
- spyOnDev(console, 'error');
77
-
75
class Wrapper extends React.Component {
76
componentDidMount() {
77
this.props.children[1] = <p key={1} />; // Mutation is illegal
@@ -355,10 +352,13 @@ describe('ReactComponent', () => {
352
});
353
354
it('throws usefully when rendering badly-typed elements', () => {
358
- spyOnDev(console, 'error');
359
-
355
const X = undefined;
361
- expect(() => ReactTestUtils.renderIntoDocument(<X />)).toThrowError(
356
+ expect(() => {
357
+ expect(() => ReactTestUtils.renderIntoDocument(<X />)).toWarnDev(
358
+ 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
359
+ 'or a class/function (for composite components) but got: undefined.',
360
+ );
361
+ }).toThrowError(
362
'Element type is invalid: expected a string (for built-in components) ' +
363
'or a class/function (for composite components) but got: undefined.' +
364
(__DEV__
@@ -368,20 +368,18 @@ describe('ReactComponent', () => {
368
);
369
370
const Y = null;
371
- expect(() => ReactTestUtils.renderIntoDocument(<Y />)).toThrowError(
371
+ expect(() => {
372
+ expect(() => ReactTestUtils.renderIntoDocument(<Y />)).toWarnDev(
373
+ 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
374
+ 'or a class/function (for composite components) but got: null.',
375
+ );
376
+ }).toThrowError(
377
'Element type is invalid: expected a string (for built-in components) ' +
378
'or a class/function (for composite components) but got: null.',
379
);
375
-
376
- if (__DEV__) {
377
- // One warning for each element creation
378
- expect(console.error.calls.count()).toBe(2);
379
- }
380
});
381
382
it('includes owner name in the error about badly-typed elements', () => {
383
- spyOnDev(console, 'error');
384
-
383
const X = undefined;
384
385
function Indirection(props) {
@@ -400,7 +398,12 @@ describe('ReactComponent', () => {
398
return <Bar />;
399
}
400
403
- expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toThrowError(
401
+ expect(() => {
402
+ expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toWarnDev(
403
+ 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
404
+ 'or a class/function (for composite components) but got: undefined.',
405
+ );
406
+ }).toThrowError(
407
'Element type is invalid: expected a string (for built-in components) ' +
408
'or a class/function (for composite components) but got: undefined.' +
409
(__DEV__
@@ -409,11 +412,6 @@ describe('ReactComponent', () => {
412
'\n\nCheck the render method of `Bar`.'
413
: ''),
414
);
412
-
413
- if (__DEV__) {
414
- // One warning for each element creation
415
- expect(console.error.calls.count()).toBe(1);
416
- }
415
});
416
417
it('throws if a plain object is used as a child', () => {
@@ -530,18 +528,13 @@ describe('ReactComponent', () => {
528
function Foo() {
529
return Foo;
530
}
533
- spyOnDev(console, 'error');
531
const container = document.createElement('div');
535
- ReactDOM.render(<Foo />, container);
536
- if (__DEV__) {
537
- expect(console.error.calls.count()).toBe(1);
538
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
539
- 'Warning: Functions are not valid as a React child. This may happen if ' +
540
- 'you return a Component instead of <Component /> from render. ' +
541
- 'Or maybe you meant to call this function rather than return it.\n' +
542
- ' in Foo (at **)',
543
- );
544
- }
532
+ expect(() => ReactDOM.render(<Foo />, container)).toWarnDev(
533
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
534
+ 'you return a Component instead of <Component /> from render. ' +
535
+ 'Or maybe you meant to call this function rather than return it.\n' +
536
+ ' in Foo (at **)',
537
+ );
538
});
539
540
it('warns on function as a return value from a class', () => {
@@ -550,18 +543,13 @@ describe('ReactComponent', () => {
543
return Foo;
544
}
545
}
553
- spyOnDev(console, 'error');
546
const container = document.createElement('div');
555
- ReactDOM.render(<Foo />, container);
556
- if (__DEV__) {
557
- expect(console.error.calls.count()).toBe(1);
558
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
559
- 'Warning: Functions are not valid as a React child. This may happen if ' +
560
- 'you return a Component instead of <Component /> from render. ' +
561
- 'Or maybe you meant to call this function rather than return it.\n' +
562
- ' in Foo (at **)',
563
- );
564
- }
547
+ expect(() => ReactDOM.render(<Foo />, container)).toWarnDev(
548
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
549
+ 'you return a Component instead of <Component /> from render. ' +
550
+ 'Or maybe you meant to call this function rather than return it.\n' +
551
+ ' in Foo (at **)',
552
+ );
553
});
554
555
it('warns on function as a child to host component', () => {
@@ -572,20 +560,15 @@ describe('ReactComponent', () => {
560
</div>
561
);
562
}
575
- spyOnDev(console, 'error');
563
const container = document.createElement('div');
577
- ReactDOM.render(<Foo />, container);
578
- if (__DEV__) {
579
- expect(console.error.calls.count()).toBe(1);
580
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
581
- 'Warning: Functions are not valid as a React child. This may happen if ' +
582
- 'you return a Component instead of <Component /> from render. ' +
583
- 'Or maybe you meant to call this function rather than return it.\n' +
584
- ' in span (at **)\n' +
585
- ' in div (at **)\n' +
586
- ' in Foo (at **)',
587
- );
588
- }
564
+ expect(() => ReactDOM.render(<Foo />, container)).toWarnDev(
565
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
566
+ 'you return a Component instead of <Component /> from render. ' +
567
+ 'Or maybe you meant to call this function rather than return it.\n' +
568
+ ' in span (at **)\n' +
569
+ ' in div (at **)\n' +
570
+ ' in Foo (at **)',
571
+ );
572
});
573
574
it('does not warn for function-as-a-child that gets resolved', () => {
@@ -601,7 +584,6 @@ describe('ReactComponent', () => {
584
});
585
586
it('deduplicates function type warnings based on component type', () => {
604
- spyOnDev(console, 'error');
587
class Foo extends React.PureComponent {
588
constructor() {
589
super();
@@ -621,26 +603,23 @@ describe('ReactComponent', () => {
603
}
604
}
605
const container = document.createElement('div');
624
- const component = ReactDOM.render(<Foo />, container);
606
+ let component;
607
+ expect(() => {
608
+ component = ReactDOM.render(<Foo />, container);
609
+ }).toWarnDev([
610
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
611
+ 'you return a Component instead of <Component /> from render. ' +
612
+ 'Or maybe you meant to call this function rather than return it.\n' +
613
+ ' in div (at **)\n' +
614
+ ' in Foo (at **)',
615
+ 'Warning: Functions are not valid as a React child. This may happen if ' +
616
+ 'you return a Component instead of <Component /> from render. ' +
617
+ 'Or maybe you meant to call this function rather than return it.\n' +
618
+ ' in span (at **)\n' +
619
+ ' in div (at **)\n' +
620
+ ' in Foo (at **)',
621
+ ]);
622
component.setState({type: 'portobello mushrooms'});
626
- if (__DEV__) {
627
- expect(console.error.calls.count()).toBe(2);
628
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
629
- 'Warning: Functions are not valid as a React child. This may happen if ' +
630
- 'you return a Component instead of <Component /> from render. ' +
631
- 'Or maybe you meant to call this function rather than return it.\n' +
632
- ' in div (at **)\n' +
633
- ' in Foo (at **)',
634
- );
635
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
636
- 'Warning: Functions are not valid as a React child. This may happen if ' +
637
- 'you return a Component instead of <Component /> from render. ' +
638
- 'Or maybe you meant to call this function rather than return it.\n' +
639
- ' in span (at **)\n' +
640
- ' in div (at **)\n' +
641
- ' in Foo (at **)',
642
- );
643
- }
623
});
624
});
625
});
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+27
-54
@@ -198,8 +198,6 @@ describe('ReactComponentLifeCycle', () => {
198
});
199
200
it('should not allow update state inside of getInitialState', () => {
201
- spyOnDev(console, 'error');
202
-
201
class StatefulComponent extends React.Component {
202
constructor(props, context) {
203
super(props, context);
@@ -213,26 +211,20 @@ describe('ReactComponentLifeCycle', () => {
211
}
212
}
213
216
- ReactTestUtils.renderIntoDocument(<StatefulComponent />);
217
- if (__DEV__) {
218
- expect(console.error.calls.count()).toBe(1);
219
- expect(console.error.calls.argsFor(0)[0]).toBe(
220
- 'Warning: setState(...): Can only update a mounted or ' +
221
- 'mounting component. This usually means you called setState() on an ' +
222
- 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
223
- 'StatefulComponent component.',
224
- );
225
- }
214
+ expect(() => {
215
+ ReactTestUtils.renderIntoDocument(<StatefulComponent />);
216
+ }).toWarnDev(
217
+ 'Warning: setState(...): Can only update a mounted or ' +
218
+ 'mounting component. This usually means you called setState() on an ' +
219
+ 'unmounted component. This is a no-op.\n\nPlease check the code for the ' +
220
+ 'StatefulComponent component.',
221
+ );
222
227
- // Check deduplication
223
+ // Check deduplication; (no extra warnings should be logged).
224
ReactTestUtils.renderIntoDocument(<StatefulComponent />);
229
- if (__DEV__) {
230
- expect(console.error.calls.count()).toBe(1);
231
- }
225
});
226
227
it('should correctly determine if a component is mounted', () => {
235
- spyOnDev(console, 'error');
228
class Component extends React.Component {
229
_isMounted() {
230
// No longer a public API, but we can test that it works internally by
@@ -253,19 +245,13 @@ describe('ReactComponentLifeCycle', () => {
245
246
const element = <Component />;
247
256
- const instance = ReactTestUtils.renderIntoDocument(element);
257
- expect(instance._isMounted()).toBeTruthy();
258
-
259
- if (__DEV__) {
260
- expect(console.error.calls.count()).toBe(1);
261
- expect(console.error.calls.argsFor(0)[0]).toContain(
262
- 'Component is accessing isMounted inside its render()',
263
- );
264
- }
248
+ expect(() => {
249
+ const instance = ReactTestUtils.renderIntoDocument(element);
250
+ expect(instance._isMounted()).toBeTruthy();
251
+ }).toWarnDev('Component is accessing isMounted inside its render()');
252
});
253
254
it('should correctly determine if a null component is mounted', () => {
268
- spyOnDev(console, 'error');
255
class Component extends React.Component {
256
_isMounted() {
257
// No longer a public API, but we can test that it works internally by
@@ -286,15 +272,10 @@ describe('ReactComponentLifeCycle', () => {
272
273
const element = <Component />;
274
289
- const instance = ReactTestUtils.renderIntoDocument(element);
290
- expect(instance._isMounted()).toBeTruthy();
291
-
292
- if (__DEV__) {
293
- expect(console.error.calls.count()).toBe(1);
294
- expect(console.error.calls.argsFor(0)[0]).toContain(
295
- 'Component is accessing isMounted inside its render()',
296
- );
297
- }
275
+ expect(() => {
276
+ const instance = ReactTestUtils.renderIntoDocument(element);
277
+ expect(instance._isMounted()).toBeTruthy();
278
+ }).toWarnDev('Component is accessing isMounted inside its render()');
279
});
280
281
it('isMounted should return false when unmounted', () => {
@@ -317,7 +298,6 @@ describe('ReactComponentLifeCycle', () => {
298
});
299
300
it('warns if findDOMNode is used inside render', () => {
320
- spyOnDev(console, 'error');
301
class Component extends React.Component {
302
state = {isMounted: false};
303
componentDidMount() {
@@ -331,18 +311,12 @@ describe('ReactComponentLifeCycle', () => {
311
}
312
}
313
334
- ReactTestUtils.renderIntoDocument(<Component />);
335
- if (__DEV__) {
336
- expect(console.error.calls.count()).toBe(1);
337
- expect(console.error.calls.argsFor(0)[0]).toContain(
338
- 'Component is accessing findDOMNode inside its render()',
339
- );
340
- }
314
+ expect(() => {
315
+ ReactTestUtils.renderIntoDocument(<Component />);
316
+ }).toWarnDev('Component is accessing findDOMNode inside its render()');
317
});
318
319
it('should carry through each of the phases of setup', () => {
344
- spyOnDev(console, 'error');
345
-
320
class LifeCycleComponent extends React.Component {
321
constructor(props, context) {
322
super(props, context);
@@ -399,7 +373,13 @@ describe('ReactComponentLifeCycle', () => {
373
// yet initialized, or rendered.
374
//
375
const container = document.createElement('div');
402
- const instance = ReactDOM.render(<LifeCycleComponent />, container);
376
+
377
+ let instance;
378
+ expect(() => {
379
+ instance = ReactDOM.render(<LifeCycleComponent />, container);
380
+ }).toWarnDev(
381
+ 'LifeCycleComponent is accessing isMounted inside its render() function',
382
+ );
383
384
// getInitialState
385
expect(instance._testJournal.returnedFromGetInitialState).toEqual(
@@ -449,13 +429,6 @@ describe('ReactComponentLifeCycle', () => {
429
// But the current lifecycle of the component is unmounted.
430
expect(getLifeCycleState(instance)).toBe('UNMOUNTED');
431
expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE);
452
-
453
- if (__DEV__) {
454
- expect(console.error.calls.count()).toBe(1);
455
- expect(console.error.calls.argsFor(0)[0]).toContain(
456
- 'LifeCycleComponent is accessing isMounted inside its render() function',
457
- );
458
- }
432
});
433
434
it('should not throw when updating an auxiliary component', () => {
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+87
-190
@@ -122,29 +122,21 @@ describe('ReactCompositeComponent', () => {
122
}
123
}
124
125
- spyOnDev(console, 'warn');
125
const markup = ReactDOMServer.renderToString(<Parent />);
126
127
// Old API based on heuristic
128
let container = document.createElement('div');
129
container.innerHTML = markup;
131
- ReactDOM.render(<Parent />, container);
132
- if (__DEV__) {
133
- expect(console.warn.calls.count()).toBe(1);
134
- expect(console.warn.calls.argsFor(0)[0]).toContain(
135
- 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
136
- 'will stop working in React v17. Replace the ReactDOM.render() call ' +
137
- 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
138
- );
139
- console.warn.calls.reset();
140
- }
130
+ expect(() => ReactDOM.render(<Parent />, container)).toLowPriorityWarnDev(
131
+ 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
132
+ 'will stop working in React v17. Replace the ReactDOM.render() call ' +
133
+ 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
134
+ );
135
+
136
// New explicit API
137
container = document.createElement('div');
138
container.innerHTML = markup;
139
ReactDOM.hydrate(<Parent />, container);
145
- if (__DEV__) {
146
- expect(console.warn.calls.count()).toBe(0);
147
- }
140
});
141
142
it('should react to state changes from callbacks', () => {
@@ -234,8 +226,6 @@ describe('ReactCompositeComponent', () => {
226
});
227
228
it('should warn about `forceUpdate` on unmounted components', () => {
237
- spyOnDev(console, 'error');
238
-
229
const container = document.createElement('div');
230
document.body.appendChild(container);
231
@@ -251,32 +241,20 @@ describe('ReactCompositeComponent', () => {
241
instance = ReactDOM.render(instance, container);
242
instance.forceUpdate();
243
254
- if (__DEV__) {
255
- expect(console.error.calls.count()).toBe(0);
256
- }
257
-
244
ReactDOM.unmountComponentAtNode(container);
245
260
- instance.forceUpdate();
261
- if (__DEV__) {
262
- expect(console.error.calls.count()).toBe(1);
263
- expect(console.error.calls.argsFor(0)[0]).toContain(
264
- 'Can only update a mounted or mounting component. This usually means ' +
265
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
266
- 'component. This is a no-op.\n\nPlease check the code for the ' +
267
- 'Component component.',
268
- );
269
- }
246
+ expect(() => instance.forceUpdate()).toWarnDev(
247
+ 'Can only update a mounted or mounting component. This usually means ' +
248
+ 'you called setState, replaceState, or forceUpdate on an unmounted ' +
249
+ 'component. This is a no-op.\n\nPlease check the code for the ' +
250
+ 'Component component.',
251
+ );
252
253
+ // No additional warning should be recorded
254
instance.forceUpdate();
272
- if (__DEV__) {
273
- expect(console.error.calls.count()).toBe(1);
274
- }
255
});
256
257
it('should warn about `setState` on unmounted components', () => {
278
- spyOnDev(console, 'error');
279
-
258
const container = document.createElement('div');
259
document.body.appendChild(container);
260
@@ -300,26 +278,20 @@ describe('ReactCompositeComponent', () => {
278
279
instance.setState({value: 1});
280
303
- if (__DEV__) {
304
- expect(console.error.calls.count()).toBe(0);
305
- }
306
-
281
expect(renders).toBe(2);
282
283
ReactDOM.unmountComponentAtNode(container);
310
- instance.setState({value: 2});
284
312
- expect(renders).toBe(2);
285
+ expect(() => {
286
+ instance.setState({value: 2});
287
+ }).toWarnDev(
288
+ 'Can only update a mounted or mounting component. This usually means ' +
289
+ 'you called setState, replaceState, or forceUpdate on an unmounted ' +
290
+ 'component. This is a no-op.\n\nPlease check the code for the ' +
291
+ 'Component component.',
292
+ );
293
314
- if (__DEV__) {
315
- expect(console.error.calls.count()).toBe(1);
316
- expect(console.error.calls.argsFor(0)[0]).toContain(
317
- 'Can only update a mounted or mounting component. This usually means ' +
318
- 'you called setState, replaceState, or forceUpdate on an unmounted ' +
319
- 'component. This is a no-op.\n\nPlease check the code for the ' +
320
- 'Component component.',
321
- );
322
- }
294
+ expect(renders).toBe(2);
295
});
296
297
it('should silently allow `setState`, not call cb on unmounting components', () => {
@@ -351,32 +323,24 @@ describe('ReactCompositeComponent', () => {
323
});
324
325
it('should warn when rendering a class with a render method that does not extend React.Component', () => {
354
- spyOnDev(console, 'error');
326
const container = document.createElement('div');
327
class ClassWithRenderNotExtended {
328
render() {
329
return <div />;
330
}
331
}
361
- if (__DEV__) {
362
- expect(console.error.calls.count()).toBe(0);
363
- }
332
expect(() => {
365
- ReactDOM.render(<ClassWithRenderNotExtended />, container);
366
- }).toThrow(TypeError);
367
- if (__DEV__) {
368
- expect(console.error.calls.count()).toBe(1);
369
- expect(console.error.calls.argsFor(0)[0]).toContain(
333
+ expect(() => {
334
+ ReactDOM.render(<ClassWithRenderNotExtended />, container);
335
+ }).toWarnDev(
336
'Warning: The <ClassWithRenderNotExtended /> component appears to have a render method, ' +
337
"but doesn't extend React.Component. This is likely to cause errors. " +
338
'Change ClassWithRenderNotExtended to extend React.Component instead.',
339
);
374
- }
340
+ }).toThrow(TypeError);
341
});
342
343
it('should warn about `setState` in render', () => {
378
- spyOnDev(console, 'error');
379
-
344
const container = document.createElement('div');
345
346
let renderedState = -1;
@@ -395,21 +359,16 @@ describe('ReactCompositeComponent', () => {
359
}
360
}
361
398
- if (__DEV__) {
399
- expect(console.error.calls.count()).toBe(0);
400
- }
401
-
402
- const instance = ReactDOM.render(<Component />, container);
362
+ let instance;
363
404
- if (__DEV__) {
405
- expect(console.error.calls.count()).toBe(1);
406
- expect(console.error.calls.argsFor(0)[0]).toContain(
407
- 'Cannot update during an existing state transition (such as within ' +
408
- "`render` or another component's constructor). Render methods should " +
409
- 'be a pure function of props and state; constructor side-effects are ' +
410
- 'an anti-pattern, but can be moved to `componentWillMount`.',
411
- );
412
- }
364
+ expect(() => {
365
+ instance = ReactDOM.render(<Component />, container);
366
+ }).toWarnDev(
367
+ 'Cannot update during an existing state transition (such as within ' +
368
+ "`render` or another component's constructor). Render methods should " +
369
+ 'be a pure function of props and state; constructor side-effects are ' +
370
+ 'an anti-pattern, but can be moved to `componentWillMount`.',
371
+ );
372
373
// The setState call is queued and then executed as a second pass. This
374
// behavior is undefined though so we're free to change it to suit the
@@ -424,17 +383,12 @@ describe('ReactCompositeComponent', () => {
383
expect(renderedState).toBe(1);
384
expect(instance2.state.value).toBe(1);
385
427
- // Test deduplication
386
+ // Test deduplication; (no additional warnings are expected).
387
ReactDOM.unmountComponentAtNode(container);
388
ReactDOM.render(<Component prop={123} />, container);
430
- if (__DEV__) {
431
- expect(console.error.calls.count()).toBe(1);
432
- }
389
});
390
391
it('should warn about `setState` in getChildContext', () => {
436
- spyOnDev(console, 'error');
437
-
392
const container = document.createElement('div');
393
394
let renderPasses = 0;
@@ -455,25 +409,20 @@ describe('ReactCompositeComponent', () => {
409
}
410
Component.childContextTypes = {};
411
458
- if (__DEV__) {
459
- expect(console.error.calls.count()).toBe(0);
460
- }
461
- const instance = ReactDOM.render(<Component />, container);
412
+ let instance;
413
+
414
+ expect(() => {
415
+ instance = ReactDOM.render(<Component />, container);
416
+ }).toWarnDev(
417
+ 'Warning: setState(...): Cannot call setState() inside getChildContext()',
418
+ );
419
+
420
expect(renderPasses).toBe(2);
421
expect(instance.state.value).toBe(1);
464
- if (__DEV__) {
465
- expect(console.error.calls.count()).toBe(1);
466
- expect(console.error.calls.argsFor(0)[0]).toBe(
467
- 'Warning: setState(...): Cannot call setState() inside getChildContext()',
468
- );
469
- }
422
471
- // Test deduplication
423
+ // Test deduplication; (no additional warnings are expected).
424
ReactDOM.unmountComponentAtNode(container);
425
ReactDOM.render(<Component />, container);
474
- if (__DEV__) {
475
- expect(console.error.calls.count()).toBe(1);
476
- }
426
});
427
428
it('should cleanup even if render() fatals', () => {
@@ -487,7 +436,7 @@ describe('ReactCompositeComponent', () => {
436
437
expect(ReactCurrentOwner.current).toBe(null);
438
490
- expect(function() {
439
+ expect(() => {
440
instance = ReactTestUtils.renderIntoDocument(instance);
441
}).toThrow();
442
@@ -525,8 +474,6 @@ describe('ReactCompositeComponent', () => {
474
});
475
476
it('should warn when shouldComponentUpdate() returns undefined', () => {
528
- spyOnDev(console, 'error');
529
-
477
class Component extends React.Component {
478
state = {bogus: false};
479
@@ -540,20 +487,14 @@ describe('ReactCompositeComponent', () => {
487
}
488
489
const instance = ReactTestUtils.renderIntoDocument(<Component />);
543
- instance.setState({bogus: true});
490
545
- if (__DEV__) {
546
- expect(console.error.calls.count()).toBe(1);
547
- expect(console.error.calls.argsFor(0)[0]).toBe(
548
- 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
549
- 'boolean value. Make sure to return true or false.',
550
- );
551
- }
491
+ expect(() => instance.setState({bogus: true})).toWarnDev(
492
+ 'Warning: Component.shouldComponentUpdate(): Returned undefined instead of a ' +
493
+ 'boolean value. Make sure to return true or false.',
494
+ );
495
});
496
497
it('should warn when componentDidUnmount method is defined', () => {
555
- spyOnDev(console, 'error');
556
-
498
class Component extends React.Component {
499
componentDidUnmount = () => {};
500
@@ -562,21 +503,14 @@ describe('ReactCompositeComponent', () => {
503
}
504
}
505
565
- ReactTestUtils.renderIntoDocument(<Component />);
566
-
567
- if (__DEV__) {
568
- expect(console.error.calls.count()).toBe(1);
569
- expect(console.error.calls.argsFor(0)[0]).toBe(
570
- 'Warning: Component has a method called ' +
571
- 'componentDidUnmount(). But there is no such lifecycle method. ' +
572
- 'Did you mean componentWillUnmount()?',
573
- );
574
- }
506
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
507
+ 'Warning: Component has a method called ' +
508
+ 'componentDidUnmount(). But there is no such lifecycle method. ' +
509
+ 'Did you mean componentWillUnmount()?',
510
+ );
511
});
512
513
it('should warn when componentDidReceiveProps method is defined', () => {
578
- spyOnDev(console, 'error');
579
-
514
class Component extends React.Component {
515
componentDidReceiveProps = () => {};
516
@@ -585,23 +519,16 @@ describe('ReactCompositeComponent', () => {
519
}
520
}
521
588
- ReactTestUtils.renderIntoDocument(<Component />);
589
-
590
- if (__DEV__) {
591
- expect(console.error.calls.count()).toBe(1);
592
- expect(console.error.calls.argsFor(0)[0]).toBe(
593
- 'Warning: Component has a method called ' +
594
- 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
595
- 'If you meant to update the state in response to changing props, ' +
596
- 'use componentWillReceiveProps(). If you meant to fetch data or ' +
597
- 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
598
- );
599
- }
522
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
523
+ 'Warning: Component has a method called ' +
524
+ 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
525
+ 'If you meant to update the state in response to changing props, ' +
526
+ 'use componentWillReceiveProps(). If you meant to fetch data or ' +
527
+ 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
528
+ );
529
});
530
531
it('should warn when defaultProps was defined as an instance property', () => {
603
- spyOnDev(console, 'error');
604
-
532
class Component extends React.Component {
533
constructor(props) {
534
super(props);
@@ -613,15 +540,10 @@ describe('ReactCompositeComponent', () => {
540
}
541
}
542
616
- ReactTestUtils.renderIntoDocument(<Component />);
617
-
618
- if (__DEV__) {
619
- expect(console.error.calls.count()).toBe(1);
620
- expect(console.error.calls.argsFor(0)[0]).toBe(
621
- 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
622
- 'and will be ignored. Instead, define defaultProps as a static property on Component.',
623
- );
624
- }
543
+ expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toWarnDev(
544
+ 'Warning: Setting defaultProps as an instance property on Component is not supported ' +
545
+ 'and will be ignored. Instead, define defaultProps as a static property on Component.',
546
+ );
547
});
548
549
it('should pass context to children when not owner', () => {
@@ -1093,8 +1015,6 @@ describe('ReactCompositeComponent', () => {
1015
});
1016
1017
it('should disallow nested render calls', () => {
1096
- spyOnDev(console, 'error');
1097
-
1018
class Inner extends React.Component {
1019
render() {
1020
return <div />;
@@ -1108,16 +1028,12 @@ describe('ReactCompositeComponent', () => {
1028
}
1029
}
1030
1111
- ReactTestUtils.renderIntoDocument(<Outer />);
1112
- if (__DEV__) {
1113
- expect(console.error.calls.count()).toBe(1);
1114
- expect(console.error.calls.argsFor(0)[0]).toMatch(
1115
- 'Render methods should be a pure function of props and state; ' +
1116
- 'triggering nested component updates from render is not allowed. If ' +
1117
- 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
1118
- 'render method of Outer.',
1119
- );
1120
- }
1031
+ expect(() => ReactTestUtils.renderIntoDocument(<Outer />)).toWarnDev(
1032
+ 'Render methods should be a pure function of props and state; ' +
1033
+ 'triggering nested component updates from render is not allowed. If ' +
1034
+ 'necessary, trigger nested updates in componentDidUpdate.\n\nCheck the ' +
1035
+ 'render method of Outer.',
1036
+ );
1037
});
1038
1039
it('only renders once if updated in componentWillReceiveProps', () => {
@@ -1392,8 +1308,6 @@ describe('ReactCompositeComponent', () => {
1308
});
1309
1310
it('should warn when mutated props are passed', () => {
1395
- spyOnDev(console, 'error');
1396
-
1311
const container = document.createElement('div');
1312
1313
class Foo extends React.Component {
@@ -1407,19 +1321,10 @@ describe('ReactCompositeComponent', () => {
1321
}
1322
}
1323
1410
- if (__DEV__) {
1411
- expect(console.error.calls.count()).toBe(0);
1412
- }
1413
-
1414
- ReactDOM.render(<Foo idx="qwe" />, container);
1415
-
1416
- if (__DEV__) {
1417
- expect(console.error.calls.count()).toBe(1);
1418
- expect(console.error.calls.argsFor(0)[0]).toContain(
1419
- 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
1420
- "up the same props that your component's constructor was passed.",
1421
- );
1422
- }
1324
+ expect(() => ReactDOM.render(<Foo idx="qwe" />, container)).toWarnDev(
1325
+ 'Foo(...): When calling super() in `Foo`, make sure to pass ' +
1326
+ "up the same props that your component's constructor was passed.",
1327
+ );
1328
});
1329
1330
it('should only call componentWillUnmount once', () => {
@@ -1456,7 +1361,7 @@ describe('ReactCompositeComponent', () => {
1361
}
1362
};
1363
1459
- expect(function() {
1364
+ expect(() => {
1365
ReactDOM.render(<App ref={setRef} stage={1} />, container);
1366
ReactDOM.render(<App ref={setRef} stage={2} />, container);
1367
}).toThrow();
@@ -1702,7 +1607,6 @@ describe('ReactCompositeComponent', () => {
1607
});
1608
1609
it('should return a meaningful warning when constructor is returned', () => {
1705
- spyOnDev(console, 'error');
1610
class RenderTextInvalidConstructor extends React.Component {
1611
constructor(props) {
1612
super(props);
@@ -1714,33 +1618,26 @@ describe('ReactCompositeComponent', () => {
1618
}
1619
}
1620
1717
- expect(function() {
1718
- ReactTestUtils.renderIntoDocument(<RenderTextInvalidConstructor />);
1719
- }).toThrow();
1720
-
1721
- if (__DEV__) {
1722
- expect(console.error.calls.count()).toBe(1);
1723
- expect(console.error.calls.mostRecent().args[0]).toBe(
1621
+ expect(() => {
1622
+ expect(() => {
1623
+ ReactTestUtils.renderIntoDocument(<RenderTextInvalidConstructor />);
1624
+ }).toWarnDev(
1625
'Warning: RenderTextInvalidConstructor(...): No `render` method found on the returned component instance: ' +
1626
'did you accidentally return an object from the constructor?',
1627
);
1727
- }
1628
+ }).toThrow();
1629
});
1630
1631
it('should return error if render is not defined', () => {
1731
- spyOnDev(console, 'error');
1632
class RenderTestUndefinedRender extends React.Component {}
1633
1734
- expect(function() {
1735
- ReactTestUtils.renderIntoDocument(<RenderTestUndefinedRender />);
1736
- }).toThrow();
1737
-
1738
- if (__DEV__) {
1739
- expect(console.error.calls.count()).toBe(1);
1740
- expect(console.error.calls.mostRecent().args[0]).toBe(
1634
+ expect(() => {
1635
+ expect(() => {
1636
+ ReactTestUtils.renderIntoDocument(<RenderTestUndefinedRender />);
1637
+ }).toWarnDev(
1638
'Warning: RenderTestUndefinedRender(...): No `render` method found on the returned ' +
1639
'component instance: you may have forgotten to define `render`.',
1640
);
1744
- }
1641
+ }).toThrow();
1642
});
1643
});
packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js
+11
-25
@@ -17,7 +17,6 @@ let TestComponent;
17
describe('ReactCompositeComponent-state', () => {
18
beforeEach(() => {
19
React = require('react');
20
-
20
ReactDOM = require('react-dom');
21
22
TestComponent = class extends React.Component {
@@ -381,8 +380,6 @@ describe('ReactCompositeComponent-state', () => {
380
});
381
382
it('should treat assigning to this.state inside cWRP as a replaceState, with a warning', () => {
384
- spyOnDev(console, 'error');
385
-
383
let ops = [];
384
class Test extends React.Component {
385
state = {step: 1, extra: true};
@@ -409,27 +406,20 @@ describe('ReactCompositeComponent-state', () => {
406
const container = document.createElement('div');
407
ReactDOM.render(<Test />, container);
408
// Update
412
- ReactDOM.render(<Test />, container);
409
+ expect(() => ReactDOM.render(<Test />, container)).toWarnDev(
410
+ 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
411
+ "this.state is deprecated (except inside a component's constructor). " +
412
+ 'Use setState instead.',
413
+ );
414
415
expect(ops).toEqual([
416
'render -- step: 1, extra: true',
417
'render -- step: 3, extra: false',
418
'callback -- step: 3, extra: false',
419
]);
419
- if (__DEV__) {
420
- expect(console.error.calls.count()).toEqual(1);
421
- expect(console.error.calls.argsFor(0)[0]).toEqual(
422
- 'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
423
- "this.state is deprecated (except inside a component's constructor). " +
424
- 'Use setState instead.',
425
- );
426
- }
420
428
- // Check deduplication
421
+ // Check deduplication; (no additional warnings are expected)
422
ReactDOM.render(<Test />, container);
430
- if (__DEV__) {
431
- expect(console.error.calls.count()).toEqual(1);
432
- }
423
});
424
425
it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
@@ -459,19 +449,15 @@ describe('ReactCompositeComponent-state', () => {
449
450
// Mount
451
const container = document.createElement('div');
462
- ReactDOM.render(<Test />, container);
452
+ expect(() => ReactDOM.render(<Test />, container)).toWarnDev(
453
+ 'Warning: Test.componentWillMount(): Assigning directly to ' +
454
+ "this.state is deprecated (except inside a component's constructor). " +
455
+ 'Use setState instead.',
456
+ );
457
458
expect(ops).toEqual([
459
'render -- step: 3, extra: false',
460
'callback -- step: 3, extra: false',
461
]);
468
- if (__DEV__) {
469
- expect(console.error.calls.count()).toEqual(1);
470
- expect(console.error.calls.argsFor(0)[0]).toEqual(
471
- 'Warning: Test.componentWillMount(): Assigning directly to ' +
472
- "this.state is deprecated (except inside a component's constructor). " +
473
- 'Use setState instead.',
474
- );
475
- }
462
});
463
});
packages/react-dom/src/__tests__/ReactDOM-test.js
+57
-57
@@ -109,8 +109,6 @@ describe('ReactDOM', () => {
109
});
110
111
it('throws in render() if the mount callback is not a function', () => {
112
- spyOnDev(console, 'error');
113
-
112
function Foo() {
113
this.a = 1;
114
this.b = 2;
@@ -125,42 +123,44 @@ describe('ReactDOM', () => {
123
}
124
125
const myDiv = document.createElement('div');
128
- expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrowError(
129
- 'Invalid argument passed as callback. Expected a function. Instead ' +
130
- 'received: no',
131
- );
132
- if (__DEV__) {
133
- expect(console.error.calls.argsFor(0)[0]).toContain(
126
+ expect(() => {
127
+ expect(() => {
128
+ ReactDOM.render(<A />, myDiv, 'no');
129
+ }).toWarnDev(
130
'render(...): Expected the last optional `callback` argument to be ' +
131
'a function. Instead received: no.',
132
);
137
- }
138
- expect(() => ReactDOM.render(<A />, myDiv, {foo: 'bar'})).toThrowError(
133
+ }).toThrowError(
134
'Invalid argument passed as callback. Expected a function. Instead ' +
140
- 'received: [object Object]',
135
+ 'received: no',
136
);
142
- if (__DEV__) {
143
- expect(console.error.calls.argsFor(1)[0]).toContain(
137
+
138
+ expect(() => {
139
+ expect(() => {
140
+ ReactDOM.render(<A />, myDiv, {foo: 'bar'});
141
+ }).toWarnDev(
142
'render(...): Expected the last optional `callback` argument to be ' +
143
'a function. Instead received: [object Object].',
144
);
147
- }
148
- expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
145
+ }).toThrowError(
146
'Invalid argument passed as callback. Expected a function. Instead ' +
147
'received: [object Object]',
148
);
152
- if (__DEV__) {
153
- expect(console.error.calls.argsFor(2)[0]).toContain(
149
+
150
+ expect(() => {
151
+ expect(() => {
152
+ ReactDOM.render(<A />, myDiv, new Foo());
153
+ }).toWarnDev(
154
'render(...): Expected the last optional `callback` argument to be ' +
155
'a function. Instead received: [object Object].',
156
);
157
- expect(console.error.calls.count()).toBe(3);
158
- }
157
+ }).toThrowError(
158
+ 'Invalid argument passed as callback. Expected a function. Instead ' +
159
+ 'received: [object Object]',
160
+ );
161
});
162
163
it('throws in render() if the update callback is not a function', () => {
162
- spyOnDev(console, 'error');
163
-
164
function Foo() {
165
this.a = 1;
166
this.b = 2;
@@ -176,39 +176,43 @@ describe('ReactDOM', () => {
176
177
const myDiv = document.createElement('div');
178
ReactDOM.render(<A />, myDiv);
179
- expect(() => ReactDOM.render(<A />, myDiv, 'no')).toThrowError(
180
- 'Invalid argument passed as callback. Expected a function. Instead ' +
181
- 'received: no',
182
- );
183
- if (__DEV__) {
184
- expect(console.error.calls.argsFor(0)[0]).toContain(
179
+ expect(() => {
180
+ expect(() => {
181
+ ReactDOM.render(<A />, myDiv, 'no');
182
+ }).toWarnDev(
183
'render(...): Expected the last optional `callback` argument to be ' +
184
'a function. Instead received: no.',
185
);
188
- }
189
- ReactDOM.render(<A />, myDiv); // Re-mount
190
- expect(() => ReactDOM.render(<A />, myDiv, {foo: 'bar'})).toThrowError(
186
+ }).toThrowError(
187
'Invalid argument passed as callback. Expected a function. Instead ' +
192
- 'received: [object Object]',
188
+ 'received: no',
189
);
194
- if (__DEV__) {
195
- expect(console.error.calls.argsFor(1)[0]).toContain(
190
+
191
+ ReactDOM.render(<A />, myDiv); // Re-mount
192
+ expect(() => {
193
+ expect(() => {
194
+ ReactDOM.render(<A />, myDiv, {foo: 'bar'});
195
+ }).toWarnDev(
196
'render(...): Expected the last optional `callback` argument to be ' +
197
'a function. Instead received: [object Object].',
198
);
199
- }
200
- ReactDOM.render(<A />, myDiv); // Re-mount
201
- expect(() => ReactDOM.render(<A />, myDiv, new Foo())).toThrowError(
199
+ }).toThrowError(
200
'Invalid argument passed as callback. Expected a function. Instead ' +
201
'received: [object Object]',
202
);
205
- if (__DEV__) {
206
- expect(console.error.calls.argsFor(2)[0]).toContain(
203
+
204
+ ReactDOM.render(<A />, myDiv); // Re-mount
205
+ expect(() => {
206
+ expect(() => {
207
+ ReactDOM.render(<A />, myDiv, new Foo());
208
+ }).toWarnDev(
209
'render(...): Expected the last optional `callback` argument to be ' +
210
'a function. Instead received: [object Object].',
211
);
210
- expect(console.error.calls.count()).toBe(3);
211
- }
212
+ }).toThrowError(
213
+ 'Invalid argument passed as callback. Expected a function. Instead ' +
214
+ 'received: [object Object]',
215
+ );
216
});
217
218
it('preserves focus', () => {
@@ -375,28 +379,24 @@ describe('ReactDOM', () => {
379
380
// https://github.com/facebook/react/issues/11689
381
it('should warn when attempting to inject an event plugin', () => {
378
- spyOnDev(console, 'warn');
379
- ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.EventPluginHub.injection.injectEventPluginsByName(
380
- {
381
- TapEventPlugin: {
382
- extractEvents() {},
382
+ expect(() => {
383
+ ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.EventPluginHub.injection.injectEventPluginsByName(
384
+ {
385
+ TapEventPlugin: {
386
+ extractEvents() {},
387
+ },
388
},
384
- },
385
- );
386
- if (__DEV__) {
387
- expect(console.warn.calls.count()).toBe(1);
388
- expect(console.warn.calls.argsFor(0)[0]).toContain(
389
- 'Injecting custom event plugins (TapEventPlugin) is deprecated ' +
390
- 'and will not work in React 17+. Please update your code ' +
391
- 'to not depend on React internals. The stack trace for this ' +
392
- 'warning should reveal the library that is using them. ' +
393
- 'See https://github.com/facebook/react/issues/11689 for a discussion.',
389
);
395
- }
390
+ }).toLowPriorityWarnDev(
391
+ 'Injecting custom event plugins (TapEventPlugin) is deprecated ' +
392
+ 'and will not work in React 17+. Please update your code ' +
393
+ 'to not depend on React internals. The stack trace for this ' +
394
+ 'warning should reveal the library that is using them. ' +
395
+ 'See https://github.com/facebook/react/issues/11689 for a discussion.',
396
+ );
397
});
398
399
it('throws in DEV if jsdom is destroyed by the time setState() is called', () => {
399
- spyOnDev(console, 'error');
400
class App extends React.Component {
401
state = {x: 1};
402
render() {
packages/react-dom/src/__tests__/ReactDOMAttribute-test.js
+38
-68
@@ -19,10 +19,6 @@ describe('ReactDOM unknown attribute', () => {
19
ReactDOM = require('react-dom');
20
});
21
22
- function normalizeCodeLocInfo(str) {
23
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
24
- }
25
-
22
function testUnknownAttributeRemoval(givenValue) {
23
const el = document.createElement('div');
24
ReactDOM.render(<div unknown="something" />, el);
@@ -46,20 +42,13 @@ describe('ReactDOM unknown attribute', () => {
42
});
43
44
it('changes values true, false to null, and also warns once', () => {
49
- spyOnDev(console, 'error');
50
-
51
- testUnknownAttributeAssignment(true, null);
45
+ expect(() => testUnknownAttributeAssignment(true, null)).toWarnDev(
46
+ 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
47
+ 'If you want to write it to the DOM, pass a string instead: ' +
48
+ 'unknown="true" or unknown={value.toString()}.\n' +
49
+ ' in div (at **)',
50
+ );
51
testUnknownAttributeAssignment(false, null);
53
-
54
- if (__DEV__) {
55
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
56
- 'Received `true` for a non-boolean attribute `unknown`.\n\n' +
57
- 'If you want to write it to the DOM, pass a string instead: ' +
58
- 'unknown="true" or unknown={value.toString()}.\n' +
59
- ' in div (at **)',
60
- );
61
- expect(console.error.calls.count()).toBe(1);
62
- }
52
});
53
54
it('removes unknown attributes that were rendered but are now missing', () => {
@@ -82,17 +71,11 @@ describe('ReactDOM unknown attribute', () => {
71
});
72
73
it('coerces NaN to strings and warns', () => {
85
- spyOnDev(console, 'error');
86
-
87
- testUnknownAttributeAssignment(NaN, 'NaN');
88
- if (__DEV__) {
89
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
90
- 'Warning: Received NaN for the `unknown` attribute. ' +
91
- 'If this is expected, cast the value to a string.\n' +
92
- ' in div (at **)',
93
- );
94
- expect(console.error.calls.count()).toBe(1);
95
- }
74
+ expect(() => testUnknownAttributeAssignment(NaN, 'NaN')).toWarnDev(
75
+ 'Warning: Received NaN for the `unknown` attribute. ' +
76
+ 'If this is expected, cast the value to a string.\n' +
77
+ ' in div (at **)',
78
+ );
79
});
80
81
it('coerces objects to strings and warns', () => {
@@ -107,54 +90,41 @@ describe('ReactDOM unknown attribute', () => {
90
});
91
92
it('removes symbols and warns', () => {
110
- spyOnDev(console, 'error');
111
-
112
- testUnknownAttributeRemoval(Symbol('foo'));
113
- if (__DEV__) {
114
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
115
- 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove it ' +
116
- 'from the element, or pass a string or number value to keep it ' +
117
- 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
118
- ' in div (at **)',
119
- );
120
- expect(console.error.calls.count()).toBe(1);
121
- }
93
+ expect(() => testUnknownAttributeRemoval(Symbol('foo'))).toWarnDev(
94
+ 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove it ' +
95
+ 'from the element, or pass a string or number value to keep it ' +
96
+ 'in the DOM. For details, see https://fb.me/react-attribute-behavior\n' +
97
+ ' in div (at **)',
98
+ );
99
});
100
101
it('removes functions and warns', () => {
125
- spyOnDev(console, 'error');
126
-
127
- testUnknownAttributeRemoval(function someFunction() {});
128
- if (__DEV__) {
129
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
130
- 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove ' +
131
- 'it from the element, or pass a string or number value to ' +
132
- 'keep it in the DOM. For details, see ' +
133
- 'https://fb.me/react-attribute-behavior\n' +
134
- ' in div (at **)',
135
- );
136
- expect(console.error.calls.count()).toBe(1);
137
- }
102
+ expect(() =>
103
+ testUnknownAttributeRemoval(function someFunction() {}),
104
+ ).toWarnDev(
105
+ 'Warning: Invalid value for prop `unknown` on <div> tag. Either remove ' +
106
+ 'it from the element, or pass a string or number value to ' +
107
+ 'keep it in the DOM. For details, see ' +
108
+ 'https://fb.me/react-attribute-behavior\n' +
109
+ ' in div (at **)',
110
+ );
111
});
112
113
it('allows camelCase unknown attributes and warns', () => {
141
- spyOnDev(console, 'error');
142
-
114
const el = document.createElement('div');
144
- ReactDOM.render(<div helloWorld="something" />, el);
145
- expect(el.firstChild.getAttribute('helloworld')).toBe('something');
115
147
- if (__DEV__) {
148
- expect(console.error.calls.count()).toBe(1);
149
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
150
- 'React does not recognize the `helloWorld` prop on a DOM element. ' +
151
- 'If you intentionally want it to appear in the DOM as a custom ' +
152
- 'attribute, spell it as lowercase `helloworld` instead. ' +
153
- 'If you accidentally passed it from a parent component, remove ' +
154
- 'it from the DOM element.\n' +
155
- ' in div (at **)',
156
- );
157
- }
116
+ expect(() =>
117
+ ReactDOM.render(<div helloWorld="something" />, el),
118
+ ).toWarnDev(
119
+ 'React does not recognize the `helloWorld` prop on a DOM element. ' +
120
+ 'If you intentionally want it to appear in the DOM as a custom ' +
121
+ 'attribute, spell it as lowercase `helloworld` instead. ' +
122
+ 'If you accidentally passed it from a parent component, remove ' +
123
+ 'it from the DOM element.\n' +
124
+ ' in div (at **)',
125
+ );
126
+
127
+ expect(el.firstChild.getAttribute('helloworld')).toBe('something');
128
});
129
});
130
});
packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+568
-867
@@ -147,110 +147,97 @@ describe('ReactDOMComponent', () => {
147
});
148
149
it('should warn for unknown prop', () => {
150
- spyOnDev(console, 'error');
150
const container = document.createElement('div');
152
- ReactDOM.render(<div foo={() => {}} />, container);
153
- if (__DEV__) {
154
- expect(console.error.calls.count(0)).toBe(1);
155
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
156
- 'Warning: Invalid value for prop `foo` on <div> tag. Either remove it ' +
157
- 'from the element, or pass a string or number value to keep ' +
158
- 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
159
- '\n in div (at **)',
160
- );
161
- }
151
+ expect(() =>
152
+ ReactDOM.render(<div foo={() => {}} />, container),
153
+ ).toWarnDev(
154
+ 'Warning: Invalid value for prop `foo` on <div> tag. Either remove it ' +
155
+ 'from the element, or pass a string or number value to keep ' +
156
+ 'it in the DOM. For details, see https://fb.me/react-attribute-behavior' +
157
+ '\n in div (at **)',
158
+ );
159
});
160
161
it('should group multiple unknown prop warnings together', () => {
165
- spyOnDev(console, 'error');
162
const container = document.createElement('div');
167
- ReactDOM.render(<div foo={() => {}} baz={() => {}} />, container);
168
- if (__DEV__) {
169
- expect(console.error.calls.count(0)).toBe(1);
170
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
171
- 'Warning: Invalid values for props `foo`, `baz` on <div> tag. Either remove ' +
172
- 'them from the element, or pass a string or number value to keep ' +
173
- 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
174
- '\n in div (at **)',
175
- );
176
- }
163
+ expect(() =>
164
+ ReactDOM.render(<div foo={() => {}} baz={() => {}} />, container),
165
+ ).toWarnDev(
166
+ 'Warning: Invalid values for props `foo`, `baz` on <div> tag. Either remove ' +
167
+ 'them from the element, or pass a string or number value to keep ' +
168
+ 'them in the DOM. For details, see https://fb.me/react-attribute-behavior' +
169
+ '\n in div (at **)',
170
+ );
171
});
172
173
it('should warn for onDblClick prop', () => {
180
- spyOnDev(console, 'error');
174
const container = document.createElement('div');
182
- ReactDOM.render(<div onDblClick={() => {}} />, container);
183
- if (__DEV__) {
184
- expect(console.error.calls.count(0)).toBe(1);
185
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
186
- 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
187
- );
188
- }
175
+ expect(() =>
176
+ ReactDOM.render(<div onDblClick={() => {}} />, container),
177
+ ).toWarnDev(
178
+ 'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n in div (at **)',
179
+ );
180
});
181
182
it('should warn for unknown string event handlers', () => {
192
- spyOnDev(console, 'error');
183
const container = document.createElement('div');
194
- ReactDOM.render(<div onUnknown="alert("hack")" />, container);
184
+ expect(() =>
185
+ ReactDOM.render(<div onUnknown="alert("hack")" />, container),
186
+ ).toWarnDev(
187
+ 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
188
+ );
189
expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
190
expect(container.firstChild.onUnknown).toBe(undefined);
197
- ReactDOM.render(<div onunknown="alert("hack")" />, container);
191
+ expect(() =>
192
+ ReactDOM.render(<div onunknown="alert("hack")" />, container),
193
+ ).toWarnDev(
194
+ 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
195
+ );
196
expect(container.firstChild.hasAttribute('onunknown')).toBe(false);
197
expect(container.firstChild.onunknown).toBe(undefined);
200
- ReactDOM.render(<div on-unknown="alert("hack")" />, container);
198
+ expect(() =>
199
+ ReactDOM.render(
200
+ <div on-unknown="alert("hack")" />,
201
+ container,
202
+ ),
203
+ ).toWarnDev(
204
+ 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
205
+ );
206
expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
207
expect(container.firstChild['on-unknown']).toBe(undefined);
203
- if (__DEV__) {
204
- expect(console.error.calls.count(0)).toBe(3);
205
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
206
- 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
207
- );
208
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
209
- 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
210
- );
211
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
212
- 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
213
- );
214
- }
208
});
209
210
it('should warn for unknown function event handlers', () => {
218
- spyOnDev(console, 'error');
211
const container = document.createElement('div');
220
- ReactDOM.render(<div onUnknown={function() {}} />, container);
212
+ expect(() =>
213
+ ReactDOM.render(<div onUnknown={function() {}} />, container),
214
+ ).toWarnDev(
215
+ 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
216
+ );
217
expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
218
expect(container.firstChild.onUnknown).toBe(undefined);
223
- ReactDOM.render(<div onunknown={function() {}} />, container);
219
+ expect(() =>
220
+ ReactDOM.render(<div onunknown={function() {}} />, container),
221
+ ).toWarnDev(
222
+ 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
223
+ );
224
expect(container.firstChild.hasAttribute('onunknown')).toBe(false);
225
expect(container.firstChild.onunknown).toBe(undefined);
226
- ReactDOM.render(<div on-unknown={function() {}} />, container);
226
+ expect(() =>
227
+ ReactDOM.render(<div on-unknown={function() {}} />, container),
228
+ ).toWarnDev(
229
+ 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
230
+ );
231
expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
232
expect(container.firstChild['on-unknown']).toBe(undefined);
229
- if (__DEV__) {
230
- expect(console.error.calls.count(0)).toBe(3);
231
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
232
- 'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n in div (at **)',
233
- );
234
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
235
- 'Warning: Unknown event handler property `onunknown`. It will be ignored.\n in div (at **)',
236
- );
237
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
238
- 'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n in div (at **)',
239
- );
240
- }
233
});
234
235
it('should warn for badly cased React attributes', () => {
244
- spyOnDev(console, 'error');
236
const container = document.createElement('div');
246
- ReactDOM.render(<div CHILDREN="5" />, container);
237
+ expect(() => ReactDOM.render(<div CHILDREN="5" />, container)).toWarnDev(
238
+ 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
239
+ );
240
expect(container.firstChild.getAttribute('CHILDREN')).toBe('5');
248
- if (__DEV__) {
249
- expect(console.error.calls.count(0)).toBe(1);
250
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
251
- 'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n in div (at **)',
252
- );
253
- }
241
});
242
243
it('should not warn for "0" as a unitless style value', () => {
@@ -264,20 +251,13 @@ describe('ReactDOMComponent', () => {
251
});
252
253
it('should warn nicely about NaN in style', () => {
267
- spyOnDev(console, 'error');
268
-
254
const style = {fontSize: NaN};
255
const div = document.createElement('div');
256
+ expect(() => ReactDOM.render(<span style={style} />, div)).toWarnDev(
257
+ 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
258
+ '\n in span (at **)',
259
+ );
260
ReactDOM.render(<span style={style} />, div);
272
- ReactDOM.render(<span style={style} />, div);
273
-
274
- if (__DEV__) {
275
- expect(console.error.calls.count()).toBe(1);
276
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toEqual(
277
- 'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
278
- '\n in span (at **)',
279
- );
280
- }
261
});
262
263
it('should update styles if initially null', () => {
@@ -575,44 +555,38 @@ describe('ReactDOMComponent', () => {
555
});
556
557
it('should reject attribute key injection attack on markup', () => {
578
- spyOnDev(console, 'error');
579
- for (let i = 0; i < 3; i++) {
580
- const container = document.createElement('div');
581
- const element = React.createElement(
582
- 'x-foo-component',
583
- {'blah" onclick="beevil" noise="hi': 'selected'},
584
- null,
585
- );
586
- ReactDOM.render(element, container);
587
- }
588
- if (__DEV__) {
589
- expect(console.error.calls.count()).toBe(1);
590
- expect(console.error.calls.argsFor(0)[0]).toEqual(
591
- 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
592
- );
593
- }
558
+ expect(() => {
559
+ for (let i = 0; i < 3; i++) {
560
+ const container = document.createElement('div');
561
+ const element = React.createElement(
562
+ 'x-foo-component',
563
+ {'blah" onclick="beevil" noise="hi': 'selected'},
564
+ null,
565
+ );
566
+ ReactDOM.render(element, container);
567
+ }
568
+ }).toWarnDev(
569
+ 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
570
+ );
571
});
572
573
it('should reject attribute key injection attack on update', () => {
597
- spyOnDev(console, 'error');
598
- for (let i = 0; i < 3; i++) {
599
- const container = document.createElement('div');
600
- const beforeUpdate = React.createElement('x-foo-component', {}, null);
601
- ReactDOM.render(beforeUpdate, container);
602
-
603
- const afterUpdate = React.createElement(
604
- 'x-foo-component',
605
- {'blah" onclick="beevil" noise="hi': 'selected'},
606
- null,
607
- );
608
- ReactDOM.render(afterUpdate, container);
609
- }
610
- if (__DEV__) {
611
- expect(console.error.calls.count()).toBe(1);
612
- expect(console.error.calls.argsFor(0)[0]).toEqual(
613
- 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
614
- );
615
- }
574
+ expect(() => {
575
+ for (let i = 0; i < 3; i++) {
576
+ const container = document.createElement('div');
577
+ const beforeUpdate = React.createElement('x-foo-component', {}, null);
578
+ ReactDOM.render(beforeUpdate, container);
579
+
580
+ const afterUpdate = React.createElement(
581
+ 'x-foo-component',
582
+ {'blah" onclick="beevil" noise="hi': 'selected'},
583
+ null,
584
+ );
585
+ ReactDOM.render(afterUpdate, container);
586
+ }
587
+ }).toWarnDev(
588
+ 'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
589
+ );
590
});
591
592
it('should update arbitrary attributes for tags containing dashes', () => {
@@ -857,17 +831,13 @@ describe('ReactDOMComponent', () => {
831
});
832
833
it('should warn about non-string "is" attribute', () => {
860
- spyOnDev(console, 'error');
834
const container = document.createElement('div');
862
- ReactDOM.render(<button is={function() {}} />, container);
863
-
864
- if (__DEV__) {
865
- expect(console.error.calls.count()).toBe(1);
866
- expect(console.error.calls.argsFor(0)[0]).toContain(
867
- 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
868
- 'the value to a string.',
869
- );
870
- }
835
+ expect(() =>
836
+ ReactDOM.render(<button is={function() {}} />, container),
837
+ ).toWarnDev(
838
+ 'Received a `function` for a string attribute `is`. If this is expected, cast ' +
839
+ 'the value to a string.',
840
+ );
841
});
842
843
it('should not update when switching between null/undefined', () => {
@@ -1020,59 +990,41 @@ describe('ReactDOMComponent', () => {
990
});
991
992
it('should not duplicate uppercased selfclosing tags', () => {
1023
- spyOnDev(console, 'error');
993
class Container extends React.Component {
994
render() {
995
return React.createElement('BR', null);
996
}
997
}
998
1030
- const returnedValue = ReactDOMServer.renderToString(<Container />);
999
+ let returnedValue;
1000
+
1001
+ expect(() => {
1002
+ returnedValue = ReactDOMServer.renderToString(<Container />);
1003
+ }).toWarnDev('<BR /> is using uppercase HTML.');
1004
expect(returnedValue).not.toContain('</BR>');
1032
- if (__DEV__) {
1033
- expect(console.error.calls.count()).toBe(1);
1034
- expect(console.error.calls.argsFor(0)[0]).toContain(
1035
- '<BR /> is using uppercase HTML.',
1036
- );
1037
- }
1005
});
1006
1007
it('should warn on upper case HTML tags, not SVG nor custom tags', () => {
1041
- spyOnDev(console, 'error');
1008
ReactTestUtils.renderIntoDocument(
1009
React.createElement('svg', null, React.createElement('PATH')),
1010
);
1045
- if (__DEV__) {
1046
- expect(console.error.calls.count()).toBe(0);
1047
- }
1011
ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG'));
1049
- if (__DEV__) {
1050
- expect(console.error.calls.count()).toBe(0);
1051
- }
1052
- ReactTestUtils.renderIntoDocument(React.createElement('IMG'));
1053
- if (__DEV__) {
1054
- expect(console.error.calls.count()).toBe(1);
1055
- expect(console.error.calls.argsFor(0)[0]).toContain(
1056
- '<IMG /> is using uppercase HTML.',
1057
- );
1058
- }
1012
+
1013
+ expect(() =>
1014
+ ReactTestUtils.renderIntoDocument(React.createElement('IMG')),
1015
+ ).toWarnDev('<IMG /> is using uppercase HTML.');
1016
});
1017
1018
it('should warn on props reserved for future use', () => {
1062
- spyOnDev(console, 'error');
1063
- ReactTestUtils.renderIntoDocument(<div aria="hello" />);
1064
- if (__DEV__) {
1065
- expect(console.error.calls.count()).toBe(1);
1066
- expect(console.error.calls.argsFor(0)[0]).toContain(
1067
- 'The `aria` attribute is reserved for future use in React. ' +
1068
- 'Pass individual `aria-` attributes instead.',
1069
- );
1070
- }
1019
+ expect(() =>
1020
+ ReactTestUtils.renderIntoDocument(<div aria="hello" />),
1021
+ ).toWarnDev(
1022
+ 'The `aria` attribute is reserved for future use in React. ' +
1023
+ 'Pass individual `aria-` attributes instead.',
1024
+ );
1025
});
1026
1027
it('should warn if the tag is unrecognized', () => {
1074
- spyOnDev(console, 'error');
1075
-
1028
let realToString;
1029
try {
1030
realToString = Object.prototype.toString;
@@ -1089,9 +1041,13 @@ describe('ReactDOMComponent', () => {
1041
};
1042
Object.prototype.toString = wrappedToString; // eslint-disable-line no-extend-native
1043
1092
- ReactTestUtils.renderIntoDocument(<bar />);
1044
+ expect(() => ReactTestUtils.renderIntoDocument(<bar />)).toWarnDev(
1045
+ 'The tag <bar> is unrecognized in this browser',
1046
+ );
1047
// Test deduplication
1094
- ReactTestUtils.renderIntoDocument(<foo />);
1048
+ expect(() => ReactTestUtils.renderIntoDocument(<foo />)).toWarnDev(
1049
+ 'The tag <foo> is unrecognized in this browser',
1050
+ );
1051
ReactTestUtils.renderIntoDocument(<foo />);
1052
// This is a funny case.
1053
// Chrome is the only major browser not shipping <time>. But as of July
@@ -1100,25 +1056,14 @@ describe('ReactDOMComponent', () => {
1056
// it soon will be, and many apps have been using it anyway.
1057
ReactTestUtils.renderIntoDocument(<time />);
1058
// Corner case. Make sure out deduplication logic doesn't break with weird tag.
1103
- ReactTestUtils.renderIntoDocument(<hasOwnProperty />);
1104
- } finally {
1105
- Object.prototype.toString = realToString; // eslint-disable-line no-extend-native
1106
- }
1107
-
1108
- if (__DEV__) {
1109
- expect(console.error.calls.count()).toBe(4);
1110
- expect(console.error.calls.argsFor(0)[0]).toContain(
1111
- 'The tag <bar> is unrecognized in this browser',
1112
- );
1113
- expect(console.error.calls.argsFor(1)[0]).toContain(
1114
- 'The tag <foo> is unrecognized in this browser',
1115
- );
1116
- expect(console.error.calls.argsFor(2)[0]).toContain(
1059
+ expect(() =>
1060
+ ReactTestUtils.renderIntoDocument(<hasOwnProperty />),
1061
+ ).toWarnDev([
1062
'<hasOwnProperty /> is using uppercase HTML',
1118
- );
1119
- expect(console.error.calls.argsFor(3)[0]).toContain(
1063
'The tag <hasOwnProperty> is unrecognized in this browser',
1121
- );
1064
+ ]);
1065
+ } finally {
1066
+ Object.prototype.toString = realToString; // eslint-disable-line no-extend-native
1067
}
1068
});
1069
@@ -1158,8 +1103,6 @@ describe('ReactDOMComponent', () => {
1103
});
1104
1105
it('should emit a warning once for a named custom component using shady DOM', () => {
1161
- spyOnDev(console, 'error');
1162
-
1106
const defaultCreateElement = document.createElement.bind(document);
1107
1108
try {
@@ -1174,26 +1117,17 @@ describe('ReactDOMComponent', () => {
1117
}
1118
}
1119
const node = document.createElement('div');
1177
- ReactDOM.render(<ShadyComponent />, node);
1178
- if (__DEV__) {
1179
- expect(console.error.calls.count()).toBe(1);
1180
- expect(console.error.calls.argsFor(0)[0]).toContain(
1181
- 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
1182
- 'cause things to break subtly.',
1183
- );
1184
- }
1120
+ expect(() => ReactDOM.render(<ShadyComponent />, node)).toWarnDev(
1121
+ 'ShadyComponent is using shady DOM. Using shady DOM with React can ' +
1122
+ 'cause things to break subtly.',
1123
+ );
1124
mountComponent({is: 'custom-shady-div2'});
1186
- if (__DEV__) {
1187
- expect(console.error.calls.count()).toBe(1);
1188
- }
1125
} finally {
1126
document.createElement = defaultCreateElement;
1127
}
1128
});
1129
1130
it('should emit a warning once for an unnamed custom component using shady DOM', () => {
1195
- spyOnDev(console, 'error');
1196
-
1131
const defaultCreateElement = document.createElement.bind(document);
1132
1133
try {
@@ -1203,19 +1137,13 @@ describe('ReactDOMComponent', () => {
1137
return container;
1138
};
1139
1206
- mountComponent({is: 'custom-shady-div'});
1207
- if (__DEV__) {
1208
- expect(console.error.calls.count()).toBe(1);
1209
- expect(console.error.calls.argsFor(0)[0]).toContain(
1210
- 'A component is using shady DOM. Using shady DOM with React can ' +
1211
- 'cause things to break subtly.',
1212
- );
1213
- }
1140
+ expect(() => mountComponent({is: 'custom-shady-div'})).toWarnDev(
1141
+ 'A component is using shady DOM. Using shady DOM with React can ' +
1142
+ 'cause things to break subtly.',
1143
+ );
1144
1145
+ // No additional warnings are expected
1146
mountComponent({is: 'custom-shady-div2'});
1216
- if (__DEV__) {
1217
- expect(console.error.calls.count()).toBe(1);
1218
- }
1147
} finally {
1148
document.createElement = defaultCreateElement;
1149
}
@@ -1223,7 +1151,6 @@ describe('ReactDOMComponent', () => {
1151
1152
it('should treat menuitem as a void element but still create the closing tag', () => {
1153
// menuitem is not implemented in jsdom, so this triggers the unknown warning error
1226
- spyOnDev(console, 'error');
1154
const container = document.createElement('div');
1155
1156
const returnedValue = ReactDOMServer.renderToString(
@@ -1256,25 +1183,15 @@ describe('ReactDOMComponent', () => {
1183
});
1184
1185
it('should validate against use of innerHTML', () => {
1259
- spyOnDev(console, 'error');
1260
- mountComponent({innerHTML: '<span>Hi Jim!</span>'});
1261
- if (__DEV__) {
1262
- expect(console.error.calls.count()).toBe(1);
1263
- expect(console.error.calls.argsFor(0)[0]).toContain(
1264
- 'Directly setting property `innerHTML` is not permitted. ',
1265
- );
1266
- }
1186
+ expect(() =>
1187
+ mountComponent({innerHTML: '<span>Hi Jim!</span>'}),
1188
+ ).toWarnDev('Directly setting property `innerHTML` is not permitted. ');
1189
});
1190
1191
it('should validate against use of innerHTML without case sensitivity', () => {
1270
- spyOnDev(console, 'error');
1271
- mountComponent({innerhtml: '<span>Hi Jim!</span>'});
1272
- if (__DEV__) {
1273
- expect(console.error.calls.count()).toBe(1);
1274
- expect(console.error.calls.argsFor(0)[0]).toContain(
1275
- 'Directly setting property `innerHTML` is not permitted. ',
1276
- );
1277
- }
1192
+ expect(() =>
1193
+ mountComponent({innerhtml: '<span>Hi Jim!</span>'}),
1194
+ ).toWarnDev('Directly setting property `innerHTML` is not permitted. ');
1195
});
1196
1197
it('should validate use of dangerouslySetInnerHTML', () => {
@@ -1302,17 +1219,14 @@ describe('ReactDOMComponent', () => {
1219
});
1220
1221
it('should warn about contentEditable and children', () => {
1305
- spyOnDev(console, 'error');
1306
- mountComponent({contentEditable: true, children: ''});
1307
- if (__DEV__) {
1308
- expect(console.error.calls.count()).toBe(1);
1309
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1310
- 'Warning: A component is `contentEditable` and contains `children` ' +
1311
- 'managed by React. It is now your responsibility to guarantee that ' +
1312
- 'none of those nodes are unexpectedly modified or duplicated. This ' +
1313
- 'is probably not intentional.\n in div (at **)',
1314
- );
1315
- }
1222
+ expect(() =>
1223
+ mountComponent({contentEditable: true, children: ''}),
1224
+ ).toWarnDev(
1225
+ 'Warning: A component is `contentEditable` and contains `children` ' +
1226
+ 'managed by React. It is now your responsibility to guarantee that ' +
1227
+ 'none of those nodes are unexpectedly modified or duplicated. This ' +
1228
+ 'is probably not intentional.\n in div (at **)',
1229
+ );
1230
});
1231
1232
it('should respect suppressContentEditableWarning', () => {
@@ -1442,17 +1356,14 @@ describe('ReactDOMComponent', () => {
1356
});
1357
1358
it('should warn about contentEditable and children', () => {
1445
- spyOnDev(console, 'error');
1446
- ReactDOM.render(
1447
- <div contentEditable={true}>
1448
- <div />
1449
- </div>,
1450
- container,
1451
- );
1452
- if (__DEV__) {
1453
- expect(console.error.calls.count()).toBe(1);
1454
- expect(console.error.calls.argsFor(0)[0]).toContain('contentEditable');
1455
- }
1359
+ expect(() => {
1360
+ ReactDOM.render(
1361
+ <div contentEditable={true}>
1362
+ <div />
1363
+ </div>,
1364
+ container,
1365
+ );
1366
+ }).toWarnDev('contentEditable');
1367
});
1368
1369
it('should validate against invalid styles', () => {
@@ -1564,56 +1475,45 @@ describe('ReactDOMComponent', () => {
1475
1476
describe('nesting validation', () => {
1477
it('warns on invalid nesting', () => {
1567
- spyOnDev(console, 'error');
1568
- ReactTestUtils.renderIntoDocument(
1569
- <div>
1570
- <tr />
1571
- <tr />
1572
- </div>,
1573
- );
1574
-
1575
- if (__DEV__) {
1576
- expect(console.error.calls.count()).toBe(2);
1577
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1578
- 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1579
- '<div>.' +
1580
- '\n in tr (at **)' +
1581
- '\n in div (at **)',
1582
- );
1583
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1584
- 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1585
- '<div>.' +
1586
- '\n in tr (at **)' +
1587
- '\n in div (at **)',
1478
+ expect(() => {
1479
+ ReactTestUtils.renderIntoDocument(
1480
+ <div>
1481
+ <tr />
1482
+ <tr />
1483
+ </div>,
1484
);
1589
- }
1485
+ }).toWarnDev([
1486
+ 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1487
+ '<div>.' +
1488
+ '\n in tr (at **)' +
1489
+ '\n in div (at **)',
1490
+ 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1491
+ '<div>.' +
1492
+ '\n in tr (at **)' +
1493
+ '\n in div (at **)',
1494
+ ]);
1495
});
1496
1497
it('warns on invalid nesting at root', () => {
1593
- spyOnDev(console, 'error');
1498
const p = document.createElement('p');
1595
- ReactDOM.render(
1596
- <span>
1597
- <p />
1598
- </span>,
1599
- p,
1600
- );
1499
1602
- if (__DEV__) {
1603
- expect(console.error.calls.count()).toBe(1);
1604
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1605
- 'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
1606
- 'of <p>.' +
1607
- // There is no outer `p` here because root container is not part of the stack.
1608
- '\n in p (at **)' +
1609
- '\n in span (at **)',
1500
+ expect(() => {
1501
+ ReactDOM.render(
1502
+ <span>
1503
+ <p />
1504
+ </span>,
1505
+ p,
1506
);
1611
- }
1507
+ }).toWarnDev(
1508
+ 'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
1509
+ 'of <p>.' +
1510
+ // There is no outer `p` here because root container is not part of the stack.
1511
+ '\n in p (at **)' +
1512
+ '\n in span (at **)',
1513
+ );
1514
});
1515
1516
it('warns nicely for table rows', () => {
1615
- spyOnDev(console, 'error');
1616
-
1517
class Row extends React.Component {
1518
render() {
1519
return <tr>x</tr>;
@@ -1630,41 +1530,29 @@ describe('ReactDOMComponent', () => {
1530
}
1531
}
1532
1633
- ReactTestUtils.renderIntoDocument(<Foo />);
1634
- if (__DEV__) {
1635
- expect(console.error.calls.count()).toBe(3);
1636
-
1637
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1638
- 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1639
- '<table>. Add a <tbody> to your code to match the DOM tree generated ' +
1640
- 'by the browser.' +
1641
- '\n in tr (at **)' +
1642
- '\n in Row (at **)' +
1643
- '\n in table (at **)' +
1644
- '\n in Foo (at **)',
1645
- );
1646
-
1647
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1648
- 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1649
- 'child of <tr>.' +
1650
- '\n in tr (at **)' +
1651
- '\n in Row (at **)' +
1652
- '\n in table (at **)' +
1653
- '\n in Foo (at **)',
1654
- );
1655
-
1656
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
1657
- 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1658
- "appear as a child of <table>. Make sure you don't have any extra " +
1659
- 'whitespace between tags on each line of your source code.' +
1660
- '\n in table (at **)' +
1661
- '\n in Foo (at **)',
1662
- );
1663
- }
1533
+ expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toWarnDev([
1534
+ 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1535
+ '<table>. Add a <tbody> to your code to match the DOM tree generated ' +
1536
+ 'by the browser.' +
1537
+ '\n in tr (at **)' +
1538
+ '\n in Row (at **)' +
1539
+ '\n in table (at **)' +
1540
+ '\n in Foo (at **)',
1541
+ 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1542
+ 'child of <tr>.' +
1543
+ '\n in tr (at **)' +
1544
+ '\n in Row (at **)' +
1545
+ '\n in table (at **)' +
1546
+ '\n in Foo (at **)',
1547
+ 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1548
+ "appear as a child of <table>. Make sure you don't have any extra " +
1549
+ 'whitespace between tags on each line of your source code.' +
1550
+ '\n in table (at **)' +
1551
+ '\n in Foo (at **)',
1552
+ ]);
1553
});
1554
1555
it('gives useful context in warnings', () => {
1667
- spyOnDev(console, 'error');
1556
function Row() {
1557
return <tr />;
1558
}
@@ -1694,19 +1582,13 @@ describe('ReactDOMComponent', () => {
1582
function App1() {
1583
return <Viz1 />;
1584
}
1697
- ReactTestUtils.renderIntoDocument(<App1 />);
1698
- if (__DEV__) {
1699
- expect(console.error.calls.count()).toBe(1);
1700
- expect(
1701
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
1702
- ).toContain(
1703
- '\n in tr (at **)' +
1704
- '\n in Row (at **)' +
1705
- '\n in FancyRow (at **)' +
1706
- '\n in table (at **)' +
1707
- '\n in Viz1 (at **)',
1708
- );
1709
- }
1585
+ expect(() => ReactTestUtils.renderIntoDocument(<App1 />)).toWarnDev(
1586
+ '\n in tr (at **)' +
1587
+ '\n in Row (at **)' +
1588
+ '\n in FancyRow (at **)' +
1589
+ '\n in table (at **)' +
1590
+ '\n in Viz1 (at **)',
1591
+ );
1592
1593
function Viz2() {
1594
return (
@@ -1718,74 +1600,56 @@ describe('ReactDOMComponent', () => {
1600
function App2() {
1601
return <Viz2 />;
1602
}
1721
- ReactTestUtils.renderIntoDocument(<App2 />);
1722
- if (__DEV__) {
1723
- expect(console.error.calls.count()).toBe(2);
1724
- expect(
1725
- normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
1726
- ).toContain(
1727
- '\n in tr (at **)' +
1728
- '\n in Row (at **)' +
1729
- '\n in FancyRow (at **)' +
1730
- '\n in table (at **)' +
1731
- '\n in Table (at **)' +
1732
- '\n in FancyTable (at **)' +
1733
- '\n in Viz2 (at **)',
1734
- );
1735
- }
1736
-
1737
- ReactTestUtils.renderIntoDocument(
1738
- <FancyTable>
1739
- <FancyRow />
1740
- </FancyTable>,
1603
+ expect(() => ReactTestUtils.renderIntoDocument(<App2 />)).toWarnDev(
1604
+ '\n in tr (at **)' +
1605
+ '\n in Row (at **)' +
1606
+ '\n in FancyRow (at **)' +
1607
+ '\n in table (at **)' +
1608
+ '\n in Table (at **)' +
1609
+ '\n in FancyTable (at **)' +
1610
+ '\n in Viz2 (at **)',
1611
);
1742
- if (__DEV__) {
1743
- expect(console.error.calls.count()).toBe(3);
1744
- expect(
1745
- normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
1746
- ).toContain(
1747
- '\n in tr (at **)' +
1748
- '\n in Row (at **)' +
1749
- '\n in FancyRow (at **)' +
1750
- '\n in table (at **)' +
1751
- '\n in Table (at **)' +
1752
- '\n in FancyTable (at **)',
1753
- );
1754
- }
1612
1756
- ReactTestUtils.renderIntoDocument(
1757
- <table>
1758
- <FancyRow />
1759
- </table>,
1760
- );
1761
- if (__DEV__) {
1762
- expect(console.error.calls.count()).toBe(4);
1763
- expect(
1764
- normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
1765
- ).toContain(
1766
- '\n in tr (at **)' +
1767
- '\n in Row (at **)' +
1768
- '\n in FancyRow (at **)' +
1769
- '\n in table (at **)',
1613
+ expect(() => {
1614
+ ReactTestUtils.renderIntoDocument(
1615
+ <FancyTable>
1616
+ <FancyRow />
1617
+ </FancyTable>,
1618
);
1771
- }
1619
+ }).toWarnDev(
1620
+ '\n in tr (at **)' +
1621
+ '\n in Row (at **)' +
1622
+ '\n in FancyRow (at **)' +
1623
+ '\n in table (at **)' +
1624
+ '\n in Table (at **)' +
1625
+ '\n in FancyTable (at **)',
1626
+ );
1627
1773
- ReactTestUtils.renderIntoDocument(
1774
- <FancyTable>
1775
- <tr />
1776
- </FancyTable>,
1628
+ expect(() => {
1629
+ ReactTestUtils.renderIntoDocument(
1630
+ <table>
1631
+ <FancyRow />
1632
+ </table>,
1633
+ );
1634
+ }).toWarnDev(
1635
+ '\n in tr (at **)' +
1636
+ '\n in Row (at **)' +
1637
+ '\n in FancyRow (at **)' +
1638
+ '\n in table (at **)',
1639
);
1778
- if (__DEV__) {
1779
- expect(console.error.calls.count()).toBe(5);
1780
- expect(
1781
- normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
1782
- ).toContain(
1783
- '\n in tr (at **)' +
1784
- '\n in table (at **)' +
1785
- '\n in Table (at **)' +
1786
- '\n in FancyTable (at **)',
1640
+
1641
+ expect(() => {
1642
+ ReactTestUtils.renderIntoDocument(
1643
+ <FancyTable>
1644
+ <tr />
1645
+ </FancyTable>,
1646
);
1788
- }
1647
+ }).toWarnDev(
1648
+ '\n in tr (at **)' +
1649
+ '\n in table (at **)' +
1650
+ '\n in Table (at **)' +
1651
+ '\n in FancyTable (at **)',
1652
+ );
1653
1654
class Link extends React.Component {
1655
render() {
@@ -1793,305 +1657,228 @@ describe('ReactDOMComponent', () => {
1657
}
1658
}
1659
1796
- ReactTestUtils.renderIntoDocument(
1797
- <Link>
1798
- <div>
1799
- <Link />
1800
- </div>
1801
- </Link>,
1802
- );
1803
- if (__DEV__) {
1804
- expect(console.error.calls.count()).toBe(6);
1805
- expect(
1806
- normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
1807
- ).toContain(
1808
- '\n in a (at **)' +
1809
- '\n in Link (at **)' +
1810
- '\n in div (at **)' +
1811
- '\n in a (at **)' +
1812
- '\n in Link (at **)',
1660
+ expect(() => {
1661
+ ReactTestUtils.renderIntoDocument(
1662
+ <Link>
1663
+ <div>
1664
+ <Link />
1665
+ </div>
1666
+ </Link>,
1667
);
1814
- }
1668
+ }).toWarnDev(
1669
+ '\n in a (at **)' +
1670
+ '\n in Link (at **)' +
1671
+ '\n in div (at **)' +
1672
+ '\n in a (at **)' +
1673
+ '\n in Link (at **)',
1674
+ );
1675
});
1676
1677
it('should warn about incorrect casing on properties (ssr)', () => {
1818
- spyOnDev(console, 'error');
1819
- ReactDOMServer.renderToString(
1820
- React.createElement('input', {type: 'text', tabindex: '1'}),
1821
- );
1822
- if (__DEV__) {
1823
- expect(console.error.calls.count()).toBe(1);
1824
- expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1825
- }
1678
+ expect(() => {
1679
+ ReactDOMServer.renderToString(
1680
+ React.createElement('input', {type: 'text', tabindex: '1'}),
1681
+ );
1682
+ }).toWarnDev('tabIndex');
1683
});
1684
1685
it('should warn about incorrect casing on event handlers (ssr)', () => {
1829
- spyOnDev(console, 'error');
1830
- ReactDOMServer.renderToString(
1831
- React.createElement('input', {type: 'text', oninput: '1'}),
1686
+ expect(() => {
1687
+ ReactDOMServer.renderToString(
1688
+ React.createElement('input', {type: 'text', oninput: '1'}),
1689
+ );
1690
+ }).toWarnDev(
1691
+ 'Invalid event handler property `oninput`. ' +
1692
+ 'React events use the camelCase naming convention, ' +
1693
+ // Note: we don't know the right event name so we
1694
+ // use a generic one (onClick) as a suggestion.
1695
+ // This is because we don't bundle the event system
1696
+ // on the server.
1697
+ 'for example `onClick`.',
1698
);
1699
ReactDOMServer.renderToString(
1700
React.createElement('input', {type: 'text', onKeydown: '1'}),
1701
);
1836
- if (__DEV__) {
1837
- expect(console.error.calls.count()).toBe(1);
1838
- expect(console.error.calls.argsFor(0)[0]).toContain(
1839
- 'Invalid event handler property `oninput`. ' +
1840
- 'React events use the camelCase naming convention, ' +
1841
- // Note: we don't know the right event name so we
1842
- // use a generic one (onClick) as a suggestion.
1843
- // This is because we don't bundle the event system
1844
- // on the server.
1845
- 'for example `onClick`.',
1846
- );
1847
- // We can't warn for `onKeydown` on the server because
1848
- // there is no way tell if this is a valid event or not
1849
- // without access to the event system (which we don't bundle).
1850
- }
1702
+ // We can't warn for `onKeydown` on the server because
1703
+ // there is no way tell if this is a valid event or not
1704
+ // without access to the event system (which we don't bundle).
1705
});
1706
1707
it('should warn about incorrect casing on properties', () => {
1854
- spyOnDev(console, 'error');
1855
- ReactTestUtils.renderIntoDocument(
1856
- React.createElement('input', {type: 'text', tabindex: '1'}),
1857
- );
1858
- if (__DEV__) {
1859
- expect(console.error.calls.count()).toBe(1);
1860
- expect(console.error.calls.argsFor(0)[0]).toContain('tabIndex');
1861
- }
1708
+ expect(() => {
1709
+ ReactTestUtils.renderIntoDocument(
1710
+ React.createElement('input', {type: 'text', tabindex: '1'}),
1711
+ );
1712
+ }).toWarnDev('tabIndex');
1713
});
1714
1715
it('should warn about incorrect casing on event handlers', () => {
1865
- spyOnDev(console, 'error');
1866
- ReactTestUtils.renderIntoDocument(
1867
- React.createElement('input', {type: 'text', oninput: '1'}),
1868
- );
1869
- ReactTestUtils.renderIntoDocument(
1870
- React.createElement('input', {type: 'text', onKeydown: '1'}),
1871
- );
1872
- if (__DEV__) {
1873
- expect(console.error.calls.count()).toBe(2);
1874
- expect(console.error.calls.argsFor(0)[0]).toContain('onInput');
1875
- expect(console.error.calls.argsFor(1)[0]).toContain('onKeyDown');
1876
- }
1716
+ expect(() => {
1717
+ ReactTestUtils.renderIntoDocument(
1718
+ React.createElement('input', {type: 'text', oninput: '1'}),
1719
+ );
1720
+ }).toWarnDev('onInput');
1721
+ expect(() => {
1722
+ ReactTestUtils.renderIntoDocument(
1723
+ React.createElement('input', {type: 'text', onKeydown: '1'}),
1724
+ );
1725
+ }).toWarnDev('onKeyDown');
1726
});
1727
1728
it('should warn about class', () => {
1880
- spyOnDev(console, 'error');
1881
- ReactTestUtils.renderIntoDocument(
1882
- React.createElement('div', {class: 'muffins'}),
1883
- );
1884
- if (__DEV__) {
1885
- expect(console.error.calls.count()).toBe(1);
1886
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
1887
- }
1729
+ expect(() => {
1730
+ ReactTestUtils.renderIntoDocument(
1731
+ React.createElement('div', {class: 'muffins'}),
1732
+ );
1733
+ }).toWarnDev('className');
1734
});
1735
1736
it('should warn about class (ssr)', () => {
1891
- spyOnDev(console, 'error');
1892
- ReactDOMServer.renderToString(
1893
- React.createElement('div', {class: 'muffins'}),
1894
- );
1895
- if (__DEV__) {
1896
- expect(console.error.calls.count()).toBe(1);
1897
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
1898
- }
1737
+ expect(() => {
1738
+ ReactDOMServer.renderToString(
1739
+ React.createElement('div', {class: 'muffins'}),
1740
+ );
1741
+ }).toWarnDev('className');
1742
});
1743
1744
it('should warn about props that are no longer supported', () => {
1902
- spyOnDev(console, 'error');
1745
ReactTestUtils.renderIntoDocument(<div />);
1904
- if (__DEV__) {
1905
- expect(console.error.calls.count()).toBe(0);
1906
- }
1746
1908
- ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />);
1909
- if (__DEV__) {
1910
- expect(console.error.calls.count()).toBe(1);
1911
- }
1912
-
1913
- ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />);
1914
- if (__DEV__) {
1915
- expect(console.error.calls.count()).toBe(2);
1916
- }
1747
+ expect(() =>
1748
+ ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />),
1749
+ ).toWarnDev(
1750
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1751
+ );
1752
+ expect(() =>
1753
+ ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />),
1754
+ ).toWarnDev(
1755
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1756
+ );
1757
});
1758
1759
it('should warn about props that are no longer supported without case sensitivity', () => {
1920
- spyOnDev(console, 'error');
1760
ReactTestUtils.renderIntoDocument(<div />);
1922
- if (__DEV__) {
1923
- expect(console.error.calls.count()).toBe(0);
1924
- }
1925
-
1926
- ReactTestUtils.renderIntoDocument(<div onfocusin={() => {}} />);
1927
- if (__DEV__) {
1928
- expect(console.error.calls.count()).toBe(1);
1929
- }
1930
-
1931
- ReactTestUtils.renderIntoDocument(<div onfocusout={() => {}} />);
1932
- if (__DEV__) {
1933
- expect(console.error.calls.count()).toBe(2);
1934
- }
1761
+ expect(() =>
1762
+ ReactTestUtils.renderIntoDocument(<div onfocusin={() => {}} />),
1763
+ ).toWarnDev(
1764
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1765
+ );
1766
+ expect(() =>
1767
+ ReactTestUtils.renderIntoDocument(<div onfocusout={() => {}} />),
1768
+ ).toWarnDev(
1769
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1770
+ );
1771
});
1772
1773
it('should warn about props that are no longer supported (ssr)', () => {
1938
- spyOnDev(console, 'error');
1774
ReactDOMServer.renderToString(<div />);
1940
- if (__DEV__) {
1941
- expect(console.error.calls.count()).toBe(0);
1942
- }
1943
-
1944
- ReactDOMServer.renderToString(<div onFocusIn={() => {}} />);
1945
- if (__DEV__) {
1946
- expect(console.error.calls.count()).toBe(1);
1947
- }
1948
-
1949
- ReactDOMServer.renderToString(<div onFocusOut={() => {}} />);
1950
- if (__DEV__) {
1951
- expect(console.error.calls.count()).toBe(2);
1952
- }
1775
+ expect(() =>
1776
+ ReactDOMServer.renderToString(<div onFocusIn={() => {}} />),
1777
+ ).toWarnDev(
1778
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1779
+ );
1780
+ expect(() =>
1781
+ ReactDOMServer.renderToString(<div onFocusOut={() => {}} />),
1782
+ ).toWarnDev(
1783
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1784
+ );
1785
});
1786
1787
it('should warn about props that are no longer supported without case sensitivity (ssr)', () => {
1956
- spyOnDev(console, 'error');
1788
ReactDOMServer.renderToString(<div />);
1958
- if (__DEV__) {
1959
- expect(console.error.calls.count()).toBe(0);
1960
- }
1961
-
1962
- ReactDOMServer.renderToString(<div onfocusin={() => {}} />);
1963
- if (__DEV__) {
1964
- expect(console.error.calls.count()).toBe(1);
1965
- }
1966
-
1967
- ReactDOMServer.renderToString(<div onfocusout={() => {}} />);
1968
- if (__DEV__) {
1969
- expect(console.error.calls.count()).toBe(2);
1970
- }
1789
+ expect(() =>
1790
+ ReactDOMServer.renderToString(<div onfocusin={() => {}} />),
1791
+ ).toWarnDev(
1792
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1793
+ );
1794
+ expect(() =>
1795
+ ReactDOMServer.renderToString(<div onfocusout={() => {}} />),
1796
+ ).toWarnDev(
1797
+ 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
1798
+ );
1799
});
1800
1801
it('gives source code refs for unknown prop warning', () => {
1974
- spyOnDev(console, 'error');
1975
- ReactTestUtils.renderIntoDocument(<div class="paladin" />);
1976
- ReactTestUtils.renderIntoDocument(<input type="text" onclick="1" />);
1977
- if (__DEV__) {
1978
- expect(console.error.calls.count()).toBe(2);
1979
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1980
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1981
- );
1982
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1983
- 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1984
- '`onClick`?\n in input (at **)',
1985
- );
1986
- }
1802
+ expect(() =>
1803
+ ReactTestUtils.renderIntoDocument(<div class="paladin" />),
1804
+ ).toWarnDev(
1805
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1806
+ );
1807
+ expect(() =>
1808
+ ReactTestUtils.renderIntoDocument(<input type="text" onclick="1" />),
1809
+ ).toWarnDev(
1810
+ 'Warning: Invalid event handler property `onclick`. Did you mean ' +
1811
+ '`onClick`?\n in input (at **)',
1812
+ );
1813
});
1814
1815
it('gives source code refs for unknown prop warning (ssr)', () => {
1990
- spyOnDev(console, 'error');
1991
- ReactDOMServer.renderToString(<div class="paladin" />);
1992
- ReactDOMServer.renderToString(<input type="text" oninput="1" />);
1993
- if (__DEV__) {
1994
- expect(console.error.calls.count()).toBe(2);
1995
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1996
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1997
- );
1998
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1999
- 'Warning: Invalid event handler property `oninput`. ' +
2000
- // Note: we don't know the right event name so we
2001
- // use a generic one (onClick) as a suggestion.
2002
- // This is because we don't bundle the event system
2003
- // on the server.
2004
- 'React events use the camelCase naming convention, for example `onClick`.' +
2005
- '\n in input (at **)',
2006
- );
2007
- }
1816
+ expect(() =>
1817
+ ReactDOMServer.renderToString(<div class="paladin" />),
1818
+ ).toWarnDev(
1819
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1820
+ );
1821
+ expect(() =>
1822
+ ReactDOMServer.renderToString(<input type="text" oninput="1" />),
1823
+ ).toWarnDev(
1824
+ 'Warning: Invalid event handler property `oninput`. ' +
1825
+ // Note: we don't know the right event name so we
1826
+ // use a generic one (onClick) as a suggestion.
1827
+ // This is because we don't bundle the event system
1828
+ // on the server.
1829
+ 'React events use the camelCase naming convention, for example `onClick`.' +
1830
+ '\n in input (at **)',
1831
+ );
1832
});
1833
1834
it('gives source code refs for unknown prop warning for update render', () => {
2011
- spyOnDev(console, 'error');
1835
const container = document.createElement('div');
1836
1837
ReactTestUtils.renderIntoDocument(<div className="paladin" />, container);
2015
- if (__DEV__) {
2016
- expect(console.error.calls.count()).toBe(0);
2017
- }
2018
-
2019
- ReactTestUtils.renderIntoDocument(<div class="paladin" />, container);
2020
- if (__DEV__) {
2021
- expect(console.error.calls.count()).toBe(1);
2022
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
2023
- 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
2024
- );
2025
- }
1838
+ expect(() =>
1839
+ ReactTestUtils.renderIntoDocument(<div class="paladin" />, container),
1840
+ ).toWarnDev(
1841
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?\n in div (at **)',
1842
+ );
1843
});
1844
1845
it('gives source code refs for unknown prop warning for exact elements', () => {
2029
- spyOnDev(console, 'error');
2030
-
2031
- ReactTestUtils.renderIntoDocument(
2032
- <div className="foo1">
2033
- <div class="foo2" />
2034
- <div onClick={() => {}} />
2035
- <div onclick={() => {}} />
2036
- <div className="foo5" />
2037
- <div className="foo6" />
2038
- </div>,
2039
- );
2040
-
2041
- if (__DEV__) {
2042
- expect(console.error.calls.count()).toBe(2);
2043
-
2044
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
2045
- let matches = console.error.calls
2046
- .argsFor(0)[0]
2047
- .match(/.*\(.*:(\d+)\).*/);
2048
- const previousLine = matches[1];
2049
-
2050
- expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2051
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
2052
- const currentLine = matches[1];
2053
-
2054
- //verify line number has a proper relative difference,
2055
- //since hard coding the line number would make test too brittle
2056
- expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
2057
- }
1846
+ expect(() =>
1847
+ ReactTestUtils.renderIntoDocument(
1848
+ <div className="foo1">
1849
+ <span class="foo2" />
1850
+ <div onClick={() => {}} />
1851
+ <strong onclick={() => {}} />
1852
+ <div className="foo5" />
1853
+ <div className="foo6" />
1854
+ </div>,
1855
+ ),
1856
+ ).toWarnDev([
1857
+ 'Invalid DOM property `class`. Did you mean `className`?\n in span (at **)',
1858
+ 'Invalid event handler property `onclick`. Did you mean `onClick`?\n in strong (at **)',
1859
+ ]);
1860
});
1861
1862
it('gives source code refs for unknown prop warning for exact elements (ssr)', () => {
2061
- spyOnDev(console, 'error');
2062
-
2063
- ReactDOMServer.renderToString(
2064
- <div className="foo1">
2065
- <div class="foo2" />
2066
- <div onClick="foo3" />
2067
- <div onclick="foo4" />
2068
- <div className="foo5" />
2069
- <div className="foo6" />
2070
- </div>,
2071
- );
2072
-
2073
- if (__DEV__) {
2074
- expect(console.error.calls.count()).toBe(2);
2075
-
2076
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
2077
- let matches = console.error.calls
2078
- .argsFor(0)[0]
2079
- .match(/.*\(.*:(\d+)\).*/);
2080
- const previousLine = (matches || [])[1];
2081
-
2082
- expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2083
- matches =
2084
- console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/) || {};
2085
- const currentLine = (matches || [])[1];
2086
-
2087
- //verify line number has a proper relative difference,
2088
- //since hard coding the line number would make test too brittle
2089
- expect(parseInt(previousLine, 10) + 2).toBe(parseInt(currentLine, 10));
2090
- }
1863
+ expect(() =>
1864
+ ReactDOMServer.renderToString(
1865
+ <div className="foo1">
1866
+ <span class="foo2" />
1867
+ <div onClick="foo3" />
1868
+ <strong onclick="foo4" />
1869
+ <div className="foo5" />
1870
+ <div className="foo6" />
1871
+ </div>,
1872
+ ),
1873
+ ).toWarnDev([
1874
+ 'Invalid DOM property `class`. Did you mean `className`?\n in span (at **)',
1875
+ 'Invalid event handler property `onclick`. ' +
1876
+ 'React events use the camelCase naming convention, for example `onClick`.' +
1877
+ '\n in strong (at **)',
1878
+ ]);
1879
});
1880
1881
it('gives source code refs for unknown prop warning for exact elements in composition', () => {
2094
- spyOnDev(console, 'error');
1882
const container = document.createElement('div');
1883
1884
class Parent extends React.Component {
@@ -2109,7 +1896,7 @@ describe('ReactDOMComponent', () => {
1896
1897
class Child1 extends React.Component {
1898
render() {
2112
- return <div class="paladin">Child1</div>;
1899
+ return <span class="paladin">Child1</span>;
1900
}
1901
}
1902
@@ -2121,7 +1908,7 @@ describe('ReactDOMComponent', () => {
1908
1909
class Child3 extends React.Component {
1910
render() {
2124
- return <div onclick="1">Child3</div>;
1911
+ return <strong onclick="1">Child3</strong>;
1912
}
1913
}
1914
@@ -2131,29 +1918,15 @@ describe('ReactDOMComponent', () => {
1918
}
1919
}
1920
2134
- ReactTestUtils.renderIntoDocument(<Parent />, container);
2135
-
2136
- if (__DEV__) {
2137
- expect(console.error.calls.count()).toBe(2);
2138
-
2139
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
2140
- let matches = console.error.calls
2141
- .argsFor(0)[0]
2142
- .match(/.*\(.*:(\d+)\).*/);
2143
- const previousLine = (matches || [])[1];
2144
-
2145
- expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2146
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
2147
- const currentLine = (matches || [])[1];
2148
-
2149
- //verify line number has a proper relative difference,
2150
- //since hard coding the line number would make test too brittle
2151
- expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
2152
- }
1921
+ expect(() =>
1922
+ ReactTestUtils.renderIntoDocument(<Parent />, container),
1923
+ ).toWarnDev([
1924
+ 'Invalid DOM property `class`. Did you mean `className`?\n in span (at **)',
1925
+ 'Invalid event handler property `onclick`. Did you mean `onClick`?\n in strong (at **)',
1926
+ ]);
1927
});
1928
1929
it('gives source code refs for unknown prop warning for exact elements in composition (ssr)', () => {
2156
- spyOnDev(console, 'error');
1930
const container = document.createElement('div');
1931
1932
class Parent extends React.Component {
@@ -2171,7 +1944,7 @@ describe('ReactDOMComponent', () => {
1944
1945
class Child1 extends React.Component {
1946
render() {
2174
- return <div class="paladin">Child1</div>;
1947
+ return <span class="paladin">Child1</span>;
1948
}
1949
}
1950
@@ -2183,7 +1956,7 @@ describe('ReactDOMComponent', () => {
1956
1957
class Child3 extends React.Component {
1958
render() {
2186
- return <div onclick="1">Child3</div>;
1959
+ return <strong onclick="1">Child3</strong>;
1960
}
1961
}
1962
@@ -2193,71 +1966,49 @@ describe('ReactDOMComponent', () => {
1966
}
1967
}
1968
2196
- ReactDOMServer.renderToString(<Parent />, container);
2197
-
2198
- if (__DEV__) {
2199
- expect(console.error.calls.count()).toBe(2);
2200
-
2201
- expect(console.error.calls.argsFor(0)[0]).toContain('className');
2202
- let matches = console.error.calls
2203
- .argsFor(0)[0]
2204
- .match(/.*\(.*:(\d+)\).*/);
2205
- const previousLine = (matches || [])[1];
2206
-
2207
- expect(console.error.calls.argsFor(1)[0]).toContain('onClick');
2208
- matches = console.error.calls.argsFor(1)[0].match(/.*\(.*:(\d+)\).*/);
2209
- const currentLine = (matches || [])[1];
2210
-
2211
- //verify line number has a proper relative difference,
2212
- //since hard coding the line number would make test too brittle
2213
- expect(parseInt(previousLine, 10) + 12).toBe(parseInt(currentLine, 10));
2214
- }
1969
+ expect(() =>
1970
+ ReactDOMServer.renderToString(<Parent />, container),
1971
+ ).toWarnDev([
1972
+ 'Invalid DOM property `class`. Did you mean `className`?\n in span (at **)',
1973
+ 'Invalid event handler property `onclick`. ' +
1974
+ 'React events use the camelCase naming convention, for example `onClick`.' +
1975
+ '\n in strong (at **)',
1976
+ ]);
1977
});
1978
1979
it('should suggest property name if available', () => {
2218
- spyOnDev(console, 'error');
2219
-
2220
- ReactTestUtils.renderIntoDocument(
2221
- React.createElement('label', {for: 'test'}),
2222
- );
2223
- ReactTestUtils.renderIntoDocument(
2224
- React.createElement('input', {type: 'text', autofocus: true}),
1980
+ expect(() =>
1981
+ ReactTestUtils.renderIntoDocument(
1982
+ React.createElement('label', {for: 'test'}),
1983
+ ),
1984
+ ).toWarnDev(
1985
+ 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
1986
);
1987
2227
- if (__DEV__) {
2228
- expect(console.error.calls.count()).toBe(2);
2229
-
2230
- expect(console.error.calls.argsFor(0)[0]).toBe(
2231
- 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
2232
- );
2233
-
2234
- expect(console.error.calls.argsFor(1)[0]).toBe(
2235
- 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2236
- );
2237
- }
1988
+ expect(() =>
1989
+ ReactTestUtils.renderIntoDocument(
1990
+ React.createElement('input', {type: 'text', autofocus: true}),
1991
+ ),
1992
+ ).toWarnDev(
1993
+ 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
1994
+ );
1995
});
1996
1997
it('should suggest property name if available (ssr)', () => {
2241
- spyOnDev(console, 'error');
2242
-
2243
- ReactDOMServer.renderToString(
2244
- React.createElement('label', {for: 'test'}),
1998
+ expect(() =>
1999
+ ReactDOMServer.renderToString(
2000
+ React.createElement('label', {for: 'test'}),
2001
+ ),
2002
+ ).toWarnDev(
2003
+ 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
2004
);
2246
- ReactDOMServer.renderToString(
2247
- React.createElement('input', {type: 'text', autofocus: true}),
2005
+ expect(() =>
2006
+ ReactDOMServer.renderToString(
2007
+ React.createElement('input', {type: 'text', autofocus: true}),
2008
+ ),
2009
+ ).toWarnDev(
2010
+ 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2011
);
2249
-
2250
- if (__DEV__) {
2251
- expect(console.error.calls.count()).toBe(2);
2252
-
2253
- expect(console.error.calls.argsFor(0)[0]).toBe(
2254
- 'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n in label',
2255
- );
2256
-
2257
- expect(console.error.calls.argsFor(1)[0]).toBe(
2258
- 'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n in input',
2259
- );
2260
- }
2012
});
2013
});
2014
@@ -2287,50 +2038,41 @@ describe('ReactDOMComponent', () => {
2038
2039
describe('Attributes with aliases', function() {
2040
it('sets aliased attributes on HTML attributes', function() {
2290
- spyOnDev(console, 'error');
2291
-
2292
- const el = ReactTestUtils.renderIntoDocument(<div class="test" />);
2041
+ let el;
2042
+ expect(() => {
2043
+ el = ReactTestUtils.renderIntoDocument(<div class="test" />);
2044
+ }).toWarnDev(
2045
+ 'Warning: Invalid DOM property `class`. Did you mean `className`?',
2046
+ );
2047
2048
expect(el.className).toBe('test');
2295
-
2296
- if (__DEV__) {
2297
- expect(console.error.calls.argsFor(0)[0]).toContain(
2298
- 'Warning: Invalid DOM property `class`. Did you mean `className`?',
2299
- );
2300
- }
2049
});
2050
2051
it('sets incorrectly cased aliased attributes on HTML attributes with a warning', function() {
2304
- spyOnDev(console, 'error');
2305
-
2306
- const el = ReactTestUtils.renderIntoDocument(<div cLASS="test" />);
2052
+ let el;
2053
+ expect(() => {
2054
+ el = ReactTestUtils.renderIntoDocument(<div cLASS="test" />);
2055
+ }).toWarnDev(
2056
+ 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
2057
+ );
2058
2059
expect(el.className).toBe('test');
2309
-
2310
- if (__DEV__) {
2311
- expect(console.error.calls.argsFor(0)[0]).toContain(
2312
- 'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
2313
- );
2314
- }
2060
});
2061
2062
it('sets aliased attributes on SVG elements with a warning', function() {
2318
- spyOnDev(console, 'error');
2319
-
2320
- const el = ReactTestUtils.renderIntoDocument(
2321
- <svg>
2322
- <text arabic-form="initial" />
2323
- </svg>,
2063
+ let el;
2064
+ expect(() => {
2065
+ el = ReactTestUtils.renderIntoDocument(
2066
+ <svg>
2067
+ <text arabic-form="initial" />
2068
+ </svg>,
2069
+ );
2070
+ }).toWarnDev(
2071
+ 'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
2072
);
2073
const text = el.querySelector('text');
2074
2075
expect(text.hasAttribute('arabic-form')).toBe(true);
2328
-
2329
- if (__DEV__) {
2330
- expect(console.error.calls.argsFor(0)[0]).toContain(
2331
- 'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
2332
- );
2333
- }
2076
});
2077
2078
it('sets aliased attributes on custom elements', function() {
@@ -2377,36 +2119,30 @@ describe('ReactDOMComponent', () => {
2119
});
2120
2121
it('does not assign a boolean custom attributes as a string', function() {
2380
- spyOnDev(console, 'error');
2381
-
2382
- const el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2122
+ let el;
2123
+ expect(() => {
2124
+ el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2125
+ }).toWarnDev(
2126
+ 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2127
+ 'If you want to write it to the DOM, pass a string instead: ' +
2128
+ 'whatever="true" or whatever={value.toString()}.',
2129
+ );
2130
2131
expect(el.hasAttribute('whatever')).toBe(false);
2385
-
2386
- if (__DEV__) {
2387
- expect(console.error.calls.argsFor(0)[0]).toContain(
2388
- 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2389
- 'If you want to write it to the DOM, pass a string instead: ' +
2390
- 'whatever="true" or whatever={value.toString()}.',
2391
- );
2392
- }
2132
});
2133
2134
it('does not assign an implicit boolean custom attributes', function() {
2396
- spyOnDev(console, 'error');
2397
-
2398
- // eslint-disable-next-line react/jsx-boolean-value
2399
- const el = ReactTestUtils.renderIntoDocument(<div whatever />);
2135
+ let el;
2136
+ expect(() => {
2137
+ // eslint-disable-next-line react/jsx-boolean-value
2138
+ el = ReactTestUtils.renderIntoDocument(<div whatever />);
2139
+ }).toWarnDev(
2140
+ 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2141
+ 'If you want to write it to the DOM, pass a string instead: ' +
2142
+ 'whatever="true" or whatever={value.toString()}.',
2143
+ );
2144
2145
expect(el.hasAttribute('whatever')).toBe(false);
2402
-
2403
- if (__DEV__) {
2404
- expect(console.error.calls.argsFor(0)[0]).toContain(
2405
- 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2406
- 'If you want to write it to the DOM, pass a string instead: ' +
2407
- 'whatever="true" or whatever={value.toString()}.',
2408
- );
2409
- }
2146
});
2147
2148
it('assigns a numeric custom attributes as a string', function() {
@@ -2416,17 +2152,12 @@ describe('ReactDOMComponent', () => {
2152
});
2153
2154
it('will not assign a function custom attributes', function() {
2419
- spyOnDev(console, 'error');
2420
-
2421
- const el = ReactTestUtils.renderIntoDocument(<div whatever={() => {}} />);
2155
+ let el;
2156
+ expect(() => {
2157
+ el = ReactTestUtils.renderIntoDocument(<div whatever={() => {}} />);
2158
+ }).toWarnDev('Warning: Invalid value for prop `whatever` on <div> tag');
2159
2160
expect(el.hasAttribute('whatever')).toBe(false);
2424
-
2425
- if (__DEV__) {
2426
- expect(console.error.calls.argsFor(0)[0]).toContain(
2427
- 'Warning: Invalid value for prop `whatever` on <div> tag',
2428
- );
2429
- }
2161
});
2162
2163
it('will assign an object custom attributes', function() {
@@ -2435,87 +2166,66 @@ describe('ReactDOMComponent', () => {
2166
});
2167
2168
it('allows cased data attributes', function() {
2438
- spyOnDev(console, 'error');
2439
-
2440
- const el = ReactTestUtils.renderIntoDocument(<div data-fooBar="true" />);
2169
+ let el;
2170
+ expect(() => {
2171
+ el = ReactTestUtils.renderIntoDocument(<div data-fooBar="true" />);
2172
+ }).toWarnDev(
2173
+ 'React does not recognize the `data-fooBar` prop on a DOM element. ' +
2174
+ 'If you intentionally want it to appear in the DOM as a custom ' +
2175
+ 'attribute, spell it as lowercase `data-foobar` instead. ' +
2176
+ 'If you accidentally passed it from a parent component, remove ' +
2177
+ 'it from the DOM element.\n' +
2178
+ ' in div (at **)',
2179
+ );
2180
expect(el.getAttribute('data-foobar')).toBe('true');
2442
-
2443
- if (__DEV__) {
2444
- expect(console.error.calls.count()).toBe(1);
2445
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
2446
- 'React does not recognize the `data-fooBar` prop on a DOM element. ' +
2447
- 'If you intentionally want it to appear in the DOM as a custom ' +
2448
- 'attribute, spell it as lowercase `data-foobar` instead. ' +
2449
- 'If you accidentally passed it from a parent component, remove ' +
2450
- 'it from the DOM element.\n' +
2451
- ' in div (at **)',
2452
- );
2453
- }
2181
});
2182
2183
it('allows cased custom attributes', function() {
2457
- spyOnDev(console, 'error');
2458
-
2459
- const el = ReactTestUtils.renderIntoDocument(<div fooBar="true" />);
2184
+ let el;
2185
+ expect(() => {
2186
+ el = ReactTestUtils.renderIntoDocument(<div fooBar="true" />);
2187
+ }).toWarnDev(
2188
+ 'React does not recognize the `fooBar` prop on a DOM element. ' +
2189
+ 'If you intentionally want it to appear in the DOM as a custom ' +
2190
+ 'attribute, spell it as lowercase `foobar` instead. ' +
2191
+ 'If you accidentally passed it from a parent component, remove ' +
2192
+ 'it from the DOM element.\n' +
2193
+ ' in div (at **)',
2194
+ );
2195
expect(el.getAttribute('foobar')).toBe('true');
2461
-
2462
- if (__DEV__) {
2463
- expect(console.error.calls.count()).toBe(1);
2464
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toMatch(
2465
- 'React does not recognize the `fooBar` prop on a DOM element. ' +
2466
- 'If you intentionally want it to appear in the DOM as a custom ' +
2467
- 'attribute, spell it as lowercase `foobar` instead. ' +
2468
- 'If you accidentally passed it from a parent component, remove ' +
2469
- 'it from the DOM element.\n' +
2470
- ' in div (at **)',
2471
- );
2472
- }
2196
});
2197
2198
it('warns on NaN attributes', function() {
2476
- spyOnDev(console, 'error');
2477
-
2478
- const el = ReactTestUtils.renderIntoDocument(<div whatever={NaN} />);
2199
+ let el;
2200
+ expect(() => {
2201
+ el = ReactTestUtils.renderIntoDocument(<div whatever={NaN} />);
2202
+ }).toWarnDev(
2203
+ 'Warning: Received NaN for the `whatever` attribute. If this is ' +
2204
+ 'expected, cast the value to a string.\n in div',
2205
+ );
2206
2207
expect(el.getAttribute('whatever')).toBe('NaN');
2481
-
2482
- if (__DEV__) {
2483
- expect(console.error.calls.argsFor(0)[0]).toContain(
2484
- 'Warning: Received NaN for the `whatever` attribute. If this is ' +
2485
- 'expected, cast the value to a string.\n in div',
2486
- );
2487
- }
2208
});
2209
2210
it('removes a property when it becomes invalid', function() {
2491
- spyOnDev(console, 'error');
2492
-
2211
const container = document.createElement('div');
2212
ReactDOM.render(<div whatever={0} />, container);
2495
- ReactDOM.render(<div whatever={() => {}} />, container);
2213
+ expect(() =>
2214
+ ReactDOM.render(<div whatever={() => {}} />, container),
2215
+ ).toWarnDev('Warning: Invalid value for prop `whatever` on <div> tag.');
2216
const el = container.firstChild;
2497
-
2217
expect(el.hasAttribute('whatever')).toBe(false);
2499
-
2500
- if (__DEV__) {
2501
- expect(console.error.calls.argsFor(0)[0]).toContain(
2502
- 'Warning: Invalid value for prop `whatever` on <div> tag.',
2503
- );
2504
- }
2218
});
2219
2220
it('warns on bad casing of known HTML attributes', function() {
2508
- spyOnDev(console, 'error');
2509
-
2510
- const el = ReactTestUtils.renderIntoDocument(<div SiZe="30" />);
2221
+ let el;
2222
+ expect(() => {
2223
+ el = ReactTestUtils.renderIntoDocument(<div SiZe="30" />);
2224
+ }).toWarnDev(
2225
+ 'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
2226
+ );
2227
2228
expect(el.getAttribute('size')).toBe('30');
2513
-
2514
- if (__DEV__) {
2515
- expect(console.error.calls.argsFor(0)[0]).toContain(
2516
- 'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
2517
- );
2518
- }
2229
});
2230
});
2231
@@ -2581,19 +2291,16 @@ describe('ReactDOMComponent', () => {
2291
2292
describe('String boolean attributes', function() {
2293
it('does not assign string boolean attributes for custom attributes', function() {
2584
- spyOnDev(console, 'error');
2585
-
2586
- const el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2294
+ let el;
2295
+ expect(() => {
2296
+ el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
2297
+ }).toWarnDev(
2298
+ 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2299
+ 'If you want to write it to the DOM, pass a string instead: ' +
2300
+ 'whatever="true" or whatever={value.toString()}.',
2301
+ );
2302
2303
expect(el.hasAttribute('whatever')).toBe(false);
2589
-
2590
- if (__DEV__) {
2591
- expect(console.error.calls.argsFor(0)[0]).toContain(
2592
- 'Received `true` for a non-boolean attribute `whatever`.\n\n' +
2593
- 'If you want to write it to the DOM, pass a string instead: ' +
2594
- 'whatever="true" or whatever={value.toString()}.',
2595
- );
2596
- }
2304
});
2305
2306
it('stringifies the boolean true for allowed attributes', function() {
@@ -2618,47 +2325,41 @@ describe('ReactDOMComponent', () => {
2325
2326
describe('Hyphenated SVG elements', function() {
2327
it('the font-face element is not a custom element', function() {
2621
- spyOnDev(console, 'error');
2622
- const el = ReactTestUtils.renderIntoDocument(
2623
- <svg>
2624
- <font-face x-height={false} />
2625
- </svg>,
2328
+ let el;
2329
+ expect(() => {
2330
+ el = ReactTestUtils.renderIntoDocument(
2331
+ <svg>
2332
+ <font-face x-height={false} />
2333
+ </svg>,
2334
+ );
2335
+ }).toWarnDev(
2336
+ 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2337
);
2338
2339
expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
2340
false,
2341
);
2631
-
2632
- if (__DEV__) {
2633
- expect(console.error.calls.count()).toBe(1);
2634
- expect(console.error.calls.argsFor(0)[0]).toContain(
2635
- 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2636
- );
2637
- }
2342
});
2343
2344
it('the font-face element does not allow unknown boolean values', function() {
2641
- spyOnDev(console, 'error');
2642
- const el = ReactTestUtils.renderIntoDocument(
2643
- <svg>
2644
- <font-face whatever={false} />
2645
- </svg>,
2345
+ let el;
2346
+ expect(() => {
2347
+ el = ReactTestUtils.renderIntoDocument(
2348
+ <svg>
2349
+ <font-face whatever={false} />
2350
+ </svg>,
2351
+ );
2352
+ }).toWarnDev(
2353
+ 'Received `false` for a non-boolean attribute `whatever`.\n\n' +
2354
+ 'If you want to write it to the DOM, pass a string instead: ' +
2355
+ 'whatever="false" or whatever={value.toString()}.\n\n' +
2356
+ 'If you used to conditionally omit it with whatever={condition && value}, ' +
2357
+ 'pass whatever={condition ? value : undefined} instead.',
2358
);
2359
2360
expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
2361
false,
2362
);
2651
-
2652
- if (__DEV__) {
2653
- expect(console.error.calls.count()).toBe(1);
2654
- expect(console.error.calls.argsFor(0)[0]).toContain(
2655
- 'Received `false` for a non-boolean attribute `whatever`.\n\n' +
2656
- 'If you want to write it to the DOM, pass a string instead: ' +
2657
- 'whatever="false" or whatever={value.toString()}.\n\n' +
2658
- 'If you used to conditionally omit it with whatever={condition && value}, ' +
2659
- 'pass whatever={condition ? value : undefined} instead.',
2660
- );
2661
- }
2363
});
2364
});
2365
packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js
+20
-38
@@ -177,42 +177,28 @@ describe('ReactDOMComponentTree', () => {
177
178
const component = <Controlled />;
179
const instance = ReactDOM.render(component, container);
180
- spyOnDev(console, 'error');
181
- if (__DEV__) {
182
- expect(console.error.calls.count()).toBe(0);
183
- }
184
- simulateInput(instance.a, finishValue);
185
- if (__DEV__) {
186
- expect(console.error.calls.count()).toBe(1);
187
- expect(console.error.calls.argsFor(0)[0]).toContain(
188
- 'Warning: A component is changing an uncontrolled input of ' +
189
- 'type text to be controlled. Input elements should not ' +
190
- 'switch from uncontrolled to controlled (or vice versa). ' +
191
- 'Decide between using a controlled or uncontrolled input ' +
192
- 'element for the lifetime of the component. More info: ' +
193
- 'https://fb.me/react-controlled-components',
194
- );
195
- }
180
+ expect(() => simulateInput(instance.a, finishValue)).toWarnDev(
181
+ 'Warning: A component is changing an uncontrolled input of ' +
182
+ 'type text to be controlled. Input elements should not ' +
183
+ 'switch from uncontrolled to controlled (or vice versa). ' +
184
+ 'Decide between using a controlled or uncontrolled input ' +
185
+ 'element for the lifetime of the component. More info: ' +
186
+ 'https://fb.me/react-controlled-components',
187
+ );
188
});
189
190
it('finds instance of node that is attempted to be unmounted', () => {
199
- spyOnDev(console, 'error');
191
const component = <div />;
192
const node = ReactDOM.render(<div>{component}</div>, container);
202
- ReactDOM.unmountComponentAtNode(node);
203
- if (__DEV__) {
204
- expect(console.error.calls.count()).toBe(1);
205
- expect(console.error.calls.argsFor(0)[0]).toContain(
206
- "unmountComponentAtNode(): The node you're attempting to unmount " +
207
- 'was rendered by React and is not a top-level container. You may ' +
208
- 'have accidentally passed in a React root node instead of its ' +
209
- 'container.',
210
- );
211
- }
193
+ expect(() => ReactDOM.unmountComponentAtNode(node)).toWarnDev(
194
+ "unmountComponentAtNode(): The node you're attempting to unmount " +
195
+ 'was rendered by React and is not a top-level container. You may ' +
196
+ 'have accidentally passed in a React root node instead of its ' +
197
+ 'container.',
198
+ );
199
});
200
201
it('finds instance from node to stop rendering over other react rendered components', () => {
215
- spyOnDev(console, 'error');
202
const component = (
203
<div>
204
<span>Hello</span>
@@ -220,15 +206,11 @@ describe('ReactDOMComponentTree', () => {
206
);
207
const anotherComponent = <div />;
208
const instance = ReactDOM.render(component, container);
223
- ReactDOM.render(anotherComponent, instance);
224
- if (__DEV__) {
225
- expect(console.error.calls.count()).toBe(1);
226
- expect(console.error.calls.argsFor(0)[0]).toContain(
227
- 'render(...): Replacing React-rendered children with a new root ' +
228
- 'component. If you intended to update the children of this node, ' +
229
- 'you should instead have the existing children update their state ' +
230
- 'and render the new components instead of calling ReactDOM.render.',
231
- );
232
- }
209
+ expect(() => ReactDOM.render(anotherComponent, instance)).toWarnDev(
210
+ 'render(...): Replacing React-rendered children with a new root ' +
211
+ 'component. If you intended to update the children of this node, ' +
212
+ 'you should instead have the existing children update their state ' +
213
+ 'and render the new components instead of calling ReactDOM.render.',
214
+ );
215
});
216
});
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+43
-82
@@ -15,10 +15,6 @@ const ReactTestUtils = require('react-dom/test-utils');
15
const PropTypes = require('prop-types');
16
17
describe('ReactDOMFiber', () => {
18
- function normalizeCodeLocInfo(str) {
19
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
20
- }
21
-
18
let container;
19
20
beforeEach(() => {
@@ -222,28 +218,24 @@ describe('ReactDOMFiber', () => {
218
219
// TODO: remove in React 17
220
it('should support unstable_createPortal alias', () => {
225
- spyOnDev(console, 'warn');
221
const portalContainer = document.createElement('div');
222
228
- ReactDOM.render(
229
- <div>
230
- {ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
231
- </div>,
232
- container,
223
+ expect(() =>
224
+ ReactDOM.render(
225
+ <div>
226
+ {ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
227
+ </div>,
228
+ container,
229
+ ),
230
+ ).toLowPriorityWarnDev(
231
+ 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
232
+ 'and will be removed in React 17+. Update your code to use ' +
233
+ 'ReactDOM.createPortal() instead. It has the exact same API, ' +
234
+ 'but without the "unstable_" prefix.',
235
);
236
expect(portalContainer.innerHTML).toBe('<div>portal</div>');
237
expect(container.innerHTML).toBe('<div></div>');
238
237
- if (__DEV__) {
238
- expect(console.warn.calls.count()).toBe(1);
239
- expect(console.warn.calls.argsFor(0)[0]).toContain(
240
- 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
241
- 'and will be removed in React 17+. Update your code to use ' +
242
- 'ReactDOM.createPortal() instead. It has the exact same API, ' +
243
- 'but without the "unstable_" prefix.',
244
- );
245
- }
246
-
239
ReactDOM.unmountComponentAtNode(container);
240
expect(portalContainer.innerHTML).toBe('');
241
expect(container.innerHTML).toBe('');
@@ -930,40 +922,30 @@ describe('ReactDOMFiber', () => {
922
});
923
924
it('should warn for non-functional event listeners', () => {
933
- spyOnDev(console, 'error');
925
class Example extends React.Component {
926
render() {
927
return <div onClick="woops" />;
928
}
929
}
939
- ReactDOM.render(<Example />, container);
940
- if (__DEV__) {
941
- expect(console.error.calls.count()).toBe(1);
942
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
943
- 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
944
- ' in div (at **)\n' +
945
- ' in Example (at **)',
946
- );
947
- }
930
+ expect(() => ReactDOM.render(<Example />, container)).toWarnDev(
931
+ 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
932
+ ' in div (at **)\n' +
933
+ ' in Example (at **)',
934
+ );
935
});
936
937
it('should warn with a special message for `false` event listeners', () => {
951
- spyOnDev(console, 'error');
938
class Example extends React.Component {
939
render() {
940
return <div onClick={false} />;
941
}
942
}
957
- ReactDOM.render(<Example />, container);
958
- if (__DEV__) {
959
- expect(console.error.calls.count()).toBe(1);
960
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
961
- 'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
962
- 'If you used to conditionally omit it with onClick={condition && value}, ' +
963
- 'pass onClick={condition ? value : undefined} instead.\n',
964
- ' in div (at **)\n' + ' in Example (at **)',
965
- );
966
- }
943
+ expect(() => ReactDOM.render(<Example />, container)).toWarnDev(
944
+ 'Expected `onClick` listener to be a function, instead got `false`.\n\n' +
945
+ 'If you used to conditionally omit it with onClick={condition && value}, ' +
946
+ 'pass onClick={condition ? value : undefined} instead.\n',
947
+ ' in div (at **)\n' + ' in Example (at **)',
948
+ );
949
});
950
951
it('should not update event handlers until commit', () => {
@@ -1059,23 +1041,15 @@ describe('ReactDOMFiber', () => {
1041
});
1042
1043
it('should not warn when rendering into an empty container', () => {
1062
- spyOnDev(console, 'error');
1044
ReactDOM.render(<div>foo</div>, container);
1045
expect(container.innerHTML).toBe('<div>foo</div>');
1046
ReactDOM.render(null, container);
1047
expect(container.innerHTML).toBe('');
1067
- if (__DEV__) {
1068
- expect(console.error.calls.count()).toBe(0);
1069
- }
1048
ReactDOM.render(<div>bar</div>, container);
1049
expect(container.innerHTML).toBe('<div>bar</div>');
1072
- if (__DEV__) {
1073
- expect(console.error.calls.count()).toBe(0);
1074
- }
1050
});
1051
1052
it('should warn when replacing a container which was manually updated outside of React', () => {
1078
- spyOnDev(console, 'error');
1053
// when not messing with the DOM outside of React
1054
ReactDOM.render(<div key="1">foo</div>, container);
1055
ReactDOM.render(<div key="1">bar</div>, container);
@@ -1083,64 +1057,51 @@ describe('ReactDOMFiber', () => {
1057
// then we mess with the DOM before an update
1058
// we know this will error - that is expected right now
1059
// It's an error of type 'NotFoundError' with no message
1060
+ container.innerHTML = '<div>MEOW.</div>';
1061
+
1062
expect(() => {
1087
- container.innerHTML = '<div>MEOW.</div>';
1088
- ReactDOM.render(<div key="2">baz</div>, container);
1089
- }).toThrowError();
1090
- if (__DEV__) {
1091
- expect(console.error.calls.count()).toBe(1);
1092
- expect(console.error.calls.argsFor(0)[0]).toContain(
1063
+ expect(() =>
1064
+ ReactDOM.render(<div key="2">baz</div>, container),
1065
+ ).toWarnDev(
1066
'render(...): ' +
1067
'It looks like the React-rendered content of this container was ' +
1068
'removed without using React. This is not supported and will ' +
1069
'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1070
'to empty a container.',
1071
);
1099
- }
1072
+ }).toThrowError();
1073
});
1074
1075
it('should warn when doing an update to a container manually updated outside of React', () => {
1103
- spyOnDev(console, 'error');
1076
// when not messing with the DOM outside of React
1077
ReactDOM.render(<div>foo</div>, container);
1078
ReactDOM.render(<div>bar</div>, container);
1079
expect(container.innerHTML).toBe('<div>bar</div>');
1080
// then we mess with the DOM before an update
1081
container.innerHTML = '<div>MEOW.</div>';
1110
- ReactDOM.render(<div>baz</div>, container);
1111
- // silently fails to update
1112
- if (__DEV__) {
1113
- expect(console.error.calls.count()).toBe(1);
1114
- expect(console.error.calls.argsFor(0)[0]).toContain(
1115
- 'render(...): ' +
1116
- 'It looks like the React-rendered content of this container was ' +
1117
- 'removed without using React. This is not supported and will ' +
1118
- 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1119
- 'to empty a container.',
1120
- );
1121
- }
1082
+ expect(() => ReactDOM.render(<div>baz</div>, container)).toWarnDev(
1083
+ 'render(...): ' +
1084
+ 'It looks like the React-rendered content of this container was ' +
1085
+ 'removed without using React. This is not supported and will ' +
1086
+ 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1087
+ 'to empty a container.',
1088
+ );
1089
});
1090
1091
it('should warn when doing an update to a container manually cleared outside of React', () => {
1125
- spyOnDev(console, 'error');
1092
// when not messing with the DOM outside of React
1093
ReactDOM.render(<div>foo</div>, container);
1094
ReactDOM.render(<div>bar</div>, container);
1095
expect(container.innerHTML).toBe('<div>bar</div>');
1096
// then we mess with the DOM before an update
1097
container.innerHTML = '';
1132
- ReactDOM.render(<div>baz</div>, container);
1133
- // silently fails to update
1134
- if (__DEV__) {
1135
- expect(console.error.calls.count()).toBe(1);
1136
- expect(console.error.calls.argsFor(0)[0]).toContain(
1137
- 'render(...): ' +
1138
- 'It looks like the React-rendered content of this container was ' +
1139
- 'removed without using React. This is not supported and will ' +
1140
- 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1141
- 'to empty a container.',
1142
- );
1143
- }
1098
+ expect(() => ReactDOM.render(<div>baz</div>, container)).toWarnDev(
1099
+ 'render(...): ' +
1100
+ 'It looks like the React-rendered content of this container was ' +
1101
+ 'removed without using React. This is not supported and will ' +
1102
+ 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1103
+ 'to empty a container.',
1104
+ );
1105
});
1106
1107
it('should render a text component with a text DOM node on the same document as the container', () => {
packages/react-dom/src/__tests__/ReactDOMInput-test.js
+277
-399
@@ -18,10 +18,6 @@ describe('ReactDOMInput', () => {
18
let ReactTestUtils;
19
let setUntrackedValue;
20
21
- function normalizeCodeLocInfo(str) {
22
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
23
- }
24
-
21
function dispatchEventOnNode(node, type) {
22
node.dispatchEvent(new Event(type, {bubbles: true, cancelable: true}));
23
}
@@ -41,16 +37,18 @@ describe('ReactDOMInput', () => {
37
});
38
39
it('should properly control a value even if no event listener exists', () => {
44
- spyOnDev(console, 'error');
40
const container = document.createElement('div');
46
- const stub = ReactDOM.render(<input type="text" value="lion" />, container);
41
+ let stub;
42
+
43
+ expect(() => {
44
+ stub = ReactDOM.render(<input type="text" value="lion" />, container);
45
+ }).toWarnDev(
46
+ 'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
47
+ );
48
49
document.body.appendChild(container);
50
51
const node = ReactDOM.findDOMNode(stub);
51
- if (__DEV__) {
52
- expect(console.error.calls.count()).toBe(1);
53
- }
52
53
setUntrackedValue.call(node, 'giraffe');
54
@@ -224,7 +222,6 @@ describe('ReactDOMInput', () => {
222
});
223
224
it('does change the string ".98" to "0.98" with no change handler', () => {
227
- spyOnDev(console, 'error');
225
class Stub extends React.Component {
226
state = {
227
value: '.98',
@@ -234,18 +231,17 @@ describe('ReactDOMInput', () => {
231
}
232
}
233
237
- const stub = ReactTestUtils.renderIntoDocument(<Stub />);
234
+ let stub;
235
+ expect(() => {
236
+ stub = ReactTestUtils.renderIntoDocument(<Stub />);
237
+ }).toWarnDev(
238
+ 'You provided a `value` prop to a form field ' +
239
+ 'without an `onChange` handler.',
240
+ );
241
const node = ReactDOM.findDOMNode(stub);
242
stub.setState({value: '0.98'});
243
244
expect(node.value).toEqual('0.98');
242
- if (__DEV__) {
243
- expect(console.error.calls.count()).toBe(1);
244
- expect(console.error.calls.argsFor(0)[0]).toContain(
245
- 'You provided a `value` prop to a form field ' +
246
- 'without an `onChange` handler.',
247
- );
248
- }
245
});
246
247
it('performs a state change from "" to 0', () => {
@@ -266,7 +262,6 @@ describe('ReactDOMInput', () => {
262
});
263
264
it('distinguishes precision for extra zeroes in string number values', () => {
269
- spyOnDev(console, 'error');
265
class Stub extends React.Component {
266
state = {
267
value: '3.0000',
@@ -276,18 +271,18 @@ describe('ReactDOMInput', () => {
271
}
272
}
273
279
- const stub = ReactTestUtils.renderIntoDocument(<Stub />);
274
+ let stub;
275
+
276
+ expect(() => {
277
+ stub = ReactTestUtils.renderIntoDocument(<Stub />);
278
+ }).toWarnDev(
279
+ 'You provided a `value` prop to a form field ' +
280
+ 'without an `onChange` handler.',
281
+ );
282
const node = ReactDOM.findDOMNode(stub);
283
stub.setState({value: '3'});
284
285
expect(node.value).toEqual('3');
284
- if (__DEV__) {
285
- expect(console.error.calls.count()).toBe(1);
286
- expect(console.error.calls.argsFor(0)[0]).toContain(
287
- 'You provided a `value` prop to a form field ' +
288
- 'without an `onChange` handler.',
289
- );
290
- }
286
});
287
288
it('should display `defaultValue` of number 0', () => {
@@ -374,23 +369,19 @@ describe('ReactDOMInput', () => {
369
});
370
371
it('should take `defaultValue` when changing to uncontrolled input', () => {
377
- spyOnDev(console, 'error');
372
const container = document.createElement('div');
373
const node = ReactDOM.render(
374
<input type="text" value="0" readOnly="true" />,
375
container,
376
);
377
expect(node.value).toBe('0');
384
- ReactDOM.render(<input type="text" defaultValue="1" />, container);
378
+ expect(() =>
379
+ ReactDOM.render(<input type="text" defaultValue="1" />, container),
380
+ ).toWarnDev(
381
+ 'A component is changing a controlled input of type ' +
382
+ 'text to be uncontrolled.',
383
+ );
384
expect(node.value).toBe('0');
386
-
387
- if (__DEV__) {
388
- expect(console.error.calls.count()).toBe(1);
389
- expect(console.error.calls.argsFor(0)[0]).toContain(
390
- 'A component is changing a controlled input of type ' +
391
- 'text to be uncontrolled.',
392
- );
393
- }
385
});
386
387
it('should render defaultValue for SSR', () => {
@@ -865,27 +856,21 @@ describe('ReactDOMInput', () => {
856
});
857
858
it('should warn with value and no onChange handler and readOnly specified', () => {
868
- spyOnDev(console, 'error');
859
ReactTestUtils.renderIntoDocument(
860
<input type="text" value="zoink" readOnly={true} />,
861
);
872
- if (__DEV__) {
873
- expect(console.error.calls.count()).toBe(0);
874
- }
862
876
- ReactTestUtils.renderIntoDocument(
877
- <input type="text" value="zoink" readOnly={false} />,
863
+ expect(() =>
864
+ ReactTestUtils.renderIntoDocument(
865
+ <input type="text" value="zoink" readOnly={false} />,
866
+ ),
867
+ ).toWarnDev(
868
+ 'Warning: Failed prop type: You provided a `value` prop to a form ' +
869
+ 'field without an `onChange` handler. This will render a read-only ' +
870
+ 'field. If the field should be mutable use `defaultValue`. ' +
871
+ 'Otherwise, set either `onChange` or `readOnly`.\n' +
872
+ ' in input (at **)',
873
);
879
- if (__DEV__) {
880
- expect(console.error.calls.count()).toBe(1);
881
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
882
- 'Warning: Failed prop type: You provided a `value` prop to a form ' +
883
- 'field without an `onChange` handler. This will render a read-only ' +
884
- 'field. If the field should be mutable use `defaultValue`. ' +
885
- 'Otherwise, set either `onChange` or `readOnly`.\n' +
886
- ' in input (at **)',
887
- );
888
- }
874
});
875
876
it('should have a this value of undefined if bind is not used', () => {
@@ -900,20 +885,19 @@ describe('ReactDOMInput', () => {
885
});
886
887
it('should warn with checked and no onChange handler with readOnly specified', () => {
903
- spyOnDev(console, 'error');
888
ReactTestUtils.renderIntoDocument(
889
<input type="checkbox" checked="false" readOnly={true} />,
890
);
907
- if (__DEV__) {
908
- expect(console.error.calls.count()).toBe(0);
909
- }
891
911
- ReactTestUtils.renderIntoDocument(
912
- <input type="checkbox" checked="false" readOnly={false} />,
892
+ expect(() =>
893
+ ReactTestUtils.renderIntoDocument(
894
+ <input type="checkbox" checked="false" readOnly={false} />,
895
+ ),
896
+ ).toWarnDev(
897
+ 'Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler. ' +
898
+ 'This will render a read-only field. If the field should be mutable use `defaultChecked`. ' +
899
+ 'Otherwise, set either `onChange` or `readOnly`.',
900
);
914
- if (__DEV__) {
915
- expect(console.error.calls.count()).toBe(1);
916
- }
901
});
902
903
it('should update defaultValue to empty string', () => {
@@ -924,42 +908,35 @@ describe('ReactDOMInput', () => {
908
});
909
910
it('should warn if value is null', () => {
927
- spyOnDev(console, 'error');
928
- ReactTestUtils.renderIntoDocument(<input type="text" value={null} />);
929
- if (__DEV__) {
930
- expect(console.error.calls.argsFor(0)[0]).toContain(
931
- '`value` prop on `input` should not be null. ' +
932
- 'Consider using an empty string to clear the component or `undefined` ' +
933
- 'for uncontrolled components.',
934
- );
935
- }
911
+ expect(() =>
912
+ ReactTestUtils.renderIntoDocument(<input type="text" value={null} />),
913
+ ).toWarnDev(
914
+ '`value` prop on `input` should not be null. ' +
915
+ 'Consider using an empty string to clear the component or `undefined` ' +
916
+ 'for uncontrolled components.',
917
+ );
918
919
ReactTestUtils.renderIntoDocument(<input type="text" value={null} />);
938
- if (__DEV__) {
939
- expect(console.error.calls.count()).toBe(1);
940
- }
920
});
921
922
it('should warn if checked and defaultChecked props are specified', () => {
944
- spyOnDev(console, 'error');
945
- ReactTestUtils.renderIntoDocument(
946
- <input
947
- type="radio"
948
- checked={true}
949
- defaultChecked={true}
950
- readOnly={true}
951
- />,
923
+ expect(() =>
924
+ ReactTestUtils.renderIntoDocument(
925
+ <input
926
+ type="radio"
927
+ checked={true}
928
+ defaultChecked={true}
929
+ readOnly={true}
930
+ />,
931
+ ),
932
+ ).toWarnDev(
933
+ 'A component contains an input of type radio with both checked and defaultChecked props. ' +
934
+ 'Input elements must be either controlled or uncontrolled ' +
935
+ '(specify either the checked prop, or the defaultChecked prop, but not ' +
936
+ 'both). Decide between using a controlled or uncontrolled input ' +
937
+ 'element and remove one of these props. More info: ' +
938
+ 'https://fb.me/react-controlled-components',
939
);
953
- if (__DEV__) {
954
- expect(console.error.calls.argsFor(0)[0]).toContain(
955
- 'A component contains an input of type radio with both checked and defaultChecked props. ' +
956
- 'Input elements must be either controlled or uncontrolled ' +
957
- '(specify either the checked prop, or the defaultChecked prop, but not ' +
958
- 'both). Decide between using a controlled or uncontrolled input ' +
959
- 'element and remove one of these props. More info: ' +
960
- 'https://fb.me/react-controlled-components',
961
- );
962
- }
940
941
ReactTestUtils.renderIntoDocument(
942
<input
@@ -969,318 +946,269 @@ describe('ReactDOMInput', () => {
946
readOnly={true}
947
/>,
948
);
972
- if (__DEV__) {
973
- expect(console.error.calls.count()).toBe(1);
974
- }
949
});
950
951
it('should warn if value and defaultValue props are specified', () => {
978
- spyOnDev(console, 'error');
979
- ReactTestUtils.renderIntoDocument(
980
- <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
952
+ expect(() =>
953
+ ReactTestUtils.renderIntoDocument(
954
+ <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
955
+ ),
956
+ ).toWarnDev(
957
+ 'A component contains an input of type text with both value and defaultValue props. ' +
958
+ 'Input elements must be either controlled or uncontrolled ' +
959
+ '(specify either the value prop, or the defaultValue prop, but not ' +
960
+ 'both). Decide between using a controlled or uncontrolled input ' +
961
+ 'element and remove one of these props. More info: ' +
962
+ 'https://fb.me/react-controlled-components',
963
);
982
- if (__DEV__) {
983
- expect(console.error.calls.argsFor(0)[0]).toContain(
984
- 'A component contains an input of type text with both value and defaultValue props. ' +
985
- 'Input elements must be either controlled or uncontrolled ' +
986
- '(specify either the value prop, or the defaultValue prop, but not ' +
987
- 'both). Decide between using a controlled or uncontrolled input ' +
988
- 'element and remove one of these props. More info: ' +
989
- 'https://fb.me/react-controlled-components',
990
- );
991
- }
964
965
ReactTestUtils.renderIntoDocument(
966
<input type="text" value="foo" defaultValue="bar" readOnly={true} />,
967
);
996
- if (__DEV__) {
997
- expect(console.error.calls.count()).toBe(1);
998
- }
968
});
969
970
it('should warn if controlled input switches to uncontrolled (value is undefined)', () => {
1002
- spyOnDev(console, 'error');
971
const stub = (
972
<input type="text" value="controlled" onChange={emptyFunction} />
973
);
974
const container = document.createElement('div');
975
ReactDOM.render(stub, container);
1008
- ReactDOM.render(<input type="text" />, container);
1009
- if (__DEV__) {
1010
- expect(console.error.calls.count()).toBe(1);
1011
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1012
- 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
1013
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1014
- 'Decide between using a controlled or uncontrolled input ' +
1015
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1016
- ' in input (at **)',
1017
- );
1018
- }
976
+ expect(() => ReactDOM.render(<input type="text" />, container)).toWarnDev(
977
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
978
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
979
+ 'Decide between using a controlled or uncontrolled input ' +
980
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
981
+ ' in input (at **)',
982
+ );
983
});
984
985
it('should warn if controlled input switches to uncontrolled (value is null)', () => {
1022
- spyOnDev(console, 'error');
986
const stub = (
987
<input type="text" value="controlled" onChange={emptyFunction} />
988
);
989
const container = document.createElement('div');
990
ReactDOM.render(stub, container);
1028
- ReactDOM.render(<input type="text" value={null} />, container);
1029
- if (__DEV__) {
1030
- expect(console.error.calls.count()).toBeGreaterThan(0);
1031
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1032
- 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
1033
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1034
- 'Decide between using a controlled or uncontrolled input ' +
1035
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1036
- ' in input (at **)',
1037
- );
1038
- }
991
+ expect(() =>
992
+ ReactDOM.render(<input type="text" value={null} />, container),
993
+ ).toWarnDev([
994
+ '`value` prop on `input` should not be null. ' +
995
+ 'Consider using an empty string to clear the component or `undefined` for uncontrolled components',
996
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
997
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
998
+ 'Decide between using a controlled or uncontrolled input ' +
999
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1000
+ ' in input (at **)',
1001
+ ]);
1002
});
1003
1004
it('should warn if controlled input switches to uncontrolled with defaultValue', () => {
1042
- spyOnDev(console, 'error');
1005
const stub = (
1006
<input type="text" value="controlled" onChange={emptyFunction} />
1007
);
1008
const container = document.createElement('div');
1009
ReactDOM.render(stub, container);
1048
- ReactDOM.render(
1049
- <input type="text" defaultValue="uncontrolled" />,
1050
- container,
1010
+ expect(() =>
1011
+ ReactDOM.render(
1012
+ <input type="text" defaultValue="uncontrolled" />,
1013
+ container,
1014
+ ),
1015
+ ).toWarnDev(
1016
+ 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
1017
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1018
+ 'Decide between using a controlled or uncontrolled input ' +
1019
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1020
+ ' in input (at **)',
1021
);
1052
- if (__DEV__) {
1053
- expect(console.error.calls.count()).toBe(1);
1054
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1055
- 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
1056
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1057
- 'Decide between using a controlled or uncontrolled input ' +
1058
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1059
- ' in input (at **)',
1060
- );
1061
- }
1022
});
1023
1024
it('should warn if uncontrolled input (value is undefined) switches to controlled', () => {
1065
- spyOnDev(console, 'error');
1025
const stub = <input type="text" />;
1026
const container = document.createElement('div');
1027
ReactDOM.render(stub, container);
1069
- ReactDOM.render(<input type="text" value="controlled" />, container);
1070
- if (__DEV__) {
1071
- expect(console.error.calls.count()).toBe(1);
1072
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1073
- 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
1074
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1075
- 'Decide between using a controlled or uncontrolled input ' +
1076
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1077
- ' in input (at **)',
1078
- );
1079
- }
1028
+ expect(() =>
1029
+ ReactDOM.render(<input type="text" value="controlled" />, container),
1030
+ ).toWarnDev(
1031
+ 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
1032
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1033
+ 'Decide between using a controlled or uncontrolled input ' +
1034
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1035
+ ' in input (at **)',
1036
+ );
1037
});
1038
1039
it('should warn if uncontrolled input (value is null) switches to controlled', () => {
1083
- spyOnDev(console, 'error');
1040
const stub = <input type="text" value={null} />;
1041
const container = document.createElement('div');
1086
- ReactDOM.render(stub, container);
1087
- ReactDOM.render(<input type="text" value="controlled" />, container);
1088
- if (__DEV__) {
1089
- expect(console.error.calls.count()).toBeGreaterThan(0);
1090
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1091
- 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
1092
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1093
- 'Decide between using a controlled or uncontrolled input ' +
1094
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1095
- ' in input (at **)',
1096
- );
1097
- }
1042
+ expect(() => ReactDOM.render(stub, container)).toWarnDev(
1043
+ '`value` prop on `input` should not be null. ' +
1044
+ 'Consider using an empty string to clear the component or `undefined` for uncontrolled components.',
1045
+ );
1046
+ expect(() =>
1047
+ ReactDOM.render(<input type="text" value="controlled" />, container),
1048
+ ).toWarnDev(
1049
+ 'Warning: A component is changing an uncontrolled input of type text to be controlled. ' +
1050
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1051
+ 'Decide between using a controlled or uncontrolled input ' +
1052
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1053
+ ' in input (at **)',
1054
+ );
1055
});
1056
1057
it('should warn if controlled checkbox switches to uncontrolled (checked is undefined)', () => {
1101
- spyOnDev(console, 'error');
1058
const stub = (
1059
<input type="checkbox" checked={true} onChange={emptyFunction} />
1060
);
1061
const container = document.createElement('div');
1062
ReactDOM.render(stub, container);
1107
- ReactDOM.render(<input type="checkbox" />, container);
1108
- if (__DEV__) {
1109
- expect(console.error.calls.count()).toBe(1);
1110
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1111
- 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1112
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1113
- 'Decide between using a controlled or uncontrolled input ' +
1114
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1115
- ' in input (at **)',
1116
- );
1117
- }
1063
+ expect(() =>
1064
+ ReactDOM.render(<input type="checkbox" />, container),
1065
+ ).toWarnDev(
1066
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1067
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1068
+ 'Decide between using a controlled or uncontrolled input ' +
1069
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1070
+ ' in input (at **)',
1071
+ );
1072
});
1073
1074
it('should warn if controlled checkbox switches to uncontrolled (checked is null)', () => {
1121
- spyOnDev(console, 'error');
1075
const stub = (
1076
<input type="checkbox" checked={true} onChange={emptyFunction} />
1077
);
1078
const container = document.createElement('div');
1079
ReactDOM.render(stub, container);
1127
- ReactDOM.render(<input type="checkbox" checked={null} />, container);
1128
- if (__DEV__) {
1129
- expect(console.error.calls.count()).toBe(1);
1130
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1131
- 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1132
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1133
- 'Decide between using a controlled or uncontrolled input ' +
1134
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1135
- ' in input (at **)',
1136
- );
1137
- }
1080
+ expect(() =>
1081
+ ReactDOM.render(<input type="checkbox" checked={null} />, container),
1082
+ ).toWarnDev(
1083
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1084
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1085
+ 'Decide between using a controlled or uncontrolled input ' +
1086
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1087
+ ' in input (at **)',
1088
+ );
1089
});
1090
1091
it('should warn if controlled checkbox switches to uncontrolled with defaultChecked', () => {
1141
- spyOnDev(console, 'error');
1092
const stub = (
1093
<input type="checkbox" checked={true} onChange={emptyFunction} />
1094
);
1095
const container = document.createElement('div');
1096
ReactDOM.render(stub, container);
1147
- ReactDOM.render(<input type="checkbox" defaultChecked={true} />, container);
1148
- if (__DEV__) {
1149
- expect(console.error.calls.count()).toBe(1);
1150
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1151
- 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1152
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1153
- 'Decide between using a controlled or uncontrolled input ' +
1154
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1155
- ' in input (at **)',
1156
- );
1157
- }
1097
+ expect(() =>
1098
+ ReactDOM.render(
1099
+ <input type="checkbox" defaultChecked={true} />,
1100
+ container,
1101
+ ),
1102
+ ).toWarnDev(
1103
+ 'Warning: A component is changing a controlled input of type checkbox to be uncontrolled. ' +
1104
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1105
+ 'Decide between using a controlled or uncontrolled input ' +
1106
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1107
+ ' in input (at **)',
1108
+ );
1109
});
1110
1111
it('should warn if uncontrolled checkbox (checked is undefined) switches to controlled', () => {
1161
- spyOnDev(console, 'error');
1112
const stub = <input type="checkbox" />;
1113
const container = document.createElement('div');
1114
ReactDOM.render(stub, container);
1165
- ReactDOM.render(<input type="checkbox" checked={true} />, container);
1166
- if (__DEV__) {
1167
- expect(console.error.calls.count()).toBe(1);
1168
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1169
- 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1170
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1171
- 'Decide between using a controlled or uncontrolled input ' +
1172
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1173
- ' in input (at **)',
1174
- );
1175
- }
1115
+ expect(() =>
1116
+ ReactDOM.render(<input type="checkbox" checked={true} />, container),
1117
+ ).toWarnDev(
1118
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1119
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1120
+ 'Decide between using a controlled or uncontrolled input ' +
1121
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1122
+ ' in input (at **)',
1123
+ );
1124
});
1125
1126
it('should warn if uncontrolled checkbox (checked is null) switches to controlled', () => {
1179
- spyOnDev(console, 'error');
1127
const stub = <input type="checkbox" checked={null} />;
1128
const container = document.createElement('div');
1129
ReactDOM.render(stub, container);
1183
- ReactDOM.render(<input type="checkbox" checked={true} />, container);
1184
- if (__DEV__) {
1185
- expect(console.error.calls.count()).toBe(1);
1186
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1187
- 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1188
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1189
- 'Decide between using a controlled or uncontrolled input ' +
1190
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1191
- ' in input (at **)',
1192
- );
1193
- }
1130
+ expect(() =>
1131
+ ReactDOM.render(<input type="checkbox" checked={true} />, container),
1132
+ ).toWarnDev(
1133
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled. ' +
1134
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1135
+ 'Decide between using a controlled or uncontrolled input ' +
1136
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1137
+ ' in input (at **)',
1138
+ );
1139
});
1140
1141
it('should warn if controlled radio switches to uncontrolled (checked is undefined)', () => {
1197
- spyOnDev(console, 'error');
1142
const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1143
const container = document.createElement('div');
1144
ReactDOM.render(stub, container);
1201
- ReactDOM.render(<input type="radio" />, container);
1202
- if (__DEV__) {
1203
- expect(console.error.calls.count()).toBe(1);
1204
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1205
- 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1206
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1207
- 'Decide between using a controlled or uncontrolled input ' +
1208
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1209
- ' in input (at **)',
1210
- );
1211
- }
1145
+ expect(() => ReactDOM.render(<input type="radio" />, container)).toWarnDev(
1146
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1147
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1148
+ 'Decide between using a controlled or uncontrolled input ' +
1149
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1150
+ ' in input (at **)',
1151
+ );
1152
});
1153
1154
it('should warn if controlled radio switches to uncontrolled (checked is null)', () => {
1215
- spyOnDev(console, 'error');
1155
const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1156
const container = document.createElement('div');
1157
ReactDOM.render(stub, container);
1219
- ReactDOM.render(<input type="radio" checked={null} />, container);
1220
- if (__DEV__) {
1221
- expect(console.error.calls.count()).toBe(1);
1222
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1223
- 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1224
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1225
- 'Decide between using a controlled or uncontrolled input ' +
1226
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1227
- ' in input (at **)',
1228
- );
1229
- }
1158
+ expect(() =>
1159
+ ReactDOM.render(<input type="radio" checked={null} />, container),
1160
+ ).toWarnDev(
1161
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1162
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1163
+ 'Decide between using a controlled or uncontrolled input ' +
1164
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1165
+ ' in input (at **)',
1166
+ );
1167
});
1168
1169
it('should warn if controlled radio switches to uncontrolled with defaultChecked', () => {
1233
- spyOnDev(console, 'error');
1170
const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1171
const container = document.createElement('div');
1172
ReactDOM.render(stub, container);
1237
- ReactDOM.render(<input type="radio" defaultChecked={true} />, container);
1238
- if (__DEV__) {
1239
- expect(console.error.calls.count()).toBe(1);
1240
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1241
- 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1242
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1243
- 'Decide between using a controlled or uncontrolled input ' +
1244
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1245
- ' in input (at **)',
1246
- );
1247
- }
1173
+ expect(() =>
1174
+ ReactDOM.render(<input type="radio" defaultChecked={true} />, container),
1175
+ ).toWarnDev(
1176
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1177
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1178
+ 'Decide between using a controlled or uncontrolled input ' +
1179
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1180
+ ' in input (at **)',
1181
+ );
1182
});
1183
1184
it('should warn if uncontrolled radio (checked is undefined) switches to controlled', () => {
1251
- spyOnDev(console, 'error');
1185
const stub = <input type="radio" />;
1186
const container = document.createElement('div');
1187
ReactDOM.render(stub, container);
1255
- ReactDOM.render(<input type="radio" checked={true} />, container);
1256
- if (__DEV__) {
1257
- expect(console.error.calls.count()).toBe(1);
1258
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1259
- 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1260
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1261
- 'Decide between using a controlled or uncontrolled input ' +
1262
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1263
- ' in input (at **)',
1264
- );
1265
- }
1188
+ expect(() =>
1189
+ ReactDOM.render(<input type="radio" checked={true} />, container),
1190
+ ).toWarnDev(
1191
+ 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1192
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1193
+ 'Decide between using a controlled or uncontrolled input ' +
1194
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1195
+ ' in input (at **)',
1196
+ );
1197
});
1198
1199
it('should warn if uncontrolled radio (checked is null) switches to controlled', () => {
1269
- spyOnDev(console, 'error');
1200
const stub = <input type="radio" checked={null} />;
1201
const container = document.createElement('div');
1202
ReactDOM.render(stub, container);
1273
- ReactDOM.render(<input type="radio" checked={true} />, container);
1274
- if (__DEV__) {
1275
- expect(console.error.calls.count()).toBe(1);
1276
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1277
- 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1278
- 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1279
- 'Decide between using a controlled or uncontrolled input ' +
1280
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1281
- ' in input (at **)',
1282
- );
1283
- }
1203
+ expect(() =>
1204
+ ReactDOM.render(<input type="radio" checked={true} />, container),
1205
+ ).toWarnDev(
1206
+ 'Warning: A component is changing an uncontrolled input of type radio to be controlled. ' +
1207
+ 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
1208
+ 'Decide between using a controlled or uncontrolled input ' +
1209
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1210
+ ' in input (at **)',
1211
+ );
1212
});
1213
1214
it('should not warn if radio value changes but never becomes controlled', () => {
@@ -1317,7 +1245,6 @@ describe('ReactDOMInput', () => {
1245
});
1246
1247
it('should warn if radio checked false changes to become uncontrolled', () => {
1320
- spyOnDev(console, 'error');
1248
const container = document.createElement('div');
1249
ReactDOM.render(
1250
<input
@@ -1328,16 +1255,15 @@ describe('ReactDOMInput', () => {
1255
/>,
1256
container,
1257
);
1331
- ReactDOM.render(<input type="radio" value="value" />, container);
1332
- if (__DEV__) {
1333
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1334
- 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1335
- 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1336
- 'Decide between using a controlled or uncontrolled input ' +
1337
- 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1338
- ' in input (at **)',
1339
- );
1340
- }
1258
+ expect(() =>
1259
+ ReactDOM.render(<input type="radio" value="value" />, container),
1260
+ ).toWarnDev(
1261
+ 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
1262
+ 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
1263
+ 'Decide between using a controlled or uncontrolled input ' +
1264
+ 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components\n' +
1265
+ ' in input (at **)',
1266
+ );
1267
});
1268
1269
it('sets type, step, min, max before value always', () => {
@@ -1556,29 +1482,19 @@ describe('ReactDOMInput', () => {
1482
}
1483
1484
it('reverts the value attribute to the initial value', () => {
1559
- spyOnDev(console, 'error');
1560
- renderInputWithStringThenWithUndefined();
1485
+ expect(renderInputWithStringThenWithUndefined).toWarnDev(
1486
+ 'Input elements should not switch from controlled to ' +
1487
+ 'uncontrolled (or vice versa).',
1488
+ );
1489
expect(input.getAttribute('value')).toBe('first');
1562
- if (__DEV__) {
1563
- expect(console.error.calls.count()).toBe(1);
1564
- expect(console.error.calls.argsFor(0)[0]).toContain(
1565
- 'Input elements should not switch from controlled to ' +
1566
- 'uncontrolled (or vice versa).',
1567
- );
1568
- }
1490
});
1491
1492
it('preserves the value property', () => {
1572
- spyOnDev(console, 'error');
1573
- renderInputWithStringThenWithUndefined();
1493
+ expect(renderInputWithStringThenWithUndefined).toWarnDev(
1494
+ 'Input elements should not switch from controlled to ' +
1495
+ 'uncontrolled (or vice versa).',
1496
+ );
1497
expect(input.value).toBe('latest');
1575
- if (__DEV__) {
1576
- expect(console.error.calls.count()).toBe(1);
1577
- expect(console.error.calls.argsFor(0)[0]).toContain(
1578
- 'Input elements should not switch from controlled to ' +
1579
- 'uncontrolled (or vice versa).',
1580
- );
1581
- }
1498
});
1499
});
1500
@@ -1605,82 +1521,56 @@ describe('ReactDOMInput', () => {
1521
}
1522
1523
it('reverts the value attribute to the initial value', () => {
1608
- spyOnDev(console, 'error');
1609
- renderInputWithStringThenWithNull();
1524
+ expect(renderInputWithStringThenWithNull).toWarnDev([
1525
+ '`value` prop on `input` should not be null. ' +
1526
+ 'Consider using an empty string to clear the component ' +
1527
+ 'or `undefined` for uncontrolled components.',
1528
+ 'Input elements should not switch from controlled ' +
1529
+ 'to uncontrolled (or vice versa).',
1530
+ ]);
1531
expect(input.getAttribute('value')).toBe('first');
1611
- if (__DEV__) {
1612
- expect(console.error.calls.count()).toBe(2);
1613
- expect(console.error.calls.argsFor(0)[0]).toContain(
1614
- '`value` prop on `input` should not be null. ' +
1615
- 'Consider using an empty string to clear the component ' +
1616
- 'or `undefined` for uncontrolled components.',
1617
- );
1618
- expect(console.error.calls.argsFor(1)[0]).toContain(
1619
- 'Input elements should not switch from controlled ' +
1620
- 'to uncontrolled (or vice versa).',
1621
- );
1622
- }
1532
});
1533
1534
it('preserves the value property', () => {
1626
- spyOnDev(console, 'error');
1627
- renderInputWithStringThenWithNull();
1535
+ expect(renderInputWithStringThenWithNull).toWarnDev([
1536
+ '`value` prop on `input` should not be null. ' +
1537
+ 'Consider using an empty string to clear the component ' +
1538
+ 'or `undefined` for uncontrolled components.',
1539
+ 'Input elements should not switch from controlled ' +
1540
+ 'to uncontrolled (or vice versa).',
1541
+ ]);
1542
expect(input.value).toBe('latest');
1629
- if (__DEV__) {
1630
- expect(console.error.calls.count()).toBe(2);
1631
- expect(console.error.calls.argsFor(0)[0]).toContain(
1632
- '`value` prop on `input` should not be null. ' +
1633
- 'Consider using an empty string to clear the component ' +
1634
- 'or `undefined` for uncontrolled components.',
1635
- );
1636
- expect(console.error.calls.argsFor(1)[0]).toContain(
1637
- 'Input elements should not switch from controlled ' +
1638
- 'to uncontrolled (or vice versa).',
1639
- );
1640
- }
1543
});
1544
});
1545
1546
describe('When given a Symbol value', function() {
1547
it('treats initial Symbol value as an empty string', function() {
1646
- spyOnDev(console, 'error');
1548
const container = document.createElement('div');
1648
- ReactDOM.render(
1649
- <input value={Symbol('foobar')} onChange={() => {}} />,
1650
- container,
1651
- );
1549
+ expect(() =>
1550
+ ReactDOM.render(
1551
+ <input value={Symbol('foobar')} onChange={() => {}} />,
1552
+ container,
1553
+ ),
1554
+ ).toWarnDev('Invalid value for prop `value`');
1555
const node = container.firstChild;
1556
1557
expect(node.value).toBe('');
1558
expect(node.getAttribute('value')).toBe('');
1656
-
1657
- if (__DEV__) {
1658
- expect(console.error.calls.count()).toBe(1);
1659
- expect(console.error.calls.argsFor(0)[0]).toContain(
1660
- 'Invalid value for prop `value`',
1661
- );
1662
- }
1559
});
1560
1561
it('treats updated Symbol value as an empty string', function() {
1666
- spyOnDev(console, 'error');
1562
const container = document.createElement('div');
1563
ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
1669
- ReactDOM.render(
1670
- <input value={Symbol('foobar')} onChange={() => {}} />,
1671
- container,
1672
- );
1564
+ expect(() =>
1565
+ ReactDOM.render(
1566
+ <input value={Symbol('foobar')} onChange={() => {}} />,
1567
+ container,
1568
+ ),
1569
+ ).toWarnDev('Invalid value for prop `value`');
1570
const node = container.firstChild;
1571
1572
expect(node.value).toBe('');
1573
expect(node.getAttribute('value')).toBe('');
1677
-
1678
- if (__DEV__) {
1679
- expect(console.error.calls.count()).toBe(1);
1680
- expect(console.error.calls.argsFor(0)[0]).toContain(
1681
- 'Invalid value for prop `value`',
1682
- );
1683
- }
1574
});
1575
1576
it('treats initial Symbol defaultValue as an empty string', function() {
@@ -1707,44 +1597,32 @@ describe('ReactDOMInput', () => {
1597
1598
describe('When given a function value', function() {
1599
it('treats initial function value as an empty string', function() {
1710
- spyOnDev(console, 'error');
1600
const container = document.createElement('div');
1712
- ReactDOM.render(
1713
- <input value={() => {}} onChange={() => {}} />,
1714
- container,
1715
- );
1601
+ expect(() =>
1602
+ ReactDOM.render(
1603
+ <input value={() => {}} onChange={() => {}} />,
1604
+ container,
1605
+ ),
1606
+ ).toWarnDev('Invalid value for prop `value`');
1607
const node = container.firstChild;
1608
1609
expect(node.value).toBe('');
1610
expect(node.getAttribute('value')).toBe('');
1720
-
1721
- if (__DEV__) {
1722
- expect(console.error.calls.count()).toBe(1);
1723
- expect(console.error.calls.argsFor(0)[0]).toContain(
1724
- 'Invalid value for prop `value`',
1725
- );
1726
- }
1611
});
1612
1613
it('treats updated function value as an empty string', function() {
1730
- spyOnDev(console, 'error');
1614
const container = document.createElement('div');
1615
ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
1733
- ReactDOM.render(
1734
- <input value={() => {}} onChange={() => {}} />,
1735
- container,
1736
- );
1616
+ expect(() =>
1617
+ ReactDOM.render(
1618
+ <input value={() => {}} onChange={() => {}} />,
1619
+ container,
1620
+ ),
1621
+ ).toWarnDev('Invalid value for prop `value`');
1622
const node = container.firstChild;
1623
1624
expect(node.value).toBe('');
1625
expect(node.getAttribute('value')).toBe('');
1741
-
1742
- if (__DEV__) {
1743
- expect(console.error.calls.count()).toBe(1);
1744
- expect(console.error.calls.argsFor(0)[0]).toContain(
1745
- 'Invalid value for prop `value`',
1746
- );
1747
- }
1626
});
1627
1628
it('treats initial function defaultValue as an empty string', function() {
packages/react-dom/src/__tests__/ReactDOMInvalidARIAHook-test.js
+25
-52
@@ -26,74 +26,47 @@ describe('ReactDOMInvalidARIAHook', () => {
26
27
describe('aria-* props', () => {
28
it('should allow valid aria-* props', () => {
29
- spyOnDev(console, 'error');
29
mountComponent({'aria-label': 'Bumble bees'});
31
- if (__DEV__) {
32
- expect(console.error.calls.count()).toBe(0);
33
- }
30
});
31
it('should warn for one invalid aria-* prop', () => {
36
- spyOnDev(console, 'error');
37
- mountComponent({'aria-badprop': 'maybe'});
38
- if (__DEV__) {
39
- expect(console.error.calls.count()).toBe(1);
40
- expect(console.error.calls.argsFor(0)[0]).toContain(
41
- 'Warning: Invalid aria prop `aria-badprop` on <div> tag. ' +
42
- 'For details, see https://fb.me/invalid-aria-prop',
43
- );
44
- }
32
+ expect(() => mountComponent({'aria-badprop': 'maybe'})).toWarnDev(
33
+ 'Warning: Invalid aria prop `aria-badprop` on <div> tag. ' +
34
+ 'For details, see https://fb.me/invalid-aria-prop',
35
+ );
36
});
37
it('should warn for many invalid aria-* props', () => {
47
- spyOnDev(console, 'error');
48
- mountComponent({
49
- 'aria-badprop': 'Very tall trees',
50
- 'aria-malprop': 'Turbulent seas',
51
- });
52
- if (__DEV__) {
53
- expect(console.error.calls.count()).toBe(1);
54
- expect(console.error.calls.argsFor(0)[0]).toContain(
55
- 'Warning: Invalid aria props `aria-badprop`, `aria-malprop` on <div> ' +
56
- 'tag. For details, see https://fb.me/invalid-aria-prop',
57
- );
58
- }
38
+ expect(() =>
39
+ mountComponent({
40
+ 'aria-badprop': 'Very tall trees',
41
+ 'aria-malprop': 'Turbulent seas',
42
+ }),
43
+ ).toWarnDev(
44
+ 'Warning: Invalid aria props `aria-badprop`, `aria-malprop` on <div> ' +
45
+ 'tag. For details, see https://fb.me/invalid-aria-prop',
46
+ );
47
});
48
it('should warn for an improperly cased aria-* prop', () => {
61
- spyOnDev(console, 'error');
49
// The valid attribute name is aria-haspopup.
63
- mountComponent({'aria-hasPopup': 'true'});
64
- if (__DEV__) {
65
- expect(console.error.calls.count()).toBe(1);
66
- expect(console.error.calls.argsFor(0)[0]).toContain(
67
- 'Warning: Unknown ARIA attribute `aria-hasPopup`. ' +
68
- 'Did you mean `aria-haspopup`?',
69
- );
70
- }
50
+ expect(() => mountComponent({'aria-hasPopup': 'true'})).toWarnDev(
51
+ 'Warning: Unknown ARIA attribute `aria-hasPopup`. ' +
52
+ 'Did you mean `aria-haspopup`?',
53
+ );
54
});
55
56
it('should warn for use of recognized camel case aria attributes', () => {
74
- spyOnDev(console, 'error');
57
// The valid attribute name is aria-haspopup.
76
- mountComponent({ariaHasPopup: 'true'});
77
- if (__DEV__) {
78
- expect(console.error.calls.count()).toBe(1);
79
- expect(console.error.calls.argsFor(0)[0]).toContain(
80
- 'Warning: Invalid ARIA attribute `ariaHasPopup`. ' +
81
- 'Did you mean `aria-haspopup`?',
82
- );
83
- }
58
+ expect(() => mountComponent({ariaHasPopup: 'true'})).toWarnDev(
59
+ 'Warning: Invalid ARIA attribute `ariaHasPopup`. ' +
60
+ 'Did you mean `aria-haspopup`?',
61
+ );
62
});
63
64
it('should warn for use of unrecognized camel case aria attributes', () => {
87
- spyOnDev(console, 'error');
65
// The valid attribute name is aria-haspopup.
89
- mountComponent({ariaSomethingInvalid: 'true'});
90
- if (__DEV__) {
91
- expect(console.error.calls.count()).toBe(1);
92
- expect(console.error.calls.argsFor(0)[0]).toContain(
93
- 'Warning: Invalid ARIA attribute `ariaSomethingInvalid`. ARIA ' +
94
- 'attributes follow the pattern aria-* and must be lowercase.',
95
- );
96
- }
66
+ expect(() => mountComponent({ariaSomethingInvalid: 'true'})).toWarnDev(
67
+ 'Warning: Invalid ARIA attribute `ariaSomethingInvalid`. ARIA ' +
68
+ 'attributes follow the pattern aria-* and must be lowercase.',
69
+ );
70
});
71
});
72
});
packages/react-dom/src/__tests__/ReactDOMOption-test.js
+8
-20
@@ -10,10 +10,6 @@
10
'use strict';
11
12
describe('ReactDOMOption', () => {
13
- function normalizeCodeLocInfo(str) {
14
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
15
- }
16
-
13
let React;
14
let ReactDOM;
15
let ReactTestUtils;
@@ -37,24 +33,21 @@ describe('ReactDOMOption', () => {
33
});
34
35
it('should ignore and warn invalid children types', () => {
40
- spyOnDev(console, 'error');
36
const el = (
37
<option>
38
{1} <div /> {2}
39
</option>
40
);
46
- const node = ReactTestUtils.renderIntoDocument(el);
41
+ let node;
42
+ expect(() => {
43
+ node = ReactTestUtils.renderIntoDocument(el);
44
+ }).toWarnDev(
45
+ '<div> cannot appear as a child of <option>.\n' +
46
+ ' in div (at **)\n' +
47
+ ' in option (at **)',
48
+ );
49
expect(node.innerHTML).toBe('1 2');
50
ReactTestUtils.renderIntoDocument(el);
49
- if (__DEV__) {
50
- // only warn once
51
- expect(console.error.calls.count()).toBe(1);
52
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toContain(
53
- '<div> cannot appear as a child of <option>.\n' +
54
- ' in div (at **)\n' +
55
- ' in option (at **)',
56
- );
57
- }
51
});
52
53
it('should ignore null/undefined/false children without warning', () => {
@@ -66,14 +59,9 @@ describe('ReactDOMOption', () => {
59
{undefined} {2}
60
</option>
61
);
69
- spyOnDev(console, 'error');
62
stub = ReactTestUtils.renderIntoDocument(stub);
63
64
const node = ReactDOM.findDOMNode(stub);
73
-
74
- if (__DEV__) {
75
- expect(console.error.calls.count()).toBe(0);
76
- }
65
expect(node.innerHTML).toBe('1 2');
66
});
67
packages/react-dom/src/__tests__/ReactDOMRoot-test.internal.js
+1
-11
@@ -130,8 +130,6 @@ describe('ReactDOMRoot', () => {
130
),
131
);
132
133
- spyOnDev(console, 'error');
134
-
133
// Does not hydrate by default
134
const container1 = document.createElement('div');
135
container1.innerHTML = markup;
@@ -142,9 +140,6 @@ describe('ReactDOMRoot', () => {
140
</div>,
141
);
142
flush();
145
- if (__DEV__) {
146
- expect(console.error.calls.count()).toBe(0);
147
- }
143
144
// Accepts `hydrate` option
145
const container2 = document.createElement('div');
@@ -155,15 +150,10 @@ describe('ReactDOMRoot', () => {
150
<span />
151
</div>,
152
);
158
- flush();
159
- if (__DEV__) {
160
- expect(console.error.calls.count()).toBe(1);
161
- expect(console.error.calls.argsFor(0)[0]).toMatch('Extra attributes');
162
- }
153
+ expect(flush).toWarnDev('Extra attributes');
154
});
155
156
it('does not clear existing children', async () => {
166
- spyOnDev(console, 'error');
157
container.innerHTML = '<div>a</div><div>b</div>';
158
const root = ReactDOM.createRoot(container);
159
root.render(
packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+45
-71
@@ -532,44 +532,37 @@ describe('ReactDOMSelect', () => {
532
});
533
534
it('should warn if value is null', () => {
535
- spyOnDev(console, 'error');
536
-
537
- ReactTestUtils.renderIntoDocument(
538
- <select value={null}>
539
- <option value="test" />
540
- </select>,
535
+ expect(() =>
536
+ ReactTestUtils.renderIntoDocument(
537
+ <select value={null}>
538
+ <option value="test" />
539
+ </select>,
540
+ ),
541
+ ).toWarnDev(
542
+ '`value` prop on `select` should not be null. ' +
543
+ 'Consider using an empty string to clear the component or `undefined` ' +
544
+ 'for uncontrolled components.',
545
);
542
- if (__DEV__) {
543
- expect(console.error.calls.count()).toBe(1);
544
- expect(console.error.calls.argsFor(0)[0]).toContain(
545
- '`value` prop on `select` should not be null. ' +
546
- 'Consider using an empty string to clear the component or `undefined` ' +
547
- 'for uncontrolled components.',
548
- );
549
- }
546
547
ReactTestUtils.renderIntoDocument(
548
<select value={null}>
549
<option value="test" />
550
</select>,
551
);
556
- if (__DEV__) {
557
- expect(console.error.calls.count()).toBe(1);
558
- }
552
});
553
554
it('should warn if selected is set on <option>', () => {
562
- spyOnDev(console, 'error');
563
-
564
- ReactTestUtils.renderIntoDocument(
565
- <select>
566
- <option selected={true} />
567
- <option selected={true} />
568
- </select>,
555
+ expect(() =>
556
+ ReactTestUtils.renderIntoDocument(
557
+ <select>
558
+ <option selected={true} />
559
+ <option selected={true} />
560
+ </select>,
561
+ ),
562
+ ).toWarnDev(
563
+ 'Use the `defaultValue` or `value` props on <select> instead of ' +
564
+ 'setting `selected` on <option>.',
565
);
570
- if (__DEV__) {
571
- expect(console.error.calls.count()).toBe(1);
572
- }
566
567
ReactTestUtils.renderIntoDocument(
568
<select>
@@ -577,41 +570,27 @@ describe('ReactDOMSelect', () => {
570
<option selected={true} />
571
</select>,
572
);
580
- if (__DEV__) {
581
- expect(console.error.calls.count()).toBe(1);
582
- expect(console.error.calls.argsFor(0)[0]).toContain(
583
- 'Use the `defaultValue` or `value` props on <select> instead of ' +
584
- 'setting `selected` on <option>.',
585
- );
586
- }
573
});
574
575
it('should warn if value is null and multiple is true', () => {
590
- spyOnDev(console, 'error');
591
- ReactTestUtils.renderIntoDocument(
592
- <select value={null} multiple={true}>
593
- <option value="test" />
594
- </select>,
576
+ expect(() =>
577
+ ReactTestUtils.renderIntoDocument(
578
+ <select value={null} multiple={true}>
579
+ <option value="test" />
580
+ </select>,
581
+ ),
582
+ ).toWarnDev(
583
+ '`value` prop on `select` should not be null. ' +
584
+ 'Consider using an empty array when `multiple` is ' +
585
+ 'set to `true` to clear the component or `undefined` ' +
586
+ 'for uncontrolled components.',
587
);
588
597
- if (__DEV__) {
598
- expect(console.error.calls.count()).toBe(1);
599
- expect(console.error.calls.argsFor(0)[0]).toContain(
600
- '`value` prop on `select` should not be null. ' +
601
- 'Consider using an empty array when `multiple` is ' +
602
- 'set to `true` to clear the component or `undefined` ' +
603
- 'for uncontrolled components.',
604
- );
605
- }
606
-
589
ReactTestUtils.renderIntoDocument(
590
<select value={null} multiple={true}>
591
<option value="test" />
592
</select>,
593
);
612
- if (__DEV__) {
613
- expect(console.error.calls.count()).toBe(1);
614
- }
594
});
595
596
it('should refresh state on change', () => {
@@ -631,23 +610,21 @@ describe('ReactDOMSelect', () => {
610
});
611
612
it('should warn if value and defaultValue props are specified', () => {
634
- spyOnDev(console, 'error');
635
- ReactTestUtils.renderIntoDocument(
636
- <select value="giraffe" defaultValue="giraffe" readOnly={true}>
637
- <option value="monkey">A monkey!</option>
638
- <option value="giraffe">A giraffe!</option>
639
- <option value="gorilla">A gorilla!</option>
640
- </select>,
613
+ expect(() =>
614
+ ReactTestUtils.renderIntoDocument(
615
+ <select value="giraffe" defaultValue="giraffe" readOnly={true}>
616
+ <option value="monkey">A monkey!</option>
617
+ <option value="giraffe">A giraffe!</option>
618
+ <option value="gorilla">A gorilla!</option>
619
+ </select>,
620
+ ),
621
+ ).toWarnDev(
622
+ 'Select elements must be either controlled or uncontrolled ' +
623
+ '(specify either the value prop, or the defaultValue prop, but not ' +
624
+ 'both). Decide between using a controlled or uncontrolled select ' +
625
+ 'element and remove one of these props. More info: ' +
626
+ 'https://fb.me/react-controlled-components',
627
);
642
- if (__DEV__) {
643
- expect(console.error.calls.argsFor(0)[0]).toContain(
644
- 'Select elements must be either controlled or uncontrolled ' +
645
- '(specify either the value prop, or the defaultValue prop, but not ' +
646
- 'both). Decide between using a controlled or uncontrolled select ' +
647
- 'element and remove one of these props. More info: ' +
648
- 'https://fb.me/react-controlled-components',
649
- );
650
- }
628
629
ReactTestUtils.renderIntoDocument(
630
<select value="giraffe" defaultValue="giraffe" readOnly={true}>
@@ -656,9 +633,6 @@ describe('ReactDOMSelect', () => {
633
<option value="gorilla">A gorilla!</option>
634
</select>,
635
);
659
- if (__DEV__) {
660
- expect(console.error.calls.count()).toBe(1);
661
- }
636
});
637
638
it('should be able to safely remove select onChange', () => {
packages/react-dom/src/__tests__/ReactDOMTextarea-test.js
+64
-72
@@ -207,8 +207,6 @@ describe('ReactDOMTextarea', () => {
207
208
expect(node.value).toBe('0');
209
210
- spyOnDev(console, 'error'); // deprecation warning for `children` content
211
-
210
ReactDOM.render(<textarea>1</textarea>, container);
211
212
expect(node.value).toBe('0');
@@ -247,15 +245,16 @@ describe('ReactDOMTextarea', () => {
245
});
246
247
it('should treat children like `defaultValue`', () => {
250
- spyOnDev(console, 'error');
251
-
248
const container = document.createElement('div');
249
let stub = <textarea>giraffe</textarea>;
254
- const node = renderTextarea(stub, container);
250
+ let node;
251
+
252
+ expect(() => {
253
+ node = renderTextarea(stub, container);
254
+ }).toWarnDev(
255
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
256
+ );
257
256
- if (__DEV__) {
257
- expect(console.error.calls.count()).toBe(1);
258
- }
258
expect(node.value).toBe('giraffe');
259
260
// Changing children should do nothing, it functions like `defaultValue`.
@@ -301,67 +300,69 @@ describe('ReactDOMTextarea', () => {
300
});
301
302
it('should allow numbers as children', () => {
304
- spyOnDev(console, 'error');
305
- const node = renderTextarea(<textarea>{17}</textarea>);
306
- if (__DEV__) {
307
- expect(console.error.calls.count()).toBe(1);
308
- }
303
+ let node;
304
+ expect(() => {
305
+ node = renderTextarea(<textarea>{17}</textarea>);
306
+ }).toWarnDev(
307
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
308
+ );
309
expect(node.value).toBe('17');
310
});
311
312
it('should allow booleans as children', () => {
313
- spyOnDev(console, 'error');
314
- const node = renderTextarea(<textarea>{false}</textarea>);
315
- if (__DEV__) {
316
- expect(console.error.calls.count()).toBe(1);
317
- }
313
+ let node;
314
+ expect(() => {
315
+ node = renderTextarea(<textarea>{false}</textarea>);
316
+ }).toWarnDev(
317
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
318
+ );
319
expect(node.value).toBe('false');
320
});
321
322
it('should allow objects as children', () => {
322
- spyOnDev(console, 'error');
323
const obj = {
324
toString: function() {
325
return 'sharkswithlasers';
326
},
327
};
328
- const node = renderTextarea(<textarea>{obj}</textarea>);
329
- if (__DEV__) {
330
- expect(console.error.calls.count()).toBe(1);
331
- }
328
+ let node;
329
+ expect(() => {
330
+ node = renderTextarea(<textarea>{obj}</textarea>);
331
+ }).toWarnDev(
332
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
333
+ );
334
expect(node.value).toBe('sharkswithlasers');
335
});
336
337
it('should throw with multiple or invalid children', () => {
336
- spyOnDev(console, 'error');
337
-
338
- expect(function() {
339
- ReactTestUtils.renderIntoDocument(
340
- <textarea>
341
- {'hello'}
342
- {'there'}
343
- </textarea>,
338
+ expect(() => {
339
+ expect(() =>
340
+ ReactTestUtils.renderIntoDocument(
341
+ <textarea>
342
+ {'hello'}
343
+ {'there'}
344
+ </textarea>,
345
+ ),
346
+ ).toWarnDev(
347
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
348
);
349
}).toThrow();
350
347
- if (__DEV__) {
348
- expect(console.error.calls.count()).toBe(1);
349
- }
350
-
351
let node;
352
- expect(function() {
353
- node = renderTextarea(
354
- <textarea>
355
- <strong />
356
- </textarea>,
352
+ expect(() => {
353
+ expect(
354
+ () =>
355
+ (node = renderTextarea(
356
+ <textarea>
357
+ <strong />
358
+ </textarea>,
359
+ )),
360
+ ).toWarnDev(
361
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
362
);
363
}).not.toThrow();
364
365
expect(node.value).toBe('[object Object]');
361
-
362
- if (__DEV__) {
363
- expect(console.error.calls.count()).toBe(2);
364
- }
366
});
367
368
it('should unmount', () => {
@@ -371,43 +372,34 @@ describe('ReactDOMTextarea', () => {
372
});
373
374
it('should warn if value is null', () => {
374
- spyOnDev(console, 'error');
375
-
376
- ReactTestUtils.renderIntoDocument(<textarea value={null} />);
377
- if (__DEV__) {
378
- expect(console.error.calls.argsFor(0)[0]).toContain(
379
- '`value` prop on `textarea` should not be null. ' +
380
- 'Consider using an empty string to clear the component or `undefined` ' +
381
- 'for uncontrolled components.',
382
- );
383
- }
375
+ expect(() =>
376
+ ReactTestUtils.renderIntoDocument(<textarea value={null} />),
377
+ ).toWarnDev(
378
+ '`value` prop on `textarea` should not be null. ' +
379
+ 'Consider using an empty string to clear the component or `undefined` ' +
380
+ 'for uncontrolled components.',
381
+ );
382
383
+ // No additional warnings are expected
384
ReactTestUtils.renderIntoDocument(<textarea value={null} />);
386
- if (__DEV__) {
387
- expect(console.error.calls.count()).toBe(1);
388
- }
385
});
386
387
it('should warn if value and defaultValue are specified', () => {
392
- spyOnDev(console, 'error');
393
- ReactTestUtils.renderIntoDocument(
394
- <textarea value="foo" defaultValue="bar" readOnly={true} />,
388
+ expect(() =>
389
+ ReactTestUtils.renderIntoDocument(
390
+ <textarea value="foo" defaultValue="bar" readOnly={true} />,
391
+ ),
392
+ ).toWarnDev(
393
+ 'Textarea elements must be either controlled or uncontrolled ' +
394
+ '(specify either the value prop, or the defaultValue prop, but not ' +
395
+ 'both). Decide between using a controlled or uncontrolled textarea ' +
396
+ 'and remove one of these props. More info: ' +
397
+ 'https://fb.me/react-controlled-components',
398
);
396
- if (__DEV__) {
397
- expect(console.error.calls.argsFor(0)[0]).toContain(
398
- 'Textarea elements must be either controlled or uncontrolled ' +
399
- '(specify either the value prop, or the defaultValue prop, but not ' +
400
- 'both). Decide between using a controlled or uncontrolled textarea ' +
401
- 'and remove one of these props. More info: ' +
402
- 'https://fb.me/react-controlled-components',
403
- );
404
- }
399
400
+ // No additional warnings are expected
401
ReactTestUtils.renderIntoDocument(
402
<textarea value="foo" defaultValue="bar" readOnly={true} />,
403
);
409
- if (__DEV__) {
410
- expect(console.error.calls.count()).toBe(1);
411
- }
404
});
405
});
packages/react-dom/src/__tests__/ReactMount-test.js
+36
-74
@@ -29,7 +29,7 @@ describe('ReactMount', () => {
29
describe('unmountComponentAtNode', () => {
30
it('throws when given a non-node', () => {
31
const nodeArray = document.getElementsByTagName('div');
32
- expect(function() {
32
+ expect(() => {
33
ReactDOM.unmountComponentAtNode(nodeArray);
34
}).toThrowError(
35
'unmountComponentAtNode(...): Target container is not a DOM element.',
@@ -53,22 +53,17 @@ describe('ReactMount', () => {
53
});
54
55
it('warns when given a factory', () => {
56
- spyOnDev(console, 'error');
56
class Component extends React.Component {
57
render() {
58
return <div />;
59
}
60
}
61
63
- ReactTestUtils.renderIntoDocument(Component);
64
- if (__DEV__) {
65
- expect(console.error.calls.count()).toBe(1);
66
- expect(console.error.calls.argsFor(0)[0]).toContain(
67
- 'Functions are not valid as a React child. ' +
68
- 'This may happen if you return a Component instead of <Component /> from render. ' +
69
- 'Or maybe you meant to call this function rather than return it.',
70
- );
71
- }
62
+ expect(() => ReactTestUtils.renderIntoDocument(Component)).toWarnDev(
63
+ 'Functions are not valid as a React child. ' +
64
+ 'This may happen if you return a Component instead of <Component /> from render. ' +
65
+ 'Or maybe you meant to call this function rather than return it.',
66
+ );
67
});
68
69
it('should render different components in same root', () => {
@@ -129,54 +124,36 @@ describe('ReactMount', () => {
124
const container = document.createElement('container');
125
container.innerHTML = ReactDOMServer.renderToString(<div />) + ' ';
126
132
- spyOnDev(console, 'error');
133
- ReactDOM.hydrate(<div />, container);
134
- if (__DEV__) {
135
- expect(console.error.calls.count()).toBe(1);
136
- expect(console.error.calls.argsFor(0)[0]).toContain(
137
- 'Did not expect server HTML to contain the text node " " in <container>.',
138
- );
139
- }
127
+ expect(() => ReactDOM.hydrate(<div />, container)).toWarnDev(
128
+ 'Did not expect server HTML to contain the text node " " in <container>.',
129
+ );
130
});
131
132
it('should warn if mounting into right padded rendered markup', () => {
133
const container = document.createElement('container');
134
container.innerHTML = ' ' + ReactDOMServer.renderToString(<div />);
135
146
- spyOnDev(console, 'error');
147
- ReactDOM.hydrate(<div />, container);
148
- if (__DEV__) {
149
- expect(console.error.calls.count()).toBe(1);
150
- expect(console.error.calls.argsFor(0)[0]).toContain(
151
- 'Did not expect server HTML to contain the text node " " in <container>.',
152
- );
153
- }
136
+ expect(() => ReactDOM.hydrate(<div />, container)).toWarnDev(
137
+ 'Did not expect server HTML to contain the text node " " in <container>.',
138
+ );
139
});
140
141
it('should not warn if mounting into non-empty node', () => {
142
const container = document.createElement('container');
143
container.innerHTML = '<div></div>';
144
160
- spyOnDev(console, 'error');
145
ReactDOM.render(<div />, container);
162
- if (__DEV__) {
163
- expect(console.error.calls.count()).toBe(0);
164
- }
146
});
147
148
it('should warn when mounting into document.body', () => {
149
const iFrame = document.createElement('iframe');
150
document.body.appendChild(iFrame);
170
- spyOnDev(console, 'error');
171
-
172
- ReactDOM.render(<div />, iFrame.contentDocument.body);
151
174
- if (__DEV__) {
175
- expect(console.error.calls.count()).toBe(1);
176
- expect(console.error.calls.argsFor(0)[0]).toContain(
177
- 'Rendering components directly into document.body is discouraged',
178
- );
179
- }
152
+ expect(() =>
153
+ ReactDOM.render(<div />, iFrame.contentDocument.body),
154
+ ).toWarnDev(
155
+ 'Rendering components directly into document.body is discouraged',
156
+ );
157
});
158
159
it('should account for escaping on a checksum mismatch', () => {
@@ -186,18 +163,15 @@ describe('ReactMount', () => {
163
);
164
div.innerHTML = markup;
165
189
- spyOnDev(console, 'error');
190
- ReactDOM.hydrate(
191
- <div>This markup contains an nbsp entity: client text</div>,
192
- div,
166
+ expect(() =>
167
+ ReactDOM.hydrate(
168
+ <div>This markup contains an nbsp entity: client text</div>,
169
+ div,
170
+ ),
171
+ ).toWarnDev(
172
+ 'Server: "This markup contains an nbsp entity: server text" ' +
173
+ 'Client: "This markup contains an nbsp entity: client text"',
174
);
194
- if (__DEV__) {
195
- expect(console.error.calls.count()).toBe(1);
196
- expect(console.error.calls.argsFor(0)[0]).toContain(
197
- 'Server: "This markup contains an nbsp entity: server text" ' +
198
- 'Client: "This markup contains an nbsp entity: client text"',
199
- );
200
- }
175
});
176
177
it('should warn if render removes React-rendered children', () => {
@@ -216,18 +190,14 @@ describe('ReactMount', () => {
190
ReactDOM.render(<Component />, container);
191
192
// Test that blasting away children throws a warning
219
- spyOnDev(console, 'error');
193
const rootNode = container.firstChild;
221
- ReactDOM.render(<span />, rootNode);
222
- if (__DEV__) {
223
- expect(console.error.calls.count()).toBe(1);
224
- expect(console.error.calls.argsFor(0)[0]).toBe(
225
- 'Warning: render(...): Replacing React-rendered children with a new ' +
226
- 'root component. If you intended to update the children of this node, ' +
227
- 'you should instead have the existing children update their state and ' +
228
- 'render the new components instead of calling ReactDOM.render.',
229
- );
230
- }
194
+
195
+ expect(() => ReactDOM.render(<span />, rootNode)).toWarnDev(
196
+ 'Warning: render(...): Replacing React-rendered children with a new ' +
197
+ 'root component. If you intended to update the children of this node, ' +
198
+ 'you should instead have the existing children update their state and ' +
199
+ 'render the new components instead of calling ReactDOM.render.',
200
+ );
201
});
202
203
it('should warn if the unmounted node was rendered by another copy of React', () => {
@@ -249,21 +219,13 @@ describe('ReactMount', () => {
219
// Make sure ReactDOM and ReactDOMOther are different copies
220
expect(ReactDOM).not.toEqual(ReactDOMOther);
221
252
- spyOnDev(console, 'error');
253
- ReactDOMOther.unmountComponentAtNode(container);
254
- if (__DEV__) {
255
- expect(console.error.calls.count()).toBe(1);
256
- expect(console.error.calls.argsFor(0)[0]).toBe(
257
- "Warning: unmountComponentAtNode(): The node you're attempting to unmount " +
258
- 'was rendered by another copy of React.',
259
- );
260
- }
222
+ expect(() => ReactDOMOther.unmountComponentAtNode(container)).toWarnDev(
223
+ "Warning: unmountComponentAtNode(): The node you're attempting to unmount " +
224
+ 'was rendered by another copy of React.',
225
+ );
226
227
// Don't throw a warning if the correct React copy unmounts the node
228
ReactDOM.unmountComponentAtNode(container);
264
- if (__DEV__) {
265
- expect(console.error.calls.count()).toBe(1);
266
- }
229
});
230
231
it('passes the correct callback context', () => {
packages/react-dom/src/__tests__/ReactMountDestruction-test.js
+12
-22
@@ -50,17 +50,12 @@ describe('ReactMount', () => {
50
51
// Test that unmounting at a root node gives a helpful warning
52
const rootDiv = mainContainerDiv.firstChild;
53
- spyOnDev(console, 'error');
54
- ReactDOM.unmountComponentAtNode(rootDiv);
55
- if (__DEV__) {
56
- expect(console.error.calls.count()).toBe(1);
57
- expect(console.error.calls.argsFor(0)[0]).toBe(
58
- "Warning: unmountComponentAtNode(): The node you're attempting to " +
59
- 'unmount was rendered by React and is not a top-level container. You ' +
60
- 'may have accidentally passed in a React root node instead of its ' +
61
- 'container.',
62
- );
63
- }
53
+ expect(() => ReactDOM.unmountComponentAtNode(rootDiv)).toWarnDev(
54
+ "Warning: unmountComponentAtNode(): The node you're attempting to " +
55
+ 'unmount was rendered by React and is not a top-level container. You ' +
56
+ 'may have accidentally passed in a React root node instead of its ' +
57
+ 'container.',
58
+ );
59
});
60
61
it('should warn when unmounting a non-container, non-root node', () => {
@@ -77,16 +72,11 @@ describe('ReactMount', () => {
72
73
// Test that unmounting at a non-root node gives a different warning
74
const nonRootDiv = mainContainerDiv.firstChild.firstChild;
80
- spyOnDev(console, 'error');
81
- ReactDOM.unmountComponentAtNode(nonRootDiv);
82
- if (__DEV__) {
83
- expect(console.error.calls.count()).toBe(1);
84
- expect(console.error.calls.argsFor(0)[0]).toBe(
85
- "Warning: unmountComponentAtNode(): The node you're attempting to " +
86
- 'unmount was rendered by React and is not a top-level container. ' +
87
- 'Instead, have the parent component update its state and rerender in ' +
88
- 'order to remove this component.',
89
- );
90
- }
75
+ expect(() => ReactDOM.unmountComponentAtNode(nonRootDiv)).toWarnDev(
76
+ "Warning: unmountComponentAtNode(): The node you're attempting to " +
77
+ 'unmount was rendered by React and is not a top-level container. ' +
78
+ 'Instead, have the parent component update its state and rerender in ' +
79
+ 'order to remove this component.',
80
+ );
81
});
82
});
packages/react-dom/src/__tests__/ReactMultiChild-test.js
+37
-62
@@ -10,10 +10,6 @@
10
'use strict';
11
12
describe('ReactMultiChild', () => {
13
- function normalizeCodeLocInfo(str) {
14
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
15
- }
16
-
13
let React;
14
let ReactDOM;
15
@@ -183,8 +179,6 @@ describe('ReactMultiChild', () => {
179
});
180
181
it('should warn for duplicated array keys with component stack info', () => {
186
- spyOnDev(console, 'error');
187
-
182
const container = document.createElement('div');
183
184
class WrapperComponent extends React.Component {
@@ -205,32 +199,25 @@ describe('ReactMultiChild', () => {
199
200
ReactDOM.render(<Parent>{[<div key="1" />]}</Parent>, container);
201
208
- ReactDOM.render(
209
- <Parent>{[<div key="1" />, <div key="1" />]}</Parent>,
210
- container,
211
- );
212
-
213
- if (__DEV__) {
214
- expect(console.error.calls.count()).toBe(1);
215
- expect(
216
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
217
- ).toContain(
218
- 'Encountered two children with the same key, `1`. ' +
219
- 'Keys should be unique so that components maintain their identity ' +
220
- 'across updates. Non-unique keys may cause children to be ' +
221
- 'duplicated and/or omitted — the behavior is unsupported and ' +
222
- 'could change in a future version.',
202
+ expect(() =>
203
+ ReactDOM.render(
204
+ <Parent>{[<div key="1" />, <div key="1" />]}</Parent>,
205
+ container,
206
+ ),
207
+ ).toWarnDev(
208
+ 'Encountered two children with the same key, `1`. ' +
209
+ 'Keys should be unique so that components maintain their identity ' +
210
+ 'across updates. Non-unique keys may cause children to be ' +
211
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
212
+ 'could change in a future version.',
213
+ ' in div (at **)\n' +
214
+ ' in WrapperComponent (at **)\n' +
215
' in div (at **)\n' +
224
- ' in WrapperComponent (at **)\n' +
225
- ' in div (at **)\n' +
226
- ' in Parent (at **)',
227
- );
228
- }
216
+ ' in Parent (at **)',
217
+ );
218
});
219
220
it('should warn for duplicated iterable keys with component stack info', () => {
232
- spyOnDev(console, 'error');
233
-
221
const container = document.createElement('div');
222
223
class WrapperComponent extends React.Component {
@@ -272,54 +259,42 @@ describe('ReactMultiChild', () => {
259
container,
260
);
261
275
- ReactDOM.render(
276
- <Parent>{createIterable([<div key="1" />, <div key="1" />])}</Parent>,
277
- container,
278
- );
279
-
280
- if (__DEV__) {
281
- expect(console.error.calls.count()).toBe(1);
282
- expect(
283
- normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
284
- ).toContain(
285
- 'Encountered two children with the same key, `1`. ' +
286
- 'Keys should be unique so that components maintain their identity ' +
287
- 'across updates. Non-unique keys may cause children to be ' +
288
- 'duplicated and/or omitted — the behavior is unsupported and ' +
289
- 'could change in a future version.',
262
+ expect(() =>
263
+ ReactDOM.render(
264
+ <Parent>{createIterable([<div key="1" />, <div key="1" />])}</Parent>,
265
+ container,
266
+ ),
267
+ ).toWarnDev(
268
+ 'Encountered two children with the same key, `1`. ' +
269
+ 'Keys should be unique so that components maintain their identity ' +
270
+ 'across updates. Non-unique keys may cause children to be ' +
271
+ 'duplicated and/or omitted — the behavior is unsupported and ' +
272
+ 'could change in a future version.',
273
+ ' in div (at **)\n' +
274
+ ' in WrapperComponent (at **)\n' +
275
' in div (at **)\n' +
291
- ' in WrapperComponent (at **)\n' +
292
- ' in div (at **)\n' +
293
- ' in Parent (at **)',
294
- );
295
- }
276
+ ' in Parent (at **)',
277
+ );
278
});
279
});
280
281
it('should warn for using maps as children with owner info', () => {
300
- spyOnDev(console, 'error');
282
class Parent extends React.Component {
283
render() {
284
return <div>{new Map([['foo', 0], ['bar', 1]])}</div>;
285
}
286
}
287
const container = document.createElement('div');
307
- ReactDOM.render(<Parent />, container);
308
- if (__DEV__) {
309
- expect(console.error.calls.count()).toBe(1);
310
- expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
311
- 'Warning: Using Maps as children is unsupported and will likely yield ' +
312
- 'unexpected results. Convert it to a sequence/iterable of keyed ' +
313
- 'ReactElements instead.\n' +
314
- ' in div (at **)\n' +
315
- ' in Parent (at **)',
316
- );
317
- }
288
+ expect(() => ReactDOM.render(<Parent />, container)).toWarnDev(
289
+ 'Warning: Using Maps as children is unsupported and will likely yield ' +
290
+ 'unexpected results. Convert it to a sequence/iterable of keyed ' +
291
+ 'ReactElements instead.\n' +
292
+ ' in div (at **)\n' +
293
+ ' in Parent (at **)',
294
+ );
295
});
296
297
it('should reorder bailed-out children', () => {
321
- spyOnDev(console, 'error');
322
-
298
class LetterInner extends React.Component {
299
render() {
300
return <div>{this.props.char}</div>;