@samitouri / QOS-React-2 / commits / 3e48422fdc

Warn for keys in fragments - third approach (#9445)

* Fix tests to pass when we warn for missing keys in fragments In most cases we just needed to add the 'key' prop. This ignores the tests which are already failing on master when running with ` REACT_DOM_JEST_USE_FIBER=1` - there are 8. All tests should now pass with `npm run test`, and the 8 which fail when running `REACT_DOM_JEST_USE_FIBER=1 npm run test` are the same 8 which are failing on master. * Added missing key warning for children in array fragments After trying twice to reuse the code between the ReactChildFiber and ReactElementValidator, I am thinking that it's simpler to just have some duplication of code. The parts that are shared are interleaved with parts which cannot be shared, either because of singleton modules that must be required differently in 'isomorphic' and the 'renderers', or the fact that 'warning' requires a hard coded string. Test Plan: - Added test to ReactChildren-test - Manually tested via fixture that was not committed. * commit updated "scripts/rollup/results.json" * Make 'ReactChildren-test' more specific, and remove unneeded nesting Based on helpful tips from @spicyj and @aweary's review - Made the unit test for the warning on missing keys more specific - Removed unneeded nesting in the code which generates missing key warning - Change test syntax to use JSX to be more consistent Also fixes flow warning. * Commit update of scripts/rollup/results.json * run "scripts/fiber/record-tests"

Flarnie Marchan committed Apr 19, 2017 at 08:05 UTC 3e48422fdc2feaf741d768a6071a87adf2fc107b
14 files changed +203 -91
scripts/fiber/tests-passing.txt
+2
@@ -76,6 +76,8 @@ src/isomorphic/children/__tests__/ReactChildren-test.js
76 * should flatten children to an array
77 * should throw on object
78 * should throw on regex
79 +* warns for keys for arrays of elements in a fragment
80 +* does not warn when there are keys on elements in a fragment
81
82 src/isomorphic/children/__tests__/onlyChild-test.js
83 * should fail when passed two children
scripts/rollup/results.json
+29 -29
@@ -1,17 +1,17 @@
1 {
2 - "branch": "master",
2 + "branch": "warnForKeysInFragmentsRefactorThirdApproach",
3 "bundleSizes": {
4 "react.development.js (UMD_DEV)": {
5 - "size": 121454,
6 - "gzip": 30515
5 + "size": 121387,
6 + "gzip": 30491
7 },
8 "react.production.min.js (UMD_PROD)": {
9 "size": 15685,
10 "gzip": 5765
11 },
12 "react-dom.development.js (UMD_DEV)": {
13 - "size": 583190,
14 - "gzip": 134534
13 + "size": 584434,
14 + "gzip": 134865
15 },
16 "react-dom.production.min.js (UMD_PROD)": {
17 "size": 120740,
@@ -26,24 +26,24 @@
26 "gzip": 33273
27 },
28 "react-art.development.js (UMD_DEV)": {
29 - "size": 342608,
30 - "gzip": 76782
29 + "size": 343852,
30 + "gzip": 77090
31 },
32 "react-art.production.min.js (UMD_PROD)": {
33 "size": 95013,
34 "gzip": 28991
35 },
36 "react.development.js (NODE_DEV)": {
37 - "size": 70266,
38 - "gzip": 17594
37 + "size": 70199,
38 + "gzip": 17572
39 },
40 "react.production.min.js (NODE_PROD)": {
41 "size": 9226,
42 "gzip": 3628
43 },
44 "React-dev.js (FB_DEV)": {
45 - "size": 72123,
46 - "gzip": 18231
45 + "size": 72056,
46 + "gzip": 18209
47 },
48 "React-prod.js (FB_PROD)": {
49 "size": 36643,
@@ -58,20 +58,20 @@
58 "gzip": 84675
59 },
60 "react-dom.development.js (NODE_DEV)": {
61 - "size": 542188,
62 - "gzip": 125158
61 + "size": 543430,
62 + "gzip": 125486
63 },
64 "react-dom.production.min.js (NODE_PROD)": {
65 "size": 116925,
66 "gzip": 36732
67 },
68 "ReactDOMFiber-dev.js (FB_DEV)": {
69 - "size": 797235,
70 - "gzip": 184122
69 + "size": 798477,
70 + "gzip": 184445
71 },
72 "ReactDOMFiber-prod.js (FB_PROD)": {
73 - "size": 407613,
74 - "gzip": 93586
73 + "size": 407677,
74 + "gzip": 93615
75 },
76 "react-dom-server.development.js (NODE_DEV)": {
77 "size": 445589,
@@ -98,20 +98,20 @@
98 "gzip": 22993
99 },
100 "react-art.development.js (NODE_DEV)": {
101 - "size": 265052,
102 - "gzip": 56927
101 + "size": 266294,
102 + "gzip": 57234
103 },
104 "react-art.production.min.js (NODE_PROD)": {
105 "size": 56628,
106 "gzip": 17152
107 },
108 "ReactARTFiber-dev.js (FB_DEV)": {
109 - "size": 264230,
110 - "gzip": 56736
109 + "size": 265472,
110 + "gzip": 57048
111 },
112 "ReactARTFiber-prod.js (FB_PROD)": {
113 - "size": 205336,
114 - "gzip": 43154
113 + "size": 205400,
114 + "gzip": 43183
115 },
116 "ReactNativeStack.js (RN)": {
117 "size": 233993,
@@ -122,20 +122,20 @@
122 "gzip": 84001
123 },
124 "ReactTestRendererFiber-dev.js (FB_DEV)": {
125 - "size": 262139,
126 - "gzip": 55704
125 + "size": 263381,
126 + "gzip": 56013
127 },
128 "ReactTestRendererStack-dev.js (FB_DEV)": {
129 "size": 151521,
130 "gzip": 34765
131 },
132 "react-noop-renderer.development.js (NODE_DEV)": {
133 - "size": 254136,
134 - "gzip": 53682
133 + "size": 255378,
134 + "gzip": 53988
135 },
136 "react-test-renderer.development.js (NODE_DEV)": {
137 - "size": 262970,
138 - "gzip": 55891
137 + "size": 264212,
138 + "gzip": 56201
139 }
140 }
141 }
\ No newline at end of file
src/isomorphic/children/__tests__/ReactChildren-test.js
+50
@@ -11,12 +11,21 @@
11
12 'use strict';
13
14 +const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
15 +
16 describe('ReactChildren', () => {
17 var React;
18 + var ReactTestUtils;
19 + var ReactFeatureFlags;
20 +
21 + function normalizeCodeLocInfo(str) {
22 + return str && str.replace(/at .+?:\d+/g, 'at **');
23 + }
24
25 beforeEach(() => {
26 jest.resetModules();
27 React = require('react');
28 + ReactTestUtils = require('ReactTestUtils');
29 });
30
31 it('should support identity for simple', () => {
@@ -850,4 +859,45 @@ describe('ReactChildren', () => {
859 'to render a collection of children, use an array instead.',
860 );
861 });
862 +
863 + if (ReactDOMFeatureFlags.useFiber) {
864 + describe('with fragments enabled', () => {
865 + beforeEach(() => {
866 + ReactFeatureFlags = require('ReactFeatureFlags');
867 + ReactFeatureFlags.disableNewFiberFeatures = false;
868 + });
869 +
870 + it('warns for keys for arrays of elements in a fragment', () => {
871 + spyOn(console, 'error');
872 + class ComponentReturningArray extends React.Component {
873 + render() {
874 + return [<div />, <div />];
875 + }
876 + }
877 +
878 + ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
879 +
880 + expectDev(console.error.calls.count()).toBe(1);
881 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
882 + 'Warning: ' +
883 + 'Each child in an array or iterator should have a unique "key" prop.' +
884 + ' See https://fb.me/react-warning-keys for more information.' +
885 + '\n in ComponentReturningArray (at **)',
886 + );
887 + });
888 +
889 + it('does not warn when there are keys on elements in a fragment', () => {
890 + spyOn(console, 'error');
891 + class ComponentReturningArray extends React.Component {
892 + render() {
893 + return [<div key="foo" />, <div key="bar" />];
894 + }
895 + }
896 +
897 + ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
898 +
899 + expectDev(console.error.calls.count()).toBe(0);
900 + });
901 + });
902 + }
903 });
src/isomorphic/classic/element/ReactElementValidator.js
+2 -5
@@ -96,14 +96,11 @@ function validateExplicitKey(element, parentType) {
96 }
97 element._store.validated = true;
98
99 - var memoizer = ownerHasKeyUseWarning.uniqueKey ||
100 - (ownerHasKeyUseWarning.uniqueKey = {});
101 -
99 var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
103 - if (memoizer[currentComponentErrorInfo]) {
100 + if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
101 return;
102 }
106 - memoizer[currentComponentErrorInfo] = true;
103 + ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
104
105 // Usually the current owner is the offender, but if it accepts children as a
106 // property, it may be the creator of the child that's responsible for
src/renderers/__tests__/ReactMultiChildText-test.js
+14 -4
@@ -191,10 +191,20 @@ describe('ReactMultiChildText', () => {
191 [true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
192 ['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
193 ]);
194 - expectDev(console.error.calls.count()).toBe(1);
195 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
196 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
197 - );
194 + if (ReactDOMFeatureFlags.useFiber) {
195 + expectDev(console.error.calls.count()).toBe(2);
196 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
197 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
198 + );
199 + expectDev(console.error.calls.argsFor(1)[0]).toContain(
200 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
201 + );
202 + } else {
203 + expectDev(console.error.calls.count()).toBe(1);
204 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
205 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
206 + );
207 + }
208 });
209
210 it('should throw if rendering both HTML and children', () => {
src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
+21 -18
@@ -118,7 +118,7 @@ describe('ReactDOMFiber', () => {
118 it('finds the first child when a component returns a fragment', () => {
119 class Fragment extends React.Component {
120 render() {
121 - return [<div />, <span />];
121 + return [<div key="a" />, <span key="b" />];
122 }
123 }
124
@@ -141,7 +141,7 @@ describe('ReactDOMFiber', () => {
141
142 class Fragment extends React.Component {
143 render() {
144 - return [<Wrapper><div /></Wrapper>, <span />];
144 + return [<Wrapper key="a"><div /></Wrapper>, <span key="b" />];
145 }
146 }
147
@@ -164,7 +164,7 @@ describe('ReactDOMFiber', () => {
164
165 class Fragment extends React.Component {
166 render() {
167 - return [<NullComponent />, <div />, <span />];
167 + return [<NullComponent key="a" />, <div key="b" />, <span key="c" />];
168 }
169 }
170
@@ -263,16 +263,16 @@ describe('ReactDOMFiber', () => {
263 render() {
264 const {step} = this.props;
265 return [
266 - <Child name={`normal[0]:${step}`} />,
266 + <Child key="a" name={`normal[0]:${step}`} />,
267 ReactDOM.unstable_createPortal(
268 - <Child name={`portal1[0]:${step}`} />,
268 + <Child key="b" name={`portal1[0]:${step}`} />,
269 portalContainer1,
270 ),
271 - <Child name={`normal[1]:${step}`} />,
271 + <Child key="c" name={`normal[1]:${step}`} />,
272 ReactDOM.unstable_createPortal(
273 [
274 - <Child name={`portal2[0]:${step}`} />,
275 - <Child name={`portal2[1]:${step}`} />,
274 + <Child key="d" name={`portal2[0]:${step}`} />,
275 + <Child key="e" name={`portal2[1]:${step}`} />,
276 ],
277 portalContainer2,
278 ),
@@ -337,23 +337,23 @@ describe('ReactDOMFiber', () => {
337
338 ReactDOM.render(
339 [
340 - <div>normal[0]</div>,
340 + <div key="a">normal[0]</div>,
341 ReactDOM.unstable_createPortal(
342 [
343 - <div>portal1[0]</div>,
343 + <div key="b">portal1[0]</div>,
344 ReactDOM.unstable_createPortal(
345 - <div>portal2[0]</div>,
345 + <div key="c">portal2[0]</div>,
346 portalContainer2,
347 ),
348 ReactDOM.unstable_createPortal(
349 - <div>portal3[0]</div>,
349 + <div key="d">portal3[0]</div>,
350 portalContainer3,
351 ),
352 - <div>portal1[1]</div>,
352 + <div key="e">portal1[1]</div>,
353 ],
354 portalContainer1,
355 ),
356 - <div>normal[1]</div>,
356 + <div key="f">normal[1]</div>,
357 ],
358 container,
359 );
@@ -943,7 +943,7 @@ describe('ReactDOMFiber', () => {
943 }
944
945 let inst;
946 - ReactDOM.render([<Example ref={n => inst = n} />], container);
946 + ReactDOM.render([<Example key="a" ref={n => inst = n} />], container);
947 const node = container.firstChild;
948 expect(node.tagName).toEqual('DIV');
949
@@ -981,7 +981,10 @@ describe('ReactDOMFiber', () => {
981 // click handler during render to simulate a click during an aborted
982 // render. I use this hack because at current time we don't have a way to
983 // test aborted ReactDOM renders.
984 - ReactDOM.render([<Example forceA={true} />, <Click />], container);
984 + ReactDOM.render(
985 + [<Example key="a" forceA={true} />, <Click key="b" />],
986 + container,
987 + );
988
989 // Because the new click handler has not yet committed, we should still
990 // invoke B.
@@ -1030,7 +1033,7 @@ describe('disableNewFiberFeatures', () => {
1033 expect(() => ReactDOM.render(false, container)).toThrow(message, container);
1034 expect(() => ReactDOM.render('Hi', container)).toThrow(message, container);
1035 expect(() => ReactDOM.render(999, container)).toThrow(message, container);
1033 - expect(() => ReactDOM.render([<div />], container)).toThrow(
1036 + expect(() => ReactDOM.render([<div key="a" />], container)).toThrow(
1037 message,
1038 container,
1039 );
@@ -1048,7 +1051,7 @@ describe('disableNewFiberFeatures', () => {
1051 /You may have returned undefined/,
1052 );
1053 expect(() =>
1051 - ReactDOM.render(<Render>[<div />]</Render>, container)).toThrow(
1054 + ReactDOM.render(<Render>[<div key="a" />]</Render>, container)).toThrow(
1055 /You may have returned undefined/,
1056 );
1057 });
src/renderers/shared/fiber/ReactChildFiber.js
+44 -3
@@ -43,6 +43,43 @@ if (__DEV__) {
43 var getComponentName = require('getComponentName');
44 var warning = require('fbjs/lib/warning');
45 var didWarnAboutMaps = false;
46 + /**
47 + * Warn if there's no key explicitly set on dynamic arrays of children or
48 + * object keys are not valid. This allows us to keep track of children between
49 + * updates.
50 + */
51 + var ownerHasKeyUseWarning = {};
52 +
53 + var warnForMissingKey = (child: mixed) => {
54 + if (child === null || typeof child !== 'object') {
55 + return;
56 + }
57 + if (!child._store || child._store.validated || child.key != null) {
58 + return;
59 + }
60 + invariant(
61 + typeof child._store === 'object',
62 + 'React Component in warnForMissingKey should have a _store',
63 + );
64 + child._store.validated = true;
65 +
66 + var currentComponentErrorInfo = 'Each child in an array or iterator should have a unique ' +
67 + '"key" prop. See https://fb.me/react-warning-keys for ' +
68 + 'more information.' +
69 + (getCurrentFiberStackAddendum(child) || '');
70 + if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
71 + return;
72 + }
73 + ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
74 +
75 + warning(
76 + false,
77 + 'Each child in an array or iterator should have a unique ' +
78 + '"key" prop. See https://fb.me/react-warning-keys for ' +
79 + 'more information.%s',
80 + getCurrentFiberStackAddendum(child),
81 + );
82 + };
83 }
84
85 const {
@@ -592,7 +629,10 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
629 return null;
630 }
631
595 - function warnOnDuplicateKey(
632 + /**
633 + * Warns if there is a duplicate or missing key
634 + */
635 + function warnOnInvalidKey(
636 child: mixed,
637 knownKeys: Set<string> | null,
638 ): Set<string> | null {
@@ -604,6 +644,7 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
644 case REACT_ELEMENT_TYPE:
645 case REACT_COROUTINE_TYPE:
646 case REACT_PORTAL_TYPE:
647 + warnForMissingKey(child);
648 const key = child.key;
649 if (typeof key !== 'string') {
650 break;
@@ -663,7 +704,7 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
704 let knownKeys = null;
705 for (let i = 0; i < newChildren.length; i++) {
706 const child = newChildren[i];
666 - knownKeys = warnOnDuplicateKey(child, knownKeys);
707 + knownKeys = warnOnInvalidKey(child, knownKeys);
708 }
709 }
710
@@ -842,7 +883,7 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
883 let step = newChildren.next();
884 for (; !step.done; step = newChildren.next()) {
885 const child = step.value;
845 - knownKeys = warnOnDuplicateKey(child, knownKeys);
886 + knownKeys = warnOnInvalidKey(child, knownKeys);
887 }
888 }
889 }
src/renderers/shared/fiber/__tests__/ReactCoroutine-test.js
+15 -9
@@ -57,13 +57,13 @@ describe('ReactCoroutine', () => {
57
58 function Indirection() {
59 ops.push('Indirection');
60 - return [<Child bar={true} />, <Child bar={false} />];
60 + return [<Child key="a" bar={true} />, <Child key="b" bar={false} />];
61 }
62
63 function HandleYields(props, yields) {
64 ops.push('HandleYields');
65 - return yields.map(y => (
66 - <y.continuation isSame={props.foo === y.props.bar} />
65 + return yields.map((y, i) => (
66 + <y.continuation key={i} isSame={props.foo === y.props.bar} />
67 ));
68 }
69
@@ -117,12 +117,12 @@ describe('ReactCoroutine', () => {
117 }
118
119 function Indirection() {
120 - return [<Child bar={true} />, <Child bar={false} />];
120 + return [<Child key="a" bar={true} />, <Child key="b" bar={false} />];
121 }
122
123 function HandleYields(props, yields) {
124 - return yields.map(y => (
125 - <y.continuation isSame={props.foo === y.props.bar} />
124 + return yields.map((y, i) => (
125 + <y.continuation key={i} isSame={props.foo === y.props.bar} />
126 ));
127 }
128
@@ -176,7 +176,9 @@ describe('ReactCoroutine', () => {
176
177 function HandleYields(props, yields) {
178 ops.push('HandleYields');
179 - return yields.map(ContinuationComponent => <ContinuationComponent />);
179 + return yields.map((ContinuationComponent, i) => (
180 + <ContinuationComponent key={i} />
181 + ));
182 }
183
184 class Parent extends React.Component {
@@ -223,8 +225,12 @@ describe('ReactCoroutine', () => {
225
226 function App(props) {
227 return ReactCoroutine.createCoroutine(
226 - [<Counter id="a" />, <Counter id="b" />, <Counter id="c" />],
227 - (p, yields) => yields.map(y => <span prop={y * 100} />),
228 + [
229 + <Counter key="a" id="a" />,
230 + <Counter key="b" id="b" />,
231 + <Counter key="c" id="c" />,
232 + ],
233 + (p, yields) => yields.map((y, i) => <span key={i} prop={y * 100} />),
234 {},
235 );
236 }
src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+11 -11
@@ -51,7 +51,7 @@ describe('ReactIncremental', () => {
51 var fooCalled = false;
52 function Foo() {
53 fooCalled = true;
54 - return [<Bar isBar={true} />, <Bar isBar={true} />];
54 + return [<Bar key="a" isBar={true} />, <Bar key="b" isBar={true} />];
55 }
56
57 ReactNoop.render(<Foo />, () => renderCallbackCalled = true);
@@ -384,8 +384,8 @@ describe('ReactIncremental', () => {
384 }
385 render() {
386 return [
387 - <Tester unused={this.props.unused} />,
388 - <bbb hidden={true}>
387 + <Tester key="a" unused={this.props.unused} />,
388 + <bbb key="b" hidden={true}>
389 <ccc>
390 <Middle>Hi</Middle>
391 </ccc>
@@ -583,8 +583,8 @@ describe('ReactIncremental', () => {
583 // low priority. I think this would be fixed by changing
584 // pendingWorkPriority and progressedPriority to be the priority of
585 // the children only, not including the fiber itself.
586 - <div><Child /></div>,
587 - <Sibling />,
586 + <div key="a"><Child /></div>,
587 + <Sibling key="b" />,
588 ];
589 }
590
@@ -1413,7 +1413,7 @@ describe('ReactIncremental', () => {
1413 }
1414
1415 function App(props) {
1416 - return [<LifeCycle x={props.x} />, <Sibling />];
1416 + return [<LifeCycle key="a" x={props.x} />, <Sibling key="b" />];
1417 }
1418
1419 ReactNoop.render(<App x={0} />);
@@ -1662,13 +1662,13 @@ describe('ReactIncremental', () => {
1662 render() {
1663 ops.push('Indirection ' + JSON.stringify(this.context));
1664 return [
1665 - <ShowLocale />,
1666 - <ShowRoute />,
1667 - <ShowNeither />,
1668 - <Intl locale="ru">
1665 + <ShowLocale key="a" />,
1666 + <ShowRoute key="b" />,
1667 + <ShowNeither key="c" />,
1668 + <Intl key="d" locale="ru">
1669 <ShowBoth />
1670 </Intl>,
1671 - <ShowBoth />,
1671 + <ShowBoth key="e" />,
1672 ];
1673 }
1674 }
src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js
+3 -3
@@ -449,12 +449,12 @@ describe('ReactDebugFiberPerf', () => {
449 }
450
451 function Indirection() {
452 - return [<CoChild bar={true} />, <CoChild bar={false} />];
452 + return [<CoChild key="a" bar={true} />, <CoChild key="b" bar={false} />];
453 }
454
455 function HandleYields(props, yields) {
456 - return yields.map(y => (
457 - <y.continuation isSame={props.foo === y.props.bar} />
456 + return yields.map((y, i) => (
457 + <y.continuation key={i} isSame={props.foo === y.props.bar} />
458 ));
459 }
460
src/renderers/shared/fiber/__tests__/ReactIncrementalReflection-test.js
+1 -1
@@ -169,7 +169,7 @@ describe('ReactIncrementalReflection', () => {
169 }
170
171 function Foo(props) {
172 - return [<Component step={props.step} />, <Sibling />];
172 + return [<Component key="a" step={props.step} />, <Sibling key="b" />];
173 }
174
175 ReactNoop.render(<Foo step={0} />);
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
+4 -1
@@ -565,7 +565,10 @@ describe('ReactIncrementalSideEffects', () => {
565 }
566 render() {
567 ops.push('Baz');
568 - return [<Bar idx={this.props.idx} />, <Bar idx={this.props.idx} />];
568 + return [
569 + <Bar key="a" idx={this.props.idx} />,
570 + <Bar key="b" idx={this.props.idx} />,
571 + ];
572 }
573 }
574 function Foo(props) {
src/renderers/shared/fiber/__tests__/ReactTopLevelFragment-test.js
+4 -4
@@ -77,7 +77,7 @@ describe('ReactTopLevelFragment', function() {
77 function Fragment({condition}) {
78 return condition
79 ? <Stateful key="a" />
80 - : [[<Stateful key="a" />, <div key="b">World</div>], <div />];
80 + : [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
81 }
82 ReactNoop.render(<Fragment />);
83 ReactNoop.flush();
@@ -106,8 +106,8 @@ describe('ReactTopLevelFragment', function() {
106
107 function Fragment({condition}) {
108 return condition
109 - ? [null, <Stateful />]
110 - : [<div>Hello</div>, <Stateful />];
109 + ? [null, <Stateful key="a" />]
110 + : [<div key="b">Hello</div>, <Stateful key="a" />];
111 }
112 ReactNoop.render(<Fragment />);
113 ReactNoop.flush();
@@ -144,7 +144,7 @@ describe('ReactTopLevelFragment', function() {
144 function Fragment({condition}) {
145 return condition
146 ? [[<div key="b">World</div>, <Stateful key="a" />]]
147 - : [[<Stateful key="a" />, <div key="b">World</div>], <div />];
147 + : [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
148 }
149 ReactNoop.render(<Fragment />);
150 ReactNoop.flush();
src/renderers/testing/__tests__/ReactTestRenderer-test.js
+3 -3
@@ -676,8 +676,8 @@ describe('ReactTestRenderer', () => {
676 var Component = props => props.children;
677
678 var renderer = ReactTestRenderer.create([
679 - <Component>Hi</Component>,
680 - <Component>Bye</Component>,
679 + <Component key="a">Hi</Component>,
680 + <Component key="b">Bye</Component>,
681 ]);
682 expect(renderer.toJSON()).toEqual(['Hi', 'Bye']);
683 renderer.update(<div />);
@@ -686,7 +686,7 @@ describe('ReactTestRenderer', () => {
686 children: null,
687 props: {},
688 });
689 - renderer.update([<div>goodbye</div>, 'world']);
689 + renderer.update([<div key="a">goodbye</div>, 'world']);
690 expect(renderer.toJSON()).toEqual([
691 {
692 type: 'div',