@samitouri / QOS-React / commits / 08d035bc8f

Remove Shallow Renderer Tests (#25475)

Sebastian Markbåge committed Oct 13, 2022 at 19:02 UTC 08d035bc8f02be2bd0f23eec2c9e8ddec51083f6
5 files changed -3655
packages/react-dom/src/__tests__/ReactTestUtils-test.js
-43
@@ -9,7 +9,6 @@
9
10 'use strict';
11
12 -import ReactShallowRenderer from 'react-test-renderer/shallow';
12 import * as React from 'react';
13 import * as ReactDOM from 'react-dom';
14 import * as ReactDOMServer from 'react-dom/server';
@@ -383,48 +382,6 @@ describe('ReactTestUtils', () => {
382 );
383 });
384
386 - it('should throw when attempting to use a React element', () => {
387 - class SomeComponent extends React.Component {
388 - render() {
389 - return <div onClick={this.props.handleClick}>hello, world.</div>;
390 - }
391 - }
392 -
393 - const handler = jest.fn().mockName('spy');
394 - const shallowRenderer = ReactShallowRenderer.createRenderer();
395 - const result = shallowRenderer.render(
396 - <SomeComponent handleClick={handler} />,
397 - );
398 -
399 - expect(() => ReactTestUtils.Simulate.click(result)).toThrowError(
400 - 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
401 - 'a React element. Pass the DOM node you wish to simulate the event on instead. ' +
402 - 'Note that TestUtils.Simulate will not work if you are using shallow rendering.',
403 - );
404 - expect(handler).not.toHaveBeenCalled();
405 - });
406 -
407 - it('should throw when attempting to use a component instance', () => {
408 - class SomeComponent extends React.Component {
409 - render() {
410 - return <div onClick={this.props.handleClick}>hello, world.</div>;
411 - }
412 - }
413 -
414 - const handler = jest.fn().mockName('spy');
415 - const container = document.createElement('div');
416 - const instance = ReactDOM.render(
417 - <SomeComponent handleClick={handler} />,
418 - container,
419 - );
420 -
421 - expect(() => ReactTestUtils.Simulate.click(instance)).toThrowError(
422 - 'TestUtils.Simulate expected a DOM node as the first argument but received ' +
423 - 'a component instance. Pass the DOM node you wish to simulate the event on instead.',
424 - );
425 - expect(handler).not.toHaveBeenCalled();
426 - });
427 -
385 it('should not warn when used with extra properties', () => {
386 const CLIENT_X = 100;
387
packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js deleted
-1604
@@ -1,1604 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -import * as PropTypes from 'prop-types';
14 -import * as React from 'react';
15 -import ReactShallowRenderer from 'react-test-renderer/shallow';
16 -
17 -const createRenderer = ReactShallowRenderer.createRenderer;
18 -
19 -describe('ReactShallowRenderer', () => {
20 - it('should call all of the legacy lifecycle hooks', () => {
21 - const logs = [];
22 - const logger = message => () => logs.push(message) || true;
23 -
24 - class SomeComponent extends React.Component {
25 - UNSAFE_componentWillMount = logger('componentWillMount');
26 - componentDidMount = logger('componentDidMount');
27 - UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps');
28 - shouldComponentUpdate = logger('shouldComponentUpdate');
29 - UNSAFE_componentWillUpdate = logger('componentWillUpdate');
30 - componentDidUpdate = logger('componentDidUpdate');
31 - componentWillUnmount = logger('componentWillUnmount');
32 - render() {
33 - return <div />;
34 - }
35 - }
36 -
37 - const shallowRenderer = createRenderer();
38 - shallowRenderer.render(<SomeComponent foo={1} />);
39 -
40 - // Calling cDU might lead to problems with host component references.
41 - // Since our components aren't really mounted, refs won't be available.
42 - expect(logs).toEqual(['componentWillMount']);
43 -
44 - logs.splice(0);
45 -
46 - const instance = shallowRenderer.getMountedInstance();
47 - instance.setState({});
48 -
49 - expect(logs).toEqual(['shouldComponentUpdate', 'componentWillUpdate']);
50 -
51 - logs.splice(0);
52 -
53 - shallowRenderer.render(<SomeComponent foo={2} />);
54 -
55 - // The previous shallow renderer did not trigger cDU for props changes.
56 - expect(logs).toEqual([
57 - 'componentWillReceiveProps',
58 - 'shouldComponentUpdate',
59 - 'componentWillUpdate',
60 - ]);
61 - });
62 -
63 - it('should call all of the new lifecycle hooks', () => {
64 - const logs = [];
65 - const logger = message => () => logs.push(message) || true;
66 -
67 - class SomeComponent extends React.Component {
68 - state = {};
69 - static getDerivedStateFromProps = logger('getDerivedStateFromProps');
70 - componentDidMount = logger('componentDidMount');
71 - shouldComponentUpdate = logger('shouldComponentUpdate');
72 - componentDidUpdate = logger('componentDidUpdate');
73 - componentWillUnmount = logger('componentWillUnmount');
74 - render() {
75 - return <div />;
76 - }
77 - }
78 -
79 - const shallowRenderer = createRenderer();
80 - shallowRenderer.render(<SomeComponent foo={1} />);
81 -
82 - // Calling cDU might lead to problems with host component references.
83 - // Since our components aren't really mounted, refs won't be available.
84 - expect(logs).toEqual(['getDerivedStateFromProps']);
85 -
86 - logs.splice(0);
87 -
88 - const instance = shallowRenderer.getMountedInstance();
89 - instance.setState({});
90 -
91 - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']);
92 -
93 - logs.splice(0);
94 -
95 - shallowRenderer.render(<SomeComponent foo={2} />);
96 -
97 - // The previous shallow renderer did not trigger cDU for props changes.
98 - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']);
99 - });
100 -
101 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
102 - class Component extends React.Component {
103 - state = {};
104 - static getDerivedStateFromProps() {
105 - return null;
106 - }
107 - componentWillMount() {
108 - throw Error('unexpected');
109 - }
110 - componentWillReceiveProps() {
111 - throw Error('unexpected');
112 - }
113 - componentWillUpdate() {
114 - throw Error('unexpected');
115 - }
116 - render() {
117 - return null;
118 - }
119 - }
120 -
121 - const shallowRenderer = createRenderer();
122 - shallowRenderer.render(<Component />);
123 - });
124 -
125 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
126 - class Component extends React.Component {
127 - getSnapshotBeforeUpdate() {
128 - return null;
129 - }
130 - componentWillMount() {
131 - throw Error('unexpected');
132 - }
133 - componentWillReceiveProps() {
134 - throw Error('unexpected');
135 - }
136 - componentWillUpdate() {
137 - throw Error('unexpected');
138 - }
139 - render() {
140 - return null;
141 - }
142 - }
143 -
144 - const shallowRenderer = createRenderer();
145 - shallowRenderer.render(<Component value={1} />);
146 - shallowRenderer.render(<Component value={2} />);
147 - });
148 -
149 - it('should not call getSnapshotBeforeUpdate or componentDidUpdate when updating since refs wont exist', () => {
150 - class Component extends React.Component {
151 - getSnapshotBeforeUpdate() {
152 - throw Error('unexpected');
153 - }
154 - componentDidUpdate() {
155 - throw Error('unexpected');
156 - }
157 - render() {
158 - return null;
159 - }
160 - }
161 -
162 - const shallowRenderer = createRenderer();
163 - shallowRenderer.render(<Component value={1} />);
164 - shallowRenderer.render(<Component value={2} />);
165 - });
166 -
167 - it('should only render 1 level deep', () => {
168 - function Parent() {
169 - return (
170 - <div>
171 - <Child />
172 - </div>
173 - );
174 - }
175 - function Child() {
176 - throw Error('This component should not render');
177 - }
178 -
179 - const shallowRenderer = createRenderer();
180 - shallowRenderer.render(React.createElement(Parent));
181 - });
182 -
183 - it('should have shallow rendering', () => {
184 - class SomeComponent extends React.Component {
185 - render() {
186 - return (
187 - <div>
188 - <span className="child1" />
189 - <span className="child2" />
190 - </div>
191 - );
192 - }
193 - }
194 -
195 - const shallowRenderer = createRenderer();
196 - const result = shallowRenderer.render(<SomeComponent />);
197 -
198 - expect(result.type).toBe('div');
199 - expect(result.props.children).toEqual([
200 - <span className="child1" />,
201 - <span className="child2" />,
202 - ]);
203 - });
204 -
205 - it('should handle ForwardRef', () => {
206 - const testRef = React.createRef();
207 - const SomeComponent = React.forwardRef((props, ref) => {
208 - expect(ref).toEqual(testRef);
209 - return (
210 - <div>
211 - <span className="child1" />
212 - <span className="child2" />
213 - </div>
214 - );
215 - });
216 -
217 - const shallowRenderer = createRenderer();
218 - const result = shallowRenderer.render(<SomeComponent ref={testRef} />);
219 -
220 - expect(result.type).toBe('div');
221 - expect(result.props.children).toEqual([
222 - <span className="child1" />,
223 - <span className="child2" />,
224 - ]);
225 - });
226 -
227 - it('should handle Profiler', () => {
228 - class SomeComponent extends React.Component {
229 - render() {
230 - return (
231 - <React.Profiler id="test" onRender={jest.fn()}>
232 - <div>
233 - <span className="child1" />
234 - <span className="child2" />
235 - </div>
236 - </React.Profiler>
237 - );
238 - }
239 - }
240 -
241 - const shallowRenderer = createRenderer();
242 - const result = shallowRenderer.render(<SomeComponent />);
243 -
244 - expect(result.type).toBe(React.Profiler);
245 - expect(result.props.children).toEqual(
246 - <div>
247 - <span className="child1" />
248 - <span className="child2" />
249 - </div>,
250 - );
251 - });
252 -
253 - it('should enable shouldComponentUpdate to prevent a re-render', () => {
254 - let renderCounter = 0;
255 - class SimpleComponent extends React.Component {
256 - state = {update: false};
257 - shouldComponentUpdate(nextProps, nextState) {
258 - return this.state.update !== nextState.update;
259 - }
260 - render() {
261 - renderCounter++;
262 - return <div>{`${renderCounter}`}</div>;
263 - }
264 - }
265 -
266 - const shallowRenderer = createRenderer();
267 - shallowRenderer.render(<SimpleComponent />);
268 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
269 -
270 - const instance = shallowRenderer.getMountedInstance();
271 - instance.setState({update: false});
272 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
273 -
274 - instance.setState({update: true});
275 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
276 - });
277 -
278 - it('should enable PureComponent to prevent a re-render', () => {
279 - let renderCounter = 0;
280 - class SimpleComponent extends React.PureComponent {
281 - state = {update: false};
282 - render() {
283 - renderCounter++;
284 - return <div>{`${renderCounter}`}</div>;
285 - }
286 - }
287 -
288 - const shallowRenderer = createRenderer();
289 - shallowRenderer.render(<SimpleComponent />);
290 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
291 -
292 - const instance = shallowRenderer.getMountedInstance();
293 - instance.setState({update: false});
294 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
295 -
296 - instance.setState({update: true});
297 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
298 - });
299 -
300 - it('should not run shouldComponentUpdate during forced update', () => {
301 - let scuCounter = 0;
302 - class SimpleComponent extends React.Component {
303 - state = {count: 1};
304 - shouldComponentUpdate() {
305 - scuCounter++;
306 - return false;
307 - }
308 - render() {
309 - return <div>{`${this.state.count}`}</div>;
310 - }
311 - }
312 -
313 - const shallowRenderer = createRenderer();
314 - shallowRenderer.render(<SimpleComponent />);
315 - expect(scuCounter).toEqual(0);
316 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
317 -
318 - // Force update the initial state. sCU should not fire.
319 - const instance = shallowRenderer.getMountedInstance();
320 - instance.forceUpdate();
321 - expect(scuCounter).toEqual(0);
322 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
323 -
324 - // Setting state updates the instance, but doesn't re-render
325 - // because sCU returned false.
326 - instance.setState(state => ({count: state.count + 1}));
327 - expect(scuCounter).toEqual(1);
328 - expect(instance.state.count).toEqual(2);
329 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
330 -
331 - // A force update updates the render output, but doesn't call sCU.
332 - instance.forceUpdate();
333 - expect(scuCounter).toEqual(1);
334 - expect(instance.state.count).toEqual(2);
335 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
336 - });
337 -
338 - it('should rerender when calling forceUpdate', () => {
339 - let renderCounter = 0;
340 - class SimpleComponent extends React.Component {
341 - render() {
342 - renderCounter += 1;
343 - return <div />;
344 - }
345 - }
346 -
347 - const shallowRenderer = createRenderer();
348 - shallowRenderer.render(<SimpleComponent />);
349 - expect(renderCounter).toEqual(1);
350 -
351 - const instance = shallowRenderer.getMountedInstance();
352 - instance.forceUpdate();
353 - expect(renderCounter).toEqual(2);
354 - });
355 -
356 - it('should shallow render a function component', () => {
357 - function SomeComponent(props, context) {
358 - return (
359 - <div>
360 - <div>{props.foo}</div>
361 - <div>{context.bar}</div>
362 - <span className="child1" />
363 - <span className="child2" />
364 - </div>
365 - );
366 - }
367 - SomeComponent.contextTypes = {
368 - bar: PropTypes.string,
369 - };
370 -
371 - const shallowRenderer = createRenderer();
372 - const result = shallowRenderer.render(<SomeComponent foo={'FOO'} />, {
373 - bar: 'BAR',
374 - });
375 -
376 - expect(result.type).toBe('div');
377 - expect(result.props.children).toEqual([
378 - <div>FOO</div>,
379 - <div>BAR</div>,
380 - <span className="child1" />,
381 - <span className="child2" />,
382 - ]);
383 - });
384 -
385 - it('should shallow render a component returning strings directly from render', () => {
386 - const Text = ({value}) => value;
387 -
388 - const shallowRenderer = createRenderer();
389 - const result = shallowRenderer.render(<Text value="foo" />);
390 - expect(result).toEqual('foo');
391 - });
392 -
393 - it('should shallow render a component returning numbers directly from render', () => {
394 - const Text = ({value}) => value;
395 -
396 - const shallowRenderer = createRenderer();
397 - const result = shallowRenderer.render(<Text value={10} />);
398 - expect(result).toEqual(10);
399 - });
400 -
401 - it('should shallow render a fragment', () => {
402 - class SomeComponent extends React.Component {
403 - render() {
404 - return <div />;
405 - }
406 - }
407 - class Fragment extends React.Component {
408 - render() {
409 - return [<div key="a" />, <span key="b" />, <SomeComponent />];
410 - }
411 - }
412 - const shallowRenderer = createRenderer();
413 - const result = shallowRenderer.render(<Fragment />);
414 - expect(result).toEqual([
415 - <div key="a" />,
416 - <span key="b" />,
417 - <SomeComponent />,
418 - ]);
419 - });
420 -
421 - it('should shallow render a React.fragment', () => {
422 - class SomeComponent extends React.Component {
423 - render() {
424 - return <div />;
425 - }
426 - }
427 - class Fragment extends React.Component {
428 - render() {
429 - return (
430 - <>
431 - <div />
432 - <span />
433 - <SomeComponent />
434 - </>
435 - );
436 - }
437 - }
438 - const shallowRenderer = createRenderer();
439 - const result = shallowRenderer.render(<Fragment />);
440 - expect(result).toEqual(
441 - <>
442 - <div />
443 - <span />
444 - <SomeComponent />
445 - </>,
446 - );
447 - });
448 -
449 - it('should throw for invalid elements', () => {
450 - class SomeComponent extends React.Component {
451 - render() {
452 - return <div />;
453 - }
454 - }
455 -
456 - const shallowRenderer = createRenderer();
457 - expect(() => shallowRenderer.render(SomeComponent)).toThrowError(
458 - 'ReactShallowRenderer render(): Invalid component element. Instead of ' +
459 - 'passing a component class, make sure to instantiate it by passing it ' +
460 - 'to React.createElement.',
461 - );
462 - expect(() => shallowRenderer.render(<div />)).toThrowError(
463 - 'ReactShallowRenderer render(): Shallow rendering works only with ' +
464 - 'custom components, not primitives (div). Instead of calling ' +
465 - '`.render(el)` and inspecting the rendered output, look at `el.props` ' +
466 - 'directly instead.',
467 - );
468 - });
469 -
470 - it('should have shallow unmounting', () => {
471 - const componentWillUnmount = jest.fn();
472 -
473 - class SomeComponent extends React.Component {
474 - componentWillUnmount = componentWillUnmount;
475 - render() {
476 - return <div />;
477 - }
478 - }
479 -
480 - const shallowRenderer = createRenderer();
481 - shallowRenderer.render(<SomeComponent />);
482 - shallowRenderer.unmount();
483 -
484 - expect(componentWillUnmount).toBeCalled();
485 - });
486 -
487 - it('can shallow render to null', () => {
488 - class SomeComponent extends React.Component {
489 - render() {
490 - return null;
491 - }
492 - }
493 -
494 - const shallowRenderer = createRenderer();
495 - const result = shallowRenderer.render(<SomeComponent />);
496 -
497 - expect(result).toBe(null);
498 - });
499 -
500 - it('can shallow render with a ref', () => {
501 - class SomeComponent extends React.Component {
502 - render() {
503 - return <div ref="hello" />;
504 - }
505 - }
506 -
507 - const shallowRenderer = createRenderer();
508 - // Shouldn't crash.
509 - shallowRenderer.render(<SomeComponent />);
510 - });
511 -
512 - it('lets you update shallowly rendered components', () => {
513 - class SomeComponent extends React.Component {
514 - state = {clicked: false};
515 -
516 - onClick = () => {
517 - this.setState({clicked: true});
518 - };
519 -
520 - render() {
521 - const className = this.state.clicked ? 'was-clicked' : '';
522 -
523 - if (this.props.aNew === 'prop') {
524 - return (
525 - <a href="#" onClick={this.onClick} className={className}>
526 - Test link
527 - </a>
528 - );
529 - } else {
530 - return (
531 - <div>
532 - <span className="child1" />
533 - <span className="child2" />
534 - </div>
535 - );
536 - }
537 - }
538 - }
539 -
540 - const shallowRenderer = createRenderer();
541 - const result = shallowRenderer.render(<SomeComponent />);
542 - expect(result.type).toBe('div');
543 - expect(result.props.children).toEqual([
544 - <span className="child1" />,
545 - <span className="child2" />,
546 - ]);
547 -
548 - const updatedResult = shallowRenderer.render(<SomeComponent aNew="prop" />);
549 - expect(updatedResult.type).toBe('a');
550 -
551 - const mockEvent = {};
552 - updatedResult.props.onClick(mockEvent);
553 -
554 - const updatedResultCausedByClick = shallowRenderer.getRenderOutput();
555 - expect(updatedResultCausedByClick.type).toBe('a');
556 - expect(updatedResultCausedByClick.props.className).toBe('was-clicked');
557 - });
558 -
559 - it('can access the mounted component instance', () => {
560 - class SimpleComponent extends React.Component {
561 - someMethod = () => {
562 - return this.props.n;
563 - };
564 -
565 - render() {
566 - return <div>{this.props.n}</div>;
567 - }
568 - }
569 -
570 - const shallowRenderer = createRenderer();
571 - shallowRenderer.render(<SimpleComponent n={5} />);
572 - expect(shallowRenderer.getMountedInstance().someMethod()).toEqual(5);
573 - });
574 -
575 - it('can shallowly render components with contextTypes', () => {
576 - class SimpleComponent extends React.Component {
577 - static contextTypes = {
578 - name: PropTypes.string,
579 - };
580 -
581 - render() {
582 - return <div />;
583 - }
584 - }
585 -
586 - const shallowRenderer = createRenderer();
587 - const result = shallowRenderer.render(<SimpleComponent />);
588 - expect(result).toEqual(<div />);
589 - });
590 -
591 - it('passes expected params to legacy component lifecycle methods', () => {
592 - const componentDidUpdateParams = [];
593 - const componentWillReceivePropsParams = [];
594 - const componentWillUpdateParams = [];
595 - const setStateParams = [];
596 - const shouldComponentUpdateParams = [];
597 -
598 - const initialProp = {prop: 'init prop'};
599 - const initialState = {state: 'init state'};
600 - const initialContext = {context: 'init context'};
601 - const updatedState = {state: 'updated state'};
602 - const updatedProp = {prop: 'updated prop'};
603 - const updatedContext = {context: 'updated context'};
604 -
605 - class SimpleComponent extends React.Component {
606 - constructor(props, context) {
607 - super(props, context);
608 - this.state = initialState;
609 - }
610 - static contextTypes = {
611 - context: PropTypes.string,
612 - };
613 - componentDidUpdate(...args) {
614 - componentDidUpdateParams.push(...args);
615 - }
616 - UNSAFE_componentWillReceiveProps(...args) {
617 - componentWillReceivePropsParams.push(...args);
618 - this.setState((...innerArgs) => {
619 - setStateParams.push(...innerArgs);
620 - return updatedState;
621 - });
622 - }
623 - UNSAFE_componentWillUpdate(...args) {
624 - componentWillUpdateParams.push(...args);
625 - }
626 - shouldComponentUpdate(...args) {
627 - shouldComponentUpdateParams.push(...args);
628 - return true;
629 - }
630 - render() {
631 - return null;
632 - }
633 - }
634 -
635 - const shallowRenderer = createRenderer();
636 - shallowRenderer.render(
637 - React.createElement(SimpleComponent, initialProp),
638 - initialContext,
639 - );
640 - expect(componentDidUpdateParams).toEqual([]);
641 - expect(componentWillReceivePropsParams).toEqual([]);
642 - expect(componentWillUpdateParams).toEqual([]);
643 - expect(setStateParams).toEqual([]);
644 - expect(shouldComponentUpdateParams).toEqual([]);
645 -
646 - // Lifecycle hooks should be invoked with the correct prev/next params on update.
647 - shallowRenderer.render(
648 - React.createElement(SimpleComponent, updatedProp),
649 - updatedContext,
650 - );
651 - expect(componentWillReceivePropsParams).toEqual([
652 - updatedProp,
653 - updatedContext,
654 - ]);
655 - expect(setStateParams).toEqual([initialState, initialProp]);
656 - expect(shouldComponentUpdateParams).toEqual([
657 - updatedProp,
658 - updatedState,
659 - updatedContext,
660 - ]);
661 - expect(componentWillUpdateParams).toEqual([
662 - updatedProp,
663 - updatedState,
664 - updatedContext,
665 - ]);
666 - expect(componentDidUpdateParams).toEqual([]);
667 - });
668 -
669 - it('passes expected params to new component lifecycle methods', () => {
670 - const componentDidUpdateParams = [];
671 - const getDerivedStateFromPropsParams = [];
672 - const shouldComponentUpdateParams = [];
673 -
674 - const initialProp = {prop: 'init prop'};
675 - const initialState = {state: 'init state'};
676 - const initialContext = {context: 'init context'};
677 - const updatedProp = {prop: 'updated prop'};
678 - const updatedContext = {context: 'updated context'};
679 -
680 - class SimpleComponent extends React.Component {
681 - constructor(props, context) {
682 - super(props, context);
683 - this.state = initialState;
684 - }
685 - static contextTypes = {
686 - context: PropTypes.string,
687 - };
688 - componentDidUpdate(...args) {
689 - componentDidUpdateParams.push(...args);
690 - }
691 - static getDerivedStateFromProps(...args) {
692 - getDerivedStateFromPropsParams.push(args);
693 - return null;
694 - }
695 - shouldComponentUpdate(...args) {
696 - shouldComponentUpdateParams.push(...args);
697 - return true;
698 - }
699 - render() {
700 - return null;
701 - }
702 - }
703 -
704 - const shallowRenderer = createRenderer();
705 -
706 - // The only lifecycle hook that should be invoked on initial render
707 - // Is the static getDerivedStateFromProps() methods
708 - shallowRenderer.render(
709 - React.createElement(SimpleComponent, initialProp),
710 - initialContext,
711 - );
712 - expect(getDerivedStateFromPropsParams).toEqual([
713 - [initialProp, initialState],
714 - ]);
715 - expect(componentDidUpdateParams).toEqual([]);
716 - expect(shouldComponentUpdateParams).toEqual([]);
717 -
718 - // Lifecycle hooks should be invoked with the correct prev/next params on update.
719 - shallowRenderer.render(
720 - React.createElement(SimpleComponent, updatedProp),
721 - updatedContext,
722 - );
723 - expect(getDerivedStateFromPropsParams).toEqual([
724 - [initialProp, initialState],
725 - [updatedProp, initialState],
726 - ]);
727 - expect(shouldComponentUpdateParams).toEqual([
728 - updatedProp,
729 - initialState,
730 - updatedContext,
731 - ]);
732 - expect(componentDidUpdateParams).toEqual([]);
733 - });
734 -
735 - it('can shallowly render components with ref as function', () => {
736 - class SimpleComponent extends React.Component {
737 - state = {clicked: false};
738 -
739 - handleUserClick = () => {
740 - this.setState({clicked: true});
741 - };
742 -
743 - render() {
744 - return (
745 - <div
746 - ref={() => {}}
747 - onClick={this.handleUserClick}
748 - className={this.state.clicked ? 'clicked' : ''}
749 - />
750 - );
751 - }
752 - }
753 -
754 - const shallowRenderer = createRenderer();
755 - shallowRenderer.render(<SimpleComponent />);
756 - let result = shallowRenderer.getRenderOutput();
757 - expect(result.type).toEqual('div');
758 - expect(result.props.className).toEqual('');
759 - result.props.onClick();
760 -
761 - result = shallowRenderer.getRenderOutput();
762 - expect(result.type).toEqual('div');
763 - expect(result.props.className).toEqual('clicked');
764 - });
765 -
766 - it('can initialize state via static getDerivedStateFromProps', () => {
767 - class SimpleComponent extends React.Component {
768 - state = {
769 - count: 1,
770 - };
771 -
772 - static getDerivedStateFromProps(props, prevState) {
773 - return {
774 - count: prevState.count + props.incrementBy,
775 - other: 'foobar',
776 - };
777 - }
778 -
779 - render() {
780 - return (
781 - <div>{`count:${this.state.count}, other:${this.state.other}`}</div>
782 - );
783 - }
784 - }
785 -
786 - const shallowRenderer = createRenderer();
787 - const result = shallowRenderer.render(<SimpleComponent incrementBy={2} />);
788 - expect(result).toEqual(<div>count:3, other:foobar</div>);
789 - });
790 -
791 - it('can setState in componentWillMount when shallow rendering', () => {
792 - class SimpleComponent extends React.Component {
793 - UNSAFE_componentWillMount() {
794 - this.setState({groovy: 'doovy'});
795 - }
796 -
797 - render() {
798 - return <div>{this.state.groovy}</div>;
799 - }
800 - }
801 -
802 - const shallowRenderer = createRenderer();
803 - const result = shallowRenderer.render(<SimpleComponent />);
804 - expect(result).toEqual(<div>doovy</div>);
805 - });
806 -
807 - it('can setState in componentWillMount repeatedly when shallow rendering', () => {
808 - class SimpleComponent extends React.Component {
809 - state = {
810 - separator: '-',
811 - };
812 -
813 - UNSAFE_componentWillMount() {
814 - this.setState({groovy: 'doovy'});
815 - this.setState({doovy: 'groovy'});
816 - }
817 -
818 - render() {
819 - const {groovy, doovy, separator} = this.state;
820 -
821 - return <div>{`${groovy}${separator}${doovy}`}</div>;
822 - }
823 - }
824 -
825 - const shallowRenderer = createRenderer();
826 - const result = shallowRenderer.render(<SimpleComponent />);
827 - expect(result).toEqual(<div>doovy-groovy</div>);
828 - });
829 -
830 - it('can setState in componentWillMount with an updater function repeatedly when shallow rendering', () => {
831 - class SimpleComponent extends React.Component {
832 - state = {
833 - separator: '-',
834 - };
835 -
836 - UNSAFE_componentWillMount() {
837 - this.setState(state => ({groovy: 'doovy'}));
838 - this.setState(state => ({doovy: state.groovy}));
839 - }
840 -
841 - render() {
842 - const {groovy, doovy, separator} = this.state;
843 -
844 - return <div>{`${groovy}${separator}${doovy}`}</div>;
845 - }
846 - }
847 -
848 - const shallowRenderer = createRenderer();
849 - const result = shallowRenderer.render(<SimpleComponent />);
850 - expect(result).toEqual(<div>doovy-doovy</div>);
851 - });
852 -
853 - it('can setState in componentWillReceiveProps when shallow rendering', () => {
854 - class SimpleComponent extends React.Component {
855 - state = {count: 0};
856 -
857 - UNSAFE_componentWillReceiveProps(nextProps) {
858 - if (nextProps.updateState) {
859 - this.setState({count: 1});
860 - }
861 - }
862 -
863 - render() {
864 - return <div>{this.state.count}</div>;
865 - }
866 - }
867 -
868 - const shallowRenderer = createRenderer();
869 - let result = shallowRenderer.render(
870 - <SimpleComponent updateState={false} />,
871 - );
872 - expect(result.props.children).toEqual(0);
873 -
874 - result = shallowRenderer.render(<SimpleComponent updateState={true} />);
875 - expect(result.props.children).toEqual(1);
876 - });
877 -
878 - it('can update state with static getDerivedStateFromProps when shallow rendering', () => {
879 - class SimpleComponent extends React.Component {
880 - state = {count: 1};
881 -
882 - static getDerivedStateFromProps(nextProps, prevState) {
883 - if (nextProps.updateState) {
884 - return {count: nextProps.incrementBy + prevState.count};
885 - }
886 -
887 - return null;
888 - }
889 -
890 - render() {
891 - return <div>{this.state.count}</div>;
892 - }
893 - }
894 -
895 - const shallowRenderer = createRenderer();
896 - let result = shallowRenderer.render(
897 - <SimpleComponent updateState={false} incrementBy={0} />,
898 - );
899 - expect(result.props.children).toEqual(1);
900 -
901 - result = shallowRenderer.render(
902 - <SimpleComponent updateState={true} incrementBy={2} />,
903 - );
904 - expect(result.props.children).toEqual(3);
905 -
906 - result = shallowRenderer.render(
907 - <SimpleComponent updateState={false} incrementBy={2} />,
908 - );
909 - expect(result.props.children).toEqual(3);
910 - });
911 -
912 - it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', () => {
913 - class SimpleComponent extends React.Component {
914 - state = {value: 0};
915 -
916 - static getDerivedStateFromProps(nextProps, prevState) {
917 - return {...prevState};
918 - }
919 -
920 - updateState = () => {
921 - this.setState(state => ({value: state.value + 1}));
922 - };
923 -
924 - render() {
925 - return <div>{`value:${this.state.value}`}</div>;
926 - }
927 - }
928 -
929 - const shallowRenderer = createRenderer();
930 - let result = shallowRenderer.render(<SimpleComponent />);
931 - expect(result).toEqual(<div>value:0</div>);
932 -
933 - const instance = shallowRenderer.getMountedInstance();
934 - instance.updateState();
935 - result = shallowRenderer.getRenderOutput();
936 - expect(result).toEqual(<div>value:1</div>);
937 - });
938 -
939 - it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => {
940 - class SimpleComponent extends React.Component {
941 - constructor(props) {
942 - super(props);
943 - this.state = {
944 - value: props.value,
945 - };
946 - }
947 -
948 - static getDerivedStateFromProps(nextProps, prevState) {
949 - if (nextProps.value === prevState.value) {
950 - return null;
951 - }
952 - return {value: nextProps.value};
953 - }
954 -
955 - shouldComponentUpdate(nextProps, nextState) {
956 - return nextState.value !== this.state.value;
957 - }
958 -
959 - render() {
960 - return <div>{`value:${this.state.value}`}</div>;
961 - }
962 - }
963 -
964 - const shallowRenderer = createRenderer();
965 - const initialResult = shallowRenderer.render(
966 - <SimpleComponent value="initial" />,
967 - );
968 - expect(initialResult).toEqual(<div>value:initial</div>);
969 - const updatedResult = shallowRenderer.render(
970 - <SimpleComponent value="updated" />,
971 - );
972 - expect(updatedResult).toEqual(<div>value:updated</div>);
973 - });
974 -
975 - it('can setState with an updater function', () => {
976 - let instance;
977 -
978 - class SimpleComponent extends React.Component {
979 - state = {
980 - counter: 0,
981 - };
982 -
983 - render() {
984 - instance = this;
985 - return (
986 - <button ref="button" onClick={this.onClick}>
987 - {this.state.counter}
988 - </button>
989 - );
990 - }
991 - }
992 -
993 - const shallowRenderer = createRenderer();
994 - let result = shallowRenderer.render(<SimpleComponent defaultCount={1} />);
995 - expect(result.props.children).toEqual(0);
996 -
997 - instance.setState((state, props) => {
998 - return {counter: props.defaultCount + 1};
999 - });
1000 -
1001 - result = shallowRenderer.getRenderOutput();
1002 - expect(result.props.children).toEqual(2);
1003 - });
1004 -
1005 - it('can access component instance from setState updater function', done => {
1006 - let instance;
1007 -
1008 - class SimpleComponent extends React.Component {
1009 - state = {};
1010 -
1011 - render() {
1012 - instance = this;
1013 - return null;
1014 - }
1015 - }
1016 -
1017 - const shallowRenderer = createRenderer();
1018 - shallowRenderer.render(<SimpleComponent />);
1019 -
1020 - instance.setState(function updater(state, props) {
1021 - expect(this).toBe(instance);
1022 - done();
1023 - });
1024 - });
1025 -
1026 - it('can setState with a callback', () => {
1027 - let instance;
1028 -
1029 - class SimpleComponent extends React.Component {
1030 - state = {
1031 - counter: 0,
1032 - };
1033 - render() {
1034 - instance = this;
1035 - return <p>{this.state.counter}</p>;
1036 - }
1037 - }
1038 -
1039 - const shallowRenderer = createRenderer();
1040 - const result = shallowRenderer.render(<SimpleComponent />);
1041 - expect(result.props.children).toBe(0);
1042 -
1043 - const callback = jest.fn(function() {
1044 - expect(this).toBe(instance);
1045 - });
1046 -
1047 - instance.setState({counter: 1}, callback);
1048 -
1049 - const updated = shallowRenderer.getRenderOutput();
1050 - expect(updated.props.children).toBe(1);
1051 - expect(callback).toHaveBeenCalled();
1052 - });
1053 -
1054 - it('can replaceState with a callback', () => {
1055 - let instance;
1056 -
1057 - class SimpleComponent extends React.Component {
1058 - state = {
1059 - counter: 0,
1060 - };
1061 - render() {
1062 - instance = this;
1063 - return <p>{this.state.counter}</p>;
1064 - }
1065 - }
1066 -
1067 - const shallowRenderer = createRenderer();
1068 - const result = shallowRenderer.render(<SimpleComponent />);
1069 - expect(result.props.children).toBe(0);
1070 -
1071 - const callback = jest.fn(function() {
1072 - expect(this).toBe(instance);
1073 - });
1074 -
1075 - // No longer a public API, but we can test that it works internally by
1076 - // reaching into the updater.
1077 - shallowRenderer._updater.enqueueReplaceState(
1078 - instance,
1079 - {counter: 1},
1080 - callback,
1081 - );
1082 -
1083 - const updated = shallowRenderer.getRenderOutput();
1084 - expect(updated.props.children).toBe(1);
1085 - expect(callback).toHaveBeenCalled();
1086 - });
1087 -
1088 - it('can forceUpdate with a callback', () => {
1089 - let instance;
1090 -
1091 - class SimpleComponent extends React.Component {
1092 - state = {
1093 - counter: 0,
1094 - };
1095 - render() {
1096 - instance = this;
1097 - return <p>{this.state.counter}</p>;
1098 - }
1099 - }
1100 -
1101 - const shallowRenderer = createRenderer();
1102 - const result = shallowRenderer.render(<SimpleComponent />);
1103 - expect(result.props.children).toBe(0);
1104 -
1105 - const callback = jest.fn(function() {
1106 - expect(this).toBe(instance);
1107 - });
1108 -
1109 - instance.forceUpdate(callback);
1110 -
1111 - const updated = shallowRenderer.getRenderOutput();
1112 - expect(updated.props.children).toBe(0);
1113 - expect(callback).toHaveBeenCalled();
1114 - });
1115 -
1116 - it('can pass context when shallowly rendering', () => {
1117 - class SimpleComponent extends React.Component {
1118 - static contextTypes = {
1119 - name: PropTypes.string,
1120 - };
1121 -
1122 - render() {
1123 - return <div>{this.context.name}</div>;
1124 - }
1125 - }
1126 -
1127 - const shallowRenderer = createRenderer();
1128 - const result = shallowRenderer.render(<SimpleComponent />, {
1129 - name: 'foo',
1130 - });
1131 - expect(result).toEqual(<div>foo</div>);
1132 - });
1133 -
1134 - it('should track context across updates', () => {
1135 - class SimpleComponent extends React.Component {
1136 - static contextTypes = {
1137 - foo: PropTypes.string,
1138 - };
1139 -
1140 - state = {
1141 - bar: 'bar',
1142 - };
1143 -
1144 - render() {
1145 - return <div>{`${this.context.foo}:${this.state.bar}`}</div>;
1146 - }
1147 - }
1148 -
1149 - const shallowRenderer = createRenderer();
1150 - let result = shallowRenderer.render(<SimpleComponent />, {
1151 - foo: 'foo',
1152 - });
1153 - expect(result).toEqual(<div>foo:bar</div>);
1154 -
1155 - const instance = shallowRenderer.getMountedInstance();
1156 - instance.setState({bar: 'baz'});
1157 -
1158 - result = shallowRenderer.getRenderOutput();
1159 - expect(result).toEqual(<div>foo:baz</div>);
1160 - });
1161 -
1162 - it('should filter context by contextTypes', () => {
1163 - class SimpleComponent extends React.Component {
1164 - static contextTypes = {
1165 - foo: PropTypes.string,
1166 - };
1167 - render() {
1168 - return <div>{`${this.context.foo}:${this.context.bar}`}</div>;
1169 - }
1170 - }
1171 -
1172 - const shallowRenderer = createRenderer();
1173 - const result = shallowRenderer.render(<SimpleComponent />, {
1174 - foo: 'foo',
1175 - bar: 'bar',
1176 - });
1177 - expect(result).toEqual(<div>foo:undefined</div>);
1178 - });
1179 -
1180 - it('can fail context when shallowly rendering', () => {
1181 - class SimpleComponent extends React.Component {
1182 - static contextTypes = {
1183 - name: PropTypes.string.isRequired,
1184 - };
1185 -
1186 - render() {
1187 - return <div>{this.context.name}</div>;
1188 - }
1189 - }
1190 -
1191 - const shallowRenderer = createRenderer();
1192 - expect(() => shallowRenderer.render(<SimpleComponent />)).toErrorDev(
1193 - 'Warning: Failed context type: The context `name` is marked as ' +
1194 - 'required in `SimpleComponent`, but its value is `undefined`.\n' +
1195 - ' in SimpleComponent (at **)',
1196 - );
1197 - });
1198 -
1199 - it('should warn about propTypes (but only once)', () => {
1200 - class SimpleComponent extends React.Component {
1201 - render() {
1202 - return React.createElement('div', null, this.props.name);
1203 - }
1204 - }
1205 -
1206 - SimpleComponent.propTypes = {
1207 - name: PropTypes.string.isRequired,
1208 - };
1209 -
1210 - const shallowRenderer = createRenderer();
1211 - expect(() =>
1212 - shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})),
1213 - ).toErrorDev(
1214 - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
1215 - 'supplied to `SimpleComponent`, expected `string`.\n' +
1216 - ' in SimpleComponent',
1217 - );
1218 - });
1219 -
1220 - it('should enable rendering of cloned element', () => {
1221 - class SimpleComponent extends React.Component {
1222 - constructor(props) {
1223 - super(props);
1224 -
1225 - this.state = {
1226 - bar: 'bar',
1227 - };
1228 - }
1229 -
1230 - render() {
1231 - return <div>{`${this.props.foo}:${this.state.bar}`}</div>;
1232 - }
1233 - }
1234 -
1235 - const shallowRenderer = createRenderer();
1236 - const el = <SimpleComponent foo="foo" />;
1237 - let result = shallowRenderer.render(el);
1238 - expect(result).toEqual(<div>foo:bar</div>);
1239 -
1240 - const cloned = React.cloneElement(el, {foo: 'baz'});
1241 - result = shallowRenderer.render(cloned);
1242 - expect(result).toEqual(<div>baz:bar</div>);
1243 - });
1244 -
1245 - it('this.state should be updated on setState callback inside componentWillMount', () => {
1246 - let stateSuccessfullyUpdated = false;
1247 -
1248 - class Component extends React.Component {
1249 - constructor(props, context) {
1250 - super(props, context);
1251 - this.state = {
1252 - hasUpdatedState: false,
1253 - };
1254 - }
1255 -
1256 - UNSAFE_componentWillMount() {
1257 - this.setState(
1258 - {hasUpdatedState: true},
1259 - () => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
1260 - );
1261 - }
1262 -
1263 - render() {
1264 - return <div>{this.props.children}</div>;
1265 - }
1266 - }
1267 -
1268 - const shallowRenderer = createRenderer();
1269 - shallowRenderer.render(<Component />);
1270 - expect(stateSuccessfullyUpdated).toBe(true);
1271 - });
1272 -
1273 - it('should handle multiple callbacks', () => {
1274 - const mockFn = jest.fn();
1275 - const shallowRenderer = createRenderer();
1276 -
1277 - class Component extends React.Component {
1278 - constructor(props, context) {
1279 - super(props, context);
1280 - this.state = {
1281 - foo: 'foo',
1282 - };
1283 - }
1284 -
1285 - UNSAFE_componentWillMount() {
1286 - this.setState({foo: 'bar'}, () => mockFn());
1287 - this.setState({foo: 'foobar'}, () => mockFn());
1288 - }
1289 -
1290 - render() {
1291 - return <div>{this.state.foo}</div>;
1292 - }
1293 - }
1294 -
1295 - shallowRenderer.render(<Component />);
1296 -
1297 - expect(mockFn).toHaveBeenCalledTimes(2);
1298 -
1299 - // Ensure the callback queue is cleared after the callbacks are invoked
1300 - const mountedInstance = shallowRenderer.getMountedInstance();
1301 - mountedInstance.setState({foo: 'bar'}, () => mockFn());
1302 - expect(mockFn).toHaveBeenCalledTimes(3);
1303 - });
1304 -
1305 - it('should call the setState callback even if shouldComponentUpdate = false', done => {
1306 - const mockFn = jest.fn().mockReturnValue(false);
1307 -
1308 - class Component extends React.Component {
1309 - constructor(props, context) {
1310 - super(props, context);
1311 - this.state = {
1312 - hasUpdatedState: false,
1313 - };
1314 - }
1315 -
1316 - shouldComponentUpdate() {
1317 - return mockFn();
1318 - }
1319 -
1320 - render() {
1321 - return <div>{this.state.hasUpdatedState}</div>;
1322 - }
1323 - }
1324 -
1325 - const shallowRenderer = createRenderer();
1326 - shallowRenderer.render(<Component />);
1327 -
1328 - const mountedInstance = shallowRenderer.getMountedInstance();
1329 - mountedInstance.setState({hasUpdatedState: true}, () => {
1330 - expect(mockFn).toBeCalled();
1331 - expect(mountedInstance.state.hasUpdatedState).toBe(true);
1332 - done();
1333 - });
1334 - });
1335 -
1336 - it('throws usefully when rendering badly-typed elements', () => {
1337 - const shallowRenderer = createRenderer();
1338 -
1339 - const renderAndVerifyWarningAndError = (Component, typeString) => {
1340 - expect(() => {
1341 - expect(() => shallowRenderer.render(<Component />)).toErrorDev(
1342 - 'React.createElement: type is invalid -- expected a string ' +
1343 - '(for built-in components) or a class/function (for composite components) ' +
1344 - `but got: ${typeString}.`,
1345 - );
1346 - }).toThrowError(
1347 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
1348 - `components, but the provided element type was \`${typeString}\`.`,
1349 - );
1350 - };
1351 -
1352 - renderAndVerifyWarningAndError(undefined, 'undefined');
1353 - renderAndVerifyWarningAndError(null, 'null');
1354 - renderAndVerifyWarningAndError([], 'array');
1355 - renderAndVerifyWarningAndError({}, 'object');
1356 - });
1357 -
1358 - it('should have initial state of null if not defined', () => {
1359 - class SomeComponent extends React.Component {
1360 - render() {
1361 - return <span />;
1362 - }
1363 - }
1364 -
1365 - const shallowRenderer = createRenderer();
1366 - shallowRenderer.render(<SomeComponent />);
1367 -
1368 - expect(shallowRenderer.getMountedInstance().state).toBeNull();
1369 - });
1370 -
1371 - it('should invoke both deprecated and new lifecycles if both are present', () => {
1372 - const log = [];
1373 -
1374 - class Component extends React.Component {
1375 - componentWillMount() {
1376 - log.push('componentWillMount');
1377 - }
1378 - componentWillReceiveProps() {
1379 - log.push('componentWillReceiveProps');
1380 - }
1381 - componentWillUpdate() {
1382 - log.push('componentWillUpdate');
1383 - }
1384 - UNSAFE_componentWillMount() {
1385 - log.push('UNSAFE_componentWillMount');
1386 - }
1387 - UNSAFE_componentWillReceiveProps() {
1388 - log.push('UNSAFE_componentWillReceiveProps');
1389 - }
1390 - UNSAFE_componentWillUpdate() {
1391 - log.push('UNSAFE_componentWillUpdate');
1392 - }
1393 - render() {
1394 - return null;
1395 - }
1396 - }
1397 -
1398 - const shallowRenderer = createRenderer();
1399 - shallowRenderer.render(<Component foo="bar" />);
1400 - expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']);
1401 -
1402 - log.length = 0;
1403 -
1404 - shallowRenderer.render(<Component foo="baz" />);
1405 - expect(log).toEqual([
1406 - 'componentWillReceiveProps',
1407 - 'UNSAFE_componentWillReceiveProps',
1408 - 'componentWillUpdate',
1409 - 'UNSAFE_componentWillUpdate',
1410 - ]);
1411 - });
1412 -
1413 - it('should stop the update when setState returns null or undefined', () => {
1414 - const log = [];
1415 - let instance;
1416 - class Component extends React.Component {
1417 - constructor(props) {
1418 - super(props);
1419 - this.state = {
1420 - count: 0,
1421 - };
1422 - }
1423 - render() {
1424 - log.push('render');
1425 - instance = this;
1426 - return null;
1427 - }
1428 - }
1429 - const shallowRenderer = createRenderer();
1430 - shallowRenderer.render(<Component />);
1431 - log.length = 0;
1432 - instance.setState(() => null);
1433 - instance.setState(() => undefined);
1434 - instance.setState(null);
1435 - instance.setState(undefined);
1436 - expect(log).toEqual([]);
1437 - instance.setState(state => ({count: state.count + 1}));
1438 - expect(log).toEqual(['render']);
1439 - });
1440 -
1441 - it('should not get this in a function component', () => {
1442 - const logs = [];
1443 - function Foo() {
1444 - logs.push(this);
1445 - return <div>foo</div>;
1446 - }
1447 - const shallowRenderer = createRenderer();
1448 - shallowRenderer.render(<Foo foo="bar" />);
1449 - expect(logs).toEqual([undefined]);
1450 - });
1451 -
1452 - it('should handle memo', () => {
1453 - function Foo() {
1454 - return <div>foo</div>;
1455 - }
1456 - const MemoFoo = React.memo(Foo);
1457 - const shallowRenderer = createRenderer();
1458 - shallowRenderer.render(<MemoFoo />);
1459 - });
1460 -
1461 - it('should enable React.memo to prevent a re-render', () => {
1462 - const logs = [];
1463 - const Foo = React.memo(({count}) => {
1464 - logs.push(`Foo: ${count}`);
1465 - return <div>{count}</div>;
1466 - });
1467 - const Bar = React.memo(({count}) => {
1468 - logs.push(`Bar: ${count}`);
1469 - return <div>{count}</div>;
1470 - });
1471 - const shallowRenderer = createRenderer();
1472 - shallowRenderer.render(<Foo count={1} />);
1473 - expect(logs).toEqual(['Foo: 1']);
1474 - logs.length = 0;
1475 - // Rendering the same element with the same props should be prevented
1476 - shallowRenderer.render(<Foo count={1} />);
1477 - expect(logs).toEqual([]);
1478 - // A different element with the same props should cause a re-render
1479 - shallowRenderer.render(<Bar count={1} />);
1480 - expect(logs).toEqual(['Bar: 1']);
1481 - });
1482 -
1483 - it('should respect a custom comparison function with React.memo', () => {
1484 - let renderCount = 0;
1485 - function areEqual(props, nextProps) {
1486 - return props.foo === nextProps.foo;
1487 - }
1488 - const Foo = React.memo(({foo, bar}) => {
1489 - renderCount++;
1490 - return (
1491 - <div>
1492 - {foo} {bar}
1493 - </div>
1494 - );
1495 - }, areEqual);
1496 -
1497 - const shallowRenderer = createRenderer();
1498 - shallowRenderer.render(<Foo foo={1} bar={1} />);
1499 - expect(renderCount).toBe(1);
1500 - // Change a prop that the comparison function ignores
1501 - shallowRenderer.render(<Foo foo={1} bar={2} />);
1502 - expect(renderCount).toBe(1);
1503 - shallowRenderer.render(<Foo foo={2} bar={2} />);
1504 - expect(renderCount).toBe(2);
1505 - });
1506 -
1507 - it('should not call the comparison function with React.memo on the initial render', () => {
1508 - const areEqual = jest.fn(() => false);
1509 - const SomeComponent = React.memo(({foo}) => {
1510 - return <div>{foo}</div>;
1511 - }, areEqual);
1512 - const shallowRenderer = createRenderer();
1513 - shallowRenderer.render(<SomeComponent foo={1} />);
1514 - expect(areEqual).not.toHaveBeenCalled();
1515 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>{1}</div>);
1516 - });
1517 -
1518 - it('should handle memo(forwardRef())', () => {
1519 - const testRef = React.createRef();
1520 - const SomeComponent = React.forwardRef((props, ref) => {
1521 - expect(ref).toEqual(testRef);
1522 - return (
1523 - <div>
1524 - <span className="child1" />
1525 - <span className="child2" />
1526 - </div>
1527 - );
1528 - });
1529 -
1530 - const SomeMemoComponent = React.memo(SomeComponent);
1531 -
1532 - const shallowRenderer = createRenderer();
1533 - const result = shallowRenderer.render(<SomeMemoComponent ref={testRef} />);
1534 -
1535 - expect(result.type).toBe('div');
1536 - expect(result.props.children).toEqual([
1537 - <span className="child1" />,
1538 - <span className="child2" />,
1539 - ]);
1540 - });
1541 -
1542 - it('should warn for forwardRef(memo())', () => {
1543 - const testRef = React.createRef();
1544 - const SomeMemoComponent = React.memo(({foo}) => {
1545 - return <div>{foo}</div>;
1546 - });
1547 - const shallowRenderer = createRenderer();
1548 - expect(() => {
1549 - expect(() => {
1550 - const SomeComponent = React.forwardRef(SomeMemoComponent);
1551 - shallowRenderer.render(<SomeComponent ref={testRef} />);
1552 - }).toErrorDev(
1553 - 'Warning: forwardRef requires a render function but received ' +
1554 - 'a `memo` component. Instead of forwardRef(memo(...)), use ' +
1555 - 'memo(forwardRef(...))',
1556 - {withoutStack: true},
1557 - );
1558 - }).toThrowError(
1559 - 'forwardRef requires a render function but was given object.',
1560 - );
1561 - });
1562 -
1563 - it('should let you change type', () => {
1564 - function Foo({prop}) {
1565 - return <div>Foo {prop}</div>;
1566 - }
1567 - function Bar({prop}) {
1568 - return <div>Bar {prop}</div>;
1569 - }
1570 -
1571 - const shallowRenderer = createRenderer();
1572 - shallowRenderer.render(<Foo prop="foo1" />);
1573 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Foo {'foo1'}</div>);
1574 - shallowRenderer.render(<Foo prop="foo2" />);
1575 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Foo {'foo2'}</div>);
1576 - shallowRenderer.render(<Bar prop="bar1" />);
1577 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Bar {'bar1'}</div>);
1578 - shallowRenderer.render(<Bar prop="bar2" />);
1579 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Bar {'bar2'}</div>);
1580 - });
1581 -
1582 - it('should let you change class type', () => {
1583 - class Foo extends React.Component {
1584 - render() {
1585 - return <div>Foo {this.props.prop}</div>;
1586 - }
1587 - }
1588 - class Bar extends React.Component {
1589 - render() {
1590 - return <div>Bar {this.props.prop}</div>;
1591 - }
1592 - }
1593 -
1594 - const shallowRenderer = createRenderer();
1595 - shallowRenderer.render(<Foo prop="foo1" />);
1596 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Foo {'foo1'}</div>);
1597 - shallowRenderer.render(<Foo prop="foo2" />);
1598 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Foo {'foo2'}</div>);
1599 - shallowRenderer.render(<Bar prop="bar1" />);
1600 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Bar {'bar1'}</div>);
1601 - shallowRenderer.render(<Bar prop="bar2" />);
1602 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>Bar {'bar2'}</div>);
1603 - });
1604 -});
packages/react-test-renderer/src/__tests__/ReactShallowRendererHooks-test.js deleted
-487
@@ -1,487 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -import * as React from 'react';
14 -import ReactShallowRenderer from 'react-test-renderer/shallow';
15 -
16 -const createRenderer = ReactShallowRenderer.createRenderer;
17 -
18 -describe('ReactShallowRenderer with hooks', () => {
19 - it('should work with useState', () => {
20 - function SomeComponent({defaultName}) {
21 - const [name] = React.useState(defaultName);
22 -
23 - return (
24 - <div>
25 - <p>
26 - Your name is: <span>{name}</span>
27 - </p>
28 - </div>
29 - );
30 - }
31 -
32 - const shallowRenderer = createRenderer();
33 - let result = shallowRenderer.render(
34 - <SomeComponent defaultName={'Dominic'} />,
35 - );
36 -
37 - expect(result).toEqual(
38 - <div>
39 - <p>
40 - Your name is: <span>Dominic</span>
41 - </p>
42 - </div>,
43 - );
44 -
45 - result = shallowRenderer.render(
46 - <SomeComponent defaultName={'Should not use this name'} />,
47 - );
48 -
49 - expect(result).toEqual(
50 - <div>
51 - <p>
52 - Your name is: <span>Dominic</span>
53 - </p>
54 - </div>,
55 - );
56 - });
57 -
58 - it('should work with updating a value from useState', () => {
59 - function SomeComponent({defaultName}) {
60 - const [name, updateName] = React.useState(defaultName);
61 -
62 - if (name !== 'Dan') {
63 - updateName('Dan');
64 - }
65 -
66 - return (
67 - <div>
68 - <p>
69 - Your name is: <span>{name}</span>
70 - </p>
71 - </div>
72 - );
73 - }
74 -
75 - const shallowRenderer = createRenderer();
76 - const result = shallowRenderer.render(
77 - <SomeComponent defaultName={'Dominic'} />,
78 - );
79 -
80 - expect(result).toEqual(
81 - <div>
82 - <p>
83 - Your name is: <span>Dan</span>
84 - </p>
85 - </div>,
86 - );
87 - });
88 -
89 - it('should work with updating a derived value from useState', () => {
90 - let _updateName;
91 -
92 - function SomeComponent({defaultName}) {
93 - const [name, updateName] = React.useState(defaultName);
94 - const [prevName, updatePrevName] = React.useState(defaultName);
95 - const [letter, updateLetter] = React.useState(name[0]);
96 -
97 - _updateName = updateName;
98 -
99 - if (name !== prevName) {
100 - updatePrevName(name);
101 - updateLetter(name[0]);
102 - }
103 -
104 - return (
105 - <div>
106 - <p>
107 - Your name is: <span>{name + ' (' + letter + ')'}</span>
108 - </p>
109 - </div>
110 - );
111 - }
112 -
113 - const shallowRenderer = createRenderer();
114 - let result = shallowRenderer.render(
115 - <SomeComponent defaultName={'Sophie'} />,
116 - );
117 - expect(result).toEqual(
118 - <div>
119 - <p>
120 - Your name is: <span>Sophie (S)</span>
121 - </p>
122 - </div>,
123 - );
124 -
125 - result = shallowRenderer.render(<SomeComponent defaultName={'Dan'} />);
126 - expect(result).toEqual(
127 - <div>
128 - <p>
129 - Your name is: <span>Sophie (S)</span>
130 - </p>
131 - </div>,
132 - );
133 -
134 - _updateName('Dan');
135 - expect(shallowRenderer.getRenderOutput()).toEqual(
136 - <div>
137 - <p>
138 - Your name is: <span>Dan (D)</span>
139 - </p>
140 - </div>,
141 - );
142 - });
143 -
144 - it('should work with useReducer', () => {
145 - function reducer(state, action) {
146 - switch (action.type) {
147 - case 'increment':
148 - return {count: state.count + 1};
149 - case 'decrement':
150 - return {count: state.count - 1};
151 - }
152 - }
153 -
154 - function SomeComponent(props) {
155 - const [state] = React.useReducer(reducer, props, p => ({
156 - count: p.initialCount,
157 - }));
158 -
159 - return (
160 - <div>
161 - <p>
162 - The counter is at: <span>{state.count.toString()}</span>
163 - </p>
164 - </div>
165 - );
166 - }
167 -
168 - const shallowRenderer = createRenderer();
169 - let result = shallowRenderer.render(<SomeComponent initialCount={0} />);
170 -
171 - expect(result).toEqual(
172 - <div>
173 - <p>
174 - The counter is at: <span>0</span>
175 - </p>
176 - </div>,
177 - );
178 -
179 - result = shallowRenderer.render(<SomeComponent initialCount={10} />);
180 -
181 - expect(result).toEqual(
182 - <div>
183 - <p>
184 - The counter is at: <span>0</span>
185 - </p>
186 - </div>,
187 - );
188 - });
189 -
190 - it('should work with a dispatched state change for a useReducer', () => {
191 - function reducer(state, action) {
192 - switch (action.type) {
193 - case 'increment':
194 - return {count: state.count + 1};
195 - case 'decrement':
196 - return {count: state.count - 1};
197 - }
198 - }
199 -
200 - function SomeComponent(props) {
201 - const [state, dispatch] = React.useReducer(reducer, props, p => ({
202 - count: p.initialCount,
203 - }));
204 -
205 - if (state.count === 0) {
206 - dispatch({type: 'increment'});
207 - }
208 -
209 - return (
210 - <div>
211 - <p>
212 - The counter is at: <span>{state.count.toString()}</span>
213 - </p>
214 - </div>
215 - );
216 - }
217 -
218 - const shallowRenderer = createRenderer();
219 - const result = shallowRenderer.render(<SomeComponent initialCount={0} />);
220 -
221 - expect(result).toEqual(
222 - <div>
223 - <p>
224 - The counter is at: <span>1</span>
225 - </p>
226 - </div>,
227 - );
228 - });
229 -
230 - it('should not trigger effects', () => {
231 - const effectsCalled = [];
232 -
233 - function SomeComponent({defaultName}) {
234 - React.useEffect(() => {
235 - effectsCalled.push('useEffect');
236 - });
237 -
238 - React.useLayoutEffect(() => {
239 - effectsCalled.push('useEffect');
240 - });
241 -
242 - return <div>Hello world</div>;
243 - }
244 -
245 - const shallowRenderer = createRenderer();
246 - shallowRenderer.render(<SomeComponent />);
247 -
248 - expect(effectsCalled).toEqual([]);
249 - });
250 -
251 - it('should work with useRef', () => {
252 - function SomeComponent() {
253 - const randomNumberRef = React.useRef({number: Math.random()});
254 -
255 - return (
256 - <div>
257 - <p>The random number is: {randomNumberRef.current.number}</p>
258 - </div>
259 - );
260 - }
261 -
262 - const shallowRenderer = createRenderer();
263 - const firstResult = shallowRenderer.render(<SomeComponent />);
264 - const secondResult = shallowRenderer.render(<SomeComponent />);
265 -
266 - expect(firstResult).toEqual(secondResult);
267 - });
268 -
269 - it('should work with useMemo', () => {
270 - function SomeComponent() {
271 - const randomNumber = React.useMemo(() => {
272 - return {number: Math.random()};
273 - }, []);
274 -
275 - return (
276 - <div>
277 - <p>The random number is: {randomNumber.number}</p>
278 - </div>
279 - );
280 - }
281 -
282 - const shallowRenderer = createRenderer();
283 - const firstResult = shallowRenderer.render(<SomeComponent />);
284 - const secondResult = shallowRenderer.render(<SomeComponent />);
285 -
286 - expect(firstResult).toEqual(secondResult);
287 - });
288 -
289 - it('should work with useContext', () => {
290 - const SomeContext = React.createContext('default');
291 -
292 - function SomeComponent() {
293 - const value = React.useContext(SomeContext);
294 -
295 - return (
296 - <div>
297 - <p>{value}</p>
298 - </div>
299 - );
300 - }
301 -
302 - const shallowRenderer = createRenderer();
303 - const result = shallowRenderer.render(<SomeComponent />);
304 -
305 - expect(result).toEqual(
306 - <div>
307 - <p>default</p>
308 - </div>,
309 - );
310 - });
311 -
312 - it('should not leak state when component type changes', () => {
313 - function SomeComponent({defaultName}) {
314 - const [name] = React.useState(defaultName);
315 -
316 - return (
317 - <div>
318 - <p>
319 - Your name is: <span>{name}</span>
320 - </p>
321 - </div>
322 - );
323 - }
324 -
325 - function SomeOtherComponent({defaultName}) {
326 - const [name] = React.useState(defaultName);
327 -
328 - return (
329 - <div>
330 - <p>
331 - Your name is: <span>{name}</span>
332 - </p>
333 - </div>
334 - );
335 - }
336 -
337 - const shallowRenderer = createRenderer();
338 - let result = shallowRenderer.render(
339 - <SomeComponent defaultName={'Dominic'} />,
340 - );
341 - expect(result).toEqual(
342 - <div>
343 - <p>
344 - Your name is: <span>Dominic</span>
345 - </p>
346 - </div>,
347 - );
348 -
349 - result = shallowRenderer.render(<SomeOtherComponent defaultName={'Dan'} />);
350 - expect(result).toEqual(
351 - <div>
352 - <p>
353 - Your name is: <span>Dan</span>
354 - </p>
355 - </div>,
356 - );
357 - });
358 -
359 - it('should work with forwardRef + any hook', () => {
360 - const SomeComponent = React.forwardRef((props, ref) => {
361 - const randomNumberRef = React.useRef({number: Math.random()});
362 -
363 - return (
364 - <div ref={ref}>
365 - <p>The random number is: {randomNumberRef.current.number}</p>
366 - </div>
367 - );
368 - });
369 -
370 - const shallowRenderer = createRenderer();
371 - const firstResult = shallowRenderer.render(<SomeComponent />);
372 - const secondResult = shallowRenderer.render(<SomeComponent />);
373 -
374 - expect(firstResult).toEqual(secondResult);
375 - });
376 -
377 - it('should update a value from useState outside the render', () => {
378 - let _dispatch;
379 -
380 - function SomeComponent({defaultName}) {
381 - const [count, dispatch] = React.useReducer(
382 - (s, a) => (a === 'inc' ? s + 1 : s),
383 - 0,
384 - );
385 - const [name, updateName] = React.useState(defaultName);
386 - _dispatch = () => dispatch('inc');
387 -
388 - return (
389 - <div onClick={() => updateName('Dan')}>
390 - <p>
391 - Your name is: <span>{name}</span> ({count})
392 - </p>
393 - </div>
394 - );
395 - }
396 -
397 - const shallowRenderer = createRenderer();
398 - const element = <SomeComponent defaultName={'Dominic'} />;
399 - const result = shallowRenderer.render(element);
400 - expect(result.props.children).toEqual(
401 - <p>
402 - Your name is: <span>Dominic</span> ({0})
403 - </p>,
404 - );
405 -
406 - result.props.onClick();
407 - let updated = shallowRenderer.render(element);
408 - expect(updated.props.children).toEqual(
409 - <p>
410 - Your name is: <span>Dan</span> ({0})
411 - </p>,
412 - );
413 -
414 - _dispatch('foo');
415 - updated = shallowRenderer.render(element);
416 - expect(updated.props.children).toEqual(
417 - <p>
418 - Your name is: <span>Dan</span> ({1})
419 - </p>,
420 - );
421 -
422 - _dispatch('inc');
423 - updated = shallowRenderer.render(element);
424 - expect(updated.props.children).toEqual(
425 - <p>
426 - Your name is: <span>Dan</span> ({2})
427 - </p>,
428 - );
429 - });
430 -
431 - it('should ignore a foreign update outside the render', () => {
432 - let _updateCountForFirstRender;
433 -
434 - function SomeComponent() {
435 - const [count, updateCount] = React.useState(0);
436 - if (!_updateCountForFirstRender) {
437 - _updateCountForFirstRender = updateCount;
438 - }
439 - return count;
440 - }
441 -
442 - const shallowRenderer = createRenderer();
443 - const element = <SomeComponent />;
444 - let result = shallowRenderer.render(element);
445 - expect(result).toEqual(0);
446 - _updateCountForFirstRender(1);
447 - result = shallowRenderer.render(element);
448 - expect(result).toEqual(1);
449 -
450 - shallowRenderer.unmount();
451 - result = shallowRenderer.render(element);
452 - expect(result).toEqual(0);
453 - _updateCountForFirstRender(1); // Should be ignored.
454 - result = shallowRenderer.render(element);
455 - expect(result).toEqual(0);
456 - });
457 -
458 - it('should not forget render phase updates', () => {
459 - let _updateCount;
460 -
461 - function SomeComponent() {
462 - const [count, updateCount] = React.useState(0);
463 - _updateCount = updateCount;
464 - if (count < 5) {
465 - updateCount(x => x + 1);
466 - }
467 - return count;
468 - }
469 -
470 - const shallowRenderer = createRenderer();
471 - const element = <SomeComponent />;
472 - let result = shallowRenderer.render(element);
473 - expect(result).toEqual(5);
474 -
475 - _updateCount(10);
476 - result = shallowRenderer.render(element);
477 - expect(result).toEqual(10);
478 -
479 - _updateCount(x => x + 1);
480 - result = shallowRenderer.render(element);
481 - expect(result).toEqual(11);
482 -
483 - _updateCount(x => x - 10);
484 - result = shallowRenderer.render(element);
485 - expect(result).toEqual(5);
486 - });
487 -});
packages/react-test-renderer/src/__tests__/ReactShallowRendererMemo-test.js deleted
-1514
@@ -1,1514 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - * @jest-environment node
9 - */
10 -
11 -'use strict';
12 -
13 -import * as PropTypes from 'prop-types';
14 -import * as React from 'react';
15 -import ReactShallowRenderer from 'react-test-renderer/shallow';
16 -
17 -const createRenderer = ReactShallowRenderer.createRenderer;
18 -
19 -describe('ReactShallowRendererMemo', () => {
20 - it('should call all of the legacy lifecycle hooks', () => {
21 - const logs = [];
22 - const logger = message => () => logs.push(message) || true;
23 -
24 - const SomeComponent = React.memo(
25 - class SomeComponent extends React.Component {
26 - UNSAFE_componentWillMount = logger('componentWillMount');
27 - componentDidMount = logger('componentDidMount');
28 - UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps');
29 - shouldComponentUpdate = logger('shouldComponentUpdate');
30 - UNSAFE_componentWillUpdate = logger('componentWillUpdate');
31 - componentDidUpdate = logger('componentDidUpdate');
32 - componentWillUnmount = logger('componentWillUnmount');
33 - render() {
34 - return <div />;
35 - }
36 - },
37 - );
38 -
39 - const shallowRenderer = createRenderer();
40 - shallowRenderer.render(<SomeComponent foo={1} />);
41 -
42 - // Calling cDU might lead to problems with host component references.
43 - // Since our components aren't really mounted, refs won't be available.
44 - expect(logs).toEqual(['componentWillMount']);
45 -
46 - logs.splice(0);
47 -
48 - const instance = shallowRenderer.getMountedInstance();
49 - instance.setState({});
50 -
51 - expect(logs).toEqual(['shouldComponentUpdate', 'componentWillUpdate']);
52 -
53 - logs.splice(0);
54 -
55 - shallowRenderer.render(<SomeComponent foo={2} />);
56 -
57 - // The previous shallow renderer did not trigger cDU for props changes.
58 - expect(logs).toEqual([
59 - 'componentWillReceiveProps',
60 - 'shouldComponentUpdate',
61 - 'componentWillUpdate',
62 - ]);
63 - });
64 -
65 - it('should call all of the new lifecycle hooks', () => {
66 - const logs = [];
67 - const logger = message => () => logs.push(message) || true;
68 -
69 - const SomeComponent = React.memo(
70 - class SomeComponent extends React.Component {
71 - state = {};
72 - static getDerivedStateFromProps = logger('getDerivedStateFromProps');
73 - componentDidMount = logger('componentDidMount');
74 - shouldComponentUpdate = logger('shouldComponentUpdate');
75 - componentDidUpdate = logger('componentDidUpdate');
76 - componentWillUnmount = logger('componentWillUnmount');
77 - render() {
78 - return <div />;
79 - }
80 - },
81 - );
82 -
83 - const shallowRenderer = createRenderer();
84 - shallowRenderer.render(<SomeComponent foo={1} />);
85 -
86 - // Calling cDU might lead to problems with host component references.
87 - // Since our components aren't really mounted, refs won't be available.
88 - expect(logs).toEqual(['getDerivedStateFromProps']);
89 -
90 - logs.splice(0);
91 -
92 - const instance = shallowRenderer.getMountedInstance();
93 - instance.setState({});
94 -
95 - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']);
96 -
97 - logs.splice(0);
98 -
99 - shallowRenderer.render(<SomeComponent foo={2} />);
100 -
101 - // The previous shallow renderer did not trigger cDU for props changes.
102 - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']);
103 - });
104 -
105 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
106 - const Component = React.memo(
107 - class Component extends React.Component {
108 - state = {};
109 - static getDerivedStateFromProps() {
110 - return null;
111 - }
112 - componentWillMount() {
113 - throw Error('unexpected');
114 - }
115 - componentWillReceiveProps() {
116 - throw Error('unexpected');
117 - }
118 - componentWillUpdate() {
119 - throw Error('unexpected');
120 - }
121 - render() {
122 - return null;
123 - }
124 - },
125 - );
126 -
127 - const shallowRenderer = createRenderer();
128 - shallowRenderer.render(<Component />);
129 - });
130 -
131 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
132 - const Component = React.memo(
133 - class Component extends React.Component {
134 - getSnapshotBeforeUpdate() {
135 - return null;
136 - }
137 - componentWillMount() {
138 - throw Error('unexpected');
139 - }
140 - componentWillReceiveProps() {
141 - throw Error('unexpected');
142 - }
143 - componentWillUpdate() {
144 - throw Error('unexpected');
145 - }
146 - render() {
147 - return null;
148 - }
149 - },
150 - );
151 -
152 - const shallowRenderer = createRenderer();
153 - shallowRenderer.render(<Component value={1} />);
154 - shallowRenderer.render(<Component value={2} />);
155 - });
156 -
157 - it('should not call getSnapshotBeforeUpdate or componentDidUpdate when updating since refs wont exist', () => {
158 - const Component = React.memo(
159 - class Component extends React.Component {
160 - getSnapshotBeforeUpdate() {
161 - throw Error('unexpected');
162 - }
163 - componentDidUpdate() {
164 - throw Error('unexpected');
165 - }
166 - render() {
167 - return null;
168 - }
169 - },
170 - );
171 -
172 - const shallowRenderer = createRenderer();
173 - shallowRenderer.render(<Component value={1} />);
174 - shallowRenderer.render(<Component value={2} />);
175 - });
176 -
177 - it('should only render 1 level deep', () => {
178 - const Parent = React.memo(function Parent() {
179 - return (
180 - <div>
181 - <Child />
182 - </div>
183 - );
184 - });
185 -
186 - function Child() {
187 - throw Error('This component should not render');
188 - }
189 -
190 - const shallowRenderer = createRenderer();
191 - shallowRenderer.render(React.createElement(Parent));
192 - });
193 -
194 - it('should have shallow rendering', () => {
195 - const SomeComponent = React.memo(
196 - class SomeComponent extends React.Component {
197 - render() {
198 - return (
199 - <div>
200 - <span className="child1" />
201 - <span className="child2" />
202 - </div>
203 - );
204 - }
205 - },
206 - );
207 -
208 - const shallowRenderer = createRenderer();
209 - const result = shallowRenderer.render(<SomeComponent />);
210 -
211 - expect(result.type).toBe('div');
212 - expect(result.props.children).toEqual([
213 - <span className="child1" />,
214 - <span className="child2" />,
215 - ]);
216 - });
217 -
218 - it('should handle Profiler', () => {
219 - const SomeComponent = React.memo(
220 - class SomeComponent extends React.Component {
221 - render() {
222 - return (
223 - <React.Profiler id="test" onRender={jest.fn()}>
224 - <div>
225 - <span className="child1" />
226 - <span className="child2" />
227 - </div>
228 - </React.Profiler>
229 - );
230 - }
231 - },
232 - );
233 -
234 - const shallowRenderer = createRenderer();
235 - const result = shallowRenderer.render(<SomeComponent />);
236 -
237 - expect(result.type).toBe(React.Profiler);
238 - expect(result.props.children).toEqual(
239 - <div>
240 - <span className="child1" />
241 - <span className="child2" />
242 - </div>,
243 - );
244 - });
245 -
246 - it('should enable shouldComponentUpdate to prevent a re-render', () => {
247 - let renderCounter = 0;
248 - const SimpleComponent = React.memo(
249 - class SimpleComponent extends React.Component {
250 - state = {update: false};
251 - shouldComponentUpdate(nextProps, nextState) {
252 - return this.state.update !== nextState.update;
253 - }
254 - render() {
255 - renderCounter++;
256 - return <div>{`${renderCounter}`}</div>;
257 - }
258 - },
259 - );
260 -
261 - const shallowRenderer = createRenderer();
262 - shallowRenderer.render(<SimpleComponent />);
263 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
264 -
265 - const instance = shallowRenderer.getMountedInstance();
266 - instance.setState({update: false});
267 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
268 -
269 - instance.setState({update: true});
270 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
271 - });
272 -
273 - it('should enable PureComponent to prevent a re-render', () => {
274 - let renderCounter = 0;
275 - const SimpleComponent = React.memo(
276 - class SimpleComponent extends React.PureComponent {
277 - state = {update: false};
278 - render() {
279 - renderCounter++;
280 - return <div>{`${renderCounter}`}</div>;
281 - }
282 - },
283 - );
284 -
285 - const shallowRenderer = createRenderer();
286 - shallowRenderer.render(<SimpleComponent />);
287 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
288 -
289 - const instance = shallowRenderer.getMountedInstance();
290 - instance.setState({update: false});
291 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
292 -
293 - instance.setState({update: true});
294 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
295 - });
296 -
297 - it('should not run shouldComponentUpdate during forced update', () => {
298 - let scuCounter = 0;
299 - const SimpleComponent = React.memo(
300 - class SimpleComponent extends React.Component {
301 - state = {count: 1};
302 - shouldComponentUpdate() {
303 - scuCounter++;
304 - return false;
305 - }
306 - render() {
307 - return <div>{`${this.state.count}`}</div>;
308 - }
309 - },
310 - );
311 -
312 - const shallowRenderer = createRenderer();
313 - shallowRenderer.render(<SimpleComponent />);
314 - expect(scuCounter).toEqual(0);
315 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
316 -
317 - // Force update the initial state. sCU should not fire.
318 - const instance = shallowRenderer.getMountedInstance();
319 - instance.forceUpdate();
320 - expect(scuCounter).toEqual(0);
321 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
322 -
323 - // Setting state updates the instance, but doesn't re-render
324 - // because sCU returned false.
325 - instance.setState(state => ({count: state.count + 1}));
326 - expect(scuCounter).toEqual(1);
327 - expect(instance.state.count).toEqual(2);
328 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
329 -
330 - // A force update updates the render output, but doesn't call sCU.
331 - instance.forceUpdate();
332 - expect(scuCounter).toEqual(1);
333 - expect(instance.state.count).toEqual(2);
334 - expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
335 - });
336 -
337 - it('should rerender when calling forceUpdate', () => {
338 - let renderCounter = 0;
339 - const SimpleComponent = React.memo(
340 - class SimpleComponent extends React.Component {
341 - render() {
342 - renderCounter += 1;
343 - return <div />;
344 - }
345 - },
346 - );
347 -
348 - const shallowRenderer = createRenderer();
349 - shallowRenderer.render(<SimpleComponent />);
350 - expect(renderCounter).toEqual(1);
351 -
352 - const instance = shallowRenderer.getMountedInstance();
353 - instance.forceUpdate();
354 - expect(renderCounter).toEqual(2);
355 - });
356 -
357 - it('should shallow render a function component', () => {
358 - function SomeComponent(props, context) {
359 - return (
360 - <div>
361 - <div>{props.foo}</div>
362 - <div>{context.bar}</div>
363 - <span className="child1" />
364 - <span className="child2" />
365 - </div>
366 - );
367 - }
368 - const SomeMemoComponent = React.memo(SomeComponent);
369 -
370 - SomeComponent.contextTypes = {
371 - bar: PropTypes.string,
372 - };
373 -
374 - const shallowRenderer = createRenderer();
375 - const result = shallowRenderer.render(<SomeMemoComponent foo={'FOO'} />, {
376 - bar: 'BAR',
377 - });
378 -
379 - expect(result.type).toBe('div');
380 - expect(result.props.children).toEqual([
381 - <div>FOO</div>,
382 - <div>BAR</div>,
383 - <span className="child1" />,
384 - <span className="child2" />,
385 - ]);
386 - });
387 -
388 - it('should shallow render a component returning strings directly from render', () => {
389 - const Text = React.memo(({value}) => value);
390 -
391 - const shallowRenderer = createRenderer();
392 - const result = shallowRenderer.render(<Text value="foo" />);
393 - expect(result).toEqual('foo');
394 - });
395 -
396 - it('should shallow render a component returning numbers directly from render', () => {
397 - const Text = React.memo(({value}) => value);
398 -
399 - const shallowRenderer = createRenderer();
400 - const result = shallowRenderer.render(<Text value={10} />);
401 - expect(result).toEqual(10);
402 - });
403 -
404 - it('should shallow render a fragment', () => {
405 - class SomeComponent extends React.Component {
406 - render() {
407 - return <div />;
408 - }
409 - }
410 - class Fragment extends React.Component {
411 - render() {
412 - return [<div key="a" />, <span key="b" />, <SomeComponent />];
413 - }
414 - }
415 - const shallowRenderer = createRenderer();
416 - const result = shallowRenderer.render(<Fragment />);
417 - expect(result).toEqual([
418 - <div key="a" />,
419 - <span key="b" />,
420 - <SomeComponent />,
421 - ]);
422 - });
423 -
424 - it('should shallow render a React.fragment', () => {
425 - class SomeComponent extends React.Component {
426 - render() {
427 - return <div />;
428 - }
429 - }
430 - class Fragment extends React.Component {
431 - render() {
432 - return (
433 - <>
434 - <div />
435 - <span />
436 - <SomeComponent />
437 - </>
438 - );
439 - }
440 - }
441 - const shallowRenderer = createRenderer();
442 - const result = shallowRenderer.render(<Fragment />);
443 - expect(result).toEqual(
444 - <>
445 - <div />
446 - <span />
447 - <SomeComponent />
448 - </>,
449 - );
450 - });
451 -
452 - it('should throw for invalid elements', () => {
453 - class SomeComponent extends React.Component {
454 - render() {
455 - return <div />;
456 - }
457 - }
458 -
459 - const shallowRenderer = createRenderer();
460 - expect(() => shallowRenderer.render(SomeComponent)).toThrowError(
461 - 'ReactShallowRenderer render(): Invalid component element. Instead of ' +
462 - 'passing a component class, make sure to instantiate it by passing it ' +
463 - 'to React.createElement.',
464 - );
465 - expect(() => shallowRenderer.render(<div />)).toThrowError(
466 - 'ReactShallowRenderer render(): Shallow rendering works only with ' +
467 - 'custom components, not primitives (div). Instead of calling ' +
468 - '`.render(el)` and inspecting the rendered output, look at `el.props` ' +
469 - 'directly instead.',
470 - );
471 - });
472 -
473 - it('should have shallow unmounting', () => {
474 - const componentWillUnmount = jest.fn();
475 -
476 - class SomeComponent extends React.Component {
477 - componentWillUnmount = componentWillUnmount;
478 - render() {
479 - return <div />;
480 - }
481 - }
482 -
483 - const shallowRenderer = createRenderer();
484 - shallowRenderer.render(<SomeComponent />);
485 - shallowRenderer.unmount();
486 -
487 - expect(componentWillUnmount).toBeCalled();
488 - });
489 -
490 - it('can shallow render to null', () => {
491 - class SomeComponent extends React.Component {
492 - render() {
493 - return null;
494 - }
495 - }
496 -
497 - const shallowRenderer = createRenderer();
498 - const result = shallowRenderer.render(<SomeComponent />);
499 -
500 - expect(result).toBe(null);
501 - });
502 -
503 - it('can shallow render with a ref', () => {
504 - class SomeComponent extends React.Component {
505 - render() {
506 - return <div ref="hello" />;
507 - }
508 - }
509 -
510 - const shallowRenderer = createRenderer();
511 - // Shouldn't crash.
512 - shallowRenderer.render(<SomeComponent />);
513 - });
514 -
515 - it('lets you update shallowly rendered components', () => {
516 - class SomeComponent extends React.Component {
517 - state = {clicked: false};
518 -
519 - onClick = () => {
520 - this.setState({clicked: true});
521 - };
522 -
523 - render() {
524 - const className = this.state.clicked ? 'was-clicked' : '';
525 -
526 - if (this.props.aNew === 'prop') {
527 - return (
528 - <a href="#" onClick={this.onClick} className={className}>
529 - Test link
530 - </a>
531 - );
532 - } else {
533 - return (
534 - <div>
535 - <span className="child1" />
536 - <span className="child2" />
537 - </div>
538 - );
539 - }
540 - }
541 - }
542 -
543 - const shallowRenderer = createRenderer();
544 - const result = shallowRenderer.render(<SomeComponent />);
545 - expect(result.type).toBe('div');
546 - expect(result.props.children).toEqual([
547 - <span className="child1" />,
548 - <span className="child2" />,
549 - ]);
550 -
551 - const updatedResult = shallowRenderer.render(<SomeComponent aNew="prop" />);
552 - expect(updatedResult.type).toBe('a');
553 -
554 - const mockEvent = {};
555 - updatedResult.props.onClick(mockEvent);
556 -
557 - const updatedResultCausedByClick = shallowRenderer.getRenderOutput();
558 - expect(updatedResultCausedByClick.type).toBe('a');
559 - expect(updatedResultCausedByClick.props.className).toBe('was-clicked');
560 - });
561 -
562 - it('can access the mounted component instance', () => {
563 - const SimpleComponent = React.memo(
564 - class SimpleComponent extends React.Component {
565 - someMethod = () => {
566 - return this.props.n;
567 - };
568 -
569 - render() {
570 - return <div>{this.props.n}</div>;
571 - }
572 - },
573 - );
574 -
575 - const shallowRenderer = createRenderer();
576 - shallowRenderer.render(<SimpleComponent n={5} />);
577 - expect(shallowRenderer.getMountedInstance().someMethod()).toEqual(5);
578 - });
579 -
580 - it('can shallowly render components with contextTypes', () => {
581 - const SimpleComponent = React.memo(
582 - class SimpleComponent extends React.Component {
583 - static contextTypes = {
584 - name: PropTypes.string,
585 - };
586 -
587 - render() {
588 - return <div />;
589 - }
590 - },
591 - );
592 -
593 - const shallowRenderer = createRenderer();
594 - const result = shallowRenderer.render(<SimpleComponent />);
595 - expect(result).toEqual(<div />);
596 - });
597 -
598 - it('passes expected params to legacy component lifecycle methods', () => {
599 - const componentDidUpdateParams = [];
600 - const componentWillReceivePropsParams = [];
601 - const componentWillUpdateParams = [];
602 - const setStateParams = [];
603 - const shouldComponentUpdateParams = [];
604 -
605 - const initialProp = {prop: 'init prop'};
606 - const initialState = {state: 'init state'};
607 - const initialContext = {context: 'init context'};
608 - const updatedState = {state: 'updated state'};
609 - const updatedProp = {prop: 'updated prop'};
610 - const updatedContext = {context: 'updated context'};
611 -
612 - const SimpleComponent = React.memo(
613 - class SimpleComponent extends React.Component {
614 - constructor(props, context) {
615 - super(props, context);
616 - this.state = initialState;
617 - }
618 - static contextTypes = {
619 - context: PropTypes.string,
620 - };
621 - componentDidUpdate(...args) {
622 - componentDidUpdateParams.push(...args);
623 - }
624 - UNSAFE_componentWillReceiveProps(...args) {
625 - componentWillReceivePropsParams.push(...args);
626 - this.setState((...innerArgs) => {
627 - setStateParams.push(...innerArgs);
628 - return updatedState;
629 - });
630 - }
631 - UNSAFE_componentWillUpdate(...args) {
632 - componentWillUpdateParams.push(...args);
633 - }
634 - shouldComponentUpdate(...args) {
635 - shouldComponentUpdateParams.push(...args);
636 - return true;
637 - }
638 - render() {
639 - return null;
640 - }
641 - },
642 - );
643 -
644 - const shallowRenderer = createRenderer();
645 - shallowRenderer.render(
646 - React.createElement(SimpleComponent, initialProp),
647 - initialContext,
648 - );
649 - expect(componentDidUpdateParams).toEqual([]);
650 - expect(componentWillReceivePropsParams).toEqual([]);
651 - expect(componentWillUpdateParams).toEqual([]);
652 - expect(setStateParams).toEqual([]);
653 - expect(shouldComponentUpdateParams).toEqual([]);
654 -
655 - // Lifecycle hooks should be invoked with the correct prev/next params on update.
656 - shallowRenderer.render(
657 - React.createElement(SimpleComponent, updatedProp),
658 - updatedContext,
659 - );
660 - expect(componentWillReceivePropsParams).toEqual([
661 - updatedProp,
662 - updatedContext,
663 - ]);
664 - expect(setStateParams).toEqual([initialState, initialProp]);
665 - expect(shouldComponentUpdateParams).toEqual([
666 - updatedProp,
667 - updatedState,
668 - updatedContext,
669 - ]);
670 - expect(componentWillUpdateParams).toEqual([
671 - updatedProp,
672 - updatedState,
673 - updatedContext,
674 - ]);
675 - expect(componentDidUpdateParams).toEqual([]);
676 - });
677 -
678 - it('passes expected params to new component lifecycle methods', () => {
679 - const componentDidUpdateParams = [];
680 - const getDerivedStateFromPropsParams = [];
681 - const shouldComponentUpdateParams = [];
682 -
683 - const initialProp = {prop: 'init prop'};
684 - const initialState = {state: 'init state'};
685 - const initialContext = {context: 'init context'};
686 - const updatedProp = {prop: 'updated prop'};
687 - const updatedContext = {context: 'updated context'};
688 -
689 - const SimpleComponent = React.memo(
690 - class SimpleComponent extends React.Component {
691 - constructor(props, context) {
692 - super(props, context);
693 - this.state = initialState;
694 - }
695 - static contextTypes = {
696 - context: PropTypes.string,
697 - };
698 - componentDidUpdate(...args) {
699 - componentDidUpdateParams.push(...args);
700 - }
701 - static getDerivedStateFromProps(...args) {
702 - getDerivedStateFromPropsParams.push(args);
703 - return null;
704 - }
705 - shouldComponentUpdate(...args) {
706 - shouldComponentUpdateParams.push(...args);
707 - return true;
708 - }
709 - render() {
710 - return null;
711 - }
712 - },
713 - );
714 -
715 - const shallowRenderer = createRenderer();
716 -
717 - // The only lifecycle hook that should be invoked on initial render
718 - // Is the static getDerivedStateFromProps() methods
719 - shallowRenderer.render(
720 - React.createElement(SimpleComponent, initialProp),
721 - initialContext,
722 - );
723 - expect(getDerivedStateFromPropsParams).toEqual([
724 - [initialProp, initialState],
725 - ]);
726 - expect(componentDidUpdateParams).toEqual([]);
727 - expect(shouldComponentUpdateParams).toEqual([]);
728 -
729 - // Lifecycle hooks should be invoked with the correct prev/next params on update.
730 - shallowRenderer.render(
731 - React.createElement(SimpleComponent, updatedProp),
732 - updatedContext,
733 - );
734 - expect(getDerivedStateFromPropsParams).toEqual([
735 - [initialProp, initialState],
736 - [updatedProp, initialState],
737 - ]);
738 - expect(shouldComponentUpdateParams).toEqual([
739 - updatedProp,
740 - initialState,
741 - updatedContext,
742 - ]);
743 - expect(componentDidUpdateParams).toEqual([]);
744 - });
745 -
746 - it('can shallowly render components with ref as function', () => {
747 - const SimpleComponent = React.memo(
748 - class SimpleComponent extends React.Component {
749 - state = {clicked: false};
750 -
751 - handleUserClick = () => {
752 - this.setState({clicked: true});
753 - };
754 -
755 - render() {
756 - return (
757 - <div
758 - ref={() => {}}
759 - onClick={this.handleUserClick}
760 - className={this.state.clicked ? 'clicked' : ''}
761 - />
762 - );
763 - }
764 - },
765 - );
766 -
767 - const shallowRenderer = createRenderer();
768 - shallowRenderer.render(<SimpleComponent />);
769 - let result = shallowRenderer.getRenderOutput();
770 - expect(result.type).toEqual('div');
771 - expect(result.props.className).toEqual('');
772 - result.props.onClick();
773 -
774 - result = shallowRenderer.getRenderOutput();
775 - expect(result.type).toEqual('div');
776 - expect(result.props.className).toEqual('clicked');
777 - });
778 -
779 - it('can initialize state via static getDerivedStateFromProps', () => {
780 - const SimpleComponent = React.memo(
781 - class SimpleComponent extends React.Component {
782 - state = {
783 - count: 1,
784 - };
785 -
786 - static getDerivedStateFromProps(props, prevState) {
787 - return {
788 - count: prevState.count + props.incrementBy,
789 - other: 'foobar',
790 - };
791 - }
792 -
793 - render() {
794 - return (
795 - <div>{`count:${this.state.count}, other:${this.state.other}`}</div>
796 - );
797 - }
798 - },
799 - );
800 -
801 - const shallowRenderer = createRenderer();
802 - const result = shallowRenderer.render(<SimpleComponent incrementBy={2} />);
803 - expect(result).toEqual(<div>count:3, other:foobar</div>);
804 - });
805 -
806 - it('can setState in componentWillMount when shallow rendering', () => {
807 - const SimpleComponent = React.memo(
808 - class SimpleComponent extends React.Component {
809 - UNSAFE_componentWillMount() {
810 - this.setState({groovy: 'doovy'});
811 - }
812 -
813 - render() {
814 - return <div>{this.state.groovy}</div>;
815 - }
816 - },
817 - );
818 -
819 - const shallowRenderer = createRenderer();
820 - const result = shallowRenderer.render(<SimpleComponent />);
821 - expect(result).toEqual(<div>doovy</div>);
822 - });
823 -
824 - it('can setState in componentWillMount repeatedly when shallow rendering', () => {
825 - const SimpleComponent = React.memo(
826 - class SimpleComponent extends React.Component {
827 - state = {
828 - separator: '-',
829 - };
830 -
831 - UNSAFE_componentWillMount() {
832 - this.setState({groovy: 'doovy'});
833 - this.setState({doovy: 'groovy'});
834 - }
835 -
836 - render() {
837 - const {groovy, doovy, separator} = this.state;
838 -
839 - return <div>{`${groovy}${separator}${doovy}`}</div>;
840 - }
841 - },
842 - );
843 -
844 - const shallowRenderer = createRenderer();
845 - const result = shallowRenderer.render(<SimpleComponent />);
846 - expect(result).toEqual(<div>doovy-groovy</div>);
847 - });
848 -
849 - it('can setState in componentWillMount with an updater function repeatedly when shallow rendering', () => {
850 - const SimpleComponent = React.memo(
851 - class SimpleComponent extends React.Component {
852 - state = {
853 - separator: '-',
854 - };
855 -
856 - UNSAFE_componentWillMount() {
857 - this.setState(state => ({groovy: 'doovy'}));
858 - this.setState(state => ({doovy: state.groovy}));
859 - }
860 -
861 - render() {
862 - const {groovy, doovy, separator} = this.state;
863 -
864 - return <div>{`${groovy}${separator}${doovy}`}</div>;
865 - }
866 - },
867 - );
868 -
869 - const shallowRenderer = createRenderer();
870 - const result = shallowRenderer.render(<SimpleComponent />);
871 - expect(result).toEqual(<div>doovy-doovy</div>);
872 - });
873 -
874 - it('can setState in componentWillReceiveProps when shallow rendering', () => {
875 - const SimpleComponent = React.memo(
876 - class SimpleComponent extends React.Component {
877 - state = {count: 0};
878 -
879 - UNSAFE_componentWillReceiveProps(nextProps) {
880 - if (nextProps.updateState) {
881 - this.setState({count: 1});
882 - }
883 - }
884 -
885 - render() {
886 - return <div>{this.state.count}</div>;
887 - }
888 - },
889 - );
890 -
891 - const shallowRenderer = createRenderer();
892 - let result = shallowRenderer.render(
893 - <SimpleComponent updateState={false} />,
894 - );
895 - expect(result.props.children).toEqual(0);
896 -
897 - result = shallowRenderer.render(<SimpleComponent updateState={true} />);
898 - expect(result.props.children).toEqual(1);
899 - });
900 -
901 - it('can update state with static getDerivedStateFromProps when shallow rendering', () => {
902 - const SimpleComponent = React.memo(
903 - class SimpleComponent extends React.Component {
904 - state = {count: 1};
905 -
906 - static getDerivedStateFromProps(nextProps, prevState) {
907 - if (nextProps.updateState) {
908 - return {count: nextProps.incrementBy + prevState.count};
909 - }
910 -
911 - return null;
912 - }
913 -
914 - render() {
915 - return <div>{this.state.count}</div>;
916 - }
917 - },
918 - );
919 -
920 - const shallowRenderer = createRenderer();
921 - let result = shallowRenderer.render(
922 - <SimpleComponent updateState={false} incrementBy={0} />,
923 - );
924 - expect(result.props.children).toEqual(1);
925 -
926 - result = shallowRenderer.render(
927 - <SimpleComponent updateState={true} incrementBy={2} />,
928 - );
929 - expect(result.props.children).toEqual(3);
930 -
931 - result = shallowRenderer.render(
932 - <SimpleComponent updateState={false} incrementBy={2} />,
933 - );
934 - expect(result.props.children).toEqual(3);
935 - });
936 -
937 - it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', () => {
938 - const SimpleComponent = React.memo(
939 - class SimpleComponent extends React.Component {
940 - state = {value: 0};
941 -
942 - static getDerivedStateFromProps(nextProps, prevState) {
943 - return {...prevState};
944 - }
945 -
946 - updateState = () => {
947 - this.setState(state => ({value: state.value + 1}));
948 - };
949 -
950 - render() {
951 - return <div>{`value:${this.state.value}`}</div>;
952 - }
953 - },
954 - );
955 -
956 - const shallowRenderer = createRenderer();
957 - let result = shallowRenderer.render(<SimpleComponent />);
958 - expect(result).toEqual(<div>value:0</div>);
959 -
960 - const instance = shallowRenderer.getMountedInstance();
961 - instance.updateState();
962 - result = shallowRenderer.getRenderOutput();
963 - expect(result).toEqual(<div>value:1</div>);
964 - });
965 -
966 - it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => {
967 - const SimpleComponent = React.memo(
968 - class SimpleComponent extends React.Component {
969 - constructor(props) {
970 - super(props);
971 - this.state = {
972 - value: props.value,
973 - };
974 - }
975 -
976 - static getDerivedStateFromProps(nextProps, prevState) {
977 - if (nextProps.value === prevState.value) {
978 - return null;
979 - }
980 - return {value: nextProps.value};
981 - }
982 -
983 - shouldComponentUpdate(nextProps, nextState) {
984 - return nextState.value !== this.state.value;
985 - }
986 -
987 - render() {
988 - return <div>{`value:${this.state.value}`}</div>;
989 - }
990 - },
991 - );
992 -
993 - const shallowRenderer = createRenderer();
994 - const initialResult = shallowRenderer.render(
995 - <SimpleComponent value="initial" />,
996 - );
997 - expect(initialResult).toEqual(<div>value:initial</div>);
998 - const updatedResult = shallowRenderer.render(
999 - <SimpleComponent value="updated" />,
1000 - );
1001 - expect(updatedResult).toEqual(<div>value:updated</div>);
1002 - });
1003 -
1004 - it('can setState with an updater function', () => {
1005 - let instance;
1006 -
1007 - const SimpleComponent = React.memo(
1008 - class SimpleComponent extends React.Component {
1009 - state = {
1010 - counter: 0,
1011 - };
1012 -
1013 - render() {
1014 - instance = this;
1015 - return (
1016 - <button ref="button" onClick={this.onClick}>
1017 - {this.state.counter}
1018 - </button>
1019 - );
1020 - }
1021 - },
1022 - );
1023 -
1024 - const shallowRenderer = createRenderer();
1025 - let result = shallowRenderer.render(<SimpleComponent defaultCount={1} />);
1026 - expect(result.props.children).toEqual(0);
1027 -
1028 - instance.setState((state, props) => {
1029 - return {counter: props.defaultCount + 1};
1030 - });
1031 -
1032 - result = shallowRenderer.getRenderOutput();
1033 - expect(result.props.children).toEqual(2);
1034 - });
1035 -
1036 - it('can access component instance from setState updater function', done => {
1037 - let instance;
1038 -
1039 - const SimpleComponent = React.memo(
1040 - class SimpleComponent extends React.Component {
1041 - state = {};
1042 -
1043 - render() {
1044 - instance = this;
1045 - return null;
1046 - }
1047 - },
1048 - );
1049 -
1050 - const shallowRenderer = createRenderer();
1051 - shallowRenderer.render(<SimpleComponent />);
1052 -
1053 - instance.setState(function updater(state, props) {
1054 - expect(this).toBe(instance);
1055 - done();
1056 - });
1057 - });
1058 -
1059 - it('can setState with a callback', () => {
1060 - let instance;
1061 -
1062 - const SimpleComponent = React.memo(
1063 - class SimpleComponent extends React.Component {
1064 - state = {
1065 - counter: 0,
1066 - };
1067 - render() {
1068 - instance = this;
1069 - return <p>{this.state.counter}</p>;
1070 - }
1071 - },
1072 - );
1073 -
1074 - const shallowRenderer = createRenderer();
1075 - const result = shallowRenderer.render(<SimpleComponent />);
1076 - expect(result.props.children).toBe(0);
1077 -
1078 - const callback = jest.fn(function() {
1079 - expect(this).toBe(instance);
1080 - });
1081 -
1082 - instance.setState({counter: 1}, callback);
1083 -
1084 - const updated = shallowRenderer.getRenderOutput();
1085 - expect(updated.props.children).toBe(1);
1086 - expect(callback).toHaveBeenCalled();
1087 - });
1088 -
1089 - it('can replaceState with a callback', () => {
1090 - let instance;
1091 -
1092 - const SimpleComponent = React.memo(
1093 - class SimpleComponent extends React.Component {
1094 - state = {
1095 - counter: 0,
1096 - };
1097 - render() {
1098 - instance = this;
1099 - return <p>{this.state.counter}</p>;
1100 - }
1101 - },
1102 - );
1103 -
1104 - const shallowRenderer = createRenderer();
1105 - const result = shallowRenderer.render(<SimpleComponent />);
1106 - expect(result.props.children).toBe(0);
1107 -
1108 - const callback = jest.fn(function() {
1109 - expect(this).toBe(instance);
1110 - });
1111 -
1112 - // No longer a public API, but we can test that it works internally by
1113 - // reaching into the updater.
1114 - shallowRenderer._updater.enqueueReplaceState(
1115 - instance,
1116 - {counter: 1},
1117 - callback,
1118 - );
1119 -
1120 - const updated = shallowRenderer.getRenderOutput();
1121 - expect(updated.props.children).toBe(1);
1122 - expect(callback).toHaveBeenCalled();
1123 - });
1124 -
1125 - it('can forceUpdate with a callback', () => {
1126 - let instance;
1127 -
1128 - const SimpleComponent = React.memo(
1129 - class SimpleComponent extends React.Component {
1130 - state = {
1131 - counter: 0,
1132 - };
1133 - render() {
1134 - instance = this;
1135 - return <p>{this.state.counter}</p>;
1136 - }
1137 - },
1138 - );
1139 -
1140 - const shallowRenderer = createRenderer();
1141 - const result = shallowRenderer.render(<SimpleComponent />);
1142 - expect(result.props.children).toBe(0);
1143 -
1144 - const callback = jest.fn(function() {
1145 - expect(this).toBe(instance);
1146 - });
1147 -
1148 - instance.forceUpdate(callback);
1149 -
1150 - const updated = shallowRenderer.getRenderOutput();
1151 - expect(updated.props.children).toBe(0);
1152 - expect(callback).toHaveBeenCalled();
1153 - });
1154 -
1155 - it('can pass context when shallowly rendering', () => {
1156 - const SimpleComponent = React.memo(
1157 - class SimpleComponent extends React.Component {
1158 - static contextTypes = {
1159 - name: PropTypes.string,
1160 - };
1161 -
1162 - render() {
1163 - return <div>{this.context.name}</div>;
1164 - }
1165 - },
1166 - );
1167 -
1168 - const shallowRenderer = createRenderer();
1169 - const result = shallowRenderer.render(<SimpleComponent />, {
1170 - name: 'foo',
1171 - });
1172 - expect(result).toEqual(<div>foo</div>);
1173 - });
1174 -
1175 - it('should track context across updates', () => {
1176 - const SimpleComponent = React.memo(
1177 - class SimpleComponent extends React.Component {
1178 - static contextTypes = {
1179 - foo: PropTypes.string,
1180 - };
1181 -
1182 - state = {
1183 - bar: 'bar',
1184 - };
1185 -
1186 - render() {
1187 - return <div>{`${this.context.foo}:${this.state.bar}`}</div>;
1188 - }
1189 - },
1190 - );
1191 -
1192 - const shallowRenderer = createRenderer();
1193 - let result = shallowRenderer.render(<SimpleComponent />, {
1194 - foo: 'foo',
1195 - });
1196 - expect(result).toEqual(<div>foo:bar</div>);
1197 -
1198 - const instance = shallowRenderer.getMountedInstance();
1199 - instance.setState({bar: 'baz'});
1200 -
1201 - result = shallowRenderer.getRenderOutput();
1202 - expect(result).toEqual(<div>foo:baz</div>);
1203 - });
1204 -
1205 - it('should filter context by contextTypes', () => {
1206 - const SimpleComponent = React.memo(
1207 - class SimpleComponent extends React.Component {
1208 - static contextTypes = {
1209 - foo: PropTypes.string,
1210 - };
1211 - render() {
1212 - return <div>{`${this.context.foo}:${this.context.bar}`}</div>;
1213 - }
1214 - },
1215 - );
1216 -
1217 - const shallowRenderer = createRenderer();
1218 - const result = shallowRenderer.render(<SimpleComponent />, {
1219 - foo: 'foo',
1220 - bar: 'bar',
1221 - });
1222 - expect(result).toEqual(<div>foo:undefined</div>);
1223 - });
1224 -
1225 - it('can fail context when shallowly rendering', () => {
1226 - const SimpleComponent = React.memo(
1227 - class SimpleComponent extends React.Component {
1228 - static contextTypes = {
1229 - name: PropTypes.string.isRequired,
1230 - };
1231 -
1232 - render() {
1233 - return <div>{this.context.name}</div>;
1234 - }
1235 - },
1236 - );
1237 -
1238 - const shallowRenderer = createRenderer();
1239 - expect(() => shallowRenderer.render(<SimpleComponent />)).toErrorDev(
1240 - 'Warning: Failed context type: The context `name` is marked as ' +
1241 - 'required in `SimpleComponent`, but its value is `undefined`.\n' +
1242 - ' in SimpleComponent (at **)',
1243 - );
1244 - });
1245 -
1246 - it('should warn about propTypes (but only once)', () => {
1247 - const SimpleComponent = React.memo(
1248 - class SimpleComponent extends React.Component {
1249 - static propTypes = {
1250 - name: PropTypes.string.isRequired,
1251 - };
1252 -
1253 - render() {
1254 - return React.createElement('div', null, this.props.name);
1255 - }
1256 - },
1257 - );
1258 -
1259 - const shallowRenderer = createRenderer();
1260 - expect(() =>
1261 - shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})),
1262 - ).toErrorDev(
1263 - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
1264 - 'supplied to `SimpleComponent`, expected `string`.\n' +
1265 - ' in SimpleComponent',
1266 - );
1267 - });
1268 -
1269 - it('should enable rendering of cloned element', () => {
1270 - const SimpleComponent = React.memo(
1271 - class SimpleComponent extends React.Component {
1272 - constructor(props) {
1273 - super(props);
1274 -
1275 - this.state = {
1276 - bar: 'bar',
1277 - };
1278 - }
1279 -
1280 - render() {
1281 - return <div>{`${this.props.foo}:${this.state.bar}`}</div>;
1282 - }
1283 - },
1284 - );
1285 -
1286 - const shallowRenderer = createRenderer();
1287 - const el = <SimpleComponent foo="foo" />;
1288 - let result = shallowRenderer.render(el);
1289 - expect(result).toEqual(<div>foo:bar</div>);
1290 -
1291 - const cloned = React.cloneElement(el, {foo: 'baz'});
1292 - result = shallowRenderer.render(cloned);
1293 - expect(result).toEqual(<div>baz:bar</div>);
1294 - });
1295 -
1296 - it('this.state should be updated on setState callback inside componentWillMount', () => {
1297 - let stateSuccessfullyUpdated = false;
1298 -
1299 - const Component = React.memo(
1300 - class Component extends React.Component {
1301 - constructor(props, context) {
1302 - super(props, context);
1303 - this.state = {
1304 - hasUpdatedState: false,
1305 - };
1306 - }
1307 -
1308 - UNSAFE_componentWillMount() {
1309 - this.setState(
1310 - {hasUpdatedState: true},
1311 - () => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
1312 - );
1313 - }
1314 -
1315 - render() {
1316 - return <div>{this.props.children}</div>;
1317 - }
1318 - },
1319 - );
1320 -
1321 - const shallowRenderer = createRenderer();
1322 - shallowRenderer.render(<Component />);
1323 - expect(stateSuccessfullyUpdated).toBe(true);
1324 - });
1325 -
1326 - it('should handle multiple callbacks', () => {
1327 - const mockFn = jest.fn();
1328 - const shallowRenderer = createRenderer();
1329 -
1330 - const Component = React.memo(
1331 - class Component extends React.Component {
1332 - constructor(props, context) {
1333 - super(props, context);
1334 - this.state = {
1335 - foo: 'foo',
1336 - };
1337 - }
1338 -
1339 - UNSAFE_componentWillMount() {
1340 - this.setState({foo: 'bar'}, () => mockFn());
1341 - this.setState({foo: 'foobar'}, () => mockFn());
1342 - }
1343 -
1344 - render() {
1345 - return <div>{this.state.foo}</div>;
1346 - }
1347 - },
1348 - );
1349 -
1350 - shallowRenderer.render(<Component />);
1351 -
1352 - expect(mockFn).toHaveBeenCalledTimes(2);
1353 -
1354 - // Ensure the callback queue is cleared after the callbacks are invoked
1355 - const mountedInstance = shallowRenderer.getMountedInstance();
1356 - mountedInstance.setState({foo: 'bar'}, () => mockFn());
1357 - expect(mockFn).toHaveBeenCalledTimes(3);
1358 - });
1359 -
1360 - it('should call the setState callback even if shouldComponentUpdate = false', done => {
1361 - const mockFn = jest.fn().mockReturnValue(false);
1362 -
1363 - const Component = React.memo(
1364 - class Component extends React.Component {
1365 - constructor(props, context) {
1366 - super(props, context);
1367 - this.state = {
1368 - hasUpdatedState: false,
1369 - };
1370 - }
1371 -
1372 - shouldComponentUpdate() {
1373 - return mockFn();
1374 - }
1375 -
1376 - render() {
1377 - return <div>{this.state.hasUpdatedState}</div>;
1378 - }
1379 - },
1380 - );
1381 -
1382 - const shallowRenderer = createRenderer();
1383 - shallowRenderer.render(<Component />);
1384 -
1385 - const mountedInstance = shallowRenderer.getMountedInstance();
1386 - mountedInstance.setState({hasUpdatedState: true}, () => {
1387 - expect(mockFn).toBeCalled();
1388 - expect(mountedInstance.state.hasUpdatedState).toBe(true);
1389 - done();
1390 - });
1391 - });
1392 -
1393 - it('throws usefully when rendering badly-typed elements', () => {
1394 - const shallowRenderer = createRenderer();
1395 -
1396 - const renderAndVerifyWarningAndError = (Component, typeString) => {
1397 - expect(() => {
1398 - expect(() => shallowRenderer.render(<Component />)).toErrorDev(
1399 - 'React.createElement: type is invalid -- expected a string ' +
1400 - '(for built-in components) or a class/function (for composite components) ' +
1401 - `but got: ${typeString}.`,
1402 - );
1403 - }).toThrowError(
1404 - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
1405 - `components, but the provided element type was \`${typeString}\`.`,
1406 - );
1407 - };
1408 -
1409 - renderAndVerifyWarningAndError(undefined, 'undefined');
1410 - renderAndVerifyWarningAndError(null, 'null');
1411 - renderAndVerifyWarningAndError([], 'array');
1412 - renderAndVerifyWarningAndError({}, 'object');
1413 - });
1414 -
1415 - it('should have initial state of null if not defined', () => {
1416 - const SomeComponent = React.memo(
1417 - class SomeComponent extends React.Component {
1418 - render() {
1419 - return <span />;
1420 - }
1421 - },
1422 - );
1423 -
1424 - const shallowRenderer = createRenderer();
1425 - shallowRenderer.render(<SomeComponent />);
1426 -
1427 - expect(shallowRenderer.getMountedInstance().state).toBeNull();
1428 - });
1429 -
1430 - it('should invoke both deprecated and new lifecycles if both are present', () => {
1431 - const log = [];
1432 -
1433 - const Component = React.memo(
1434 - class Component extends React.Component {
1435 - componentWillMount() {
1436 - log.push('componentWillMount');
1437 - }
1438 - componentWillReceiveProps() {
1439 - log.push('componentWillReceiveProps');
1440 - }
1441 - componentWillUpdate() {
1442 - log.push('componentWillUpdate');
1443 - }
1444 - UNSAFE_componentWillMount() {
1445 - log.push('UNSAFE_componentWillMount');
1446 - }
1447 - UNSAFE_componentWillReceiveProps() {
1448 - log.push('UNSAFE_componentWillReceiveProps');
1449 - }
1450 - UNSAFE_componentWillUpdate() {
1451 - log.push('UNSAFE_componentWillUpdate');
1452 - }
1453 - render() {
1454 - return null;
1455 - }
1456 - },
1457 - );
1458 -
1459 - const shallowRenderer = createRenderer();
1460 - shallowRenderer.render(<Component foo="bar" />);
1461 - expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']);
1462 -
1463 - log.length = 0;
1464 -
1465 - shallowRenderer.render(<Component foo="baz" />);
1466 - expect(log).toEqual([
1467 - 'componentWillReceiveProps',
1468 - 'UNSAFE_componentWillReceiveProps',
1469 - 'componentWillUpdate',
1470 - 'UNSAFE_componentWillUpdate',
1471 - ]);
1472 - });
1473 -
1474 - it('should stop the update when setState returns null or undefined', () => {
1475 - const log = [];
1476 - let instance;
1477 - const Component = React.memo(
1478 - class Component extends React.Component {
1479 - constructor(props) {
1480 - super(props);
1481 - this.state = {
1482 - count: 0,
1483 - };
1484 - }
1485 - render() {
1486 - log.push('render');
1487 - instance = this;
1488 - return null;
1489 - }
1490 - },
1491 - );
1492 - const shallowRenderer = createRenderer();
1493 - shallowRenderer.render(<Component />);
1494 - log.length = 0;
1495 - instance.setState(() => null);
1496 - instance.setState(() => undefined);
1497 - instance.setState(null);
1498 - instance.setState(undefined);
1499 - expect(log).toEqual([]);
1500 - instance.setState(state => ({count: state.count + 1}));
1501 - expect(log).toEqual(['render']);
1502 - });
1503 -
1504 - it('should not get this in a function component', () => {
1505 - const logs = [];
1506 - const Foo = React.memo(function Foo() {
1507 - logs.push(this);
1508 - return <div>foo</div>;
1509 - });
1510 - const shallowRenderer = createRenderer();
1511 - shallowRenderer.render(<Foo foo="bar" />);
1512 - expect(logs).toEqual([undefined]);
1513 - });
1514 -});
scripts/rollup/forks.js
-7
@@ -36,13 +36,6 @@ const __EXPERIMENTAL__ =
36 // algorithm because 1) require.resolve doesn't work with ESM modules, and 2)
37 // the behavior is easier to predict.
38 const forks = Object.freeze({
39 - // NOTE: This is hard-coded to the main entry point of the (third-party)
40 - // react-shallow-renderer package.
41 - './node_modules/react-shallow-renderer/index.js': () => {
42 - // Use ESM build of `react-shallow-renderer`.
43 - return './node_modules/react-shallow-renderer/esm/index.js';
44 - },
45 -
39 // Without this fork, importing `shared/ReactSharedInternals` inside
40 // the `react` package itself would not work due to a cyclical dependency.
41 './packages/shared/ReactSharedInternals.js': (