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': (