@samitouri / QOS-React / commits / 3ff1540e9b

Prefer JSX in ReactNoop assertions (to combat out-of-memory test runs) (#26127)

## Summary Prefer `getChildrenAsJSX` or `toMatchRenderedOutput` over `getChildren`. Use `dangerouslyGetChildren` if you really need to (e.g. for `toBe` assertions). Prefer `getPendingChildrenAsJSX` over `getPendingChildren`. Use `dangerouslyGetPendingChildren` if you really need to (e.g. for `toBe` assertions). `ReactNoop.getChildren` contains the fibers as non-enumerable properties. If you pass the children to `toEqual` and have a mismatch, Jest performance is very poor (to the point of causing out-of-memory crashes e.g. https://app.circleci.com/pipelines/github/facebook/react/38084/workflows/02ca0cbb-bab4-4c19-8d7d-ada814eeebb9/jobs/624297/parallel-runs/5?filterBy=ALL&invite=true#step-106-27). Mismatches can sometimes be intended e.g. on gated tests. Instead, I converted almost all of the `toEqual` assertions to `toMatchRenderedOutput` assertions or compare the JSX instead. For ReactNoopPersistent we still use `getChildren` since we have assertions on referential equality. `toMatchRenderedOutput` is more accurate in some instances anyway. I highlighted some of those more accurate assertions in review-comments. ## How did you test this change? - [x] `CIRCLE_NODE_TOTAL=20 CIRCLE_NODE_INDEX=5 yarn test -r=experimental --env=development --ci`: Can take up to 350s (and use up to 7GB of memory) on `main` but 11s on this branch - [x] No more slow `yarn test` parallel runs of `yarn_test` jobs (the steps in these runs should take <1min but sometimes they take 3min and end with OOM like https://app.circleci.com/pipelines/github/facebook/react/38084/workflows/02ca0cbb-bab4-4c19-8d7d-ada814eeebb9/jobs/624258/parallel-runs/5?filterBy=ALL: Looks good with a sample size of 1 https://app.circleci.com/pipelines/github/facebook/react/38110/workflows/745109a2-b86b-429f-8c01-9b23a245417a/jobs/624651

Sebastian Silbermann committed Feb 9, 2023 at 11:54 UTC 3ff1540e9bbe30aae52e2c9ab61c843bd0c94237
20 files changed +1985 -1372
packages/react-noop-renderer/src/ReactNoop.js
+2
@@ -20,7 +20,9 @@ import createReactNoop from './createReactNoop';
20 export const {
21 _Scheduler,
22 getChildren,
23 + dangerouslyGetChildren,
24 getPendingChildren,
25 + dangerouslyGetPendingChildren,
26 getOrCreateRootContainer,
27 createRoot,
28 createLegacyRoot,
packages/react-noop-renderer/src/ReactNoopPersistent.js
+2
@@ -20,7 +20,9 @@ import createReactNoop from './createReactNoop';
20 export const {
21 _Scheduler,
22 getChildren,
23 + dangerouslyGetChildren,
24 getPendingChildren,
25 + dangerouslyGetPendingChildren,
26 getOrCreateRootContainer,
27 createRoot,
28 createLegacyRoot,
packages/react-noop-renderer/src/createReactNoop.js
+25 -1
@@ -789,11 +789,35 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
789 _Scheduler: Scheduler,
790
791 getChildren(rootID: string = DEFAULT_ROOT_ID) {
792 + throw new Error(
793 + 'No longer supported due to bad performance when used with `expect()`. ' +
794 + 'Use `ReactNoop.getChildrenAsJSX()` instead or, if you really need to, `dangerouslyGetChildren` after you carefully considered the warning in its JSDOC.',
795 + );
796 + },
797 +
798 + getPendingChildren(rootID: string = DEFAULT_ROOT_ID) {
799 + throw new Error(
800 + 'No longer supported due to bad performance when used with `expect()`. ' +
801 + 'Use `ReactNoop.getPendingChildrenAsJSX()` instead or, if you really need to, `dangerouslyGetPendingChildren` after you carefully considered the warning in its JSDOC.',
802 + );
803 + },
804 +
805 + /**
806 + * Prefer using `getChildrenAsJSX`.
807 + * Using the returned children in `.toEqual` has very poor performance on mismatch due to deep equality checking of fiber structures.
808 + * Make sure you deeply remove enumerable properties before passing it to `.toEqual`, or, better, use `getChildrenAsJSX` or `toMatchRenderedOutput`.
809 + */
810 + dangerouslyGetChildren(rootID: string = DEFAULT_ROOT_ID) {
811 const container = rootContainers.get(rootID);
812 return getChildren(container);
813 },
814
796 - getPendingChildren(rootID: string = DEFAULT_ROOT_ID) {
815 + /**
816 + * Prefer using `getPendingChildrenAsJSX`.
817 + * Using the returned children in `.toEqual` has very poor performance on mismatch due to deep equality checking of fiber structures.
818 + * Make sure you deeply remove enumerable properties before passing it to `.toEqual`, or, better, use `getChildrenAsJSX` or `toMatchRenderedOutput`.
819 + */
820 + dangerouslyGetPendingChildren(rootID: string = DEFAULT_ROOT_ID) {
821 const container = rootContainers.get(rootID);
822 return getPendingChildren(container);
823 },
packages/react-reconciler/src/__tests__/ReactExpiration-test.js
+10 -14
@@ -105,10 +105,6 @@ describe('ReactExpiration', () => {
105 }
106 }
107
108 - function span(prop) {
109 - return {type: 'span', children: [], prop, hidden: false};
110 - }
111 -
108 function flushNextRenderIfExpired() {
109 // This will start rendering the next level of work. If the work hasn't
110 // expired yet, React will exit without doing anything. If it has expired,
@@ -127,21 +123,21 @@ describe('ReactExpiration', () => {
123 ReactNoop.render(<span prop="done" />);
124 }
125
130 - expect(ReactNoop.getChildren()).toEqual([]);
126 + expect(ReactNoop).toMatchRenderedOutput(null);
127
128 // Nothing has expired yet because time hasn't advanced.
129 flushNextRenderIfExpired();
134 - expect(ReactNoop.getChildren()).toEqual([]);
130 + expect(ReactNoop).toMatchRenderedOutput(null);
131
132 // Advance time a bit, but not enough to expire the low pri update.
133 ReactNoop.expire(4500);
134 flushNextRenderIfExpired();
139 - expect(ReactNoop.getChildren()).toEqual([]);
135 + expect(ReactNoop).toMatchRenderedOutput(null);
136
137 // Advance by another second. Now the update should expire and flush.
138 ReactNoop.expire(500);
139 flushNextRenderIfExpired();
144 - expect(ReactNoop.getChildren()).toEqual([span('done')]);
140 + expect(ReactNoop).toMatchRenderedOutput(<span prop="done" />);
141 });
142
143 it('two updates of like priority in the same event always flush within the same batch', () => {
@@ -181,20 +177,20 @@ describe('ReactExpiration', () => {
177
178 // Don't advance time by enough to expire the first update.
179 expect(Scheduler).toHaveYielded([]);
184 - expect(ReactNoop.getChildren()).toEqual([]);
180 + expect(ReactNoop).toMatchRenderedOutput(null);
181
182 // Schedule another update.
183 ReactNoop.render(<TextClass text="B" />);
184 // Both updates are batched
185 expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
190 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
186 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
187
188 // Now do the same thing again, except this time don't flush any work in
189 // between the two updates.
190 ReactNoop.render(<TextClass text="A" />);
191 Scheduler.unstable_advanceTime(2000);
192 expect(Scheduler).toHaveYielded([]);
197 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
193 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
194 // Schedule another update.
195 ReactNoop.render(<TextClass text="B" />);
196 // The updates should flush in the same batch, since as far as the scheduler
@@ -242,20 +238,20 @@ describe('ReactExpiration', () => {
238
239 // Don't advance time by enough to expire the first update.
240 expect(Scheduler).toHaveYielded([]);
245 - expect(ReactNoop.getChildren()).toEqual([]);
241 + expect(ReactNoop).toMatchRenderedOutput(null);
242
243 // Schedule another update.
244 ReactNoop.render(<TextClass text="B" />);
245 // Both updates are batched
246 expect(Scheduler).toFlushAndYield(['B [render]', 'B [commit]']);
251 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
247 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
248
249 // Now do the same thing again, except this time don't flush any work in
250 // between the two updates.
251 ReactNoop.render(<TextClass text="A" />);
252 Scheduler.unstable_advanceTime(2000);
253 expect(Scheduler).toHaveYielded([]);
258 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
254 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
255
256 // Perform some synchronous work. The scheduler must assume we're inside
257 // the same event.
packages/react-reconciler/src/__tests__/ReactFragment-test.js
+107 -57
@@ -22,21 +22,6 @@ describe('ReactFragment', () => {
22 Scheduler = require('scheduler');
23 });
24
25 - function div(...children) {
26 - children = children.map(c =>
27 - typeof c === 'string' ? {text: c, hidden: false} : c,
28 - );
29 - return {type: 'div', children, prop: undefined, hidden: false};
30 - }
31 -
32 - function span(prop) {
33 - return {type: 'span', children: [], prop, hidden: false};
34 - }
35 -
36 - function text(t) {
37 - return {text: t, hidden: false};
38 - }
39 -
25 it('should render a single child via noop renderer', () => {
26 const element = (
27 <>
@@ -47,7 +32,7 @@ describe('ReactFragment', () => {
32 ReactNoop.render(element);
33 expect(Scheduler).toFlushWithoutYielding();
34
50 - expect(ReactNoop.getChildren()).toEqual([span()]);
35 + expect(ReactNoop).toMatchRenderedOutput(<span>foo</span>);
36 });
37
38 it('should render zero children via noop renderer', () => {
@@ -56,7 +41,7 @@ describe('ReactFragment', () => {
41 ReactNoop.render(element);
42 expect(Scheduler).toFlushWithoutYielding();
43
59 - expect(ReactNoop.getChildren()).toEqual([]);
44 + expect(ReactNoop).toMatchRenderedOutput(null);
45 });
46
47 it('should render multiple children via noop renderer', () => {
@@ -69,7 +54,11 @@ describe('ReactFragment', () => {
54 ReactNoop.render(element);
55 expect(Scheduler).toFlushWithoutYielding();
56
72 - expect(ReactNoop.getChildren()).toEqual([text('hello '), span()]);
57 + expect(ReactNoop).toMatchRenderedOutput(
58 + <>
59 + hello <span>world</span>
60 + </>,
61 + );
62 });
63
64 it('should render an iterable via noop renderer', () => {
@@ -80,7 +69,12 @@ describe('ReactFragment', () => {
69 ReactNoop.render(element);
70 expect(Scheduler).toFlushWithoutYielding();
71
83 - expect(ReactNoop.getChildren()).toEqual([span(), span()]);
72 + expect(ReactNoop).toMatchRenderedOutput(
73 + <>
74 + <span>hi</span>
75 + <span>bye</span>
76 + </>,
77 + );
78 });
79
80 it('should preserve state of children with 1 level nesting', function () {
@@ -114,13 +108,18 @@ describe('ReactFragment', () => {
108 expect(Scheduler).toFlushWithoutYielding();
109
110 expect(ops).toEqual(['Update Stateful']);
117 - expect(ReactNoop.getChildren()).toEqual([div(), div()]);
111 + expect(ReactNoop).toMatchRenderedOutput(
112 + <>
113 + <div>Hello</div>
114 + <div>World</div>
115 + </>,
116 + );
117
118 ReactNoop.render(<Foo condition={true} />);
119 expect(Scheduler).toFlushWithoutYielding();
120
121 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
123 - expect(ReactNoop.getChildren()).toEqual([div()]);
122 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
123 });
124
125 it('should preserve state between top-level fragments', function () {
@@ -155,13 +154,13 @@ describe('ReactFragment', () => {
154 expect(Scheduler).toFlushWithoutYielding();
155
156 expect(ops).toEqual(['Update Stateful']);
158 - expect(ReactNoop.getChildren()).toEqual([div()]);
157 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
158
159 ReactNoop.render(<Foo condition={true} />);
160 expect(Scheduler).toFlushWithoutYielding();
161
162 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
164 - expect(ReactNoop.getChildren()).toEqual([div()]);
163 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
164 });
165
166 it('should preserve state of children nested at same level', function () {
@@ -205,13 +204,18 @@ describe('ReactFragment', () => {
204 expect(Scheduler).toFlushWithoutYielding();
205
206 expect(ops).toEqual(['Update Stateful']);
208 - expect(ReactNoop.getChildren()).toEqual([div(), div()]);
207 + expect(ReactNoop).toMatchRenderedOutput(
208 + <>
209 + <div />
210 + <div>Hello</div>
211 + </>,
212 + );
213
214 ReactNoop.render(<Foo condition={true} />);
215 expect(Scheduler).toFlushWithoutYielding();
216
217 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
214 - expect(ReactNoop.getChildren()).toEqual([div()]);
218 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
219 });
220
221 it('should not preserve state in non-top-level fragment nesting', function () {
@@ -248,13 +252,13 @@ describe('ReactFragment', () => {
252 expect(Scheduler).toFlushWithoutYielding();
253
254 expect(ops).toEqual([]);
251 - expect(ReactNoop.getChildren()).toEqual([div()]);
255 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
256
257 ReactNoop.render(<Foo condition={true} />);
258 expect(Scheduler).toFlushWithoutYielding();
259
260 expect(ops).toEqual([]);
257 - expect(ReactNoop.getChildren()).toEqual([div()]);
261 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
262 });
263
264 it('should not preserve state of children if nested 2 levels without siblings', function () {
@@ -289,13 +293,13 @@ describe('ReactFragment', () => {
293 expect(Scheduler).toFlushWithoutYielding();
294
295 expect(ops).toEqual([]);
292 - expect(ReactNoop.getChildren()).toEqual([div()]);
296 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
297
298 ReactNoop.render(<Foo condition={true} />);
299 expect(Scheduler).toFlushWithoutYielding();
300
301 expect(ops).toEqual([]);
298 - expect(ReactNoop.getChildren()).toEqual([div()]);
302 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
303 });
304
305 it('should not preserve state of children if nested 2 levels with siblings', function () {
@@ -331,13 +335,18 @@ describe('ReactFragment', () => {
335 expect(Scheduler).toFlushWithoutYielding();
336
337 expect(ops).toEqual([]);
334 - expect(ReactNoop.getChildren()).toEqual([div(), div()]);
338 + expect(ReactNoop).toMatchRenderedOutput(
339 + <>
340 + <div>Hello</div>
341 + <div />
342 + </>,
343 + );
344
345 ReactNoop.render(<Foo condition={true} />);
346 expect(Scheduler).toFlushWithoutYielding();
347
348 expect(ops).toEqual([]);
340 - expect(ReactNoop.getChildren()).toEqual([div()]);
349 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
350 });
351
352 it('should preserve state between array nested in fragment and fragment', function () {
@@ -370,13 +379,13 @@ describe('ReactFragment', () => {
379 expect(Scheduler).toFlushWithoutYielding();
380
381 expect(ops).toEqual(['Update Stateful']);
373 - expect(ReactNoop.getChildren()).toEqual([div()]);
382 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
383
384 ReactNoop.render(<Foo condition={true} />);
385 expect(Scheduler).toFlushWithoutYielding();
386
387 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
379 - expect(ReactNoop.getChildren()).toEqual([div()]);
388 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
389 });
390
391 it('should preserve state between top level fragment and array', function () {
@@ -409,13 +418,13 @@ describe('ReactFragment', () => {
418 expect(Scheduler).toFlushWithoutYielding();
419
420 expect(ops).toEqual(['Update Stateful']);
412 - expect(ReactNoop.getChildren()).toEqual([div()]);
421 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
422
423 ReactNoop.render(<Foo condition={true} />);
424 expect(Scheduler).toFlushWithoutYielding();
425
426 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
418 - expect(ReactNoop.getChildren()).toEqual([div()]);
427 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
428 });
429
430 it('should not preserve state between array nested in fragment and double nested fragment', function () {
@@ -450,13 +459,13 @@ describe('ReactFragment', () => {
459 expect(Scheduler).toFlushWithoutYielding();
460
461 expect(ops).toEqual([]);
453 - expect(ReactNoop.getChildren()).toEqual([div()]);
462 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
463
464 ReactNoop.render(<Foo condition={true} />);
465 expect(Scheduler).toFlushWithoutYielding();
466
467 expect(ops).toEqual([]);
459 - expect(ReactNoop.getChildren()).toEqual([div()]);
468 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
469 });
470
471 it('should not preserve state between array nested in fragment and double nested array', function () {
@@ -487,13 +496,13 @@ describe('ReactFragment', () => {
496 expect(Scheduler).toFlushWithoutYielding();
497
498 expect(ops).toEqual([]);
490 - expect(ReactNoop.getChildren()).toEqual([div()]);
499 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
500
501 ReactNoop.render(<Foo condition={true} />);
502 expect(Scheduler).toFlushWithoutYielding();
503
504 expect(ops).toEqual([]);
496 - expect(ReactNoop.getChildren()).toEqual([div()]);
505 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
506 });
507
508 it('should preserve state between double nested fragment and double nested array', function () {
@@ -528,13 +537,13 @@ describe('ReactFragment', () => {
537 expect(Scheduler).toFlushWithoutYielding();
538
539 expect(ops).toEqual(['Update Stateful']);
531 - expect(ReactNoop.getChildren()).toEqual([div()]);
540 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
541
542 ReactNoop.render(<Foo condition={true} />);
543 expect(Scheduler).toFlushWithoutYielding();
544
545 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
537 - expect(ReactNoop.getChildren()).toEqual([div()]);
546 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
547 });
548
549 it('should not preserve state of children when the keys are different', function () {
@@ -570,13 +579,18 @@ describe('ReactFragment', () => {
579 expect(Scheduler).toFlushWithoutYielding();
580
581 expect(ops).toEqual([]);
573 - expect(ReactNoop.getChildren()).toEqual([div(), span()]);
582 + expect(ReactNoop).toMatchRenderedOutput(
583 + <>
584 + <div>Hello</div>
585 + <span>World</span>
586 + </>,
587 + );
588
589 ReactNoop.render(<Foo condition={true} />);
590 expect(Scheduler).toFlushWithoutYielding();
591
592 expect(ops).toEqual([]);
579 - expect(ReactNoop.getChildren()).toEqual([div()]);
593 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
594 });
595
596 it('should not preserve state between unkeyed and keyed fragment', function () {
@@ -611,13 +625,13 @@ describe('ReactFragment', () => {
625 expect(Scheduler).toFlushWithoutYielding();
626
627 expect(ops).toEqual([]);
614 - expect(ReactNoop.getChildren()).toEqual([div()]);
628 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
629
630 ReactNoop.render(<Foo condition={true} />);
631 expect(Scheduler).toFlushWithoutYielding();
632
633 expect(ops).toEqual([]);
620 - expect(ReactNoop.getChildren()).toEqual([div()]);
634 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
635 });
636
637 it('should preserve state with reordering in multiple levels', function () {
@@ -664,13 +678,29 @@ describe('ReactFragment', () => {
678 expect(Scheduler).toFlushWithoutYielding();
679
680 expect(ops).toEqual(['Update Stateful']);
667 - expect(ReactNoop.getChildren()).toEqual([div(span(), div(div()), span())]);
681 + expect(ReactNoop).toMatchRenderedOutput(
682 + <div>
683 + <span>beep</span>
684 + <div>
685 + <div>Hello</div>
686 + </div>
687 + <span>bar</span>
688 + </div>,
689 + );
690
691 ReactNoop.render(<Foo condition={true} />);
692 expect(Scheduler).toFlushWithoutYielding();
693
694 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
673 - expect(ReactNoop.getChildren()).toEqual([div(span(), div(div()), span())]);
695 + expect(ReactNoop).toMatchRenderedOutput(
696 + <div>
697 + <span>foo</span>
698 + <div>
699 + <div>Hello</div>
700 + </div>
701 + <span>boop</span>
702 + </div>,
703 + );
704 });
705
706 it('should not preserve state when switching to a keyed fragment to an array', function () {
@@ -713,13 +743,23 @@ describe('ReactFragment', () => {
743 );
744
745 expect(ops).toEqual([]);
716 - expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
746 + expect(ReactNoop).toMatchRenderedOutput(
747 + <div>
748 + <div>Hello</div>
749 + <span />
750 + </div>,
751 + );
752
753 ReactNoop.render(<Foo condition={true} />);
754 expect(Scheduler).toFlushWithoutYielding();
755
756 expect(ops).toEqual([]);
722 - expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
757 + expect(ReactNoop).toMatchRenderedOutput(
758 + <div>
759 + <div>Hello</div>
760 + <span />
761 + </div>,
762 + );
763 });
764
765 it('should not preserve state when switching a nested unkeyed fragment to a passthrough component', function () {
@@ -762,13 +802,13 @@ describe('ReactFragment', () => {
802 expect(Scheduler).toFlushWithoutYielding();
803
804 expect(ops).toEqual([]);
765 - expect(ReactNoop.getChildren()).toEqual([div()]);
805 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
806
807 ReactNoop.render(<Foo condition={true} />);
808 expect(Scheduler).toFlushWithoutYielding();
809
810 expect(ops).toEqual([]);
771 - expect(ReactNoop.getChildren()).toEqual([div()]);
811 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
812 });
813
814 it('should not preserve state when switching a nested keyed fragment to a passthrough component', function () {
@@ -811,13 +851,13 @@ describe('ReactFragment', () => {
851 expect(Scheduler).toFlushWithoutYielding();
852
853 expect(ops).toEqual([]);
814 - expect(ReactNoop.getChildren()).toEqual([div()]);
854 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
855
856 ReactNoop.render(<Foo condition={true} />);
857 expect(Scheduler).toFlushWithoutYielding();
858
859 expect(ops).toEqual([]);
820 - expect(ReactNoop.getChildren()).toEqual([div()]);
860 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
861 });
862
863 it('should not preserve state when switching a nested keyed array to a passthrough component', function () {
@@ -856,13 +896,13 @@ describe('ReactFragment', () => {
896 expect(Scheduler).toFlushWithoutYielding();
897
898 expect(ops).toEqual([]);
859 - expect(ReactNoop.getChildren()).toEqual([div()]);
899 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
900
901 ReactNoop.render(<Foo condition={true} />);
902 expect(Scheduler).toFlushWithoutYielding();
903
904 expect(ops).toEqual([]);
865 - expect(ReactNoop.getChildren()).toEqual([div()]);
905 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
906 });
907
908 it('should preserve state when it does not change positions', function () {
@@ -904,13 +944,23 @@ describe('ReactFragment', () => {
944 expect(Scheduler).toFlushWithoutYielding();
945
946 expect(ops).toEqual(['Update Stateful']);
907 - expect(ReactNoop.getChildren()).toEqual([span(), div()]);
947 + expect(ReactNoop).toMatchRenderedOutput(
948 + <>
949 + <span />
950 + <div>Hello</div>
951 + </>,
952 + );
953
954 ReactNoop.render(<Foo condition={true} />);
955 // The key warning gets deduped because it's in the same component.
956 expect(Scheduler).toFlushWithoutYielding();
957
958 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
914 - expect(ReactNoop.getChildren()).toEqual([span(), div()]);
959 + expect(ReactNoop).toMatchRenderedOutput(
960 + <>
961 + <span />
962 + <div>Hello</div>
963 + </>,
964 + );
965 });
966 });
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+252 -175
@@ -113,10 +113,6 @@ describe('ReactHooksWithNoopRenderer', () => {
113 };
114 });
115
116 - function span(prop) {
117 - return {type: 'span', hidden: false, children: [], prop};
118 - }
119 -
116 function Text(props) {
117 Scheduler.unstable_yieldValue(props.text);
118 return <span prop={props.text} />;
@@ -167,7 +163,7 @@ describe('ReactHooksWithNoopRenderer', () => {
163 const counter = React.createRef(null);
164 ReactNoop.render(<Counter label="Count" ref={counter} />);
165 expect(Scheduler).toFlushAndYield(['Count: 0']);
170 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
166 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
167
168 // Schedule some updates
169 act(() => {
@@ -183,7 +179,7 @@ describe('ReactHooksWithNoopRenderer', () => {
179
180 // Partially flush without committing
181 expect(Scheduler).toFlushAndYieldThrough(['Count: 11']);
186 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
182 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
183
184 // Interrupt with a high priority update
185 ReactNoop.flushSync(() => {
@@ -193,7 +189,7 @@ describe('ReactHooksWithNoopRenderer', () => {
189
190 // Resume rendering
191 expect(Scheduler).toFlushAndYield(['Total: 11']);
196 - expect(ReactNoop.getChildren()).toEqual([span('Total: 11')]);
192 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Total: 11" />);
193 });
194 });
195
@@ -289,15 +285,15 @@ describe('ReactHooksWithNoopRenderer', () => {
285 const counter = React.createRef(null);
286 ReactNoop.render(<Counter ref={counter} />);
287 expect(Scheduler).toFlushAndYield(['Count: 0']);
292 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
288 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
289
290 act(() => counter.current.updateCount(1));
291 expect(Scheduler).toHaveYielded(['Count: 1']);
296 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
292 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
293
294 act(() => counter.current.updateCount(count => count + 10));
295 expect(Scheduler).toHaveYielded(['Count: 11']);
300 - expect(ReactNoop.getChildren()).toEqual([span('Count: 11')]);
296 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
297 });
298
299 it('lazy state initializer', () => {
@@ -313,11 +309,11 @@ describe('ReactHooksWithNoopRenderer', () => {
309 const counter = React.createRef(null);
310 ReactNoop.render(<Counter initialState={42} ref={counter} />);
311 expect(Scheduler).toFlushAndYield(['getInitialState', 'Count: 42']);
316 - expect(ReactNoop.getChildren()).toEqual([span('Count: 42')]);
312 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 42" />);
313
314 act(() => counter.current.updateCount(7));
315 expect(Scheduler).toHaveYielded(['Count: 7']);
320 - expect(ReactNoop.getChildren()).toEqual([span('Count: 7')]);
316 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 7" />);
317 });
318
319 it('multiple states', () => {
@@ -331,7 +327,7 @@ describe('ReactHooksWithNoopRenderer', () => {
327 const counter = React.createRef(null);
328 ReactNoop.render(<Counter ref={counter} />);
329 expect(Scheduler).toFlushAndYield(['Count: 0']);
334 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
330 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
331
332 act(() => counter.current.updateCount(7));
333 expect(Scheduler).toHaveYielded(['Count: 7']);
@@ -349,19 +345,19 @@ describe('ReactHooksWithNoopRenderer', () => {
345 }
346 ReactNoop.render(<Counter />);
347 expect(Scheduler).toFlushAndYield(['Count: 0']);
352 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
348 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
349
350 const firstUpdater = updater;
351
352 act(() => firstUpdater(1));
353 expect(Scheduler).toHaveYielded(['Count: 1']);
358 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
354 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
355
356 const secondUpdater = updater;
357
358 act(() => firstUpdater(count => count + 10));
359 expect(Scheduler).toHaveYielded(['Count: 11']);
364 - expect(ReactNoop.getChildren()).toEqual([span('Count: 11')]);
360 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
361
362 expect(firstUpdater).toBe(secondUpdater);
363 });
@@ -392,15 +388,15 @@ describe('ReactHooksWithNoopRenderer', () => {
388
389 ReactNoop.render(<Counter />);
390 expect(Scheduler).toFlushAndYield(['Count: 0']);
395 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
391 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
392
393 ReactNoop.render(<Counter />);
394 expect(Scheduler).toFlushAndYield([]);
399 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
395 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
396
397 act(() => _updateCount(1));
398 expect(Scheduler).toHaveYielded(['Count: 1']);
403 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
399 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
400 });
401 });
402
@@ -421,27 +417,39 @@ describe('ReactHooksWithNoopRenderer', () => {
417
418 ReactNoop.render(<ScrollView row={1} />);
419 expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
424 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: false')]);
420 + expect(ReactNoop).toMatchRenderedOutput(
421 + <span prop="Scrolling down: false" />,
422 + );
423
424 ReactNoop.render(<ScrollView row={5} />);
425 expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
428 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: true')]);
426 + expect(ReactNoop).toMatchRenderedOutput(
427 + <span prop="Scrolling down: true" />,
428 + );
429
430 ReactNoop.render(<ScrollView row={5} />);
431 expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
432 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: true')]);
432 + expect(ReactNoop).toMatchRenderedOutput(
433 + <span prop="Scrolling down: true" />,
434 + );
435
436 ReactNoop.render(<ScrollView row={10} />);
437 expect(Scheduler).toFlushAndYield(['Scrolling down: true']);
436 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: true')]);
438 + expect(ReactNoop).toMatchRenderedOutput(
439 + <span prop="Scrolling down: true" />,
440 + );
441
442 ReactNoop.render(<ScrollView row={2} />);
443 expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
440 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: false')]);
444 + expect(ReactNoop).toMatchRenderedOutput(
445 + <span prop="Scrolling down: false" />,
446 + );
447
448 ReactNoop.render(<ScrollView row={2} />);
449 expect(Scheduler).toFlushAndYield(['Scrolling down: false']);
444 - expect(ReactNoop.getChildren()).toEqual([span('Scrolling down: false')]);
450 + expect(ReactNoop).toMatchRenderedOutput(
451 + <span prop="Scrolling down: false" />,
452 + );
453 });
454
455 it('warns about render phase update on a different component', async () => {
@@ -517,7 +525,7 @@ describe('ReactHooksWithNoopRenderer', () => {
525 'Render: 3',
526 3,
527 ]);
520 - expect(ReactNoop.getChildren()).toEqual([span(3)]);
528 + expect(ReactNoop).toMatchRenderedOutput(<span prop={3} />);
529 });
530
531 it('updates multiple times within same render function', () => {
@@ -542,7 +550,7 @@ describe('ReactHooksWithNoopRenderer', () => {
550 'Render: 12',
551 12,
552 ]);
545 - expect(ReactNoop.getChildren()).toEqual([span(12)]);
553 + expect(ReactNoop).toMatchRenderedOutput(<span prop={12} />);
554 });
555
556 it('throws after too many iterations', () => {
@@ -580,7 +588,7 @@ describe('ReactHooksWithNoopRenderer', () => {
588 'Render: 3',
589 3,
590 ]);
583 - expect(ReactNoop.getChildren()).toEqual([span(3)]);
591 + expect(ReactNoop).toMatchRenderedOutput(<span prop={3} />);
592 });
593
594 it('uses reducer passed at time of render, not time of dispatch', () => {
@@ -632,7 +640,7 @@ describe('ReactHooksWithNoopRenderer', () => {
640 'Render: 21',
641 21,
642 ]);
635 - expect(ReactNoop.getChildren()).toEqual([span(21)]);
643 + expect(ReactNoop).toMatchRenderedOutput(<span prop={21} />);
644
645 // Test that it works on update, too. This time the log is a bit different
646 // because we started with reducerB instead of reducerA.
@@ -648,7 +656,7 @@ describe('ReactHooksWithNoopRenderer', () => {
656 'Render: 22',
657 22,
658 ]);
651 - expect(ReactNoop.getChildren()).toEqual([span(22)]);
659 + expect(ReactNoop).toMatchRenderedOutput(<span prop={22} />);
660 });
661
662 it('discards render phase updates if something suspends', async () => {
@@ -873,11 +881,11 @@ describe('ReactHooksWithNoopRenderer', () => {
881 const counter = React.createRef(null);
882 ReactNoop.render(<Counter ref={counter} />);
883 expect(Scheduler).toFlushAndYield(['Count: 0']);
876 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
884 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
885
886 act(() => counter.current.dispatch(INCREMENT));
887 expect(Scheduler).toHaveYielded(['Count: 1']);
880 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
888 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
889 act(() => {
890 counter.current.dispatch(DECREMENT);
891 counter.current.dispatch(DECREMENT);
@@ -885,7 +893,7 @@ describe('ReactHooksWithNoopRenderer', () => {
893 });
894
895 expect(Scheduler).toHaveYielded(['Count: -2']);
888 - expect(ReactNoop.getChildren()).toEqual([span('Count: -2')]);
896 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: -2" />);
897 });
898
899 it('lazy init', () => {
@@ -915,11 +923,11 @@ describe('ReactHooksWithNoopRenderer', () => {
923 const counter = React.createRef(null);
924 ReactNoop.render(<Counter initialCount={10} ref={counter} />);
925 expect(Scheduler).toFlushAndYield(['Init', 'Count: 10']);
918 - expect(ReactNoop.getChildren()).toEqual([span('Count: 10')]);
926 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 10" />);
927
928 act(() => counter.current.dispatch(INCREMENT));
929 expect(Scheduler).toHaveYielded(['Count: 11']);
922 - expect(ReactNoop.getChildren()).toEqual([span('Count: 11')]);
930 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 11" />);
931
932 act(() => {
933 counter.current.dispatch(DECREMENT);
@@ -928,7 +936,7 @@ describe('ReactHooksWithNoopRenderer', () => {
936 });
937
938 expect(Scheduler).toHaveYielded(['Count: 8']);
931 - expect(ReactNoop.getChildren()).toEqual([span('Count: 8')]);
939 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 8" />);
940 });
941
942 // Regression test for https://github.com/facebook/react/issues/14360
@@ -950,7 +958,7 @@ describe('ReactHooksWithNoopRenderer', () => {
958 ReactNoop.render(<Counter ref={counter} />);
959
960 expect(Scheduler).toFlushAndYield(['Count: 0']);
953 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
961 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
962
963 ReactNoop.batchedUpdates(() => {
964 counter.current.dispatch(INCREMENT);
@@ -963,12 +971,12 @@ describe('ReactHooksWithNoopRenderer', () => {
971 });
972 if (gate(flags => flags.enableUnifiedSyncLane)) {
973 expect(Scheduler).toHaveYielded(['Count: 4']);
966 - expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
974 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 4" />);
975 } else {
976 expect(Scheduler).toHaveYielded(['Count: 1']);
969 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
977 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
978 expect(Scheduler).toFlushAndYield(['Count: 4']);
971 - expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
979 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 4" />);
980 }
981 });
982 });
@@ -986,7 +994,7 @@ describe('ReactHooksWithNoopRenderer', () => {
994 Scheduler.unstable_yieldValue('Sync effect'),
995 );
996 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
989 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
997 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
998 // Effects are deferred until after the commit
999 expect(Scheduler).toFlushAndYield(['Passive effect [0]']);
1000 });
@@ -996,7 +1004,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1004 Scheduler.unstable_yieldValue('Sync effect'),
1005 );
1006 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
999 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1007 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1008 // Effects are deferred until after the commit
1009 expect(Scheduler).toFlushAndYield(['Passive effect [1]']);
1010 });
@@ -1023,15 +1031,17 @@ describe('ReactHooksWithNoopRenderer', () => {
1031 'Passive',
1032 'Layout effect',
1033 ]);
1026 - expect(ReactNoop.getChildren()).toEqual([
1027 - span('Layout'),
1028 - span('Passive'),
1029 - ]);
1034 + expect(ReactNoop).toMatchRenderedOutput(
1035 + <>
1036 + <span prop="Layout" />
1037 + <span prop="Passive" />
1038 + </>,
1039 + );
1040 // Destroying the first child shouldn't prevent the passive effect from
1041 // being executed
1042 ReactNoop.render([passive]);
1043 expect(Scheduler).toFlushAndYield(['Passive effect']);
1034 - expect(ReactNoop.getChildren()).toEqual([span('Passive')]);
1044 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Passive" />);
1045 });
1046 // exiting act calls flushPassiveEffects(), but there are none left to flush.
1047 expect(Scheduler).toHaveYielded([]);
@@ -1069,10 +1079,12 @@ describe('ReactHooksWithNoopRenderer', () => {
1079 ]);
1080 });
1081
1072 - expect(ReactNoop.getChildren()).toEqual([
1073 - span('Passive'),
1074 - span('Layout'),
1075 - ]);
1082 + expect(ReactNoop).toMatchRenderedOutput(
1083 + <>
1084 + <span prop="Passive" />
1085 + <span prop="Layout" />
1086 + </>,
1087 + );
1088 });
1089
1090 it('flushes passive effects even if siblings schedule a new root', () => {
@@ -1099,10 +1111,12 @@ describe('ReactHooksWithNoopRenderer', () => {
1111 'Passive effect',
1112 'New Root',
1113 ]);
1102 - expect(ReactNoop.getChildren()).toEqual([
1103 - span('Passive'),
1104 - span('Layout'),
1105 - ]);
1114 + expect(ReactNoop).toMatchRenderedOutput(
1115 + <>
1116 + <span prop="Passive" />
1117 + <span prop="Layout" />
1118 + </>,
1119 + );
1120 });
1121 });
1122
@@ -1111,11 +1125,11 @@ describe('ReactHooksWithNoopRenderer', () => {
1125 "new ones, if they haven't already fired",
1126 () => {
1127 function getCommittedText() {
1114 - const children = ReactNoop.getChildren();
1128 + const children = ReactNoop.getChildrenAsJSX();
1129 if (children === null) {
1130 return null;
1131 }
1118 - return children[0].prop;
1132 + return children.props.prop;
1133 }
1134
1135 function Counter(props) {
@@ -1131,7 +1145,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1145 Scheduler.unstable_yieldValue('Sync effect'),
1146 );
1147 expect(Scheduler).toFlushAndYieldThrough([0, 'Sync effect']);
1134 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
1148 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
1149 // Before the effects have a chance to flush, schedule another update
1150 ReactNoop.render(<Counter count={1} />, () =>
1151 Scheduler.unstable_yieldValue('Sync effect'),
@@ -1142,7 +1156,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1156 1,
1157 'Sync effect',
1158 ]);
1145 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
1159 + expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
1160 });
1161
1162 expect(Scheduler).toHaveYielded([
@@ -1622,7 +1636,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1636 'Count: (empty)',
1637 'Sync effect',
1638 ]);
1625 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1639 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: (empty)" />);
1640 ReactNoop.flushPassiveEffects();
1641 expect(Scheduler).toHaveYielded(['Schedule update [0]']);
1642 expect(Scheduler).toFlushAndYield(['Count: 0']);
@@ -1633,7 +1647,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1647 Scheduler.unstable_yieldValue('Sync effect'),
1648 );
1649 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1636 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1650 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1651 ReactNoop.flushPassiveEffects();
1652 expect(Scheduler).toHaveYielded(['Schedule update [1]']);
1653 expect(Scheduler).toFlushAndYield(['Count: 1']);
@@ -1657,7 +1671,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1671 'Count: (empty)',
1672 'Sync effect',
1673 ]);
1660 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1674 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: (empty)" />);
1675
1676 // Rendering again should flush the previous commit's effects
1677 if (gate(flags => flags.enableSyncDefaultUpdates)) {
@@ -1678,7 +1692,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1692 ]);
1693
1694 if (gate(flags => flags.enableSyncDefaultUpdates)) {
1681 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1695 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1696 expect(Scheduler).toFlushAndYieldThrough([
1697 'Count: 0',
1698 'Sync effect',
@@ -1686,16 +1700,18 @@ describe('ReactHooksWithNoopRenderer', () => {
1700 'Count: 1',
1701 ]);
1702 } else {
1689 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1703 + expect(ReactNoop).toMatchRenderedOutput(
1704 + <span prop="Count: (empty)" />,
1705 + );
1706 expect(Scheduler).toFlushAndYieldThrough(['Sync effect']);
1691 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1707 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1708
1709 ReactNoop.flushPassiveEffects();
1710 expect(Scheduler).toHaveYielded(['Schedule update [1]']);
1711 expect(Scheduler).toFlushAndYield(['Count: 1']);
1712 }
1713
1698 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1714 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1715 });
1716 });
1717
@@ -1715,7 +1731,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1731 Scheduler.unstable_yieldValue('Sync effect'),
1732 );
1733 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1718 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1734 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1735 // A flush sync doesn't cause the passive effects to fire.
1736 // So we haven't added the other update yet.
1737 act(() => {
@@ -1737,7 +1753,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1753 ]);
1754 }
1755
1740 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1756 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1757 });
1758
1759 it(
@@ -1765,14 +1781,16 @@ describe('ReactHooksWithNoopRenderer', () => {
1781
1782 // Even in legacy mode, effects are deferred until after paint
1783 expect(Scheduler).toHaveYielded(['Count: (empty)']);
1768 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1784 + expect(ReactNoop).toMatchRenderedOutput(
1785 + <span prop="Count: (empty)" />,
1786 + );
1787 });
1788
1789 // effects get forced on exiting act()
1790 // There were multiple updates, but there should only be a
1791 // single render
1792 expect(Scheduler).toHaveYielded(['Count: 0']);
1775 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1793 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1794 },
1795 );
1796
@@ -1784,10 +1802,11 @@ describe('ReactHooksWithNoopRenderer', () => {
1802 ReactNoop.flushSync(() => {
1803 updateCount(props.count);
1804 });
1805 + expect(Scheduler).toHaveYielded([`Schedule update [${props.count}]`]);
1806 // This shouldn't flush synchronously.
1788 - expect(ReactNoop.getChildren()).not.toEqual([
1789 - span('Count: ' + props.count),
1790 - ]);
1807 + expect(ReactNoop).not.toMatchRenderedOutput(
1808 + <span prop={`Count: ${props.count}`} />,
1809 + );
1810 }, [props.count]);
1811 return <Text text={'Count: ' + count} />;
1812 }
@@ -1800,10 +1819,13 @@ describe('ReactHooksWithNoopRenderer', () => {
1819 'Count: (empty)',
1820 'Sync effect',
1821 ]);
1803 - expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);
1822 + expect(ReactNoop).toMatchRenderedOutput(
1823 + <span prop="Count: (empty)" />,
1824 + );
1825 });
1826 }).toErrorDev('flushSync was called from inside a lifecycle method');
1806 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1827 + expect(Scheduler).toHaveYielded([`Count: 0`]);
1828 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1829 });
1830
1831 it('unmounts previous effect', () => {
@@ -1821,7 +1843,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1843 Scheduler.unstable_yieldValue('Sync effect'),
1844 );
1845 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1824 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1846 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1847 });
1848
1849 expect(Scheduler).toHaveYielded(['Did create [0]']);
@@ -1831,7 +1853,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1853 Scheduler.unstable_yieldValue('Sync effect'),
1854 );
1855 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1834 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1856 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1857 });
1858
1859 expect(Scheduler).toHaveYielded(['Did destroy [0]', 'Did create [1]']);
@@ -1852,14 +1874,14 @@ describe('ReactHooksWithNoopRenderer', () => {
1874 Scheduler.unstable_yieldValue('Sync effect'),
1875 );
1876 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1855 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1877 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1878 });
1879
1880 expect(Scheduler).toHaveYielded(['Did create [0]']);
1881
1882 ReactNoop.render(null);
1883 expect(Scheduler).toFlushAndYield(['Did destroy [0]']);
1862 - expect(ReactNoop.getChildren()).toEqual([]);
1884 + expect(ReactNoop).toMatchRenderedOutput(null);
1885 });
1886
1887 it('unmounts on deletion after skipped effect', () => {
@@ -1877,7 +1899,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1899 Scheduler.unstable_yieldValue('Sync effect'),
1900 );
1901 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1880 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1902 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1903 });
1904
1905 expect(Scheduler).toHaveYielded(['Did create [0]']);
@@ -1887,14 +1909,14 @@ describe('ReactHooksWithNoopRenderer', () => {
1909 Scheduler.unstable_yieldValue('Sync effect'),
1910 );
1911 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1890 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1912 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1913 });
1914
1915 expect(Scheduler).toHaveYielded([]);
1916
1917 ReactNoop.render(null);
1918 expect(Scheduler).toFlushAndYield(['Did destroy [0]']);
1897 - expect(ReactNoop.getChildren()).toEqual([]);
1919 + expect(ReactNoop).toMatchRenderedOutput(null);
1920 });
1921
1922 it('always fires effects if no dependencies are provided', () => {
@@ -1913,7 +1935,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1935 Scheduler.unstable_yieldValue('Sync effect'),
1936 );
1937 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
1916 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1938 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1939 });
1940
1941 expect(Scheduler).toHaveYielded(['Did create']);
@@ -1923,14 +1945,14 @@ describe('ReactHooksWithNoopRenderer', () => {
1945 Scheduler.unstable_yieldValue('Sync effect'),
1946 );
1947 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1926 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1948 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1949 });
1950
1951 expect(Scheduler).toHaveYielded(['Did destroy', 'Did create']);
1952
1953 ReactNoop.render(null);
1954 expect(Scheduler).toFlushAndYield(['Did destroy']);
1933 - expect(ReactNoop.getChildren()).toEqual([]);
1955 + expect(ReactNoop).toMatchRenderedOutput(null);
1956 });
1957
1958 it('skips effect if inputs have not changed', () => {
@@ -1952,7 +1974,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1974 });
1975
1976 expect(Scheduler).toHaveYielded(['Did create [Count: 0]']);
1955 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
1977 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
1978
1979 act(() => {
1980 ReactNoop.render(<Counter label="Count" count={1} />, () =>
@@ -1960,7 +1982,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1982 );
1983 // Count changed
1984 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
1963 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
1985 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
1986 });
1987
1988 expect(Scheduler).toHaveYielded([
@@ -1977,7 +1999,7 @@ describe('ReactHooksWithNoopRenderer', () => {
1999 });
2000
2001 expect(Scheduler).toHaveYielded([]);
1980 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2002 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2003
2004 act(() => {
2005 ReactNoop.render(<Counter label="Total" count={1} />, () =>
@@ -1985,7 +2007,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2007 );
2008 // Label changed
2009 expect(Scheduler).toFlushAndYieldThrough(['Total: 1', 'Sync effect']);
1988 - expect(ReactNoop.getChildren()).toEqual([span('Total: 1')]);
2010 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Total: 1" />);
2011 });
2012
2013 expect(Scheduler).toHaveYielded([
@@ -2009,7 +2031,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2031 Scheduler.unstable_yieldValue('Sync effect'),
2032 );
2033 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2012 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2034 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2035 });
2036
2037 expect(Scheduler).toHaveYielded(['Did commit 1 [0]', 'Did commit 2 [0]']);
@@ -2019,7 +2041,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2041 Scheduler.unstable_yieldValue('Sync effect'),
2042 );
2043 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2022 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2044 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2045 });
2046 expect(Scheduler).toHaveYielded(['Did commit 1 [1]', 'Did commit 2 [1]']);
2047 });
@@ -2045,7 +2067,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2067 Scheduler.unstable_yieldValue('Sync effect'),
2068 );
2069 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2048 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2070 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2071 });
2072
2073 expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
@@ -2055,7 +2077,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2077 Scheduler.unstable_yieldValue('Sync effect'),
2078 );
2079 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2058 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2080 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2081 });
2082 expect(Scheduler).toHaveYielded([
2083 'Unmount A [0]',
@@ -2084,7 +2106,12 @@ describe('ReactHooksWithNoopRenderer', () => {
2106 () => Scheduler.unstable_yieldValue('Sync effect'),
2107 );
2108 expect(Scheduler).toFlushAndYieldThrough(['A 0', 'B 0', 'Sync effect']);
2087 - expect(ReactNoop.getChildren()).toEqual([span('A 0'), span('B 0')]);
2109 + expect(ReactNoop).toMatchRenderedOutput(
2110 + <>
2111 + <span prop="A 0" />
2112 + <span prop="B 0" />
2113 + </>,
2114 + );
2115 });
2116
2117 expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
@@ -2098,7 +2125,12 @@ describe('ReactHooksWithNoopRenderer', () => {
2125 () => Scheduler.unstable_yieldValue('Sync effect'),
2126 );
2127 expect(Scheduler).toFlushAndYieldThrough(['A 1', 'B 1', 'Sync effect']);
2101 - expect(ReactNoop.getChildren()).toEqual([span('A 1'), span('B 1')]);
2128 + expect(ReactNoop).toMatchRenderedOutput(
2129 + <>
2130 + <span prop="A 1" />
2131 + <span prop="B 1" />
2132 + </>,
2133 + );
2134 });
2135 expect(Scheduler).toHaveYielded([
2136 'Unmount A [0]',
@@ -2116,7 +2148,12 @@ describe('ReactHooksWithNoopRenderer', () => {
2148 () => Scheduler.unstable_yieldValue('Sync effect'),
2149 );
2150 expect(Scheduler).toFlushAndYieldThrough(['B 2', 'C 0', 'Sync effect']);
2119 - expect(ReactNoop.getChildren()).toEqual([span('B 2'), span('C 0')]);
2151 + expect(ReactNoop).toMatchRenderedOutput(
2152 + <>
2153 + <span prop="B 2" />
2154 + <span prop="C 0" />
2155 + </>,
2156 + );
2157 });
2158 expect(Scheduler).toHaveYielded([
2159 'Unmount A [1]',
@@ -2150,7 +2187,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2187 Scheduler.unstable_yieldValue('Sync effect'),
2188 );
2189 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2153 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2190 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2191 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2192 });
2193
@@ -2161,7 +2198,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2198 // never mounted.
2199 'Unmount A [0]',
2200 ]);
2164 - expect(ReactNoop.getChildren()).toEqual([]);
2201 + expect(ReactNoop).toMatchRenderedOutput(null);
2202 });
2203
2204 it('handles errors in create on update', () => {
@@ -2189,7 +2226,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2226 Scheduler.unstable_yieldValue('Sync effect'),
2227 );
2228 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2192 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2229 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2230 ReactNoop.flushPassiveEffects();
2231 expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
2232 });
@@ -2200,7 +2237,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2237 Scheduler.unstable_yieldValue('Sync effect'),
2238 );
2239 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2203 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2240 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2241 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2242 expect(Scheduler).toHaveYielded([
2243 'Unmount A [0]',
@@ -2208,7 +2245,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2245 'Mount A [1]',
2246 'Oops!',
2247 ]);
2211 - expect(ReactNoop.getChildren()).toEqual([]);
2248 + expect(ReactNoop).toMatchRenderedOutput(null);
2249 });
2250 expect(Scheduler).toHaveYielded([
2251 // Clean up effect A runs passively on unmount.
@@ -2242,7 +2279,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2279 Scheduler.unstable_yieldValue('Sync effect'),
2280 );
2281 expect(Scheduler).toFlushAndYieldThrough(['Count: 0', 'Sync effect']);
2245 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2282 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2283 ReactNoop.flushPassiveEffects();
2284 expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
2285 });
@@ -2253,7 +2290,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2290 Scheduler.unstable_yieldValue('Sync effect'),
2291 );
2292 expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
2256 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2293 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2294 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
2295
2296 // This branch enables a feature flag that flushes all passive destroys in a
@@ -2272,7 +2309,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2309 // The remaining destroy functions are run later on unmount, since they're passive.
2310 // In this case, one of them throws again (because of how the test is written).
2311 expect(Scheduler).toHaveYielded(['Oops!', 'Unmount B [1]']);
2275 - expect(ReactNoop.getChildren()).toEqual([]);
2312 + expect(ReactNoop).toMatchRenderedOutput(null);
2313 });
2314
2315 it('works with memo', () => {
@@ -2293,7 +2330,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2330 'Mount: 0',
2331 'Sync effect',
2332 ]);
2296 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
2333 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
2334
2335 ReactNoop.render(<Counter count={1} />, () =>
2336 Scheduler.unstable_yieldValue('Sync effect'),
@@ -2304,11 +2341,11 @@ describe('ReactHooksWithNoopRenderer', () => {
2341 'Mount: 1',
2342 'Sync effect',
2343 ]);
2307 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
2344 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
2345
2346 ReactNoop.render(null);
2347 expect(Scheduler).toFlushAndYieldThrough(['Unmount: 1']);
2311 - expect(ReactNoop.getChildren()).toEqual([]);
2348 + expect(ReactNoop).toMatchRenderedOutput(null);
2349 });
2350
2351 describe('errors thrown in passive destroy function within unmounted trees', () => {
@@ -2473,9 +2510,9 @@ describe('ReactHooksWithNoopRenderer', () => {
2510 'ErrorBoundary componentDidCatch',
2511 ]);
2512
2476 - expect(ReactNoop.getChildren()).toEqual([
2477 - span('ErrorBoundary fallback'),
2478 - ]);
2513 + expect(ReactNoop).toMatchRenderedOutput(
2514 + <span prop="ErrorBoundary fallback" />,
2515 + );
2516 });
2517
2518 // @gate skipUnmountedBoundaries
@@ -2511,7 +2548,7 @@ describe('ReactHooksWithNoopRenderer', () => {
2548 'BrokenUseEffectCleanup useEffect destroy',
2549 ]);
2550
2514 - expect(ReactNoop.getChildren()).toEqual([]);
2551 + expect(ReactNoop).toMatchRenderedOutput(null);
2552 });
2553 });
2554
@@ -3191,12 +3228,12 @@ describe('ReactHooksWithNoopRenderer', () => {
3228 it('fires layout effects after the host has been mutated', () => {
3229 function getCommittedText() {
3230 const yields = Scheduler.unstable_clearYields();
3194 - const children = ReactNoop.getChildren();
3231 + const children = ReactNoop.getChildrenAsJSX();
3232 Scheduler.unstable_yieldValue(yields);
3233 if (children === null) {
3234 return null;
3235 }
3199 - return children[0].prop;
3236 + return children.props.prop;
3237 }
3238
3239 function Counter(props) {
@@ -3214,7 +3251,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3251 'Current: 0',
3252 'Sync effect',
3253 ]);
3217 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
3254 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
3255
3256 ReactNoop.render(<Counter count={1} />, () =>
3257 Scheduler.unstable_yieldValue('Sync effect'),
@@ -3224,7 +3261,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3261 'Current: 1',
3262 'Sync effect',
3263 ]);
3227 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
3264 + expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
3265 });
3266
3267 it('force flushes passive effects before firing new layout effects', () => {
@@ -3341,10 +3378,12 @@ describe('ReactHooksWithNoopRenderer', () => {
3378 'InnerBoundary render success',
3379 'BrokenLayoutEffectDestroy render',
3380 ]);
3344 - expect(ReactNoop.getChildren()).toEqual([
3345 - span('sibling'),
3346 - span('broken'),
3347 - ]);
3381 + expect(ReactNoop).toMatchRenderedOutput(
3382 + <>
3383 + <span prop="sibling" />
3384 + <span prop="broken" />
3385 + </>,
3386 + );
3387
3388 ReactNoop.render(
3389 <ErrorBoundary id="OuterBoundary" fallbackID="OuterFallback">
@@ -3361,7 +3400,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3400 'OuterBoundary render error',
3401 'Component render OuterFallback',
3402 ]);
3364 - expect(ReactNoop.getChildren()).toEqual([span('OuterFallback')]);
3403 + expect(ReactNoop).toMatchRenderedOutput(<span prop="OuterFallback" />);
3404 });
3405
3406 it('assumes layout effect destroy function is either a function or undefined', () => {
@@ -3441,10 +3480,12 @@ describe('ReactHooksWithNoopRenderer', () => {
3480 const button = React.createRef(null);
3481 ReactNoop.render(<Counter incrementBy={1} />);
3482 expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
3444 - expect(ReactNoop.getChildren()).toEqual([
3445 - span('Increment'),
3446 - span('Count: 0'),
3447 - ]);
3483 + expect(ReactNoop).toMatchRenderedOutput(
3484 + <>
3485 + <span prop="Increment" />
3486 + <span prop="Count: 0" />
3487 + </>,
3488 + );
3489
3490 act(button.current.increment);
3491 expect(Scheduler).toHaveYielded([
@@ -3452,10 +3493,12 @@ describe('ReactHooksWithNoopRenderer', () => {
3493 // 'Increment',
3494 'Count: 1',
3495 ]);
3455 - expect(ReactNoop.getChildren()).toEqual([
3456 - span('Increment'),
3457 - span('Count: 1'),
3458 - ]);
3496 + expect(ReactNoop).toMatchRenderedOutput(
3497 + <>
3498 + <span prop="Increment" />
3499 + <span prop="Count: 1" />
3500 + </>,
3501 + );
3502
3503 // Increase the increment amount
3504 ReactNoop.render(<Counter incrementBy={10} />);
@@ -3464,18 +3507,22 @@ describe('ReactHooksWithNoopRenderer', () => {
3507 'Increment',
3508 'Count: 1',
3509 ]);
3467 - expect(ReactNoop.getChildren()).toEqual([
3468 - span('Increment'),
3469 - span('Count: 1'),
3470 - ]);
3510 + expect(ReactNoop).toMatchRenderedOutput(
3511 + <>
3512 + <span prop="Increment" />
3513 + <span prop="Count: 1" />
3514 + </>,
3515 + );
3516
3517 // Callback should have updated
3518 act(button.current.increment);
3519 expect(Scheduler).toHaveYielded(['Count: 11']);
3475 - expect(ReactNoop.getChildren()).toEqual([
3476 - span('Increment'),
3477 - span('Count: 11'),
3478 - ]);
3520 + expect(ReactNoop).toMatchRenderedOutput(
3521 + <>
3522 + <span prop="Increment" />
3523 + <span prop="Count: 11" />
3524 + </>,
3525 + );
3526 });
3527 });
3528
@@ -3492,19 +3539,19 @@ describe('ReactHooksWithNoopRenderer', () => {
3539
3540 ReactNoop.render(<CapitalizedText text="hello" />);
3541 expect(Scheduler).toFlushAndYield(["Capitalize 'hello'", 'HELLO']);
3495 - expect(ReactNoop.getChildren()).toEqual([span('HELLO')]);
3542 + expect(ReactNoop).toMatchRenderedOutput(<span prop="HELLO" />);
3543
3544 ReactNoop.render(<CapitalizedText text="hi" />);
3545 expect(Scheduler).toFlushAndYield(["Capitalize 'hi'", 'HI']);
3499 - expect(ReactNoop.getChildren()).toEqual([span('HI')]);
3546 + expect(ReactNoop).toMatchRenderedOutput(<span prop="HI" />);
3547
3548 ReactNoop.render(<CapitalizedText text="hi" />);
3549 expect(Scheduler).toFlushAndYield(['HI']);
3503 - expect(ReactNoop.getChildren()).toEqual([span('HI')]);
3550 + expect(ReactNoop).toMatchRenderedOutput(<span prop="HI" />);
3551
3552 ReactNoop.render(<CapitalizedText text="goodbye" />);
3553 expect(Scheduler).toFlushAndYield(["Capitalize 'goodbye'", 'GOODBYE']);
3507 - expect(ReactNoop.getChildren()).toEqual([span('GOODBYE')]);
3554 + expect(ReactNoop).toMatchRenderedOutput(<span prop="GOODBYE" />);
3555 });
3556
3557 it('always re-computes if no inputs are provided', () => {
@@ -3583,14 +3630,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3630 const counter = React.createRef(null);
3631 ReactNoop.render(<Counter ref={counter} />);
3632 expect(Scheduler).toFlushAndYield(['Count: 0']);
3586 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
3633 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3634 expect(counter.current.count).toBe(0);
3635
3636 act(() => {
3637 counter.current.dispatch(INCREMENT);
3638 });
3639 expect(Scheduler).toHaveYielded(['Count: 1']);
3593 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
3640 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3641 // Intentionally not updated because of [] deps:
3642 expect(counter.current.count).toBe(0);
3643 });
@@ -3613,14 +3660,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3660 const counter = React.createRef(null);
3661 ReactNoop.render(<Counter ref={counter} />);
3662 expect(Scheduler).toFlushAndYield(['Count: 0']);
3616 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
3663 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3664 expect(counter.current.count).toBe(0);
3665
3666 act(() => {
3667 counter.current.dispatch(INCREMENT);
3668 });
3669 expect(Scheduler).toHaveYielded(['Count: 1']);
3623 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
3670 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3671 expect(counter.current.count).toBe(1);
3672 });
3673
@@ -3649,7 +3696,7 @@ describe('ReactHooksWithNoopRenderer', () => {
3696 const counter = React.createRef(null);
3697 ReactNoop.render(<Counter ref={counter} />);
3698 expect(Scheduler).toFlushAndYield(['Count: 0']);
3652 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
3699 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
3700 expect(counter.current.count).toBe(0);
3701 expect(totalRefUpdates).toBe(1);
3702
@@ -3657,14 +3704,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3704 counter.current.dispatch(INCREMENT);
3705 });
3706 expect(Scheduler).toHaveYielded(['Count: 1']);
3660 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
3707 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3708 expect(counter.current.count).toBe(1);
3709 expect(totalRefUpdates).toBe(2);
3710
3711 // Update that doesn't change the ref dependencies
3712 ReactNoop.render(<Counter ref={counter} />);
3713 expect(Scheduler).toFlushAndYield(['Count: 1']);
3667 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
3714 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
3715 expect(counter.current.count).toBe(1);
3716 expect(totalRefUpdates).toBe(2); // Should not increase since last time
3717 });
@@ -3694,9 +3741,9 @@ describe('ReactHooksWithNoopRenderer', () => {
3741 }
3742 ReactNoop.render(<App />);
3743 expect(Scheduler).toFlushAndYield(['Before... Pending: false']);
3697 - expect(ReactNoop.getChildren()).toEqual([
3698 - span('Before... Pending: false'),
3699 - ]);
3744 + expect(ReactNoop).toMatchRenderedOutput(
3745 + <span prop="Before... Pending: false" />,
3746 + );
3747
3748 await act(async () => {
3749 transition();
@@ -3706,27 +3753,27 @@ describe('ReactHooksWithNoopRenderer', () => {
3753 'Suspend! [After... Pending: false]',
3754 'Loading... Pending: false',
3755 ]);
3709 - expect(ReactNoop.getChildren()).toEqual([
3710 - span('Before... Pending: true'),
3711 - ]);
3756 + expect(ReactNoop).toMatchRenderedOutput(
3757 + <span prop="Before... Pending: true" />,
3758 + );
3759 Scheduler.unstable_advanceTime(500);
3760 await advanceTimers(500);
3761
3762 // Even after a long amount of time, we still don't show a placeholder.
3763 Scheduler.unstable_advanceTime(100000);
3764 await advanceTimers(100000);
3718 - expect(ReactNoop.getChildren()).toEqual([
3719 - span('Before... Pending: true'),
3720 - ]);
3765 + expect(ReactNoop).toMatchRenderedOutput(
3766 + <span prop="Before... Pending: true" />,
3767 + );
3768
3769 await resolveText('After... Pending: false');
3770 expect(Scheduler).toHaveYielded([
3771 'Promise resolved [After... Pending: false]',
3772 ]);
3773 expect(Scheduler).toFlushAndYield(['After... Pending: false']);
3727 - expect(ReactNoop.getChildren()).toEqual([
3728 - span('After... Pending: false'),
3729 - ]);
3774 + expect(ReactNoop).toMatchRenderedOutput(
3775 + <span prop="After... Pending: false" />,
3776 + );
3777 });
3778 });
3779 });
@@ -3759,12 +3806,22 @@ describe('ReactHooksWithNoopRenderer', () => {
3806 });
3807
3808 expect(Scheduler).toHaveYielded(['A', 'Suspend! [A]', 'Loading']);
3762 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('Loading')]);
3809 + expect(ReactNoop).toMatchRenderedOutput(
3810 + <>
3811 + <span prop="A" />
3812 + <span prop="Loading" />
3813 + </>,
3814 + );
3815
3816 await resolveText('A');
3817 expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
3818 expect(Scheduler).toFlushAndYield(['A']);
3767 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('A')]);
3819 + expect(ReactNoop).toMatchRenderedOutput(
3820 + <>
3821 + <span prop="A" />
3822 + <span prop="A" />
3823 + </>,
3824 + );
3825
3826 await act(async () => {
3827 _setText('B');
@@ -3776,7 +3833,12 @@ describe('ReactHooksWithNoopRenderer', () => {
3833 'Loading',
3834 ]);
3835 expect(Scheduler).toFlushAndYield([]);
3779 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('A')]);
3836 + expect(ReactNoop).toMatchRenderedOutput(
3837 + <>
3838 + <span prop="B" />
3839 + <span prop="A" />
3840 + </>,
3841 + );
3842 });
3843
3844 await act(async () => {
@@ -3784,19 +3846,34 @@ describe('ReactHooksWithNoopRenderer', () => {
3846 await advanceTimers(250);
3847 });
3848 expect(Scheduler).toHaveYielded([]);
3787 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('A')]);
3849 + expect(ReactNoop).toMatchRenderedOutput(
3850 + <>
3851 + <span prop="B" />
3852 + <span prop="A" />
3853 + </>,
3854 + );
3855
3856 // Even after a long amount of time, we don't show a fallback
3857 Scheduler.unstable_advanceTime(100000);
3858 await advanceTimers(100000);
3859 expect(Scheduler).toFlushAndYield([]);
3793 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('A')]);
3860 + expect(ReactNoop).toMatchRenderedOutput(
3861 + <>
3862 + <span prop="B" />
3863 + <span prop="A" />
3864 + </>,
3865 + );
3866
3867 await act(async () => {
3868 await resolveText('B');
3869 });
3870 expect(Scheduler).toHaveYielded(['Promise resolved [B]', 'B', 'B']);
3799 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('B')]);
3871 + expect(ReactNoop).toMatchRenderedOutput(
3872 + <>
3873 + <span prop="B" />
3874 + <span prop="B" />
3875 + </>,
3876 + );
3877 });
3878 });
3879
@@ -3824,9 +3901,9 @@ describe('ReactHooksWithNoopRenderer', () => {
3901
3902 ReactNoop.render(<App loadC={false} />);
3903 expect(Scheduler).toFlushAndYield(['A: 0, B: 0, C: [not loaded]']);
3827 - expect(ReactNoop.getChildren()).toEqual([
3828 - span('A: 0, B: 0, C: [not loaded]'),
3829 - ]);
3904 + expect(ReactNoop).toMatchRenderedOutput(
3905 + <span prop="A: 0, B: 0, C: [not loaded]" />,
3906 + );
3907
3908 act(() => {
3909 updateA(2);
@@ -3834,9 +3911,9 @@ describe('ReactHooksWithNoopRenderer', () => {
3911 });
3912
3913 expect(Scheduler).toHaveYielded(['A: 2, B: 3, C: [not loaded]']);
3837 - expect(ReactNoop.getChildren()).toEqual([
3838 - span('A: 2, B: 3, C: [not loaded]'),
3839 - ]);
3914 + expect(ReactNoop).toMatchRenderedOutput(
3915 + <span prop="A: 2, B: 3, C: [not loaded]" />,
3916 + );
3917
3918 ReactNoop.render(<App loadC={true} />);
3919 expect(() => {
@@ -3856,11 +3933,11 @@ describe('ReactHooksWithNoopRenderer', () => {
3933 ]);
3934
3935 // Uncomment if/when we support this again
3859 - // expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 0')]);
3936 + // expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 0" />]);
3937
3938 // updateC(4);
3939 // expect(Scheduler).toFlushAndYield(['A: 2, B: 3, C: 4']);
3863 - // expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);
3940 + // expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 4" />]);
3941 });
3942
3943 it('unmount state', () => {
@@ -3888,14 +3965,14 @@ describe('ReactHooksWithNoopRenderer', () => {
3965
3966 ReactNoop.render(<App loadC={true} />);
3967 expect(Scheduler).toFlushAndYield(['A: 0, B: 0, C: 0']);
3891 - expect(ReactNoop.getChildren()).toEqual([span('A: 0, B: 0, C: 0')]);
3968 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 0, B: 0, C: 0" />);
3969 act(() => {
3970 updateA(2);
3971 updateB(3);
3972 updateC(4);
3973 });
3974 expect(Scheduler).toHaveYielded(['A: 2, B: 3, C: 4']);
3898 - expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);
3975 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 4" />);
3976 ReactNoop.render(<App loadC={false} />);
3977 expect(Scheduler).toFlushAndThrow(
3978 'Rendered fewer hooks than expected. This may be caused by an ' +
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+117 -90
@@ -29,15 +29,6 @@ describe('ReactIncrementalErrorHandling', () => {
29 act = require('jest-react').act;
30 });
31
32 - function div(...children) {
33 - children = children.map(c => (typeof c === 'string' ? {text: c} : c));
34 - return {type: 'div', children, prop: undefined, hidden: false};
35 - }
36 -
37 - function span(prop) {
38 - return {type: 'span', children: [], prop, hidden: false};
39 - }
40 -
32 function normalizeCodeLocInfo(str) {
33 return (
34 str &&
@@ -149,7 +140,7 @@ describe('ReactIncrementalErrorHandling', () => {
140
141 // Since the error was thrown during an async render, React won't commit
142 // the result yet.
152 - expect(ReactNoop.getChildren()).toEqual([]);
143 + expect(ReactNoop).toMatchRenderedOutput(null);
144
145 // Instead, it will try rendering one more time, synchronously, in case that
146 // happens to fix the error.
@@ -169,7 +160,9 @@ describe('ReactIncrementalErrorHandling', () => {
160 'ErrorMessage',
161 ]);
162
172 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
163 + expect(ReactNoop).toMatchRenderedOutput(
164 + <span prop="Caught an error: oops!" />,
165 + );
166 });
167
168 it('recovers from errors asynchronously (legacy, no getDerivedStateFromError)', () => {
@@ -267,7 +260,9 @@ describe('ReactIncrementalErrorHandling', () => {
260 'ErrorBoundary (catch)',
261 'ErrorMessage',
262 ]);
270 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
263 + expect(ReactNoop).toMatchRenderedOutput(
264 + <span prop="Caught an error: oops!" />,
265 + );
266 });
267
268 it("retries at a lower priority if there's additional pending work", async () => {
@@ -307,7 +302,9 @@ describe('ReactIncrementalErrorHandling', () => {
302 'commit',
303 'commit',
304 ]);
310 - expect(ReactNoop.getChildren()).toEqual([span('Everything is fine.')]);
305 + expect(ReactNoop).toMatchRenderedOutput(
306 + <span prop="Everything is fine." />,
307 + );
308 });
309
310 // @gate www
@@ -424,7 +421,7 @@ describe('ReactIncrementalErrorHandling', () => {
421 'Sibling',
422 'commit',
423 ]);
427 - expect(ReactNoop.getChildren()).toEqual([]);
424 + expect(ReactNoop).toMatchRenderedOutput(null);
425 });
426
427 it('retries one more time if an error occurs during a render that expires midway through the tree', async () => {
@@ -484,7 +481,7 @@ describe('ReactIncrementalErrorHandling', () => {
481 'C',
482 'D',
483 ]);
487 - expect(ReactNoop.getChildren()).toEqual([]);
484 + expect(ReactNoop).toMatchRenderedOutput(null);
485 });
486
487 it('calls componentDidCatch multiple times for multiple errors', () => {
@@ -531,7 +528,9 @@ describe('ReactIncrementalErrorHandling', () => {
528 'componentDidCatch: Error 2',
529 'componentDidCatch: Error 3',
530 ]);
534 - expect(ReactNoop.getChildren()).toEqual([span('Number of errors: 3')]);
531 + expect(ReactNoop).toMatchRenderedOutput(
532 + <span prop="Number of errors: 3" />,
533 + );
534 });
535
536 it('catches render error in a boundary during full deferred mounting', () => {
@@ -560,7 +559,9 @@ describe('ReactIncrementalErrorHandling', () => {
559 </ErrorBoundary>,
560 );
561 expect(Scheduler).toFlushWithoutYielding();
563 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
562 + expect(ReactNoop).toMatchRenderedOutput(
563 + <span prop="Caught an error: Hello." />,
564 + );
565 });
566
567 it('catches render error in a boundary during partial deferred mounting', () => {
@@ -604,7 +605,7 @@ describe('ReactIncrementalErrorHandling', () => {
605 }
606
607 expect(Scheduler).toFlushAndYieldThrough(['ErrorBoundary render success']);
607 - expect(ReactNoop.getChildren()).toEqual([]);
608 + expect(ReactNoop).toMatchRenderedOutput(null);
609
610 expect(Scheduler).toFlushAndYield([
611 'BrokenRender',
@@ -616,7 +617,9 @@ describe('ReactIncrementalErrorHandling', () => {
617 'ErrorBoundary componentDidCatch',
618 'ErrorBoundary render error',
619 ]);
619 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
620 + expect(ReactNoop).toMatchRenderedOutput(
621 + <span prop="Caught an error: Hello." />,
622 + );
623 });
624
625 it('catches render error in a boundary during synchronous mounting', () => {
@@ -663,7 +666,9 @@ describe('ReactIncrementalErrorHandling', () => {
666 'ErrorBoundary componentDidCatch',
667 'ErrorBoundary render error',
668 ]);
666 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
669 + expect(ReactNoop).toMatchRenderedOutput(
670 + <span prop="Caught an error: Hello." />,
671 + );
672 });
673
674 it('catches render error in a boundary during batched mounting', () => {
@@ -711,7 +716,9 @@ describe('ReactIncrementalErrorHandling', () => {
716 'ErrorBoundary componentDidCatch',
717 'ErrorBoundary render error',
718 ]);
714 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);
719 + expect(ReactNoop).toMatchRenderedOutput(
720 + <span prop="Caught an error: Hello." />,
721 + );
722 });
723
724 it('propagates an error from a noop error boundary during full deferred mounting', () => {
@@ -750,7 +757,7 @@ describe('ReactIncrementalErrorHandling', () => {
757 'RethrowErrorBoundary componentDidCatch',
758 ]);
759 }).toThrow('Hello');
753 - expect(ReactNoop.getChildren()).toEqual([]);
760 + expect(ReactNoop.getChildrenAsJSX()).toEqual(null);
761 });
762
763 it('propagates an error from a noop error boundary during partial deferred mounting', () => {
@@ -801,7 +808,7 @@ describe('ReactIncrementalErrorHandling', () => {
808 // Errored again on retry. Now handle it.
809 'RethrowErrorBoundary componentDidCatch',
810 ]);
804 - expect(ReactNoop.getChildren()).toEqual([]);
811 + expect(ReactNoop).toMatchRenderedOutput(null);
812 });
813
814 it('propagates an error from a noop error boundary during synchronous mounting', () => {
@@ -841,7 +848,7 @@ describe('ReactIncrementalErrorHandling', () => {
848 // Errored again on retry. Now handle it.
849 'RethrowErrorBoundary componentDidCatch',
850 ]);
844 - expect(ReactNoop.getChildren()).toEqual([]);
851 + expect(ReactNoop).toMatchRenderedOutput(null);
852 });
853
854 it('propagates an error from a noop error boundary during batched mounting', () => {
@@ -884,7 +891,7 @@ describe('ReactIncrementalErrorHandling', () => {
891 // Errored again on retry. Now handle it.
892 'RethrowErrorBoundary componentDidCatch',
893 ]);
887 - expect(ReactNoop.getChildren()).toEqual([]);
894 + expect(ReactNoop).toMatchRenderedOutput(null);
895 });
896
897 it('applies batched updates regardless despite errors in scheduling', () => {
@@ -897,7 +904,7 @@ describe('ReactIncrementalErrorHandling', () => {
904 });
905 }).toThrow('Hello');
906 expect(Scheduler).toFlushWithoutYielding();
900 - expect(ReactNoop.getChildren()).toEqual([span('a:3')]);
907 + expect(ReactNoop).toMatchRenderedOutput(<span prop="a:3" />);
908 });
909
910 it('applies nested batched updates despite errors in scheduling', () => {
@@ -914,7 +921,7 @@ describe('ReactIncrementalErrorHandling', () => {
921 });
922 }).toThrow('Hello');
923 expect(Scheduler).toFlushWithoutYielding();
917 - expect(ReactNoop.getChildren()).toEqual([span('a:5')]);
924 + expect(ReactNoop).toMatchRenderedOutput(<span prop="a:5" />);
925 });
926
927 // TODO: Is this a breaking change?
@@ -930,7 +937,7 @@ describe('ReactIncrementalErrorHandling', () => {
937 });
938 }).toThrow('Hello');
939 Scheduler.unstable_flushAll();
933 - expect(ReactNoop.getChildren()).toEqual([span('a:3')]);
940 + expect(ReactNoop).toMatchRenderedOutput(<span prop="a:3" />);
941 });
942
943 it('can schedule updates after uncaught error in render on mount', () => {
@@ -1127,11 +1134,11 @@ describe('ReactIncrementalErrorHandling', () => {
1134 );
1135 ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
1136 expect(Scheduler).toFlushWithoutYielding();
1130 - expect(ReactNoop.getChildren('a')).toEqual([
1131 - span('Caught an error: Hello.'),
1132 - ]);
1137 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(
1138 + <span prop="Caught an error: Hello." />,
1139 + );
1140 expect(Scheduler).toFlushWithoutYielding();
1134 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
1141 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:1" />);
1142 });
1143
1144 it('continues work on other roots despite uncaught errors', () => {
@@ -1143,7 +1150,7 @@ describe('ReactIncrementalErrorHandling', () => {
1150 expect(() => {
1151 expect(Scheduler).toFlushWithoutYielding();
1152 }).toThrow('a');
1146 - expect(ReactNoop.getChildren('a')).toEqual([]);
1153 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1154
1155 ReactNoop.renderToRootWithID(<BrokenRender label="a" />, 'a');
1156 ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
@@ -1152,16 +1159,16 @@ describe('ReactIncrementalErrorHandling', () => {
1159 }).toThrow('a');
1160
1161 expect(Scheduler).toFlushWithoutYielding();
1155 - expect(ReactNoop.getChildren('a')).toEqual([]);
1156 - expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
1162 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1163 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:2" />);
1164
1165 ReactNoop.renderToRootWithID(<span prop="a:3" />, 'a');
1166 ReactNoop.renderToRootWithID(<BrokenRender label="b" />, 'b');
1167 expect(() => {
1168 expect(Scheduler).toFlushWithoutYielding();
1169 }).toThrow('b');
1163 - expect(ReactNoop.getChildren('a')).toEqual([span('a:3')]);
1164 - expect(ReactNoop.getChildren('b')).toEqual([]);
1170 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:3" />);
1171 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1172
1173 ReactNoop.renderToRootWithID(<span prop="a:4" />, 'a');
1174 ReactNoop.renderToRootWithID(<BrokenRender label="b" />, 'b');
@@ -1170,9 +1177,9 @@ describe('ReactIncrementalErrorHandling', () => {
1177 expect(Scheduler).toFlushWithoutYielding();
1178 }).toThrow('b');
1179 expect(Scheduler).toFlushWithoutYielding();
1173 - expect(ReactNoop.getChildren('a')).toEqual([span('a:4')]);
1174 - expect(ReactNoop.getChildren('b')).toEqual([]);
1175 - expect(ReactNoop.getChildren('c')).toEqual([span('c:4')]);
1180 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:4" />);
1181 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1182 + expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:4" />);
1183
1184 ReactNoop.renderToRootWithID(<span prop="a:5" />, 'a');
1185 ReactNoop.renderToRootWithID(<span prop="b:5" />, 'b');
@@ -1183,11 +1190,11 @@ describe('ReactIncrementalErrorHandling', () => {
1190 expect(Scheduler).toFlushWithoutYielding();
1191 }).toThrow('e');
1192 expect(Scheduler).toFlushWithoutYielding();
1186 - expect(ReactNoop.getChildren('a')).toEqual([span('a:5')]);
1187 - expect(ReactNoop.getChildren('b')).toEqual([span('b:5')]);
1188 - expect(ReactNoop.getChildren('c')).toEqual([span('c:5')]);
1189 - expect(ReactNoop.getChildren('d')).toEqual([span('d:5')]);
1190 - expect(ReactNoop.getChildren('e')).toEqual([]);
1193 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:5" />);
1194 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:5" />);
1195 + expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:5" />);
1196 + expect(ReactNoop.getChildrenAsJSX('d')).toEqual(<span prop="d:5" />);
1197 + expect(ReactNoop.getChildrenAsJSX('e')).toEqual(null);
1198
1199 ReactNoop.renderToRootWithID(<BrokenRender label="a" />, 'a');
1200 ReactNoop.renderToRootWithID(<span prop="b:6" />, 'b');
@@ -1207,12 +1214,12 @@ describe('ReactIncrementalErrorHandling', () => {
1214 }).toThrow('e');
1215
1216 expect(Scheduler).toFlushWithoutYielding();
1210 - expect(ReactNoop.getChildren('a')).toEqual([]);
1211 - expect(ReactNoop.getChildren('b')).toEqual([span('b:6')]);
1212 - expect(ReactNoop.getChildren('c')).toEqual([]);
1213 - expect(ReactNoop.getChildren('d')).toEqual([span('d:6')]);
1214 - expect(ReactNoop.getChildren('e')).toEqual([]);
1215 - expect(ReactNoop.getChildren('f')).toEqual([span('f:6')]);
1217 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1218 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:6" />);
1219 + expect(ReactNoop.getChildrenAsJSX('c')).toEqual(null);
1220 + expect(ReactNoop.getChildrenAsJSX('d')).toEqual(<span prop="d:6" />);
1221 + expect(ReactNoop.getChildrenAsJSX('e')).toEqual(null);
1222 + expect(ReactNoop.getChildrenAsJSX('f')).toEqual(<span prop="f:6" />);
1223
1224 ReactNoop.unmountRootWithID('a');
1225 ReactNoop.unmountRootWithID('b');
@@ -1221,12 +1228,12 @@ describe('ReactIncrementalErrorHandling', () => {
1228 ReactNoop.unmountRootWithID('e');
1229 ReactNoop.unmountRootWithID('f');
1230 expect(Scheduler).toFlushWithoutYielding();
1224 - expect(ReactNoop.getChildren('a')).toEqual(null);
1225 - expect(ReactNoop.getChildren('b')).toEqual(null);
1226 - expect(ReactNoop.getChildren('c')).toEqual(null);
1227 - expect(ReactNoop.getChildren('d')).toEqual(null);
1228 - expect(ReactNoop.getChildren('e')).toEqual(null);
1229 - expect(ReactNoop.getChildren('f')).toEqual(null);
1231 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(null);
1232 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(null);
1233 + expect(ReactNoop.getChildrenAsJSX('c')).toEqual(null);
1234 + expect(ReactNoop.getChildrenAsJSX('d')).toEqual(null);
1235 + expect(ReactNoop.getChildrenAsJSX('e')).toEqual(null);
1236 + expect(ReactNoop.getChildrenAsJSX('f')).toEqual(null);
1237 });
1238
1239 it('unwinds the context stack correctly on error', () => {
@@ -1284,7 +1291,7 @@ describe('ReactIncrementalErrorHandling', () => {
1291 expect(Scheduler).toFlushWithoutYielding();
1292
1293 // If the context stack does not unwind, span will get 'abcde'
1287 - expect(ReactNoop.getChildren()).toEqual([span('a')]);
1294 + expect(ReactNoop).toMatchRenderedOutput(<span prop="a" />);
1295 });
1296
1297 it('catches reconciler errors in a boundary during mounting', () => {
@@ -1315,17 +1322,19 @@ describe('ReactIncrementalErrorHandling', () => {
1322 // React retries once on error
1323 'Warning: React.createElement: type is invalid -- expected a string',
1324 ]);
1318 - expect(ReactNoop.getChildren()).toEqual([
1319 - span(
1320 - 'Element type is invalid: expected a string (for built-in components) or ' +
1325 + expect(ReactNoop).toMatchRenderedOutput(
1326 + <span
1327 + prop={
1328 + 'Element type is invalid: expected a string (for built-in components) or ' +
1329 'a class/function (for composite components) but got: undefined.' +
1330 (__DEV__
1331 ? " You likely forgot to export your component from the file it's " +
1332 'defined in, or you might have mixed up default and named imports.' +
1333 '\n\nCheck the render method of `BrokenRender`.'
1326 - : ''),
1327 - ),
1328 - ]);
1334 + : '')
1335 + }
1336 + />,
1337 + );
1338 });
1339
1340 it('catches reconciler errors in a boundary during update', () => {
@@ -1364,17 +1373,19 @@ describe('ReactIncrementalErrorHandling', () => {
1373 // React retries once on error
1374 'Warning: React.createElement: type is invalid -- expected a string',
1375 ]);
1367 - expect(ReactNoop.getChildren()).toEqual([
1368 - span(
1369 - 'Element type is invalid: expected a string (for built-in components) or ' +
1376 + expect(ReactNoop).toMatchRenderedOutput(
1377 + <span
1378 + prop={
1379 + 'Element type is invalid: expected a string (for built-in components) or ' +
1380 'a class/function (for composite components) but got: undefined.' +
1381 (__DEV__
1382 ? " You likely forgot to export your component from the file it's " +
1383 'defined in, or you might have mixed up default and named imports.' +
1384 '\n\nCheck the render method of `BrokenRender`.'
1375 - : ''),
1376 - ),
1377 - ]);
1385 + : '')
1386 + }
1387 + />,
1388 + );
1389 });
1390
1391 it('recovers from uncaught reconciler errors', () => {
@@ -1394,7 +1405,7 @@ describe('ReactIncrementalErrorHandling', () => {
1405
1406 ReactNoop.render(<span prop="hi" />);
1407 expect(Scheduler).toFlushWithoutYielding();
1397 - expect(ReactNoop.getChildren()).toEqual([span('hi')]);
1408 + expect(ReactNoop).toMatchRenderedOutput(<span prop="hi" />);
1409 });
1410
1411 it('unmounts components with uncaught errors', () => {
@@ -1447,7 +1458,7 @@ describe('ReactIncrementalErrorHandling', () => {
1458 'Parent componentWillUnmount [!]',
1459 'BrokenRenderAndUnmount componentWillUnmount',
1460 ]);
1450 - expect(ReactNoop.getChildren()).toEqual([]);
1461 + expect(ReactNoop).toMatchRenderedOutput(null);
1462
1463 expect(() => {
1464 ReactNoop.flushSync();
@@ -1478,7 +1489,11 @@ describe('ReactIncrementalErrorHandling', () => {
1489
1490 ReactNoop.render(<Foo />);
1491 expect(Scheduler).toFlushAndYield(['barRef attach']);
1481 - expect(ReactNoop.getChildren()).toEqual([div(span('Bar'))]);
1492 + expect(ReactNoop).toMatchRenderedOutput(
1493 + <div>
1494 + <span prop="Bar" />
1495 + </div>,
1496 + );
1497
1498 // Unmount
1499 ReactNoop.render(<Foo hide={true} />);
@@ -1489,7 +1504,7 @@ describe('ReactIncrementalErrorHandling', () => {
1504 'Bar unmount',
1505 ]);
1506 // Because there was an error, entire tree should unmount
1492 - expect(ReactNoop.getChildren()).toEqual([]);
1507 + expect(ReactNoop).toMatchRenderedOutput(null);
1508 });
1509
1510 it('handles error thrown by host config while working on failed root', () => {
@@ -1562,7 +1577,9 @@ describe('ReactIncrementalErrorHandling', () => {
1577 'componentDidCatch',
1578 'ErrorBoundary (catch)',
1579 ]);
1565 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops')]);
1580 + expect(ReactNoop).toMatchRenderedOutput(
1581 + <span prop="Caught an error: oops" />,
1582 + );
1583
1584 if (__DEV__) {
1585 expect(console.error).toHaveBeenCalledTimes(1);
@@ -1635,7 +1652,9 @@ describe('ReactIncrementalErrorHandling', () => {
1652 'ErrorBoundary (catch)',
1653 'ErrorMessage',
1654 ]);
1638 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
1655 + expect(ReactNoop).toMatchRenderedOutput(
1656 + <span prop="Caught an error: oops!" />,
1657 + );
1658 });
1659
1660 it('calls the correct lifecycles on the error boundary after catching an error (mixed)', () => {
@@ -1676,7 +1695,9 @@ describe('ReactIncrementalErrorHandling', () => {
1695 'render error message',
1696 'did update',
1697 ]);
1679 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);
1698 + expect(ReactNoop).toMatchRenderedOutput(
1699 + <span prop="Caught an error: oops!" />,
1700 + );
1701 });
1702
1703 it('provides component stack to the error boundary with componentDidCatch', () => {
@@ -1710,13 +1731,15 @@ describe('ReactIncrementalErrorHandling', () => {
1731 </ErrorBoundary>,
1732 );
1733 expect(Scheduler).toFlushAndYield(['render error message']);
1713 - expect(ReactNoop.getChildren()).toEqual([
1714 - span(
1715 - 'Caught an error:\n' +
1734 + expect(ReactNoop).toMatchRenderedOutput(
1735 + <span
1736 + prop={
1737 + 'Caught an error:\n' +
1738 ' in BrokenRender (at **)\n' +
1717 - ' in ErrorBoundary (at **).',
1718 - ),
1719 - ]);
1739 + ' in ErrorBoundary (at **).'
1740 + }
1741 + />,
1742 + );
1743 });
1744
1745 it('does not provide component stack to the error boundary with getDerivedStateFromError', () => {
@@ -1744,7 +1767,9 @@ describe('ReactIncrementalErrorHandling', () => {
1767 </ErrorBoundary>,
1768 );
1769 expect(Scheduler).toFlushWithoutYielding();
1747 - expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello')]);
1770 + expect(ReactNoop).toMatchRenderedOutput(
1771 + <span prop="Caught an error: Hello" />,
1772 + );
1773 });
1774
1775 it('provides component stack even if overriding prepareStackTrace', () => {
@@ -1794,13 +1819,15 @@ describe('ReactIncrementalErrorHandling', () => {
1819 expect(Scheduler).toFlushAndYield(['render error message']);
1820 Error.prepareStackTrace = undefined;
1821
1797 - expect(ReactNoop.getChildren()).toEqual([
1798 - span(
1799 - 'Caught an error:\n' +
1822 + expect(ReactNoop).toMatchRenderedOutput(
1823 + <span
1824 + prop={
1825 + 'Caught an error:\n' +
1826 ' in BrokenRender (at **)\n' +
1801 - ' in ErrorBoundary (at **).',
1802 - ),
1803 - ]);
1827 + ' in ErrorBoundary (at **).'
1828 + }
1829 + />,
1830 + );
1831 });
1832
1833 if (!ReactFeatureFlags.disableModulePatternComponents) {
packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js
+4 -8
@@ -25,13 +25,9 @@ describe('ReactIncrementalScheduling', () => {
25 act = require('jest-react').act;
26 });
27
28 - function span(prop) {
29 - return {type: 'span', children: [], prop, hidden: false};
30 - }
31 -
28 it('schedules and flushes deferred work', () => {
29 ReactNoop.render(<span prop="1" />);
34 - expect(ReactNoop.getChildren()).toEqual([]);
30 + expect(ReactNoop).toMatchRenderedOutput(null);
31
32 expect(Scheduler).toFlushWithoutYielding();
33 expect(ReactNoop).toMatchRenderedOutput(<span prop="1" />);
@@ -44,9 +40,9 @@ describe('ReactIncrementalScheduling', () => {
40
41 expect(Scheduler).toFlushWithoutYielding();
42
47 - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
48 - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
49 - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
43 + expect(ReactNoop.getChildrenAsJSX('a')).toEqual(<span prop="a:1" />);
44 + expect(ReactNoop.getChildrenAsJSX('b')).toEqual(<span prop="b:1" />);
45 + expect(ReactNoop.getChildrenAsJSX('c')).toEqual(<span prop="c:1" />);
46 });
47
48 it('schedules top-level updates in order of priority', () => {
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+260 -194
@@ -23,21 +23,6 @@ describe('ReactIncrementalSideEffects', () => {
23 Scheduler = require('scheduler');
24 });
25
26 - function div(...children) {
27 - children = children.map(c =>
28 - typeof c === 'string' ? {text: c, hidden: false} : c,
29 - );
30 - return {type: 'div', children, prop: undefined, hidden: false};
31 - }
32 -
33 - function span(prop) {
34 - return {type: 'span', children: [], prop, hidden: false};
35 - }
36 -
37 - function text(t) {
38 - return {text: t, hidden: false};
39 - }
40 -
26 // Note: This is based on a similar component we use in www. We can delete
27 // once the extra div wrapper is no longer necessary.
28 function LegacyHiddenDiv({children, mode}) {
@@ -67,11 +52,20 @@ describe('ReactIncrementalSideEffects', () => {
52
53 ReactNoop.render(<Foo text="Hello" />);
54 expect(Scheduler).toFlushWithoutYielding();
70 - expect(ReactNoop.getChildren()).toEqual([div(span())]);
55 + expect(ReactNoop).toMatchRenderedOutput(
56 + <div>
57 + <span>Hello</span>
58 + </div>,
59 + );
60
61 ReactNoop.render(<Foo text="World" />);
62 expect(Scheduler).toFlushWithoutYielding();
74 - expect(ReactNoop.getChildren()).toEqual([div(span(), span())]);
63 + expect(ReactNoop).toMatchRenderedOutput(
64 + <div>
65 + <span>World</span>
66 + <span>World</span>
67 + </div>,
68 + );
69 });
70
71 it('can update child nodes of a fragment', function () {
@@ -95,19 +89,34 @@ describe('ReactIncrementalSideEffects', () => {
89
90 ReactNoop.render(<Foo text="Hello" />);
91 expect(Scheduler).toFlushWithoutYielding();
98 - expect(ReactNoop.getChildren()).toEqual([div(span(), span('test'))]);
92 + expect(ReactNoop).toMatchRenderedOutput(
93 + <div>
94 + <span>Hello</span>
95 + <span prop="test" />
96 + </div>,
97 + );
98
99 ReactNoop.render(<Foo text="World" />);
100 expect(Scheduler).toFlushWithoutYielding();
102 - expect(ReactNoop.getChildren()).toEqual([
103 - div(span(), span(), div(), span('test')),
104 - ]);
101 + expect(ReactNoop).toMatchRenderedOutput(
102 + <div>
103 + <span>World</span>
104 + <span>World</span>
105 + <div />
106 + <span prop="test" />
107 + </div>,
108 + );
109
110 ReactNoop.render(<Foo text="Hi" />);
111 expect(Scheduler).toFlushWithoutYielding();
108 - expect(ReactNoop.getChildren()).toEqual([
109 - div(span(), div(), span(), span('test')),
110 - ]);
112 + expect(ReactNoop).toMatchRenderedOutput(
113 + <div>
114 + <span>Hi</span>
115 + <div />
116 + <span>Hi</span>
117 + <span prop="test" />
118 + </div>,
119 + );
120 });
121
122 it('can update child nodes rendering into text nodes', function () {
@@ -128,11 +137,11 @@ describe('ReactIncrementalSideEffects', () => {
137
138 ReactNoop.render(<Foo text="Hello" />);
139 expect(Scheduler).toFlushWithoutYielding();
131 - expect(ReactNoop.getChildren()).toEqual([div('Hello')]);
140 + expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
141
142 ReactNoop.render(<Foo text="World" />);
143 expect(Scheduler).toFlushWithoutYielding();
135 - expect(ReactNoop.getChildren()).toEqual([div('World', 'World', '!')]);
144 + expect(ReactNoop).toMatchRenderedOutput(<div>WorldWorld!</div>);
145 });
146
147 it('can deletes children either components, host or text', function () {
@@ -152,13 +161,17 @@ describe('ReactIncrementalSideEffects', () => {
161
162 ReactNoop.render(<Foo show={true} />);
163 expect(Scheduler).toFlushWithoutYielding();
155 - expect(ReactNoop.getChildren()).toEqual([
156 - div(div(), span('Hello'), 'World'),
157 - ]);
164 + expect(ReactNoop).toMatchRenderedOutput(
165 + <div>
166 + <div />
167 + <span prop="Hello" />
168 + World
169 + </div>,
170 + );
171
172 ReactNoop.render(<Foo show={false} />);
173 expect(Scheduler).toFlushWithoutYielding();
161 - expect(ReactNoop.getChildren()).toEqual([div()]);
174 + expect(ReactNoop).toMatchRenderedOutput(<div />);
175 });
176
177 it('can delete a child that changes type - implicit keys', function () {
@@ -194,23 +207,33 @@ describe('ReactIncrementalSideEffects', () => {
207
208 ReactNoop.render(<Foo useClass={true} />);
209 expect(Scheduler).toFlushWithoutYielding();
197 - expect(ReactNoop.getChildren()).toEqual([div(span('Class'), 'Trail')]);
210 + expect(ReactNoop).toMatchRenderedOutput(
211 + <div>
212 + <span prop="Class" />
213 + Trail
214 + </div>,
215 + );
216
217 expect(unmounted).toBe(false);
218
219 ReactNoop.render(<Foo useFunction={true} />);
220 expect(Scheduler).toFlushWithoutYielding();
203 - expect(ReactNoop.getChildren()).toEqual([div(span('Function'), 'Trail')]);
221 + expect(ReactNoop).toMatchRenderedOutput(
222 + <div>
223 + <span prop="Function" />
224 + Trail
225 + </div>,
226 + );
227
228 expect(unmounted).toBe(true);
229
230 ReactNoop.render(<Foo useText={true} />);
231 expect(Scheduler).toFlushWithoutYielding();
209 - expect(ReactNoop.getChildren()).toEqual([div('Text', 'Trail')]);
232 + expect(ReactNoop).toMatchRenderedOutput(<div>TextTrail</div>);
233
234 ReactNoop.render(<Foo />);
235 expect(Scheduler).toFlushWithoutYielding();
213 - expect(ReactNoop.getChildren()).toEqual([div('Trail')]);
236 + expect(ReactNoop).toMatchRenderedOutput(<div>Trail</div>);
237 });
238
239 it('can delete a child that changes type - explicit keys', function () {
@@ -244,19 +267,29 @@ describe('ReactIncrementalSideEffects', () => {
267
268 ReactNoop.render(<Foo useClass={true} />);
269 expect(Scheduler).toFlushWithoutYielding();
247 - expect(ReactNoop.getChildren()).toEqual([div(span('Class'), 'Trail')]);
270 + expect(ReactNoop).toMatchRenderedOutput(
271 + <div>
272 + <span prop="Class" />
273 + Trail
274 + </div>,
275 + );
276
277 expect(unmounted).toBe(false);
278
279 ReactNoop.render(<Foo useFunction={true} />);
280 expect(Scheduler).toFlushWithoutYielding();
253 - expect(ReactNoop.getChildren()).toEqual([div(span('Function'), 'Trail')]);
281 + expect(ReactNoop).toMatchRenderedOutput(
282 + <div>
283 + <span prop="Function" />
284 + Trail
285 + </div>,
286 + );
287
288 expect(unmounted).toBe(true);
289
290 ReactNoop.render(<Foo />);
291 expect(Scheduler).toFlushWithoutYielding();
259 - expect(ReactNoop.getChildren()).toEqual([div('Trail')]);
292 + expect(ReactNoop).toMatchRenderedOutput(<div>Trail</div>);
293 });
294
295 it('can delete a child when it unmounts inside a portal', () => {
@@ -280,12 +313,14 @@ describe('ReactIncrementalSideEffects', () => {
313 </div>,
314 );
315 expect(Scheduler).toFlushWithoutYielding();
283 - expect(ReactNoop.getChildren()).toEqual([div()]);
284 - expect(ReactNoop.getChildren('portalContainer')).toEqual([
285 - div(),
286 - span('Hello'),
287 - text('World'),
288 - ]);
316 + expect(ReactNoop).toMatchRenderedOutput(<div />);
317 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
318 + <>
319 + <div />
320 + <span prop="Hello" />
321 + World
322 + </>,
323 + );
324
325 ReactNoop.render(
326 <div>
@@ -293,8 +328,8 @@ describe('ReactIncrementalSideEffects', () => {
328 </div>,
329 );
330 expect(Scheduler).toFlushWithoutYielding();
296 - expect(ReactNoop.getChildren()).toEqual([div()]);
297 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
331 + expect(ReactNoop).toMatchRenderedOutput(<div />);
332 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
333
334 ReactNoop.render(
335 <div>
@@ -302,36 +337,40 @@ describe('ReactIncrementalSideEffects', () => {
337 </div>,
338 );
339 expect(Scheduler).toFlushWithoutYielding();
305 - expect(ReactNoop.getChildren()).toEqual([div()]);
306 - expect(ReactNoop.getChildren('portalContainer')).toEqual([
307 - div(),
308 - span('Hello'),
309 - text('World'),
310 - ]);
340 + expect(ReactNoop).toMatchRenderedOutput(<div />);
341 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
342 + <>
343 + <div />
344 + <span prop="Hello" />
345 + World
346 + </>,
347 + );
348
349 ReactNoop.render(null);
350 expect(Scheduler).toFlushWithoutYielding();
314 - expect(ReactNoop.getChildren()).toEqual([]);
315 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
351 + expect(ReactNoop).toMatchRenderedOutput(null);
352 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
353
354 ReactNoop.render(<Foo show={false} />);
355 expect(Scheduler).toFlushWithoutYielding();
319 - expect(ReactNoop.getChildren()).toEqual([]);
320 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
356 + expect(ReactNoop).toMatchRenderedOutput(null);
357 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
358
359 ReactNoop.render(<Foo show={true} />);
360 expect(Scheduler).toFlushWithoutYielding();
324 - expect(ReactNoop.getChildren()).toEqual([]);
325 - expect(ReactNoop.getChildren('portalContainer')).toEqual([
326 - div(),
327 - span('Hello'),
328 - text('World'),
329 - ]);
361 + expect(ReactNoop).toMatchRenderedOutput(null);
362 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
363 + <>
364 + <div />
365 + <span prop="Hello" />
366 + World
367 + </>,
368 + );
369
370 ReactNoop.render(null);
371 expect(Scheduler).toFlushWithoutYielding();
333 - expect(ReactNoop.getChildren()).toEqual([]);
334 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
372 + expect(ReactNoop).toMatchRenderedOutput(null);
373 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
374 });
375
376 it('can delete a child when it unmounts with a portal', () => {
@@ -355,31 +394,35 @@ describe('ReactIncrementalSideEffects', () => {
394 </div>,
395 );
396 expect(Scheduler).toFlushWithoutYielding();
358 - expect(ReactNoop.getChildren()).toEqual([div()]);
359 - expect(ReactNoop.getChildren('portalContainer')).toEqual([
360 - div(),
361 - span('Hello'),
362 - text('World'),
363 - ]);
397 + expect(ReactNoop).toMatchRenderedOutput(<div />);
398 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
399 + <>
400 + <div />
401 + <span prop="Hello" />
402 + World
403 + </>,
404 + );
405
406 ReactNoop.render(null);
407 expect(Scheduler).toFlushWithoutYielding();
367 - expect(ReactNoop.getChildren()).toEqual([]);
368 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
408 + expect(ReactNoop).toMatchRenderedOutput(null);
409 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
410
411 ReactNoop.render(<Foo />);
412 expect(Scheduler).toFlushWithoutYielding();
372 - expect(ReactNoop.getChildren()).toEqual([]);
373 - expect(ReactNoop.getChildren('portalContainer')).toEqual([
374 - div(),
375 - span('Hello'),
376 - text('World'),
377 - ]);
413 + expect(ReactNoop).toMatchRenderedOutput(null);
414 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(
415 + <>
416 + <div />
417 + <span prop="Hello" />
418 + World
419 + </>,
420 + );
421
422 ReactNoop.render(null);
423 expect(Scheduler).toFlushWithoutYielding();
381 - expect(ReactNoop.getChildren()).toEqual([]);
382 - expect(ReactNoop.getChildren('portalContainer')).toEqual([]);
424 + expect(ReactNoop).toMatchRenderedOutput(null);
425 + expect(ReactNoop.getChildrenAsJSX('portalContainer')).toEqual(null);
426 });
427
428 it('does not update child nodes if a flush is aborted', () => {
@@ -403,9 +446,15 @@ describe('ReactIncrementalSideEffects', () => {
446
447 ReactNoop.render(<Foo text="Hello" />);
448 expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'Bar', 'Bar']);
406 - expect(ReactNoop.getChildren()).toEqual([
407 - div(div(span('Hello'), span('Hello')), span('Yo')),
408 - ]);
449 + expect(ReactNoop).toMatchRenderedOutput(
450 + <div>
451 + <div>
452 + <span prop="Hello" />
453 + <span prop="Hello" />
454 + </div>
455 + <span prop="Yo" />
456 + </div>,
457 + );
458
459 if (gate(flags => flags.enableSyncDefaultUpdates)) {
460 React.startTransition(() => {
@@ -417,9 +466,15 @@ describe('ReactIncrementalSideEffects', () => {
466
467 // Flush some of the work without committing
468 expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Bar']);
420 - expect(ReactNoop.getChildren()).toEqual([
421 - div(div(span('Hello'), span('Hello')), span('Yo')),
422 - ]);
469 + expect(ReactNoop).toMatchRenderedOutput(
470 + <div>
471 + <div>
472 + <span prop="Hello" />
473 + <span prop="Hello" />
474 + </div>
475 + <span prop="Yo" />
476 + </div>,
477 + );
478 });
479
480 // @gate www
@@ -729,55 +784,61 @@ describe('ReactIncrementalSideEffects', () => {
784 }
785 ReactNoop.render(<Foo tick={0} idx={0} />);
786 ReactNoop.flushDeferredPri(40 + 25);
732 - expect(ReactNoop.getChildren()).toEqual([
733 - div(
734 - span(0),
735 - div(/*the spans are down-prioritized and not rendered yet*/),
736 - ),
737 - ]);
787 + expect(ReactNoop).toMatchRenderedOutput(
788 + <div>
789 + <span prop={0} />
790 + <div />
791 + </div>,
792 + );
793 ReactNoop.render(<Foo tick={1} idx={0} />);
794 ReactNoop.flushDeferredPri(35 + 25);
740 - expect(ReactNoop.getChildren()).toEqual([
741 - div(span(1), div(/*still not rendered yet*/)),
742 - ]);
795 + expect(ReactNoop).toMatchRenderedOutput(
796 + <div>
797 + <span prop={1} />
798 + <div>{/*still not rendered yet*/}</div>
799 + </div>,
800 + );
801 ReactNoop.flushDeferredPri(30 + 25);
744 - expect(ReactNoop.getChildren()).toEqual([
745 - div(
746 - span(1),
747 - div(
748 - // Now we had enough time to finish the spans.
749 - span(0),
750 - span(1),
751 - ),
752 - ),
753 - ]);
754 - const innerSpanA = ReactNoop.getChildren()[0].children[1].children[1];
802 + expect(ReactNoop).toMatchRenderedOutput(
803 + <div>
804 + <span prop={1} />
805 + <div>
806 + {/* Now we had enough time to finish the spans. */}
807 + <span prop={0} />
808 + <span prop={1} />
809 + </div>
810 + ,
811 + </div>,
812 + );
813 + const innerSpanA =
814 + ReactNoop.dangerouslyGetChildren()[0].children[1].children[1];
815 ReactNoop.render(<Foo tick={2} idx={1} />);
816 ReactNoop.flushDeferredPri(30 + 25);
757 - expect(ReactNoop.getChildren()).toEqual([
758 - div(
759 - span(2),
760 - div(
761 - // Still same old numbers.
762 - span(0),
763 - span(1),
764 - ),
765 - ),
766 - ]);
817 + expect(ReactNoop).toMatchRenderedOutput(
818 + <div>
819 + <span prop={2} />
820 + <div>
821 + {/* Still same old numbers. */}
822 + <span prop={0} />
823 + <span prop={1} />
824 + </div>
825 + </div>,
826 + );
827 ReactNoop.render(<Foo tick={3} idx={1} />);
828 expect(Scheduler).toFlushWithoutYielding();
769 - expect(ReactNoop.getChildren()).toEqual([
770 - div(
771 - span(3),
772 - div(
773 - // New numbers.
774 - span(1),
775 - span(2),
776 - ),
777 - ),
778 - ]);
829 + expect(ReactNoop).toMatchRenderedOutput(
830 + <div>
831 + <span prop={3} />
832 + <div>
833 + {/* New numbers. */}
834 + <span prop={1} />
835 + <span prop={2} />
836 + </div>
837 + </div>,
838 + );
839
780 - const innerSpanB = ReactNoop.getChildren()[0].children[1].children[1];
840 + const innerSpanB =
841 + ReactNoop.dangerouslyGetChildren()[0].children[1].children[1];
842 // This should have been an update to an existing instance, not recreation.
843 // We verify that by ensuring that the child instance was the same as
844 // before.
@@ -823,39 +884,44 @@ describe('ReactIncrementalSideEffects', () => {
884 }
885 ReactNoop.render(<Foo tick={0} idx={0} />);
886 ReactNoop.flushDeferredPri(65 + 5);
826 - expect(ReactNoop.getChildren()).toEqual([
827 - div(
828 - span(0),
829 - div(/*the spans are down-prioritized and not rendered yet*/),
830 - ),
831 - ]);
887 + expect(ReactNoop).toMatchRenderedOutput(
888 + <div>
889 + <span prop={0} />
890 + {/*the spans are down-prioritized and not rendered yet*/}
891 + <div />
892 + </div>,
893 + );
894
895 expect(ops).toEqual(['Foo', 'Baz', 'Bar']);
896 ops = [];
897
898 ReactNoop.render(<Foo tick={1} idx={0} />);
899 ReactNoop.flushDeferredPri(70);
838 - expect(ReactNoop.getChildren()).toEqual([
839 - div(span(1), div(/*still not rendered yet*/)),
840 - ]);
900 + expect(ReactNoop).toMatchRenderedOutput(
901 + <div>
902 + <span prop={1} />
903 + {/*still not rendered yet*/}
904 + <div />
905 + </div>,
906 + );
907
908 expect(ops).toEqual(['Foo']);
909 ops = [];
910
911 expect(Scheduler).toFlushWithoutYielding();
846 - expect(ReactNoop.getChildren()).toEqual([
847 - div(
848 - span(1),
849 - div(
850 - // Now we had enough time to finish the spans.
851 - span(0),
852 - span(0),
853 - span(0),
854 - span(0),
855 - span(0),
856 - span(0),
857 - ),
858 - ),
912 + expect(ReactNoop).toMatchRenderedOutput([
913 + <div>
914 + <span prop={1} />,
915 + <div>
916 + {/* Now we had enough time to finish the spans. */}
917 + <span prop={0} />,
918 + <span prop={0} />,
919 + <span prop={0} />,
920 + <span prop={0} />,
921 + <span prop={0} />,
922 + <span prop={0} />,
923 + </div>
924 + </div>,
925 ]);
926
927 expect(ops).toEqual(['Bar', 'Baz', 'Bar', 'Bar', 'Baz', 'Bar', 'Bar']);
@@ -865,20 +931,20 @@ describe('ReactIncrementalSideEffects', () => {
931 // way through.
932 ReactNoop.render(<Foo tick={2} idx={1} />);
933 ReactNoop.flushDeferredPri(95);
868 - expect(ReactNoop.getChildren()).toEqual([
869 - div(
870 - span(2),
871 - div(
872 - // Still same old numbers.
873 - span(0),
874 - span(0),
875 - span(0),
876 - span(0),
877 - span(0),
878 - span(0),
879 - ),
880 - ),
881 - ]);
934 + expect(ReactNoop).toMatchRenderedOutput(
935 + <div>
936 + <span prop={2} />,
937 + <div>
938 + {/* Still same old numbers. */}
939 + <span prop={0} />
940 + <span prop={0} />
941 + <span prop={0} />
942 + <span prop={0} />
943 + <span prop={0} />
944 + <span prop={0} />
945 + </div>
946 + </div>,
947 + );
948
949 // We let it finish half way through. That means we'll have one fully
950 // completed Baz, one half-way completed Baz and one fully incomplete Baz.
@@ -889,20 +955,20 @@ describe('ReactIncrementalSideEffects', () => {
955 // way through.
956 ReactNoop.render(<Foo tick={3} idx={1} />);
957 ReactNoop.flushDeferredPri(50);
892 - expect(ReactNoop.getChildren()).toEqual([
893 - div(
894 - span(3),
895 - div(
896 - // Old numbers.
897 - span(0),
898 - span(0),
899 - span(0),
900 - span(0),
901 - span(0),
902 - span(0),
903 - ),
904 - ),
905 - ]);
958 + expect(ReactNoop).toMatchRenderedOutput(
959 + <div>
960 + <span prop={3} />
961 + <div>
962 + {/* Old numbers. */}
963 + <span prop={0} />
964 + <span prop={0} />
965 + <span prop={0} />
966 + <span prop={0} />
967 + <span prop={0} />
968 + <span prop={0} />
969 + </div>
970 + </div>,
971 + );
972
973 expect(ops).toEqual(['Foo']);
974 ops = [];
@@ -910,19 +976,19 @@ describe('ReactIncrementalSideEffects', () => {
976 // We should now be able to reuse some of the work we've already done
977 // and replay those side-effects.
978 expect(Scheduler).toFlushWithoutYielding();
913 - expect(ReactNoop.getChildren()).toEqual([
914 - div(
915 - span(3),
916 - div(
917 - // New numbers.
918 - span(1),
919 - span(1),
920 - span(1),
921 - span(1),
922 - span(1),
923 - span(1),
924 - ),
925 - ),
979 + expect(ReactNoop).toMatchRenderedOutput([
980 + <div>
981 + <span prop={3} />,
982 + <div>
983 + {/* New numbers. */}
984 + <span prop={1} />
985 + <span prop={1} />
986 + <span prop={1} />
987 + <span prop={1} />
988 + <span prop={1} />
989 + <span prop={1} />
990 + </div>
991 + </div>,
992 ]);
993
994 expect(ops).toEqual(['Bar', 'Baz', 'Bar', 'Bar']);
@@ -1039,10 +1105,10 @@ describe('ReactIncrementalSideEffects', () => {
1105
1106 ReactNoop.render(<Foo />);
1107 expect(Scheduler).toFlushWithoutYielding();
1042 - expect(ReactNoop.getChildren()).toEqual([span('foo')]);
1108 + expect(ReactNoop).toMatchRenderedOutput(<span prop="foo" />);
1109 let called = false;
1110 instance.setState({text: 'bar'}, () => {
1045 - expect(ReactNoop.getChildren()).toEqual([span('bar')]);
1111 + expect(ReactNoop).toMatchRenderedOutput(<span prop="bar" />);
1112 called = true;
1113 });
1114 expect(Scheduler).toFlushWithoutYielding();
@@ -1067,7 +1133,7 @@ describe('ReactIncrementalSideEffects', () => {
1133
1134 ReactNoop.render(<Foo />);
1135 expect(Scheduler).toFlushWithoutYielding();
1070 - expect(ReactNoop.getChildren()).toEqual([span('foo')]);
1136 + expect(ReactNoop).toMatchRenderedOutput(<span prop="foo" />);
1137 let called = false;
1138 instance.setState({}, () => {
1139 called = true;
@@ -1253,7 +1319,7 @@ describe('ReactIncrementalSideEffects', () => {
1319 expect(ops).toEqual([
1320 classInstance,
1321 // no call for function components
1256 - div(),
1322 + {type: 'div', children: [], prop: undefined, hidden: false},
1323 ]);
1324
1325 ops = [];
@@ -1267,7 +1333,7 @@ describe('ReactIncrementalSideEffects', () => {
1333 null,
1334 // reattach as a separate phase
1335 classInstance,
1270 - div(),
1336 + {type: 'div', children: [], prop: undefined, hidden: false},
1337 ]);
1338
1339 ops = [];
packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.js
+3 -2
@@ -316,7 +316,8 @@ describe('ReactIncrementalTriangle', () => {
316 reset();
317
318 function assertConsistentTree(activeTriangleIndices = new Set(), counter) {
319 - const children = ReactNoop.getChildren(rootID);
319 + const childrenJSX = ReactNoop.getPendingChildrenAsJSX(rootID);
320 + const children = childrenJSX === null ? [] : childrenJSX.props.children;
321
322 if (children.length !== TOTAL_CHILDREN) {
323 throw new Error('Wrong number of children.');
@@ -327,7 +328,7 @@ describe('ReactIncrementalTriangle', () => {
328 for (let i = 0; i < children.length; i++) {
329 const child = children[i];
330
330 - const output = JSON.parse(child.prop);
331 + const output = JSON.parse(child.props.prop);
332 const prop = output.prop;
333 const isActive = output.isActive;
334 const counterContext = output.counterContext;
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+10 -14
@@ -28,10 +28,6 @@ describe('ReactIncrementalUpdates', () => {
28 require('react-reconciler/constants').ContinuousEventPriority;
29 });
30
31 - function span(prop) {
32 - return {type: 'span', children: [], prop, hidden: false};
33 - }
34 -
31 function flushNextRenderIfExpired() {
32 // This will start rendering the next level of work. If the work hasn't
33 // expired yet, React will exit without doing anything. If it has expired,
@@ -174,7 +170,7 @@ describe('ReactIncrementalUpdates', () => {
170
171 // Begin the updates but don't flush them yet
172 expect(Scheduler).toFlushAndYieldThrough(['a', 'b', 'c']);
177 - expect(ReactNoop.getChildren()).toEqual([span('')]);
173 + expect(ReactNoop).toMatchRenderedOutput(<span prop="" />);
174
175 // Schedule some more updates at different priorities
176 instance.setState(createUpdate('d'));
@@ -193,11 +189,11 @@ describe('ReactIncrementalUpdates', () => {
189 )
190 ) {
191 expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
196 - expect(ReactNoop.getChildren()).toEqual([span('def')]);
192 + expect(ReactNoop).toMatchRenderedOutput(<span prop="def" />);
193 } else {
194 // Update d was dropped and replaced by e.
195 expect(Scheduler).toHaveYielded(['e', 'f']);
200 - expect(ReactNoop.getChildren()).toEqual([span('ef')]);
196 + expect(ReactNoop).toMatchRenderedOutput(<span prop="ef" />);
197 }
198
199 // Now flush the remaining work. Even though e and f were already processed,
@@ -235,7 +231,7 @@ describe('ReactIncrementalUpdates', () => {
231 'g',
232 ]);
233 }
238 - expect(ReactNoop.getChildren()).toEqual([span('abcdefg')]);
234 + expect(ReactNoop).toMatchRenderedOutput(<span prop="abcdefg" />);
235 });
236
237 it('can abort an update, schedule a replaceState, and resume', () => {
@@ -279,7 +275,7 @@ describe('ReactIncrementalUpdates', () => {
275
276 // Begin the updates but don't flush them yet
277 expect(Scheduler).toFlushAndYieldThrough(['a', 'b', 'c']);
282 - expect(ReactNoop.getChildren()).toEqual([span('')]);
278 + expect(ReactNoop).toMatchRenderedOutput(<span prop="" />);
279
280 // Schedule some more updates at different priorities
281 instance.setState(createUpdate('d'));
@@ -305,7 +301,7 @@ describe('ReactIncrementalUpdates', () => {
301 // Update d was dropped and replaced by e.
302 expect(Scheduler).toHaveYielded(['e', 'f']);
303 }
308 - expect(ReactNoop.getChildren()).toEqual([span('f')]);
304 + expect(ReactNoop).toMatchRenderedOutput(<span prop="f" />);
305
306 // Now flush the remaining work. Even though e and f were already processed,
307 // they should be processed again, to ensure that the terminal state
@@ -342,7 +338,7 @@ describe('ReactIncrementalUpdates', () => {
338 'g',
339 ]);
340 }
345 - expect(ReactNoop.getChildren()).toEqual([span('fg')]);
341 + expect(ReactNoop).toMatchRenderedOutput(<span prop="fg" />);
342 });
343
344 it('passes accumulation of previous updates to replaceState updater function', () => {
@@ -537,7 +533,7 @@ describe('ReactIncrementalUpdates', () => {
533 ReactNoop.flushSync(() => {
534 ReactNoop.render(<Foo />);
535 });
540 - expect(ReactNoop.getChildren()).toEqual([span('derived state')]);
536 + expect(ReactNoop).toMatchRenderedOutput(<span prop="derived state" />);
537
538 ReactNoop.flushSync(() => {
539 // Triggers getDerivedStateFromProps again
@@ -546,12 +542,12 @@ describe('ReactIncrementalUpdates', () => {
542 // led to this bug. Removing it causes it to "accidentally" work.
543 foo.setState({value: 'update state'}, function noop() {});
544 });
549 - expect(ReactNoop.getChildren()).toEqual([span('derived state')]);
545 + expect(ReactNoop).toMatchRenderedOutput(<span prop="derived state" />);
546
547 ReactNoop.flushSync(() => {
548 bar.setState({});
549 });
554 - expect(ReactNoop.getChildren()).toEqual([span('derived state')]);
550 + expect(ReactNoop).toMatchRenderedOutput(<span prop="derived state" />);
551 });
552
553 it('regression: does not expire soon due to layout effects in the last batch', () => {
packages/react-reconciler/src/__tests__/ReactMemo-test.js
+15 -19
@@ -31,10 +31,6 @@ describe('memo', () => {
31 ({Suspense} = React);
32 });
33
34 - function span(prop) {
35 - return {type: 'span', children: [], prop, hidden: false};
36 - }
37 -
34 function Text(props) {
35 Scheduler.unstable_yieldValue(props.text);
36 return <span prop={props.text} />;
@@ -112,7 +108,7 @@ describe('memo', () => {
108 expect(Scheduler).toFlushAndYield(['Loading...']);
109 await Promise.resolve();
110 expect(Scheduler).toFlushAndYield([0]);
115 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
111 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
112
113 // Should bail out because props have not changed
114 ReactNoop.render(
@@ -121,7 +117,7 @@ describe('memo', () => {
117 </Suspense>,
118 );
119 expect(Scheduler).toFlushAndYield([]);
124 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
120 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
121
122 // Should update because count prop changed
123 ReactNoop.render(
@@ -130,7 +126,7 @@ describe('memo', () => {
126 </Suspense>,
127 );
128 expect(Scheduler).toFlushAndYield([1]);
133 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
129 + expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
130 });
131
132 it("does not bail out if there's a context change", async () => {
@@ -167,17 +163,17 @@ describe('memo', () => {
163 expect(Scheduler).toFlushAndYield(['Loading...']);
164 await Promise.resolve();
165 expect(Scheduler).toFlushAndYield(['Count: 0']);
170 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
166 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
167
168 // Should bail out because props have not changed
169 ReactNoop.render(<Parent ref={parent} />);
170 expect(Scheduler).toFlushAndYield([]);
175 - expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
171 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 0" />);
172
173 // Should update because there was a context change
174 parent.current.setState({count: 1});
175 expect(Scheduler).toFlushAndYield(['Count: 1']);
180 - expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
176 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Count: 1" />);
177 });
178
179 it('consistent behavior for reusing props object across different function component types', async () => {
@@ -352,7 +348,7 @@ describe('memo', () => {
348 expect(Scheduler).toFlushAndYield(['Loading...']);
349 await Promise.resolve();
350 expect(Scheduler).toFlushAndYield([0]);
355 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
351 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
352
353 // Should bail out because props have not changed
354 ReactNoop.render(
@@ -361,7 +357,7 @@ describe('memo', () => {
357 </Suspense>,
358 );
359 expect(Scheduler).toFlushAndYield(['Old count: 0, New count: 0']);
364 - expect(ReactNoop.getChildren()).toEqual([span(0)]);
360 + expect(ReactNoop).toMatchRenderedOutput(<span prop={0} />);
361
362 // Should update because count prop changed
363 ReactNoop.render(
@@ -370,7 +366,7 @@ describe('memo', () => {
366 </Suspense>,
367 );
368 expect(Scheduler).toFlushAndYield(['Old count: 0, New count: 1', 1]);
373 - expect(ReactNoop.getChildren()).toEqual([span(1)]);
369 + expect(ReactNoop).toMatchRenderedOutput(<span prop={1} />);
370 });
371
372 it('supports non-pure class components', async () => {
@@ -390,7 +386,7 @@ describe('memo', () => {
386 expect(Scheduler).toFlushAndYield(['Loading...']);
387 await Promise.resolve();
388 expect(Scheduler).toFlushAndYield(['0!']);
393 - expect(ReactNoop.getChildren()).toEqual([span('0!')]);
389 + expect(ReactNoop).toMatchRenderedOutput(<span prop="0!" />);
390
391 // Should bail out because props have not changed
392 ReactNoop.render(
@@ -399,7 +395,7 @@ describe('memo', () => {
395 </Suspense>,
396 );
397 expect(Scheduler).toFlushAndYield([]);
402 - expect(ReactNoop.getChildren()).toEqual([span('0!')]);
398 + expect(ReactNoop).toMatchRenderedOutput(<span prop="0!" />);
399
400 // Should update because count prop changed
401 ReactNoop.render(
@@ -408,7 +404,7 @@ describe('memo', () => {
404 </Suspense>,
405 );
406 expect(Scheduler).toFlushAndYield(['1!']);
411 - expect(ReactNoop.getChildren()).toEqual([span('1!')]);
407 + expect(ReactNoop).toMatchRenderedOutput(<span prop="1!" />);
408 });
409
410 it('supports defaultProps defined on the memo() return value', async () => {
@@ -447,7 +443,7 @@ describe('memo', () => {
443 }).toErrorDev([
444 'Counter: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
445 ]);
450 - expect(ReactNoop.getChildren()).toEqual([span(15)]);
446 + expect(ReactNoop).toMatchRenderedOutput(<span prop={15} />);
447
448 // Should bail out because props have not changed
449 ReactNoop.render(
@@ -456,7 +452,7 @@ describe('memo', () => {
452 </Suspense>,
453 );
454 expect(Scheduler).toFlushAndYield([]);
459 - expect(ReactNoop.getChildren()).toEqual([span(15)]);
455 + expect(ReactNoop).toMatchRenderedOutput(<span prop={15} />);
456
457 // Should update because count prop changed
458 ReactNoop.render(
@@ -465,7 +461,7 @@ describe('memo', () => {
461 </Suspense>,
462 );
463 expect(Scheduler).toFlushAndYield([20]);
468 - expect(ReactNoop.getChildren()).toEqual([span(20)]);
464 + expect(ReactNoop).toMatchRenderedOutput(<span prop={20} />);
465 });
466
467 it('warns if the first argument is undefined', () => {
packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+106 -74
@@ -128,12 +128,12 @@ describe('ReactNewContext', () => {
128
129 ReactNoop.render(<App value={2} />);
130 expect(Scheduler).toFlushWithoutYielding();
131 - expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);
131 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
132
133 // Update
134 ReactNoop.render(<App value={3} />);
135 expect(Scheduler).toFlushWithoutYielding();
136 - expect(ReactNoop.getChildren()).toEqual([span('Result: 3')]);
136 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 3" />);
137 });
138
139 it('propagates through shouldComponentUpdate false', () => {
@@ -193,7 +193,7 @@ describe('ReactNewContext', () => {
193 'Consumer',
194 'Consumer render prop',
195 ]);
196 - expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);
196 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
197
198 // Update
199 ReactNoop.render(<App value={3} />);
@@ -202,7 +202,7 @@ describe('ReactNewContext', () => {
202 'Provider',
203 'Consumer render prop',
204 ]);
205 - expect(ReactNoop.getChildren()).toEqual([span('Result: 3')]);
205 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 3" />);
206 });
207
208 it('consumers bail out if context value is the same', () => {
@@ -262,7 +262,7 @@ describe('ReactNewContext', () => {
262 'Consumer',
263 'Consumer render prop',
264 ]);
265 - expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);
265 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
266
267 // Update with the same context value
268 ReactNoop.render(<App value={2} />);
@@ -271,7 +271,7 @@ describe('ReactNewContext', () => {
271 'Provider',
272 // Don't call render prop again
273 ]);
274 - expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);
274 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
275 });
276
277 it('nested providers', () => {
@@ -322,12 +322,12 @@ describe('ReactNewContext', () => {
322
323 ReactNoop.render(<App value={2} />);
324 expect(Scheduler).toFlushWithoutYielding();
325 - expect(ReactNoop.getChildren()).toEqual([span('Result: 8')]);
325 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 8" />);
326
327 // Update
328 ReactNoop.render(<App value={3} />);
329 expect(Scheduler).toFlushWithoutYielding();
330 - expect(ReactNoop.getChildren()).toEqual([span('Result: 12')]);
330 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 12" />);
331 });
332
333 it('should provide the correct (default) values to consumers outside of a provider', () => {
@@ -417,26 +417,32 @@ describe('ReactNewContext', () => {
417
418 ReactNoop.render(<App value={2} />);
419 expect(Scheduler).toFlushWithoutYielding();
420 - expect(ReactNoop.getChildren()).toEqual([
421 - span('Result: 4'),
422 - span('Result: 2'),
423 - ]);
420 + expect(ReactNoop).toMatchRenderedOutput(
421 + <>
422 + <span prop="Result: 4" />
423 + <span prop="Result: 2" />
424 + </>,
425 + );
426
427 // Update
428 ReactNoop.render(<App value={3} />);
429 expect(Scheduler).toFlushWithoutYielding();
428 - expect(ReactNoop.getChildren()).toEqual([
429 - span('Result: 6'),
430 - span('Result: 3'),
431 - ]);
430 + expect(ReactNoop).toMatchRenderedOutput(
431 + <>
432 + <span prop="Result: 6" />
433 + <span prop="Result: 3" />
434 + </>,
435 + );
436
437 // Another update
438 ReactNoop.render(<App value={4} />);
439 expect(Scheduler).toFlushWithoutYielding();
436 - expect(ReactNoop.getChildren()).toEqual([
437 - span('Result: 8'),
438 - span('Result: 4'),
439 - ]);
440 + expect(ReactNoop).toMatchRenderedOutput(
441 + <>
442 + <span prop="Result: 8" />
443 + <span prop="Result: 4" />
444 + </>,
445 + );
446 });
447
448 it('compares context values with Object.is semantics', () => {
@@ -496,7 +502,7 @@ describe('ReactNewContext', () => {
502 'Consumer',
503 'Consumer render prop',
504 ]);
499 - expect(ReactNoop.getChildren()).toEqual([span('Result: NaN')]);
505 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: NaN" />);
506
507 // Update
508 ReactNoop.render(<App value={NaN} />);
@@ -506,7 +512,7 @@ describe('ReactNewContext', () => {
512 // Consumer should not re-render again
513 // 'Consumer render prop',
514 ]);
509 - expect(ReactNoop.getChildren()).toEqual([span('Result: NaN')]);
515 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: NaN" />);
516 });
517
518 it('context unwinds when interrupted', () => {
@@ -555,10 +561,10 @@ describe('ReactNewContext', () => {
561
562 ReactNoop.render(<App value="A" />);
563 expect(Scheduler).toFlushWithoutYielding();
558 - expect(ReactNoop.getChildren()).toEqual([
564 + expect(ReactNoop).toMatchRenderedOutput(
565 // The second provider should use the default value.
560 - span('Result: Does not unwind'),
561 - ]);
566 + <span prop="Result: Does not unwind" />,
567 + );
568 });
569
570 it("does not re-render if there's an update in a child", () => {
@@ -594,11 +600,15 @@ describe('ReactNewContext', () => {
600 // Initial mount
601 ReactNoop.render(<App value={1} />);
602 expect(Scheduler).toFlushAndYield(['Consumer render prop', 'Child']);
597 - expect(ReactNoop.getChildren()).toEqual([span('Context: 1, Step: 0')]);
603 + expect(ReactNoop).toMatchRenderedOutput(
604 + <span prop="Context: 1, Step: 0" />,
605 + );
606
607 child.setState({step: 1});
608 expect(Scheduler).toFlushAndYield(['Child']);
601 - expect(ReactNoop.getChildren()).toEqual([span('Context: 1, Step: 1')]);
609 + expect(ReactNoop).toMatchRenderedOutput(
610 + <span prop="Context: 1, Step: 1" />,
611 + );
612 });
613
614 it('consumer bails out if value is unchanged and something above bailed out', () => {
@@ -654,17 +664,32 @@ describe('ReactNewContext', () => {
664 'ChildWithCachedRenderCallback',
665 'Consumer',
666 ]);
657 - expect(ReactNoop.getChildren()).toEqual([span(1), span(1)]);
667 + expect(ReactNoop).toMatchRenderedOutput(
668 + <>
669 + <span prop={1} />
670 + <span prop={1} />
671 + </>,
672 + );
673
674 // Update (bailout)
675 ReactNoop.render(<App value={1} />);
676 expect(Scheduler).toFlushAndYield(['App']);
662 - expect(ReactNoop.getChildren()).toEqual([span(1), span(1)]);
677 + expect(ReactNoop).toMatchRenderedOutput(
678 + <>
679 + <span prop={1} />
680 + <span prop={1} />
681 + </>,
682 + );
683
684 // Update (no bailout)
685 ReactNoop.render(<App value={2} />);
686 expect(Scheduler).toFlushAndYield(['App', 'Consumer', 'Consumer']);
667 - expect(ReactNoop.getChildren()).toEqual([span(2), span(2)]);
687 + expect(ReactNoop).toMatchRenderedOutput(
688 + <>
689 + <span prop={2} />
690 + <span prop={2} />
691 + </>,
692 + );
693 });
694
695 // @gate www
@@ -788,26 +813,32 @@ describe('ReactNewContext', () => {
813 let inst;
814 ReactNoop.render(<App ref={ref => (inst = ref)} />);
815 expect(Scheduler).toFlushAndYield(['App']);
791 - expect(ReactNoop.getChildren()).toEqual([
792 - span('static 1'),
793 - span('static 2'),
794 - ]);
816 + expect(ReactNoop).toMatchRenderedOutput(
817 + <>
818 + <span prop="static 1" />
819 + <span prop="static 2" />
820 + </>,
821 + );
822 // Update the first time
823 inst.setState({step: 1});
824 expect(Scheduler).toFlushAndYield(['App', 'Consumer']);
798 - expect(ReactNoop.getChildren()).toEqual([
799 - span('static 1'),
800 - span('static 2'),
801 - span(1),
802 - ]);
825 + expect(ReactNoop).toMatchRenderedOutput(
826 + <>
827 + <span prop="static 1" />
828 + <span prop="static 2" />
829 + <span prop={1} />
830 + </>,
831 + );
832 // Update the second time
833 inst.setState({step: 2});
834 expect(Scheduler).toFlushAndYield(['App', 'Consumer']);
806 - expect(ReactNoop.getChildren()).toEqual([
807 - span('static 1'),
808 - span('static 2'),
809 - span(2),
810 - ]);
835 + expect(ReactNoop).toMatchRenderedOutput(
836 + <>
837 + <span prop="static 1" />
838 + <span prop="static 2" />
839 + <span prop={2} />
840 + </>,
841 + );
842 });
843 });
844 }
@@ -936,7 +967,7 @@ describe('ReactNewContext', () => {
967 // Initial mount
968 ReactNoop.render(<App value={1} />);
969 expect(Scheduler).toFlushAndYield(['App', 'Child']);
939 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
970 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
971
972 // Update
973 ReactNoop.render(<App value={1} />);
@@ -944,7 +975,7 @@ describe('ReactNewContext', () => {
975 'App',
976 // Child does not re-render
977 ]);
947 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
978 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
979 });
980
981 it('provider does not bail out if legacy context changed above', () => {
@@ -995,22 +1026,22 @@ describe('ReactNewContext', () => {
1026 </LegacyProvider>,
1027 );
1028 expect(Scheduler).toFlushAndYield(['LegacyProvider', 'App', 'Child']);
998 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
1029 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
1030
1031 // Update App with same value (should bail out)
1032 appRef.current.setState({value: 1});
1033 expect(Scheduler).toFlushAndYield(['App']);
1003 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
1034 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
1035
1036 // Update LegacyProvider (should not bail out)
1037 legacyProviderRef.current.setState({value: 1});
1038 expect(Scheduler).toFlushAndYield(['LegacyProvider', 'App', 'Child']);
1008 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
1039 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
1040
1041 // Update App with same value (should bail out)
1042 appRef.current.setState({value: 1});
1043 expect(Scheduler).toFlushAndYield(['App']);
1013 - expect(ReactNoop.getChildren()).toEqual([span('Child')]);
1044 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
1045 });
1046 });
1047
@@ -1066,17 +1097,17 @@ describe('ReactNewContext', () => {
1097
1098 ReactNoop.render(<App foo={1} bar={1} />);
1099 expect(Scheduler).toFlushAndYield(['Foo: 1, Bar: 1']);
1069 - expect(ReactNoop.getChildren()).toEqual([span('Foo: 1, Bar: 1')]);
1100 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 1, Bar: 1" />);
1101
1102 // Update foo
1103 ReactNoop.render(<App foo={2} bar={1} />);
1104 expect(Scheduler).toFlushAndYield(['Foo: 2, Bar: 1']);
1074 - expect(ReactNoop.getChildren()).toEqual([span('Foo: 2, Bar: 1')]);
1105 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 2, Bar: 1" />);
1106
1107 // Update bar
1108 ReactNoop.render(<App foo={2} bar={2} />);
1109 expect(Scheduler).toFlushAndYield(['Foo: 2, Bar: 2']);
1079 - expect(ReactNoop.getChildren()).toEqual([span('Foo: 2, Bar: 2')]);
1110 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 2, Bar: 2" />);
1111 });
1112
1113 // Context consumer bails out on propagating "deep" updates when `value` hasn't changed.
@@ -1112,12 +1143,12 @@ describe('ReactNewContext', () => {
1143 let inst;
1144 ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
1145 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1115 - expect(ReactNoop.getChildren()).toEqual([span('hello')]);
1146 + expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
1147
1148 // Update
1149 inst.setState({text: 'goodbye'});
1150 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1120 - expect(ReactNoop.getChildren()).toEqual([span('goodbye')]);
1151 + expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
1152 });
1153 });
1154
@@ -1186,33 +1217,33 @@ describe('ReactNewContext', () => {
1217
1218 ReactNoop.render(<App foo={1} bar={1} baz={1} />);
1219 expect(Scheduler).toFlushAndYield(['Foo: 1, Bar: 1', 'Baz: 1']);
1189 - expect(ReactNoop.getChildren()).toEqual([
1190 - span('Foo: 1, Bar: 1'),
1191 - span('Baz: 1'),
1220 + expect(ReactNoop).toMatchRenderedOutput([
1221 + <span prop="Foo: 1, Bar: 1" />,
1222 + <span prop="Baz: 1" />,
1223 ]);
1224
1225 // Update only foo
1226 ReactNoop.render(<App foo={2} bar={1} baz={1} />);
1227 expect(Scheduler).toFlushAndYield(['Foo: 2, Bar: 1']);
1197 - expect(ReactNoop.getChildren()).toEqual([
1198 - span('Foo: 2, Bar: 1'),
1199 - span('Baz: 1'),
1228 + expect(ReactNoop).toMatchRenderedOutput([
1229 + <span prop="Foo: 2, Bar: 1" />,
1230 + <span prop="Baz: 1" />,
1231 ]);
1232
1233 // Update only bar
1234 ReactNoop.render(<App foo={2} bar={2} baz={1} />);
1235 expect(Scheduler).toFlushAndYield(['Foo: 2, Bar: 2']);
1205 - expect(ReactNoop.getChildren()).toEqual([
1206 - span('Foo: 2, Bar: 2'),
1207 - span('Baz: 1'),
1236 + expect(ReactNoop).toMatchRenderedOutput([
1237 + <span prop="Foo: 2, Bar: 2" />,
1238 + <span prop="Baz: 1" />,
1239 ]);
1240
1241 // Update only baz
1242 ReactNoop.render(<App foo={2} bar={2} baz={2} />);
1243 expect(Scheduler).toFlushAndYield(['Baz: 2']);
1213 - expect(ReactNoop.getChildren()).toEqual([
1214 - span('Foo: 2, Bar: 2'),
1215 - span('Baz: 2'),
1244 + expect(ReactNoop).toMatchRenderedOutput([
1245 + <span prop="Foo: 2, Bar: 2" />,
1246 + <span prop="Baz: 2" />,
1247 ]);
1248 });
1249
@@ -1255,12 +1286,12 @@ describe('ReactNewContext', () => {
1286 let inst;
1287 ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
1288 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1258 - expect(ReactNoop.getChildren()).toEqual([span('hello')]);
1289 + expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
1290
1291 // Update
1292 inst.setState({text: 'goodbye'});
1293 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1263 - expect(ReactNoop.getChildren()).toEqual([span('goodbye')]);
1294 + expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
1295 });
1296
1297 it('warns when reading context inside render phase class setState updater', () => {
@@ -1366,12 +1397,12 @@ describe('ReactNewContext', () => {
1397 let inst;
1398 ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
1399 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1369 - expect(ReactNoop.getChildren()).toEqual([span('hello')]);
1400 + expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
1401
1402 // Update
1403 inst.setState({text: 'goodbye'});
1404 expect(Scheduler).toFlushAndYield(['App', 'App#renderConsumer']);
1374 - expect(ReactNoop.getChildren()).toEqual([span('goodbye')]);
1405 + expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
1406 });
1407 });
1408
@@ -1393,7 +1424,7 @@ describe('ReactNewContext', () => {
1424 </Context.Provider>,
1425 );
1426 expect(Scheduler).toFlushWithoutYielding();
1396 - expect(ReactNoop.getChildren()).toEqual([span(10)]);
1427 + expect(ReactNoop).toMatchRenderedOutput(<span prop={10} />);
1428 });
1429
1430 describe('fuzz test', () => {
@@ -1518,9 +1549,10 @@ describe('ReactNewContext', () => {
1549 );
1550
1551 function assertConsistentTree(expectedValues = {}) {
1521 - const children = ReactNoop.getChildren();
1552 + const jsx = ReactNoop.getChildrenAsJSX();
1553 + const children = jsx === null ? [] : jsx.props.children;
1554 children.forEach(child => {
1523 - const text = child.prop;
1555 + const text = child.props.prop;
1556 const key = text[0];
1557 const value = parseInt(text[2], 10);
1558 const expectedValue = expectedValues[key];
packages/react-reconciler/src/__tests__/ReactNoopRendererAct-test.js
+1 -1
@@ -58,6 +58,6 @@ describe('internal act()', () => {
58 });
59 expect(Scheduler).toHaveYielded(['stage 1', 'stage 2']);
60 expect(Scheduler).toFlushWithoutYielding();
61 - expect(ReactNoop.getChildren()).toEqual([{text: '1', hidden: false}]);
61 + expect(ReactNoop).toMatchRenderedOutput('1');
62 });
63 });
packages/react-reconciler/src/__tests__/ReactPersistent-test.js
+14 -10
@@ -49,8 +49,12 @@ describe('ReactPersistent', () => {
49 return {type: 'span', children: [], prop, hidden: false};
50 }
51
52 - function getChildren() {
53 - return ReactNoopPersistent.getChildren();
52 + // For persistent renderers we have to mix deep equality and reference equality checks
53 + // for which we need the actual children.
54 + // None of the tests are gated and the underlying implementation is rarely touch
55 + // so it's unlikely we deal with failing `toEqual` checks which cause bad performance.
56 + function dangerouslyGetChildren() {
57 + return ReactNoopPersistent.dangerouslyGetChildren();
58 }
59
60 it('can update child nodes of a host instance', () => {
@@ -69,12 +73,12 @@ describe('ReactPersistent', () => {
73
74 render(<Foo text="Hello" />);
75 expect(Scheduler).toFlushWithoutYielding();
72 - const originalChildren = getChildren();
76 + const originalChildren = dangerouslyGetChildren();
77 expect(originalChildren).toEqual([div(span())]);
78
79 render(<Foo text="World" />);
80 expect(Scheduler).toFlushWithoutYielding();
77 - const newChildren = getChildren();
81 + const newChildren = dangerouslyGetChildren();
82 expect(newChildren).toEqual([div(span(), span())]);
83
84 expect(originalChildren).toEqual([div(span())]);
@@ -103,12 +107,12 @@ describe('ReactPersistent', () => {
107
108 render(<Foo text="Hello" />);
109 expect(Scheduler).toFlushWithoutYielding();
106 - const originalChildren = getChildren();
110 + const originalChildren = dangerouslyGetChildren();
111 expect(originalChildren).toEqual([div(span('Hello'))]);
112
113 render(<Foo text="World" />);
114 expect(Scheduler).toFlushWithoutYielding();
111 - const newChildren = getChildren();
115 + const newChildren = dangerouslyGetChildren();
116 expect(newChildren).toEqual([div(span('Hello'), span('World'))]);
117
118 expect(originalChildren).toEqual([div(span('Hello'))]);
@@ -129,12 +133,12 @@ describe('ReactPersistent', () => {
133
134 render(<Foo text="Hello" />);
135 expect(Scheduler).toFlushWithoutYielding();
132 - const originalChildren = getChildren();
136 + const originalChildren = dangerouslyGetChildren();
137 expect(originalChildren).toEqual([div('Hello', span())]);
138
139 render(<Foo text="World" />);
140 expect(Scheduler).toFlushWithoutYielding();
137 - const newChildren = getChildren();
141 + const newChildren = dangerouslyGetChildren();
142 expect(newChildren).toEqual([div('World', span())]);
143
144 expect(originalChildren).toEqual([div('Hello', span())]);
@@ -173,7 +177,7 @@ describe('ReactPersistent', () => {
177
178 expect(emptyPortalChildSet).toEqual([]);
179
176 - const originalChildren = getChildren();
180 + const originalChildren = dangerouslyGetChildren();
181 expect(originalChildren).toEqual([div()]);
182 const originalPortalChildren = portalContainer.children;
183 expect(originalPortalChildren).toEqual([div(span())]);
@@ -185,7 +189,7 @@ describe('ReactPersistent', () => {
189 );
190 expect(Scheduler).toFlushWithoutYielding();
191
188 - const newChildren = getChildren();
192 + const newChildren = dangerouslyGetChildren();
193 expect(newChildren).toEqual([div()]);
194 const newPortalChildren = portalContainer.children;
195 expect(newPortalChildren).toEqual([div(span(), 'Hello ', 'World')]);
packages/react-reconciler/src/__tests__/ReactSuspenseCallback-test.js
+9 -16
@@ -22,10 +22,6 @@ describe('ReactSuspense', () => {
22 Scheduler = require('scheduler');
23 });
24
25 - function text(t) {
26 - return {text: t, hidden: false};
27 - }
28 -
25 function createThenable() {
26 let completed = false;
27 let resolve;
@@ -88,13 +84,13 @@ describe('ReactSuspense', () => {
84
85 ReactNoop.render(element);
86 expect(Scheduler).toFlushWithoutYielding();
91 - expect(ReactNoop.getChildren()).toEqual([text('Waiting')]);
87 + expect(ReactNoop).toMatchRenderedOutput('Waiting');
88 expect(ops).toEqual([new Set([promise])]);
89 ops = [];
90
91 await resolve();
92 expect(Scheduler).toFlushWithoutYielding();
97 - expect(ReactNoop.getChildren()).toEqual([text('Done')]);
93 + expect(ReactNoop).toMatchRenderedOutput('Done');
94 expect(ops).toEqual([]);
95 });
96
@@ -127,21 +123,21 @@ describe('ReactSuspense', () => {
123
124 ReactNoop.render(element);
125 expect(Scheduler).toFlushWithoutYielding();
130 - expect(ReactNoop.getChildren()).toEqual([text('Waiting Tier 1')]);
126 + expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 1');
127 expect(ops).toEqual([new Set([promise1, promise2])]);
128 ops = [];
129
130 await resolve1();
131 ReactNoop.render(element);
132 expect(Scheduler).toFlushWithoutYielding();
137 - expect(ReactNoop.getChildren()).toEqual([text('Waiting Tier 1')]);
133 + expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 1');
134 expect(ops).toEqual([new Set([promise2])]);
135 ops = [];
136
137 await resolve2();
138 ReactNoop.render(element);
139 expect(Scheduler).toFlushWithoutYielding();
144 - expect(ReactNoop.getChildren()).toEqual([text('Done'), text('Done')]);
140 + expect(ReactNoop).toMatchRenderedOutput('DoneDone');
141 expect(ops).toEqual([]);
142 });
143
@@ -172,7 +168,7 @@ describe('ReactSuspense', () => {
168
169 ReactNoop.render(element);
170 expect(Scheduler).toFlushWithoutYielding();
175 - expect(ReactNoop.getChildren()).toEqual([text('Waiting Tier 2')]);
171 + expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 2');
172 expect(ops1).toEqual([]);
173 expect(ops2).toEqual([new Set([promise])]);
174 });
@@ -214,7 +210,7 @@ describe('ReactSuspense', () => {
210
211 ReactNoop.render(element);
212 expect(Scheduler).toFlushWithoutYielding();
217 - expect(ReactNoop.getChildren()).toEqual([text('Waiting Tier 1')]);
213 + expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 1');
214 expect(ops1).toEqual([new Set([promise1])]);
215 expect(ops2).toEqual([]);
216 ops1 = [];
@@ -228,10 +224,7 @@ describe('ReactSuspense', () => {
224 // TODO: Should be able to use `act` here.
225 jest.runAllTimers();
226
231 - expect(ReactNoop.getChildren()).toEqual([
232 - text('Waiting Tier 2'),
233 - text('Done'),
234 - ]);
227 + expect(ReactNoop).toMatchRenderedOutput('Waiting Tier 2Done');
228 expect(ops1).toEqual([]);
229 expect(ops2).toEqual([new Set([promise2])]);
230 ops1 = [];
@@ -240,7 +233,7 @@ describe('ReactSuspense', () => {
233 await resolve2();
234 ReactNoop.render(element);
235 expect(Scheduler).toFlushWithoutYielding();
243 - expect(ReactNoop.getChildren()).toEqual([text('Done'), text('Done')]);
236 + expect(ReactNoop).toMatchRenderedOutput('DoneDone');
237 expect(ops1).toEqual([]);
238 expect(ops2).toEqual([]);
239 });
packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemantics-test.js
+545 -392
@@ -185,14 +185,6 @@ describe('ReactSuspenseEffectsSemantics', () => {
185
186 const resolveText = resolveMostRecentTextCache;
187
188 - function span(prop, children = []) {
189 - return {type: 'span', children, prop, hidden: false};
190 - }
191 -
192 - function spanHidden(prop, children = []) {
193 - return {type: 'span', children, prop, hidden: true};
194 - }
195 -
188 function advanceTimers(ms) {
189 // Note: This advances Jest's virtual time but not React's. Use
190 // ReactNoop.expire for that.
@@ -278,10 +270,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
270 'Text:Outside create passive',
271 'App create passive',
272 ]);
281 - expect(ReactNoop.getChildren()).toEqual([
282 - span('Fallback'),
283 - span('Outside'),
284 - ]);
273 + expect(ReactNoop).toMatchRenderedOutput(
274 + <>
275 + <span prop="Fallback" />
276 + <span prop="Outside" />
277 + </>,
278 + );
279
280 // Resolving the suspended resource should
281 await act(async () => {
@@ -299,12 +293,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
293 'Text:Inside:Before create passive',
294 'AsyncText:Async create passive',
295 ]);
302 - expect(ReactNoop.getChildren()).toEqual([
303 - span('Inside:Before'),
304 - span('Async'),
305 - span('Inside:After'),
306 - span('Outside'),
307 - ]);
296 + expect(ReactNoop).toMatchRenderedOutput(
297 + <>
298 + <span prop="Inside:Before" />
299 + <span prop="Async" />
300 + <span prop="Inside:After" />
301 + <span prop="Outside" />
302 + </>,
303 + );
304
305 await act(async () => {
306 ReactNoop.render(null);
@@ -320,7 +316,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
316 'AsyncText:Async destroy passive',
317 'Text:Outside destroy passive',
318 ]);
323 - expect(ReactNoop.getChildren()).toEqual([]);
319 + expect(ReactNoop).toMatchRenderedOutput(null);
320 });
321
322 // @gate enableLegacyCache
@@ -398,12 +394,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
394 'Text:Outside create passive',
395 'App create passive',
396 ]);
401 - expect(ReactNoop.getChildren()).toEqual([
402 - spanHidden('Inside:Before'),
403 - spanHidden('Inside:After'),
404 - span('Fallback'),
405 - span('Outside'),
406 - ]);
397 + expect(ReactNoop).toMatchRenderedOutput(
398 + <>
399 + <span prop="Inside:Before" hidden={true} />
400 + <span prop="Inside:After" hidden={true} />
401 + <span prop="Fallback" />
402 + <span prop="Outside" />
403 + </>,
404 + );
405
406 // Resolving the suspended resource should
407 await act(async () => {
@@ -416,12 +414,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
414 'Text:Fallback destroy passive',
415 'AsyncText:Async create passive',
416 ]);
419 - expect(ReactNoop.getChildren()).toEqual([
420 - span('Inside:Before'),
421 - span('Async'),
422 - span('Inside:After'),
423 - span('Outside'),
424 - ]);
417 + expect(ReactNoop).toMatchRenderedOutput(
418 + <>
419 + <span prop="Inside:Before" />
420 + <span prop="Async" />
421 + <span prop="Inside:After" />
422 + <span prop="Outside" />
423 + </>,
424 + );
425
426 await act(async () => {
427 ReactNoop.renderLegacySyncRoot(null);
@@ -437,7 +437,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
437 'AsyncText:Async destroy passive',
438 'Text:Outside destroy passive',
439 ]);
440 - expect(ReactNoop.getChildren()).toEqual([]);
440 + expect(ReactNoop).toMatchRenderedOutput(null);
441 });
442 });
443
@@ -488,11 +488,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
488 'Text:Outside create passive',
489 'App create passive',
490 ]);
491 - expect(ReactNoop.getChildren()).toEqual([
492 - span('Inside:Before'),
493 - span('Inside:After'),
494 - span('Outside'),
495 - ]);
491 + expect(ReactNoop).toMatchRenderedOutput(
492 + <>
493 + <span prop="Inside:Before" />
494 + <span prop="Inside:After" />
495 + <span prop="Outside" />
496 + </>,
497 + );
498
499 // Schedule an update that causes React to suspend.
500 act(() => {
@@ -512,23 +514,27 @@ describe('ReactSuspenseEffectsSemantics', () => {
514 'Text:Fallback create layout',
515 'Text:Fallback create passive',
516 ]);
515 - expect(ReactNoop.getChildren()).toEqual([
516 - spanHidden('Inside:Before'),
517 - spanHidden('Inside:After'),
518 - span('Fallback'),
519 - span('Outside'),
520 - ]);
517 + expect(ReactNoop).toMatchRenderedOutput(
518 + <>
519 + <span prop="Inside:Before" hidden={true} />
520 + <span prop="Inside:After" hidden={true} />
521 + <span prop="Fallback" />
522 + <span prop="Outside" />
523 + </>,
524 + );
525
526 await advanceTimers(1000);
527
528 // Noop since sync root has already committed
529 expect(Scheduler).toHaveYielded([]);
526 - expect(ReactNoop.getChildren()).toEqual([
527 - spanHidden('Inside:Before'),
528 - spanHidden('Inside:After'),
529 - span('Fallback'),
530 - span('Outside'),
531 - ]);
530 + expect(ReactNoop).toMatchRenderedOutput(
531 + <>
532 + <span prop="Inside:Before" hidden={true} />
533 + <span prop="Inside:After" hidden={true} />
534 + <span prop="Fallback" />
535 + <span prop="Outside" />
536 + </>,
537 + );
538
539 // Resolving the suspended resource should re-create inner layout effects.
540 await act(async () => {
@@ -541,12 +547,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
547 'Text:Fallback destroy passive',
548 'AsyncText:Async create passive',
549 ]);
544 - expect(ReactNoop.getChildren()).toEqual([
545 - span('Inside:Before'),
546 - span('Async'),
547 - span('Inside:After'),
548 - span('Outside'),
549 - ]);
550 + expect(ReactNoop).toMatchRenderedOutput(
551 + <>
552 + <span prop="Inside:Before" />
553 + <span prop="Async" />
554 + <span prop="Inside:After" />
555 + <span prop="Outside" />
556 + </>,
557 + );
558
559 await act(async () => {
560 ReactNoop.renderLegacySyncRoot(null);
@@ -610,11 +618,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
618 'Text:Outside create passive',
619 'App create passive',
620 ]);
613 - expect(ReactNoop.getChildren()).toEqual([
614 - span('Inside:Before'),
615 - span('Inside:After'),
616 - span('Outside'),
617 - ]);
621 + expect(ReactNoop).toMatchRenderedOutput(
622 + <>
623 + <span prop="Inside:Before" />
624 + <span prop="Inside:After" />
625 + <span prop="Outside" />
626 + </>,
627 + );
628
629 // Schedule an update that causes React to suspend.
630 act(() => {
@@ -632,11 +642,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
642 'Text:Fallback render',
643 'Text:Outside render',
644 ]);
635 - expect(ReactNoop.getChildren()).toEqual([
636 - span('Inside:Before'),
637 - span('Inside:After'),
638 - span('Outside'),
639 - ]);
645 + expect(ReactNoop).toMatchRenderedOutput(
646 + <>
647 + <span prop="Inside:Before" />
648 + <span prop="Inside:After" />
649 + <span prop="Outside" />
650 + </>,
651 + );
652
653 await advanceTimers(1000);
654
@@ -647,12 +659,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
659 'Text:Fallback create layout',
660 ]);
661 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
650 - expect(ReactNoop.getChildren()).toEqual([
651 - spanHidden('Inside:Before'),
652 - spanHidden('Inside:After'),
653 - span('Fallback'),
654 - span('Outside'),
655 - ]);
662 + expect(ReactNoop).toMatchRenderedOutput(
663 + <>
664 + <span prop="Inside:Before" hidden={true} />
665 + <span prop="Inside:After" hidden={true} />
666 + <span prop="Fallback" />
667 + <span prop="Outside" />
668 + </>,
669 + );
670
671 // Resolving the suspended resource should re-create inner layout effects.
672 await act(async () => {
@@ -669,12 +683,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
683 'Text:Fallback destroy passive',
684 'AsyncText:Async create passive',
685 ]);
672 - expect(ReactNoop.getChildren()).toEqual([
673 - span('Inside:Before'),
674 - span('Async'),
675 - span('Inside:After'),
676 - span('Outside'),
677 - ]);
686 + expect(ReactNoop).toMatchRenderedOutput(
687 + <>
688 + <span prop="Inside:Before" />
689 + <span prop="Async" />
690 + <span prop="Inside:After" />
691 + <span prop="Outside" />
692 + </>,
693 + );
694
695 await act(async () => {
696 ReactNoop.render(null);
@@ -758,11 +774,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
774 'App create layout',
775 'App create passive',
776 ]);
761 - expect(ReactNoop.getChildren()).toEqual([
762 - span('Inside:Before'),
763 - span('Inside:After'),
764 - span('Outside'),
765 - ]);
777 + expect(ReactNoop).toMatchRenderedOutput(
778 + <>
779 + <span prop="Inside:Before" />
780 + <span prop="Inside:After" />
781 + <span prop="Outside" />
782 + </>,
783 + );
784
785 // Schedule an update that causes React to suspend.
786 act(() => {
@@ -780,11 +798,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
798 'ClassText:Fallback render',
799 'ClassText:Outside render',
800 ]);
783 - expect(ReactNoop.getChildren()).toEqual([
784 - span('Inside:Before'),
785 - span('Inside:After'),
786 - span('Outside'),
787 - ]);
801 + expect(ReactNoop).toMatchRenderedOutput(
802 + <>
803 + <span prop="Inside:Before" />
804 + <span prop="Inside:After" />
805 + <span prop="Outside" />
806 + </>,
807 + );
808
809 await advanceTimers(1000);
810
@@ -795,12 +815,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
815 'ClassText:Fallback componentDidMount',
816 'ClassText:Outside componentDidUpdate',
817 ]);
798 - expect(ReactNoop.getChildren()).toEqual([
799 - spanHidden('Inside:Before'),
800 - spanHidden('Inside:After'),
801 - span('Fallback'),
802 - span('Outside'),
803 - ]);
818 + expect(ReactNoop).toMatchRenderedOutput(
819 + <>
820 + <span prop="Inside:Before" hidden={true} />
821 + <span prop="Inside:After" hidden={true} />
822 + <span prop="Fallback" />
823 + <span prop="Outside" />
824 + </>,
825 + );
826
827 // Resolving the suspended resource should re-create inner layout effects.
828 await act(async () => {
@@ -816,13 +838,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
838 'ClassText:Inside:After componentDidMount',
839 'AsyncText:Async create passive',
840 ]);
819 - expect(ReactNoop.getChildren()).toEqual([
820 - span('Inside:Before'),
821 - span('Async'),
822 - span('Inside:After'),
823 - span('Outside'),
824 - ]);
825 -
841 + expect(ReactNoop).toMatchRenderedOutput(
842 + <>
843 + <span prop="Inside:Before" />
844 + <span prop="Async" />
845 + <span prop="Inside:After" />
846 + <span prop="Outside" />
847 + </>,
848 + );
849 await act(async () => {
850 ReactNoop.render(null);
851 });
@@ -878,7 +901,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
901 'Text:Outer create passive',
902 'App create passive',
903 ]);
881 - expect(ReactNoop.getChildren()).toEqual([span('Outer', [span('Inner')])]);
904 + expect(ReactNoop).toMatchRenderedOutput(
905 + <span prop="Outer">
906 + <span prop="Inner" />
907 + </span>,
908 + );
909
910 // Schedule an update that causes React to suspend.
911 act(() => {
@@ -895,7 +922,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
922 'Text:Inner render',
923 'Text:Fallback render',
924 ]);
898 - expect(ReactNoop.getChildren()).toEqual([span('Outer', [span('Inner')])]);
925 + expect(ReactNoop).toMatchRenderedOutput(
926 + <span prop="Outer">
927 + <span prop="Inner" />
928 + </span>,
929 + );
930
931 await advanceTimers(1000);
932
@@ -906,10 +937,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
937 'Text:Fallback create layout',
938 ]);
939 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
909 - expect(ReactNoop.getChildren()).toEqual([
910 - spanHidden('Outer', [span('Inner')]),
911 - span('Fallback'),
912 - ]);
940 + expect(ReactNoop).toMatchRenderedOutput(
941 + <>
942 + <span hidden={true} prop="Outer">
943 + <span prop="Inner" />
944 + </span>
945 + <span prop="Fallback" />
946 + </>,
947 + );
948
949 // Resolving the suspended resource should re-create inner layout effects.
950 await act(async () => {
@@ -926,10 +961,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
961 'Text:Fallback destroy passive',
962 'AsyncText:Async create passive',
963 ]);
929 - expect(ReactNoop.getChildren()).toEqual([
930 - span('Async'),
931 - span('Outer', [span('Inner')]),
932 - ]);
964 + expect(ReactNoop).toMatchRenderedOutput(
965 + <>
966 + <span prop="Async" />
967 + <span prop="Outer">
968 + <span prop="Inner" />
969 + </span>
970 + </>,
971 + );
972
973 await act(async () => {
974 ReactNoop.render(null);
@@ -989,9 +1028,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
1028 'Text:Outer create passive',
1029 'App create passive',
1030 ]);
992 - expect(ReactNoop.getChildren()).toEqual([
993 - span('Outer', [span('MemoizedInner')]),
994 - ]);
1031 + expect(ReactNoop).toMatchRenderedOutput(
1032 + <span prop="Outer">
1033 + <span prop="MemoizedInner" />
1034 + </span>,
1035 + );
1036
1037 // Schedule an update that causes React to suspend.
1038 act(() => {
@@ -1008,9 +1049,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
1049 // Text:MemoizedInner is memoized
1050 'Text:Fallback render',
1051 ]);
1011 - expect(ReactNoop.getChildren()).toEqual([
1012 - span('Outer', [span('MemoizedInner')]),
1013 - ]);
1052 + expect(ReactNoop).toMatchRenderedOutput(
1053 + <span prop="Outer">
1054 + <span prop="MemoizedInner" />
1055 + </span>,
1056 + );
1057
1058 await advanceTimers(1000);
1059
@@ -1022,10 +1065,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1065 'Text:Fallback create layout',
1066 ]);
1067 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
1025 - expect(ReactNoop.getChildren()).toEqual([
1026 - spanHidden('Outer', [span('MemoizedInner')]),
1027 - span('Fallback'),
1028 - ]);
1068 + expect(ReactNoop).toMatchRenderedOutput(
1069 + <>
1070 + <span hidden={true} prop="Outer">
1071 + <span prop="MemoizedInner" />
1072 + </span>
1073 + <span prop="Fallback" />
1074 + </>,
1075 + );
1076
1077 // Resolving the suspended resource should re-create inner layout effects.
1078 await act(async () => {
@@ -1041,10 +1088,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1088 'Text:Fallback destroy passive',
1089 'AsyncText:Async create passive',
1090 ]);
1044 - expect(ReactNoop.getChildren()).toEqual([
1045 - span('Async'),
1046 - span('Outer', [span('MemoizedInner')]),
1047 - ]);
1091 + expect(ReactNoop).toMatchRenderedOutput(
1092 + <>
1093 + <span prop="Async" />
1094 + <span prop="Outer">
1095 + <span prop="MemoizedInner" />
1096 + </span>
1097 + </>,
1098 + );
1099
1100 await act(async () => {
1101 ReactNoop.render(null);
@@ -1088,7 +1139,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1139 'Text:Outer create passive',
1140 'Text:Inner create passive',
1141 ]);
1091 - expect(ReactNoop.getChildren()).toEqual([span('Outer'), span('Inner')]);
1142 + expect(ReactNoop).toMatchRenderedOutput(
1143 + <>
1144 + <span prop="Outer" />
1145 + <span prop="Inner" />
1146 + </>,
1147 + );
1148
1149 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
1150 act(() => {
@@ -1106,11 +1162,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1162 'Text:InnerFallback create layout',
1163 ]);
1164 expect(Scheduler).toFlushAndYield(['Text:InnerFallback create passive']);
1109 - expect(ReactNoop.getChildren()).toEqual([
1110 - span('Outer'),
1111 - spanHidden('Inner'),
1112 - span('InnerFallback'),
1113 - ]);
1165 + expect(ReactNoop).toMatchRenderedOutput(
1166 + <>
1167 + <span prop="Outer" />
1168 + <span prop="Inner" hidden={true} />
1169 + <span prop="InnerFallback" />
1170 + </>,
1171 + );
1172
1173 // Suspend the outer Suspense subtree (outer effects and inner fallback effects should be destroyed)
1174 // (This check also ensures we don't destroy effects for mounted inner fallback.)
@@ -1135,12 +1193,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1193 'Text:OuterFallback create layout',
1194 ]);
1195 expect(Scheduler).toFlushAndYield(['Text:OuterFallback create passive']);
1138 - expect(ReactNoop.getChildren()).toEqual([
1139 - spanHidden('Outer'),
1140 - spanHidden('Inner'),
1141 - spanHidden('InnerFallback'),
1142 - span('OuterFallback'),
1143 - ]);
1196 + expect(ReactNoop).toMatchRenderedOutput(
1197 + <>
1198 + <span prop="Outer" hidden={true} />
1199 + <span prop="Inner" hidden={true} />
1200 + <span prop="InnerFallback" hidden={true} />
1201 + <span prop="OuterFallback" />
1202 + </>,
1203 + );
1204
1205 // Show the inner Suspense subtree (no effects should be recreated)
1206 await act(async () => {
@@ -1152,12 +1212,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1212 'Text:Inner render',
1213 'AsyncText:InnerAsync_1 render',
1214 ]);
1155 - expect(ReactNoop.getChildren()).toEqual([
1156 - spanHidden('Outer'),
1157 - spanHidden('Inner'),
1158 - spanHidden('InnerFallback'),
1159 - span('OuterFallback'),
1160 - ]);
1215 + expect(ReactNoop).toMatchRenderedOutput(
1216 + <>
1217 + <span prop="Outer" hidden={true} />
1218 + <span prop="Inner" hidden={true} />
1219 + <span prop="InnerFallback" hidden={true} />
1220 + <span prop="OuterFallback" />
1221 + </>,
1222 + );
1223
1224 // Suspend the inner Suspense subtree (no effects should be destroyed)
1225 act(() => {
@@ -1177,12 +1239,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1239 'Text:InnerFallback render',
1240 'Text:OuterFallback render',
1241 ]);
1180 - expect(ReactNoop.getChildren()).toEqual([
1181 - spanHidden('Outer'),
1182 - spanHidden('Inner'),
1183 - spanHidden('InnerFallback'),
1184 - span('OuterFallback'),
1185 - ]);
1242 + expect(ReactNoop).toMatchRenderedOutput(
1243 + <>
1244 + <span prop="Outer" hidden={true} />
1245 + <span prop="Inner" hidden={true} />
1246 + <span prop="InnerFallback" hidden={true} />
1247 + <span prop="OuterFallback" />
1248 + </>,
1249 + );
1250
1251 // Show the outer Suspense subtree (only outer effects should be recreated)
1252 await act(async () => {
@@ -1201,12 +1265,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1265 'Text:OuterFallback destroy passive',
1266 'AsyncText:OuterAsync_1 create passive',
1267 ]);
1204 - expect(ReactNoop.getChildren()).toEqual([
1205 - span('Outer'),
1206 - span('OuterAsync_1'),
1207 - spanHidden('Inner'),
1208 - span('InnerFallback'),
1209 - ]);
1268 + expect(ReactNoop).toMatchRenderedOutput(
1269 + <>
1270 + <span prop="Outer" />
1271 + <span prop="OuterAsync_1" />
1272 + <span prop="Inner" hidden={true} />
1273 + <span prop="InnerFallback" />
1274 + </>,
1275 + );
1276
1277 // Show the inner Suspense subtree (only inner effects should be recreated)
1278 await act(async () => {
@@ -1221,12 +1287,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1287 'Text:InnerFallback destroy passive',
1288 'AsyncText:InnerAsync_2 create passive',
1289 ]);
1224 - expect(ReactNoop.getChildren()).toEqual([
1225 - span('Outer'),
1226 - span('OuterAsync_1'),
1227 - span('Inner'),
1228 - span('InnerAsync_2'),
1229 - ]);
1290 + expect(ReactNoop).toMatchRenderedOutput(
1291 + <>
1292 + <span prop="Outer" />
1293 + <span prop="OuterAsync_1" />
1294 + <span prop="Inner" />
1295 + <span prop="InnerAsync_2" />
1296 + </>,
1297 + );
1298
1299 // Suspend the outer Suspense subtree (all effects should be destroyed)
1300 act(() => {
@@ -1250,13 +1318,15 @@ describe('ReactSuspenseEffectsSemantics', () => {
1318 'AsyncText:InnerAsync_2 destroy layout',
1319 'Text:OuterFallback create layout',
1320 ]);
1253 - expect(ReactNoop.getChildren()).toEqual([
1254 - spanHidden('Outer'),
1255 - spanHidden('OuterAsync_1'),
1256 - spanHidden('Inner'),
1257 - spanHidden('InnerAsync_2'),
1258 - span('OuterFallback'),
1259 - ]);
1321 + expect(ReactNoop).toMatchRenderedOutput(
1322 + <>
1323 + <span prop="Outer" hidden={true} />
1324 + <span prop="OuterAsync_1" hidden={true} />
1325 + <span prop="Inner" hidden={true} />
1326 + <span prop="InnerAsync_2" hidden={true} />
1327 + <span prop="OuterFallback" />
1328 + </>,
1329 + );
1330
1331 // Show the outer Suspense subtree (all effects should be recreated)
1332 await act(async () => {
@@ -1275,12 +1345,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1345 'AsyncText:InnerAsync_2 create layout',
1346 'Text:OuterFallback destroy passive',
1347 ]);
1278 - expect(ReactNoop.getChildren()).toEqual([
1279 - span('Outer'),
1280 - span('OuterAsync_2'),
1281 - span('Inner'),
1282 - span('InnerAsync_2'),
1283 - ]);
1348 + expect(ReactNoop).toMatchRenderedOutput(
1349 + <>
1350 + <span prop="Outer" />
1351 + <span prop="OuterAsync_2" />
1352 + <span prop="Inner" />
1353 + <span prop="InnerAsync_2" />
1354 + </>,
1355 + );
1356 });
1357
1358 // @gate enableLegacyCache
@@ -1310,7 +1382,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1382 'Text:Outer create passive',
1383 'Text:Inner create passive',
1384 ]);
1313 - expect(ReactNoop.getChildren()).toEqual([span('Outer'), span('Inner')]);
1385 + expect(ReactNoop).toMatchRenderedOutput(
1386 + <>
1387 + <span prop="Outer" />
1388 + <span prop="Inner" />
1389 + </>,
1390 + );
1391
1392 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
1393 act(() => {
@@ -1328,11 +1405,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1405 'Text:InnerFallback create layout',
1406 ]);
1407 expect(Scheduler).toFlushAndYield(['Text:InnerFallback create passive']);
1331 - expect(ReactNoop.getChildren()).toEqual([
1332 - span('Outer'),
1333 - spanHidden('Inner'),
1334 - span('InnerFallback'),
1335 - ]);
1408 + expect(ReactNoop).toMatchRenderedOutput(
1409 + <>
1410 + <span prop="Outer" />
1411 + <span prop="Inner" hidden={true} />
1412 + <span prop="InnerFallback" />
1413 + </>,
1414 + );
1415
1416 // Suspend the outer Suspense subtree (outer effects and inner fallback effects should be destroyed)
1417 // (This check also ensures we don't destroy effects for mounted inner fallback.)
@@ -1357,12 +1436,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1436 'Text:OuterFallback create layout',
1437 ]);
1438 expect(Scheduler).toFlushAndYield(['Text:OuterFallback create passive']);
1360 - expect(ReactNoop.getChildren()).toEqual([
1361 - spanHidden('Outer'),
1362 - spanHidden('Inner'),
1363 - spanHidden('InnerFallback'),
1364 - span('OuterFallback'),
1365 - ]);
1439 + expect(ReactNoop).toMatchRenderedOutput(
1440 + <>
1441 + <span prop="Outer" hidden={true} />
1442 + <span prop="Inner" hidden={true} />
1443 + <span prop="InnerFallback" hidden={true} />
1444 + <span prop="OuterFallback" />
1445 + </>,
1446 + );
1447
1448 // Resolve both suspended trees.
1449 await act(async () => {
@@ -1384,12 +1465,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1465 'AsyncText:OuterAsync_1 create passive',
1466 'AsyncText:InnerAsync_1 create passive',
1467 ]);
1387 - expect(ReactNoop.getChildren()).toEqual([
1388 - span('Outer'),
1389 - span('OuterAsync_1'),
1390 - span('Inner'),
1391 - span('InnerAsync_1'),
1392 - ]);
1468 + expect(ReactNoop).toMatchRenderedOutput(
1469 + <>
1470 + <span prop="Outer" />
1471 + <span prop="OuterAsync_1" />
1472 + <span prop="Inner" />
1473 + <span prop="InnerAsync_1" />
1474 + </>,
1475 + );
1476 });
1477
1478 // @gate enableLegacyCache
@@ -1427,10 +1510,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1510 'Text:Inside create passive',
1511 'Text:Outside create passive',
1512 ]);
1430 - expect(ReactNoop.getChildren()).toEqual([
1431 - span('Inside'),
1432 - span('Outside'),
1433 - ]);
1513 + expect(ReactNoop).toMatchRenderedOutput(
1514 + <>
1515 + <span prop="Inside" />
1516 + <span prop="Outside" />
1517 + </>,
1518 + );
1519
1520 // Suspend the outer shell
1521 act(() => {
@@ -1445,10 +1530,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1530 'Text:Fallback:Outside render',
1531 'Text:Outside render',
1532 ]);
1448 - expect(ReactNoop.getChildren()).toEqual([
1449 - span('Inside'),
1450 - span('Outside'),
1451 - ]);
1533 + expect(ReactNoop).toMatchRenderedOutput(
1534 + <>
1535 + <span prop="Inside" />
1536 + <span prop="Outside" />
1537 + </>,
1538 + );
1539
1540 // Timing out should commit the fallback and destroy inner layout effects.
1541 await advanceTimers(1000);
@@ -1461,12 +1548,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1548 'Text:Fallback:Inside create passive',
1549 'Text:Fallback:Outside create passive',
1550 ]);
1464 - expect(ReactNoop.getChildren()).toEqual([
1465 - spanHidden('Inside'),
1466 - span('Fallback:Inside'),
1467 - span('Fallback:Outside'),
1468 - span('Outside'),
1469 - ]);
1551 + expect(ReactNoop).toMatchRenderedOutput(
1552 + <>
1553 + <span prop="Inside" hidden={true} />
1554 + <span prop="Fallback:Inside" />
1555 + <span prop="Fallback:Outside" />
1556 + <span prop="Outside" />
1557 + </>,
1558 + );
1559
1560 // Suspend the fallback and verify that it's effects get cleaned up as well
1561 act(() => {
@@ -1486,12 +1575,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1575 'Text:Fallback:Outside render',
1576 'Text:Outside render',
1577 ]);
1489 - expect(ReactNoop.getChildren()).toEqual([
1490 - spanHidden('Inside'),
1491 - span('Fallback:Inside'),
1492 - span('Fallback:Outside'),
1493 - span('Outside'),
1494 - ]);
1578 + expect(ReactNoop).toMatchRenderedOutput(
1579 + <>
1580 + <span prop="Inside" hidden={true} />
1581 + <span prop="Fallback:Inside" />
1582 + <span prop="Fallback:Outside" />
1583 + <span prop="Outside" />
1584 + </>,
1585 + );
1586
1587 // Timing out should commit the inner fallback and destroy outer fallback layout effects.
1588 await advanceTimers(1000);
@@ -1502,13 +1593,15 @@ describe('ReactSuspenseEffectsSemantics', () => {
1593 expect(Scheduler).toFlushAndYield([
1594 'Text:Fallback:Fallback create passive',
1595 ]);
1505 - expect(ReactNoop.getChildren()).toEqual([
1506 - spanHidden('Inside'),
1507 - spanHidden('Fallback:Inside'),
1508 - span('Fallback:Fallback'),
1509 - span('Fallback:Outside'),
1510 - span('Outside'),
1511 - ]);
1596 + expect(ReactNoop).toMatchRenderedOutput(
1597 + <>
1598 + <span prop="Inside" hidden={true} />
1599 + <span prop="Fallback:Inside" hidden={true} />
1600 + <span prop="Fallback:Fallback" />
1601 + <span prop="Fallback:Outside" />
1602 + <span prop="Outside" />
1603 + </>,
1604 + );
1605
1606 // Resolving both resources should cleanup fallback effects and recreate main effects
1607 await act(async () => {
@@ -1527,11 +1620,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1620 'Text:Fallback:Outside destroy passive',
1621 'AsyncText:OutsideAsync create passive',
1622 ]);
1530 - expect(ReactNoop.getChildren()).toEqual([
1531 - span('Inside'),
1532 - span('OutsideAsync'),
1533 - span('Outside'),
1534 - ]);
1623 + expect(ReactNoop).toMatchRenderedOutput(
1624 + <>
1625 + <span prop="Inside" />
1626 + <span prop="OutsideAsync" />
1627 + <span prop="Outside" />
1628 + </>,
1629 + );
1630 });
1631
1632 // @gate enableLegacyCache
@@ -1569,10 +1664,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1664 'Text:Inside create passive',
1665 'Text:Outside create passive',
1666 ]);
1572 - expect(ReactNoop.getChildren()).toEqual([
1573 - span('Inside'),
1574 - span('Outside'),
1575 - ]);
1667 + expect(ReactNoop).toMatchRenderedOutput(
1668 + <>
1669 + <span prop="Inside" />
1670 + <span prop="Outside" />
1671 + </>,
1672 + );
1673
1674 // Suspend both the outer boundary and the fallback
1675 act(() => {
@@ -1600,12 +1697,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1697 'Text:Fallback:Fallback create passive',
1698 'Text:Fallback:Outside create passive',
1699 ]);
1603 - expect(ReactNoop.getChildren()).toEqual([
1604 - spanHidden('Inside'),
1605 - span('Fallback:Fallback'),
1606 - span('Fallback:Outside'),
1607 - span('Outside'),
1608 - ]);
1700 + expect(ReactNoop).toMatchRenderedOutput(
1701 + <>
1702 + <span prop="Inside" hidden={true} />
1703 + <span prop="Fallback:Fallback" />
1704 + <span prop="Fallback:Outside" />
1705 + <span prop="Outside" />
1706 + </>,
1707 + );
1708
1709 // Resolving the inside fallback
1710 await act(async () => {
@@ -1621,13 +1720,15 @@ describe('ReactSuspenseEffectsSemantics', () => {
1720 'Text:Fallback:Inside create passive',
1721 'AsyncText:FallbackAsync create passive',
1722 ]);
1624 - expect(ReactNoop.getChildren()).toEqual([
1625 - spanHidden('Inside'),
1626 - span('Fallback:Inside'),
1627 - span('FallbackAsync'),
1628 - span('Fallback:Outside'),
1629 - span('Outside'),
1630 - ]);
1723 + expect(ReactNoop).toMatchRenderedOutput(
1724 + <>
1725 + <span prop="Inside" hidden={true} />
1726 + <span prop="Fallback:Inside" />
1727 + <span prop="FallbackAsync" />
1728 + <span prop="Fallback:Outside" />
1729 + <span prop="Outside" />
1730 + </>,
1731 + );
1732
1733 // Resolving the outer fallback only
1734 await act(async () => {
@@ -1646,11 +1747,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1747 'Text:Fallback:Outside destroy passive',
1748 'AsyncText:OutsideAsync create passive',
1749 ]);
1649 - expect(ReactNoop.getChildren()).toEqual([
1650 - span('Inside'),
1651 - span('OutsideAsync'),
1652 - span('Outside'),
1653 - ]);
1750 + expect(ReactNoop).toMatchRenderedOutput(
1751 + <>
1752 + <span prop="Inside" />
1753 + <span prop="OutsideAsync" />
1754 + <span prop="Outside" />
1755 + </>,
1756 + );
1757 });
1758
1759 // @gate enableLegacyCache
@@ -1685,10 +1788,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1788 'Text:Inside create passive',
1789 'Text:Outside create passive',
1790 ]);
1688 - expect(ReactNoop.getChildren()).toEqual([
1689 - span('Inside'),
1690 - span('Outside'),
1691 - ]);
1791 + expect(ReactNoop).toMatchRenderedOutput(
1792 + <>
1793 + <span prop="Inside" />
1794 + <span prop="Outside" />
1795 + </>,
1796 + );
1797
1798 // Suspending a component in the middle of the tree
1799 // should still properly cleanup effects deeper in the tree
@@ -1700,10 +1805,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1805 'Text:Fallback render',
1806 'Text:Outside render',
1807 ]);
1703 - expect(ReactNoop.getChildren()).toEqual([
1704 - span('Inside'),
1705 - span('Outside'),
1706 - ]);
1808 + expect(ReactNoop).toMatchRenderedOutput(
1809 + <>
1810 + <span prop="Inside" />
1811 + <span prop="Outside" />
1812 + </>,
1813 + );
1814
1815 // Timing out should commit the inner fallback and destroy outer fallback layout effects.
1816 await advanceTimers(1000);
@@ -1712,11 +1819,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1819 'Text:Fallback create layout',
1820 ]);
1821 expect(Scheduler).toFlushAndYield(['Text:Fallback create passive']);
1715 - expect(ReactNoop.getChildren()).toEqual([
1716 - spanHidden('Inside'),
1717 - span('Fallback'),
1718 - span('Outside'),
1719 - ]);
1822 + expect(ReactNoop).toMatchRenderedOutput(
1823 + <>
1824 + <span prop="Inside" hidden={true} />
1825 + <span prop="Fallback" />
1826 + <span prop="Outside" />
1827 + </>,
1828 + );
1829
1830 // Resolving should cleanup.
1831 await act(async () => {
@@ -1728,10 +1837,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
1837 'Text:Inside create layout',
1838 'Text:Fallback destroy passive',
1839 ]);
1731 - expect(ReactNoop.getChildren()).toEqual([
1732 - span('Inside'),
1733 - span('Outside'),
1734 - ]);
1840 + expect(ReactNoop).toMatchRenderedOutput(
1841 + <>
1842 + <span prop="Inside" />
1843 + <span prop="Outside" />
1844 + </>,
1845 + );
1846 });
1847
1848 describe('that throw errors', () => {
@@ -1797,11 +1908,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
1908 'Text:Inside create passive',
1909 'Text:Outside create passive',
1910 ]);
1800 - expect(ReactNoop.getChildren()).toEqual([
1801 - span('ThrowsInDidMount'),
1802 - span('Inside'),
1803 - span('Outside'),
1804 - ]);
1911 + expect(ReactNoop).toMatchRenderedOutput(
1912 + <>
1913 + <span prop="ThrowsInDidMount" />
1914 + <span prop="Inside" />
1915 + <span prop="Outside" />
1916 + </>,
1917 + );
1918
1919 // Schedule an update that causes React to suspend.
1920 await act(async () => {
@@ -1826,12 +1939,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
1939 'Text:Fallback create layout',
1940 'Text:Fallback create passive',
1941 ]);
1829 - expect(ReactNoop.getChildren()).toEqual([
1830 - spanHidden('ThrowsInDidMount'),
1831 - spanHidden('Inside'),
1832 - span('Fallback'),
1833 - span('Outside'),
1834 - ]);
1942 + expect(ReactNoop).toMatchRenderedOutput(
1943 + <>
1944 + <span prop="ThrowsInDidMount" hidden={true} />
1945 + <span prop="Inside" hidden={true} />
1946 + <span prop="Fallback" />
1947 + <span prop="Outside" />
1948 + </>,
1949 + );
1950
1951 // Resolve the pending suspense and throw
1952 componentDidMountShouldThrow = true;
@@ -1870,7 +1985,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
1985 'Text:Error create layout',
1986 'Text:Error create passive',
1987 ]);
1873 - expect(ReactNoop.getChildren()).toEqual([span('Error')]);
1988 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Error" />);
1989 });
1990
1991 // @gate enableLegacyCache
@@ -1933,11 +2048,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2048 'Text:Inside create passive',
2049 'Text:Outside create passive',
2050 ]);
1936 - expect(ReactNoop.getChildren()).toEqual([
1937 - span('ThrowsInWillUnmount'),
1938 - span('Inside'),
1939 - span('Outside'),
1940 - ]);
2051 + expect(ReactNoop).toMatchRenderedOutput(
2052 + <>
2053 + <span prop="ThrowsInWillUnmount" />
2054 + <span prop="Inside" />
2055 + <span prop="Outside" />
2056 + </>,
2057 + );
2058
2059 // Schedule an update that suspends and triggers our error code.
2060 await act(async () => {
@@ -1981,7 +2098,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2098 'Text:Error create layout',
2099 'Text:Error create passive',
2100 ]);
1984 - expect(ReactNoop.getChildren()).toEqual([span('Error')]);
2101 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Error" />);
2102 });
2103
2104 // @gate enableLegacyCache
@@ -2047,11 +2164,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2164 'Text:Inside create passive',
2165 'Text:Outside create passive',
2166 ]);
2050 - expect(ReactNoop.getChildren()).toEqual([
2051 - span('ThrowsInLayoutEffect'),
2052 - span('Inside'),
2053 - span('Outside'),
2054 - ]);
2167 + expect(ReactNoop).toMatchRenderedOutput(
2168 + <>
2169 + <span prop="ThrowsInLayoutEffect" />
2170 + <span prop="Inside" />
2171 + <span prop="Outside" />
2172 + </>,
2173 + );
2174
2175 // Schedule an update that causes React to suspend.
2176 await act(async () => {
@@ -2076,12 +2195,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
2195 'Text:Fallback create layout',
2196 'Text:Fallback create passive',
2197 ]);
2079 - expect(ReactNoop.getChildren()).toEqual([
2080 - spanHidden('ThrowsInLayoutEffect'),
2081 - spanHidden('Inside'),
2082 - span('Fallback'),
2083 - span('Outside'),
2084 - ]);
2198 + expect(ReactNoop).toMatchRenderedOutput(
2199 + <>
2200 + <span prop="ThrowsInLayoutEffect" hidden={true} />
2201 + <span prop="Inside" hidden={true} />
2202 + <span prop="Fallback" />
2203 + <span prop="Outside" />
2204 + </>,
2205 + );
2206
2207 // Resolve the pending suspense and throw
2208 useLayoutEffectShouldThrow = true;
@@ -2120,7 +2241,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2241 'Text:Error create layout',
2242 'Text:Error create passive',
2243 ]);
2123 - expect(ReactNoop.getChildren()).toEqual([span('Error')]);
2244 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Error" />);
2245 });
2246
2247 // @gate enableLegacyCache
@@ -2182,11 +2303,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2303 'Text:Inside create passive',
2304 'Text:Outside create passive',
2305 ]);
2185 - expect(ReactNoop.getChildren()).toEqual([
2186 - span('ThrowsInLayoutEffectDestroy'),
2187 - span('Inside'),
2188 - span('Outside'),
2189 - ]);
2306 + expect(ReactNoop).toMatchRenderedOutput(
2307 + <>
2308 + <span prop="ThrowsInLayoutEffectDestroy" />
2309 + <span prop="Inside" />
2310 + <span prop="Outside" />
2311 + </>,
2312 + );
2313
2314 // Schedule an update that suspends and triggers our error code.
2315 await act(async () => {
@@ -2230,7 +2353,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2353 'Text:Error create layout',
2354 'Text:Error create passive',
2355 ]);
2233 - expect(ReactNoop.getChildren()).toEqual([span('Error')]);
2356 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Error" />);
2357 });
2358 });
2359
@@ -2278,10 +2401,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
2401 'ClassText:Class componentDidMount',
2402 'Text:Function create passive',
2403 ]);
2281 - expect(ReactNoop.getChildren()).toEqual([
2282 - span('Function'),
2283 - span('Class'),
2284 - ]);
2404 + expect(ReactNoop).toMatchRenderedOutput(
2405 + <>
2406 + <span prop="Function" />
2407 + <span prop="Class" />
2408 + </>,
2409 + );
2410
2411 // Schedule an update that causes React to suspend.
2412 act(() => {
@@ -2299,10 +2424,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
2424 'ClassText:Class render',
2425 'ClassText:Fallback render',
2426 ]);
2302 - expect(ReactNoop.getChildren()).toEqual([
2303 - span('Function'),
2304 - span('Class'),
2305 - ]);
2427 + expect(ReactNoop).toMatchRenderedOutput(
2428 + <>
2429 + <span prop="Function" />
2430 + <span prop="Class" />
2431 + </>,
2432 + );
2433
2434 await advanceTimers(1000);
2435
@@ -2312,11 +2439,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2439 'ClassText:Class componentWillUnmount',
2440 'ClassText:Fallback componentDidMount',
2441 ]);
2315 - expect(ReactNoop.getChildren()).toEqual([
2316 - spanHidden('Function'),
2317 - spanHidden('Class'),
2318 - span('Fallback'),
2319 - ]);
2442 + expect(ReactNoop).toMatchRenderedOutput(
2443 + <>
2444 + <span prop="Function" hidden={true} />
2445 + <span prop="Class" hidden={true} />
2446 + <span prop="Fallback" />
2447 + </>,
2448 + );
2449
2450 // Resolving the suspended resource should re-create inner layout effects.
2451 await act(async () => {
@@ -2328,11 +2457,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2457 'Suspend:Async_2',
2458 'ClassText:Class render',
2459 ]);
2331 - expect(ReactNoop.getChildren()).toEqual([
2332 - spanHidden('Function'),
2333 - spanHidden('Class'),
2334 - span('Fallback'),
2335 - ]);
2460 + expect(ReactNoop).toMatchRenderedOutput(
2461 + <>
2462 + <span prop="Function" hidden={true} />
2463 + <span prop="Class" hidden={true} />
2464 + <span prop="Fallback" />
2465 + </>,
2466 + );
2467
2468 // Resolving the suspended resource should re-create inner layout effects.
2469 await act(async () => {
@@ -2351,12 +2482,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
2482 'AsyncText:Async_1 create passive',
2483 'AsyncText:Async_2 create passive',
2484 ]);
2354 - expect(ReactNoop.getChildren()).toEqual([
2355 - span('Function'),
2356 - span('Async_1'),
2357 - span('Async_2'),
2358 - span('Class'),
2359 - ]);
2485 + expect(ReactNoop).toMatchRenderedOutput(
2486 + <>
2487 + <span prop="Function" />
2488 + <span prop="Async_1" />
2489 + <span prop="Async_2" />
2490 + <span prop="Class" />
2491 + </>,
2492 + );
2493
2494 await act(async () => {
2495 ReactNoop.render(null);
@@ -2427,11 +2560,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2560 'ClassText:Class componentDidMount',
2561 'Text:Function create passive',
2562 ]);
2430 - expect(ReactNoop.getChildren()).toEqual([
2431 - span('Function'),
2432 - span('Suspender'),
2433 - span('Class'),
2434 - ]);
2563 + expect(ReactNoop).toMatchRenderedOutput(
2564 + <>
2565 + <span prop="Function" />
2566 + <span prop="Suspender" />
2567 + <span prop="Class" />
2568 + </>,
2569 + );
2570
2571 // Schedule an update that causes React to suspend.
2572 textToRead = 'A';
@@ -2445,11 +2580,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2580 'ClassText:Class render',
2581 'ClassText:Fallback render',
2582 ]);
2448 - expect(ReactNoop.getChildren()).toEqual([
2449 - span('Function'),
2450 - span('Suspender'),
2451 - span('Class'),
2452 - ]);
2583 + expect(ReactNoop).toMatchRenderedOutput(
2584 + <>
2585 + <span prop="Function" />
2586 + <span prop="Suspender" />
2587 + <span prop="Class" />
2588 + </>,
2589 + );
2590
2591 await advanceTimers(1000);
2592
@@ -2459,12 +2596,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
2596 'ClassText:Class componentWillUnmount',
2597 'ClassText:Fallback componentDidMount',
2598 ]);
2462 - expect(ReactNoop.getChildren()).toEqual([
2463 - spanHidden('Function'),
2464 - spanHidden('Suspender'),
2465 - spanHidden('Class'),
2466 - span('Fallback'),
2467 - ]);
2599 + expect(ReactNoop).toMatchRenderedOutput(
2600 + <>
2601 + <span prop="Function" hidden={true} />
2602 + <span prop="Suspender" hidden={true} />
2603 + <span prop="Class" hidden={true} />
2604 + <span prop="Fallback" />
2605 + </>,
2606 + );
2607
2608 // Resolving the suspended resource should re-create inner layout effects.
2609 textToRead = 'B';
@@ -2477,12 +2616,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
2616 'Suspend:B',
2617 'ClassText:Class render',
2618 ]);
2480 - expect(ReactNoop.getChildren()).toEqual([
2481 - spanHidden('Function'),
2482 - spanHidden('Suspender'),
2483 - spanHidden('Class'),
2484 - span('Fallback'),
2485 - ]);
2619 + expect(ReactNoop).toMatchRenderedOutput(
2620 + <>
2621 + <span prop="Function" hidden={true} />
2622 + <span prop="Suspender" hidden={true} />
2623 + <span prop="Class" hidden={true} />
2624 + <span prop="Fallback" />
2625 + </>,
2626 + );
2627
2628 // Resolving the suspended resource should re-create inner layout effects.
2629 await act(async () => {
@@ -2496,11 +2637,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2637 'Text:Function create layout',
2638 'ClassText:Class componentDidMount',
2639 ]);
2499 - expect(ReactNoop.getChildren()).toEqual([
2500 - span('Function'),
2501 - span('Suspender'),
2502 - span('Class'),
2503 - ]);
2640 + expect(ReactNoop).toMatchRenderedOutput(
2641 + <>
2642 + <span prop="Function" />
2643 + <span prop="Suspender" />
2644 + <span prop="Class" />
2645 + </>,
2646 + );
2647
2648 await act(async () => {
2649 ReactNoop.render(null);
@@ -2609,7 +2752,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2752 'RefCheckerOuter refCallback value? true',
2753 'RefCheckerOuter create layout refObject? true refCallback? true',
2754 ]);
2612 - expect(ReactNoop.getChildren()).toEqual([]);
2755 + expect(ReactNoop).toMatchRenderedOutput(null);
2756
2757 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
2758 act(() => {
@@ -2630,7 +2773,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2773 'Text:Fallback create layout',
2774 'Text:Fallback create passive',
2775 ]);
2633 - expect(ReactNoop.getChildren()).toEqual([span('Fallback')]);
2776 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Fallback" />);
2777
2778 // Resolving the suspended resource should re-create inner layout effects.
2779 await act(async () => {
@@ -2643,7 +2786,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2786 'Text:Fallback destroy passive',
2787 'AsyncText:Async create passive',
2788 ]);
2646 - expect(ReactNoop.getChildren()).toEqual([span('Async')]);
2789 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
2790
2791 await act(async () => {
2792 ReactNoop.renderLegacySyncRoot(null);
@@ -2656,7 +2799,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2799 'RefCheckerInner:refCallback destroy layout ref? false',
2800 'AsyncText:Async destroy passive',
2801 ]);
2659 - expect(ReactNoop.getChildren()).toEqual([]);
2802 + expect(ReactNoop).toMatchRenderedOutput(null);
2803 });
2804
2805 // @gate enableLegacyCache
@@ -2685,10 +2828,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
2828 'RefCheckerOuter refCallback value? true',
2829 'RefCheckerOuter create layout refObject? true refCallback? true',
2830 ]);
2688 - expect(ReactNoop.getChildren()).toEqual([
2689 - span('refObject'),
2690 - span('refCallback'),
2691 - ]);
2831 + expect(ReactNoop).toMatchRenderedOutput(
2832 + <>
2833 + <span prop="refObject" />
2834 + <span prop="refCallback" />
2835 + </>,
2836 + );
2837
2838 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
2839 act(() => {
@@ -2710,11 +2855,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2855 'RefCheckerInner:refCallback destroy layout ref? false',
2856 'Text:Fallback create layout',
2857 ]);
2713 - expect(ReactNoop.getChildren()).toEqual([
2714 - spanHidden('refObject'),
2715 - spanHidden('refCallback'),
2716 - span('Fallback'),
2717 - ]);
2858 + expect(ReactNoop).toMatchRenderedOutput(
2859 + <>
2860 + <span prop="refObject" hidden={true} />
2861 + <span prop="refCallback" hidden={true} />
2862 + <span prop="Fallback" />
2863 + </>,
2864 + );
2865
2866 // Resolving the suspended resource should re-create inner layout effects.
2867 await act(async () => {
@@ -2735,11 +2882,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
2882 'Text:Fallback destroy passive',
2883 'AsyncText:Async create passive',
2884 ]);
2738 - expect(ReactNoop.getChildren()).toEqual([
2739 - span('Async'),
2740 - span('refObject'),
2741 - span('refCallback'),
2742 - ]);
2885 + expect(ReactNoop).toMatchRenderedOutput(
2886 + <>
2887 + <span prop="Async" />
2888 + <span prop="refObject" />
2889 + <span prop="refCallback" />
2890 + </>,
2891 + );
2892
2893 await act(async () => {
2894 ReactNoop.render(null);
@@ -2752,7 +2901,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2901 'RefCheckerInner:refCallback destroy layout ref? false',
2902 'AsyncText:Async destroy passive',
2903 ]);
2755 - expect(ReactNoop.getChildren()).toEqual([]);
2904 + expect(ReactNoop).toMatchRenderedOutput(null);
2905 });
2906
2907 // @gate enableLegacyCache
@@ -2792,7 +2941,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2941 'RefCheckerOuter refCallback value? true',
2942 'RefCheckerOuter create layout refObject? true refCallback? true',
2943 ]);
2795 - expect(ReactNoop.getChildren()).toEqual([]);
2944 + expect(ReactNoop).toMatchRenderedOutput(null);
2945
2946 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
2947 act(() => {
@@ -2816,7 +2965,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2965 'RefCheckerInner:refCallback destroy layout ref? false',
2966 'Text:Fallback create layout',
2967 ]);
2819 - expect(ReactNoop.getChildren()).toEqual([span('Fallback')]);
2968 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Fallback" />);
2969
2970 // Resolving the suspended resource should re-create inner layout effects.
2971 await act(async () => {
@@ -2839,7 +2988,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
2988 'Text:Fallback destroy passive',
2989 'AsyncText:Async create passive',
2990 ]);
2842 - expect(ReactNoop.getChildren()).toEqual([span('Async')]);
2991 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
2992
2993 await act(async () => {
2994 ReactNoop.render(null);
@@ -2852,7 +3001,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3001 'RefCheckerInner:refCallback destroy layout ref? false',
3002 'AsyncText:Async destroy passive',
3003 ]);
2855 - expect(ReactNoop.getChildren()).toEqual([]);
3004 + expect(ReactNoop).toMatchRenderedOutput(null);
3005 });
3006
3007 // @gate enableLegacyCache
@@ -2896,7 +3045,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3045 'RefCheckerOuter refCallback value? true',
3046 'RefCheckerOuter create layout refObject? true refCallback? true',
3047 ]);
2899 - expect(ReactNoop.getChildren()).toEqual([]);
3048 + expect(ReactNoop).toMatchRenderedOutput(null);
3049
3050 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
3051 act(() => {
@@ -2920,7 +3069,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3069 'RefCheckerInner:refCallback destroy layout ref? false',
3070 'Text:Fallback create layout',
3071 ]);
2923 - expect(ReactNoop.getChildren()).toEqual([span('Fallback')]);
3072 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Fallback" />);
3073
3074 // Resolving the suspended resource should re-create inner layout effects.
3075 await act(async () => {
@@ -2943,7 +3092,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3092 'Text:Fallback destroy passive',
3093 'AsyncText:Async create passive',
3094 ]);
2946 - expect(ReactNoop.getChildren()).toEqual([span('Async')]);
3095 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
3096
3097 await act(async () => {
3098 ReactNoop.render(null);
@@ -2956,7 +3105,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3105 'RefCheckerInner:refCallback destroy layout ref? false',
3106 'AsyncText:Async destroy passive',
3107 ]);
2959 - expect(ReactNoop.getChildren()).toEqual([]);
3108 + expect(ReactNoop).toMatchRenderedOutput(null);
3109 });
3110
3111 // @gate enableLegacyCache
@@ -3009,7 +3158,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3158 'RefChecker create layout ref? true',
3159 'App create layout ref? true',
3160 ]);
3012 - expect(ReactNoop.getChildren()).toEqual([]);
3161 + expect(ReactNoop).toMatchRenderedOutput(null);
3162
3163 // Suspend the inner Suspense subtree (only inner effects should be destroyed)
3164 act(() => {
@@ -3026,7 +3175,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3175 'RefChecker destroy layout ref? true',
3176 'Text:Fallback create layout',
3177 ]);
3029 - expect(ReactNoop.getChildren()).toEqual([span('Fallback')]);
3178 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Fallback" />);
3179
3180 // Resolving the suspended resource should re-create inner layout effects.
3181 await act(async () => {
@@ -3042,7 +3191,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3191 'Text:Fallback destroy passive',
3192 'AsyncText:Async create passive',
3193 ]);
3045 - expect(ReactNoop.getChildren()).toEqual([span('Async')]);
3194 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
3195
3196 await act(async () => {
3197 ReactNoop.render(null);
@@ -3053,7 +3202,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3202 'RefChecker destroy layout ref? true',
3203 'AsyncText:Async destroy passive',
3204 ]);
3056 - expect(ReactNoop.getChildren()).toEqual([]);
3205 + expect(ReactNoop).toMatchRenderedOutput(null);
3206 });
3207
3208 describe('that throw errors', () => {
@@ -3115,11 +3264,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
3264 'Text:Inside create passive',
3265 'Text:Outside create passive',
3266 ]);
3118 - expect(ReactNoop.getChildren()).toEqual([
3119 - span('ThrowsInRefCallback'),
3120 - span('Inside'),
3121 - span('Outside'),
3122 - ]);
3267 + expect(ReactNoop).toMatchRenderedOutput(
3268 + <>
3269 + <span prop="ThrowsInRefCallback" />
3270 + <span prop="Inside" />
3271 + <span prop="Outside" />
3272 + </>,
3273 + );
3274
3275 // Schedule an update that causes React to suspend.
3276 await act(async () => {
@@ -3144,12 +3295,14 @@ describe('ReactSuspenseEffectsSemantics', () => {
3295 'Text:Fallback create layout',
3296 'Text:Fallback create passive',
3297 ]);
3147 - expect(ReactNoop.getChildren()).toEqual([
3148 - spanHidden('ThrowsInRefCallback'),
3149 - spanHidden('Inside'),
3150 - span('Fallback'),
3151 - span('Outside'),
3152 - ]);
3298 + expect(ReactNoop).toMatchRenderedOutput(
3299 + <>
3300 + <span prop="ThrowsInRefCallback" hidden={true} />
3301 + <span prop="Inside" hidden={true} />
3302 + <span prop="Fallback" />
3303 + <span prop="Outside" />
3304 + </>,
3305 + );
3306
3307 // Resolve the pending suspense and throw
3308 useRefCallbackShouldThrow = true;
@@ -3188,7 +3341,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
3341 'Text:Error create layout',
3342 'Text:Error create passive',
3343 ]);
3191 - expect(ReactNoop.getChildren()).toEqual([span('Error')]);
3344 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Error" />);
3345 });
3346 });
3347 });
packages/react-reconciler/src/__tests__/ReactSuspenseFallback-test.js
+6 -10
@@ -127,10 +127,6 @@ describe('ReactSuspenseFallback', () => {
127 return <span prop={fullText} />;
128 }
129
130 - function span(prop) {
131 - return {type: 'span', children: [], prop, hidden: false};
132 - }
133 -
130 // @gate enableLegacyCache
131 it('suspends and shows fallback', () => {
132 ReactNoop.render(
@@ -140,7 +136,7 @@ describe('ReactSuspenseFallback', () => {
136 );
137
138 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
143 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
139 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
140 });
141
142 // @gate enableLegacyCache
@@ -155,7 +151,7 @@ describe('ReactSuspenseFallback', () => {
151 'Suspend! [A]',
152 // null
153 ]);
158 - expect(ReactNoop.getChildren()).toEqual([]);
154 + expect(ReactNoop).toMatchRenderedOutput(null);
155 });
156
157 // @gate enableLegacyCache
@@ -170,7 +166,7 @@ describe('ReactSuspenseFallback', () => {
166 'Suspend! [A]',
167 // null
168 ]);
173 - expect(ReactNoop.getChildren()).toEqual([]);
169 + expect(ReactNoop).toMatchRenderedOutput(null);
170 });
171
172 // @gate enableLegacyCache
@@ -184,7 +180,7 @@ describe('ReactSuspenseFallback', () => {
180 );
181
182 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
187 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
183 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
184 });
185
186 // @gate enableLegacyCache
@@ -201,7 +197,7 @@ describe('ReactSuspenseFallback', () => {
197 'Suspend! [A]',
198 // null
199 ]);
204 - expect(ReactNoop.getChildren()).toEqual([]);
200 + expect(ReactNoop).toMatchRenderedOutput(null);
201 });
202
203 // @gate enableLegacyCache
@@ -218,6 +214,6 @@ describe('ReactSuspenseFallback', () => {
214 'Suspend! [A]',
215 // null
216 ]);
221 - expect(ReactNoop.getChildren()).toEqual([]);
217 + expect(ReactNoop).toMatchRenderedOutput(null);
218 });
219 });
packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js
+335 -179
@@ -157,14 +157,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
157
158 const rejectText = rejectMostRecentTextCache;
159
160 - function span(prop) {
161 - return {type: 'span', children: [], prop, hidden: false};
162 - }
163 -
164 - function hiddenSpan(prop) {
165 - return {type: 'span', children: [], prop, hidden: true};
166 - }
167 -
160 function advanceTimers(ms) {
161 // Note: This advances Jest's virtual time but not React's. Use
162 // ReactNoop.expire for that.
@@ -231,7 +223,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
223 'C',
224 // We leave D incomplete.
225 ]);
234 - expect(ReactNoop.getChildren()).toEqual([]);
226 + expect(ReactNoop).toMatchRenderedOutput(null);
227
228 // Flush the promise completely
229 await resolveText('A');
@@ -239,11 +231,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
231 // Even though the promise has resolved, we should now flush
232 // and commit the in progress render instead of restarting.
233 expect(Scheduler).toFlushAndYield(['D']);
242 - expect(ReactNoop.getChildren()).toEqual([
243 - span('Loading...'),
244 - span('C'),
245 - span('D'),
246 - ]);
234 + expect(ReactNoop).toMatchRenderedOutput(
235 + <>
236 + <span prop="Loading..." />
237 + <span prop="C" />
238 + <span prop="D" />
239 + </>,
240 + );
241
242 // Await one micro task to attach the retry listeners.
243 await null;
@@ -251,12 +245,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
245 // Next, we'll flush the complete content.
246 expect(Scheduler).toFlushAndYield(['Bar', 'A', 'B']);
247
254 - expect(ReactNoop.getChildren()).toEqual([
255 - span('A'),
256 - span('B'),
257 - span('C'),
258 - span('D'),
259 - ]);
248 + expect(ReactNoop).toMatchRenderedOutput(
249 + <>
250 + <span prop="A" />
251 + <span prop="B" />
252 + <span prop="C" />
253 + <span prop="D" />
254 + </>,
255 + );
256 });
257
258 // @gate enableLegacyCache
@@ -301,13 +297,18 @@ describe('ReactSuspenseWithNoopRenderer', () => {
297 'B',
298 'Loading...',
299 ]);
304 - expect(ReactNoop.getChildren()).toEqual([]);
300 + expect(ReactNoop).toMatchRenderedOutput(null);
301
302 // Resolve the data
303 await resolveText('A');
304 // Renders successfully
305 expect(Scheduler).toFlushAndYield(['Foo', 'Bar', 'A', 'B']);
310 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
306 + expect(ReactNoop).toMatchRenderedOutput(
307 + <>
308 + <span prop="A" />
309 + <span prop="B" />
310 + </>,
311 + );
312 });
313
314 // @gate enableLegacyCache
@@ -329,24 +330,36 @@ describe('ReactSuspenseWithNoopRenderer', () => {
330 'Suspend! [B]',
331 'Loading B...',
332 ]);
332 - expect(ReactNoop.getChildren()).toEqual([
333 - span('Loading A...'),
334 - span('Loading B...'),
335 - ]);
333 + expect(ReactNoop).toMatchRenderedOutput(
334 + <>
335 + <span prop="Loading A..." />
336 + <span prop="Loading B..." />
337 + </>,
338 + );
339
340 // Resolve first Suspense's promise so that it switches switches back to the
341 // normal view. The second Suspense should still show the placeholder.
342 await resolveText('A');
343
344 expect(Scheduler).toFlushAndYield(['A']);
342 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('Loading B...')]);
345 + expect(ReactNoop).toMatchRenderedOutput(
346 + <>
347 + <span prop="A" />
348 + <span prop="Loading B..." />
349 + </>,
350 + );
351
352 // Resolve the second Suspense's promise so that it switches back to the
353 // normal view.
354 await resolveText('B');
355
356 expect(Scheduler).toFlushAndYield(['B']);
349 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
357 + expect(ReactNoop).toMatchRenderedOutput(
358 + <>
359 + <span prop="A" />
360 + <span prop="B" />
361 + </>,
362 + );
363 });
364
365 // @gate enableLegacyCache
@@ -385,18 +398,20 @@ describe('ReactSuspenseWithNoopRenderer', () => {
398 'Loading...',
399 ]);
400 // Did not commit yet.
388 - expect(ReactNoop.getChildren()).toEqual([]);
401 + expect(ReactNoop).toMatchRenderedOutput(null);
402
403 // Wait for data to resolve
404 await resolveText('B');
405 // Renders successfully
406 expect(Scheduler).toFlushAndYield(['A', 'B', 'C', 'D']);
394 - expect(ReactNoop.getChildren()).toEqual([
395 - span('A'),
396 - span('B'),
397 - span('C'),
398 - span('D'),
399 - ]);
407 + expect(ReactNoop).toMatchRenderedOutput(
408 + <>
409 + <span prop="A" />
410 + <span prop="B" />
411 + <span prop="C" />
412 + <span prop="D" />
413 + </>,
414 + );
415 });
416
417 // Second condition is redundant but guarantees that the test runs in prod.
@@ -435,7 +450,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
450
451 ReactNoop.render(<App />);
452 expect(Scheduler).toFlushAndYield([]);
438 - expect(ReactNoop.getChildren()).toEqual([]);
453 + expect(ReactNoop).toMatchRenderedOutput(null);
454
455 if (gate(flags => flags.enableSyncDefaultUpdates)) {
456 React.startTransition(() => {
@@ -445,7 +460,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
460 ReactNoop.render(<App renderContent={true} />);
461 }
462 expect(Scheduler).toFlushAndYield(['Suspend! [Result]', 'Loading...']);
448 - expect(ReactNoop.getChildren()).toEqual([]);
463 + expect(ReactNoop).toMatchRenderedOutput(null);
464
465 await rejectText('Result', new Error('Failed to load: Result'));
466
@@ -458,9 +473,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
473 // Errored again on retry. Now handle it.
474 'Caught error: Failed to load: Result',
475 ]);
461 - expect(ReactNoop.getChildren()).toEqual([
462 - span('Caught error: Failed to load: Result'),
463 - ]);
476 + expect(ReactNoop).toMatchRenderedOutput(
477 + <span prop="Caught error: Failed to load: Result" />,
478 + );
479 });
480
481 // Second condition is redundant but guarantees that the test runs in prod.
@@ -497,7 +512,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
512
513 ReactNoop.render(<App />);
514 expect(Scheduler).toFlushAndYield(['Suspend! [Result]', 'Loading...']);
500 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
515 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
516
517 await rejectText('Result', new Error('Failed to load: Result'));
518
@@ -510,9 +525,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
525 // Errored again on retry. Now handle it.
526 'Caught error: Failed to load: Result',
527 ]);
513 - expect(ReactNoop.getChildren()).toEqual([
514 - span('Caught error: Failed to load: Result'),
515 - ]);
528 + expect(ReactNoop).toMatchRenderedOutput(
529 + <span prop="Caught error: Failed to load: Result" />,
530 + );
531 });
532
533 // @gate enableLegacyCache
@@ -531,7 +546,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
546 expect(Scheduler).toFlushAndYield(['A', 'Suspend! [1]', 'Loading...']);
547 await resolveText('1');
548 expect(Scheduler).toFlushAndYield(['A', '1']);
534 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('1')]);
549 + expect(ReactNoop).toMatchRenderedOutput(
550 + <>
551 + <span prop="A" />
552 + <span prop="1" />
553 + </>,
554 + );
555
556 // Update the low-pri text
557 ReactNoop.render(<App highPri="A" lowPri="2" />);
@@ -548,11 +568,21 @@ describe('ReactSuspenseWithNoopRenderer', () => {
568 ReactNoop.render(<App highPri="B" lowPri="1" />);
569 });
570 expect(Scheduler).toHaveYielded(['B', '1']);
551 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('1')]);
571 + expect(ReactNoop).toMatchRenderedOutput(
572 + <>
573 + <span prop="B" />
574 + <span prop="1" />
575 + </>,
576 + );
577
578 // Unblock the low-pri text and finish
579 await resolveText('2');
555 - expect(ReactNoop.getChildren()).toEqual([span('B'), span('1')]);
580 + expect(ReactNoop).toMatchRenderedOutput(
581 + <>
582 + <span prop="B" />
583 + <span prop="1" />
584 + </>,
585 + );
586 });
587
588 // @gate enableLegacyCache
@@ -571,7 +601,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
601
602 ReactNoop.render(<App showA={false} showB={false} />);
603 expect(Scheduler).toFlushAndYield([]);
574 - expect(ReactNoop.getChildren()).toEqual([]);
604 + expect(ReactNoop).toMatchRenderedOutput(null);
605
606 if (gate(flags => flags.enableSyncDefaultUpdates)) {
607 React.startTransition(() => {
@@ -581,7 +611,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
611 ReactNoop.render(<App showA={true} showB={false} />);
612 }
613 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
584 - expect(ReactNoop.getChildren()).toEqual([]);
614 + expect(ReactNoop).toMatchRenderedOutput(null);
615
616 // Advance React's virtual time by enough to fall into a new async bucket,
617 // but not enough to expire the suspense timeout.
@@ -594,11 +624,16 @@ describe('ReactSuspenseWithNoopRenderer', () => {
624 ReactNoop.render(<App showA={true} showB={true} />);
625 }
626 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'B', 'Loading...']);
597 - expect(ReactNoop.getChildren()).toEqual([]);
627 + expect(ReactNoop).toMatchRenderedOutput(null);
628
629 await resolveText('A');
630 expect(Scheduler).toFlushAndYield(['A', 'B']);
601 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
631 + expect(ReactNoop).toMatchRenderedOutput(
632 + <>
633 + <span prop="A" />
634 + <span prop="B" />
635 + </>,
636 + );
637 });
638
639 // @gate enableLegacyCache
@@ -628,7 +663,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
663 // but we have another pending update that we can work on
664 '(empty)',
665 ]);
631 - expect(ReactNoop.getChildren()).toEqual([span('(empty)')]);
666 + expect(ReactNoop).toMatchRenderedOutput(<span prop="(empty)" />);
667 });
668
669 // Note: This test was written to test a heuristic used in the expiration
@@ -735,7 +770,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
770 'Sync',
771 ]);
772 // The update hasn't expired yet, so we commit nothing.
738 - expect(ReactNoop.getChildren()).toEqual([]);
773 + expect(ReactNoop).toMatchRenderedOutput(null);
774
775 // Advance both React's virtual time and Jest's timers by enough to expire
776 // the update.
@@ -745,12 +780,22 @@ describe('ReactSuspenseWithNoopRenderer', () => {
780 // the placeholder.
781 expect(Scheduler).toHaveYielded([]);
782 // Should have committed the placeholder.
748 - expect(ReactNoop.getChildren()).toEqual([span('Loading...'), span('Sync')]);
783 + expect(ReactNoop).toMatchRenderedOutput(
784 + <>
785 + <span prop="Loading..." />
786 + <span prop="Sync" />
787 + </>,
788 + );
789
790 // Once the promise resolves, we render the suspended view
791 await resolveText('Async');
792 expect(Scheduler).toFlushAndYield(['Async']);
753 - expect(ReactNoop.getChildren()).toEqual([span('Async'), span('Sync')]);
793 + expect(ReactNoop).toMatchRenderedOutput(
794 + <>
795 + <span prop="Async" />
796 + <span prop="Sync" />
797 + </>,
798 + );
799 });
800
801 // @gate enableLegacyCache
@@ -779,10 +824,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
824 'Loading outer...',
825 ]);
826 // The outer loading state finishes immediately.
782 - expect(ReactNoop.getChildren()).toEqual([
783 - span('Sync'),
784 - span('Loading outer...'),
785 - ]);
827 + expect(ReactNoop).toMatchRenderedOutput(
828 + <>
829 + <span prop="Sync" />
830 + <span prop="Loading outer..." />
831 + </>,
832 + );
833
834 // Resolve the outer promise.
835 await resolveText('Outer content');
@@ -792,30 +839,36 @@ describe('ReactSuspenseWithNoopRenderer', () => {
839 'Loading inner...',
840 ]);
841 // Don't commit the inner placeholder yet.
795 - expect(ReactNoop.getChildren()).toEqual([
796 - span('Sync'),
797 - span('Loading outer...'),
798 - ]);
842 + expect(ReactNoop).toMatchRenderedOutput(
843 + <>
844 + <span prop="Sync" />
845 + <span prop="Loading outer..." />
846 + </>,
847 + );
848
849 // Expire the inner timeout.
850 ReactNoop.expire(500);
851 await advanceTimers(500);
852 // Now that 750ms have elapsed since the outer placeholder timed out,
853 // we can timeout the inner placeholder.
805 - expect(ReactNoop.getChildren()).toEqual([
806 - span('Sync'),
807 - span('Outer content'),
808 - span('Loading inner...'),
809 - ]);
854 + expect(ReactNoop).toMatchRenderedOutput(
855 + <>
856 + <span prop="Sync" />
857 + <span prop="Outer content" />
858 + <span prop="Loading inner..." />
859 + </>,
860 + );
861
862 // Finally, flush the inner promise. We should see the complete screen.
863 await resolveText('Inner content');
864 expect(Scheduler).toFlushAndYield(['Inner content']);
814 - expect(ReactNoop.getChildren()).toEqual([
815 - span('Sync'),
816 - span('Outer content'),
817 - span('Inner content'),
818 - ]);
865 + expect(ReactNoop).toMatchRenderedOutput(
866 + <>
867 + <span prop="Sync" />
868 + <span prop="Outer content" />
869 + <span prop="Inner content" />
870 + </>,
871 + );
872 });
873
874 // @gate enableLegacyCache
@@ -841,12 +894,22 @@ describe('ReactSuspenseWithNoopRenderer', () => {
894 'Sync',
895 ]);
896 // The tree commits synchronously
844 - expect(ReactNoop.getChildren()).toEqual([span('Loading...'), span('Sync')]);
897 + expect(ReactNoop).toMatchRenderedOutput(
898 + <>
899 + <span prop="Loading..." />
900 + <span prop="Sync" />
901 + </>,
902 + );
903
904 // Once the promise resolves, we render the suspended view
905 await resolveText('Async');
906 expect(Scheduler).toFlushAndYield(['Async']);
849 - expect(ReactNoop.getChildren()).toEqual([span('Async'), span('Sync')]);
907 + expect(ReactNoop).toMatchRenderedOutput(
908 + <>
909 + <span prop="Async" />
910 + <span prop="Sync" />
911 + </>,
912 + );
913 });
914
915 // @gate enableLegacyCache
@@ -870,7 +933,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
933 'Loading (outer)...',
934 ]);
935 // The tree commits synchronously
873 - expect(ReactNoop.getChildren()).toEqual([span('Loading (outer)...')]);
936 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading (outer)..." />);
937 });
938
939 // @gate enableLegacyCache
@@ -899,7 +962,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
962 'Sync',
963 ]);
964 // The update hasn't expired yet, so we commit nothing.
902 - expect(ReactNoop.getChildren()).toEqual([]);
965 + expect(ReactNoop).toMatchRenderedOutput(null);
966
967 // Advance both React's virtual time and Jest's timers by enough to trigger
968 // the timeout, but not by enough to flush the promise or reach the true
@@ -907,12 +970,22 @@ describe('ReactSuspenseWithNoopRenderer', () => {
970 ReactNoop.expire(2000);
971 await advanceTimers(2000);
972 expect(Scheduler).toFlushWithoutYielding();
910 - expect(ReactNoop.getChildren()).toEqual([span('Loading...'), span('Sync')]);
973 + expect(ReactNoop).toMatchRenderedOutput(
974 + <>
975 + <span prop="Loading..." />
976 + <span prop="Sync" />
977 + </>,
978 + );
979
980 // Once the promise resolves, we render the suspended view
981 await resolveText('Async');
982 expect(Scheduler).toFlushAndYield(['Async']);
915 - expect(ReactNoop.getChildren()).toEqual([span('Async'), span('Sync')]);
983 + expect(ReactNoop).toMatchRenderedOutput(
984 + <>
985 + <span prop="Async" />
986 + <span prop="Sync" />
987 + </>,
988 + );
989 });
990
991 // @gate enableLegacyCache
@@ -943,22 +1016,27 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1016 'Sync',
1017 ]);
1018 // The update hasn't expired yet, so we commit nothing.
946 - expect(ReactNoop.getChildren()).toEqual([]);
1019 + expect(ReactNoop).toMatchRenderedOutput(null);
1020
1021 // Advance both React's virtual time and Jest's timers,
1022 // but not by enough to flush the promise or reach the true expiration time.
1023 ReactNoop.expire(2000);
1024 await advanceTimers(2000);
952 - expect(ReactNoop.getChildren()).toEqual([]);
1025 + expect(ReactNoop).toMatchRenderedOutput(null);
1026
1027 // Even flushing won't yield a fallback in a transition.
1028 expect(Scheduler).toFlushWithoutYielding();
956 - expect(ReactNoop.getChildren()).toEqual([]);
1029 + expect(ReactNoop).toMatchRenderedOutput(null);
1030
1031 // Once the promise resolves, we render the suspended view
1032 await resolveText('Async');
1033 expect(Scheduler).toFlushAndYield(['Async', 'Sync']);
961 - expect(ReactNoop.getChildren()).toEqual([span('Async'), span('Sync')]);
1034 + expect(ReactNoop).toMatchRenderedOutput(
1035 + <>
1036 + <span prop="Async" />
1037 + <span prop="Sync" />
1038 + </>,
1039 + );
1040 });
1041
1042 // @gate enableLegacyCache
@@ -1038,13 +1116,18 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1116 'Suspend! [B]',
1117 'Loading...',
1118 ]);
1041 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1119 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1120
1121 await resolveText('A');
1122 await resolveText('B');
1123
1124 expect(Scheduler).toFlushAndYield(['A', 'B']);
1047 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
1125 + expect(ReactNoop).toMatchRenderedOutput(
1126 + <>
1127 + <span prop="A" />
1128 + <span prop="B" />
1129 + </>,
1130 + );
1131 });
1132
1133 // @gate enableLegacyCache
@@ -1068,13 +1151,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1151 );
1152 }
1153 expect(Scheduler).toFlushAndYield(['Suspend! [Async]', 'Loading...']);
1071 - expect(ReactNoop.getChildren()).toEqual([]);
1154 + expect(ReactNoop).toMatchRenderedOutput(null);
1155
1156 // Resolve the promise
1157 await resolveText('Async');
1158 // We can now resume rendering
1159 expect(Scheduler).toFlushAndYield(['Async']);
1077 - expect(ReactNoop.getChildren()).toEqual([span('Async')]);
1160 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Async" />);
1161 });
1162
1163 // @gate enableLegacyCache
@@ -1100,32 +1183,32 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1183 React.startTransition(() => ReactNoop.render(<App text="A" />));
1184 // The update should suspend.
1185 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1103 - expect(ReactNoop.getChildren()).toEqual([span('S')]);
1186 + expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1187
1188 // Advance time until right before it expires.
1189 await advanceTimers(4999);
1190 ReactNoop.expire(4999);
1191 expect(Scheduler).toFlushWithoutYielding();
1109 - expect(ReactNoop.getChildren()).toEqual([span('S')]);
1192 + expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1193
1194 // Schedule another low priority update.
1195 React.startTransition(() => ReactNoop.render(<App text="B" />));
1196 // This update should also suspend.
1197 expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1115 - expect(ReactNoop.getChildren()).toEqual([span('S')]);
1198 + expect(ReactNoop).toMatchRenderedOutput(<span prop="S" />);
1199
1200 // Schedule a regular update. Its expiration time will fall between
1201 // the expiration times of the previous two updates.
1202 ReactNoop.render(<App text="C" />);
1203 expect(Scheduler).toFlushAndYield(['C']);
1121 - expect(ReactNoop.getChildren()).toEqual([span('C')]);
1204 + expect(ReactNoop).toMatchRenderedOutput(<span prop="C" />);
1205
1206 // Flush the remaining work.
1207 await resolveText('A');
1208 await resolveText('B');
1209 // Nothing else to render.
1210 expect(Scheduler).toFlushWithoutYielding();
1128 - expect(ReactNoop.getChildren()).toEqual([span('C')]);
1211 + expect(ReactNoop).toMatchRenderedOutput(<span prop="C" />);
1212 });
1213
1214 // TODO: This test was written against the old Expiration Times
@@ -1166,13 +1249,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1249 'Loading...',
1250 'Commit: goodbye',
1251 ]);
1169 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1252 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1253
1254 await resolveText('goodbye');
1172 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1255 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1256
1257 expect(Scheduler).toFlushAndYield(['goodbye']);
1175 - expect(ReactNoop.getChildren()).toEqual([span('goodbye')]);
1258 + expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
1259 });
1260
1261 // @gate enableLegacyCache
@@ -1231,14 +1314,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1314 // Times out immediately, ignoring the specified threshold.
1315 ReactNoop.renderLegacySyncRoot(<App />);
1316 expect(Scheduler).toHaveYielded(['Suspend! [Result]', 'Loading...']);
1234 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1317 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1318
1319 await act(async () => {
1320 resolveText('Result');
1321 });
1322
1323 expect(Scheduler).toHaveYielded(['Result']);
1241 - expect(ReactNoop.getChildren()).toEqual([span('Result')]);
1324 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Result" />);
1325 });
1326
1327 // @gate enableLegacyCache
@@ -1420,7 +1503,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1503 'Suspend! [Hi]',
1504 'Loading...',
1505 ]);
1423 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1506 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1507
1508 await act(async () => {
1509 resolveText('Hi');
@@ -1431,7 +1514,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1514 'Hi',
1515 'componentDidMount',
1516 ]);
1434 - expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
1517 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Hi" />);
1518 });
1519
1520 // @gate enableLegacyCache
@@ -1468,12 +1551,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1551 // Re-render due to lifecycle update
1552 'Loading...',
1553 ]);
1471 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1554 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1555 await act(async () => {
1556 resolveText('Hi');
1557 });
1558 expect(Scheduler).toHaveYielded(['Hi']);
1476 - expect(ReactNoop.getChildren()).toEqual([span('Hi')]);
1559 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Hi" />);
1560 });
1561
1562 if (global.__PERSISTENT__) {
@@ -1891,7 +1974,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1974 'Loading...',
1975 ]);
1976 // We're now suspended and we haven't shown anything yet.
1894 - expect(ReactNoop.getChildren()).toEqual([]);
1977 + expect(ReactNoop).toMatchRenderedOutput(null);
1978
1979 // Flush some of the time
1980 Scheduler.unstable_advanceTime(450);
@@ -1899,7 +1982,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1982 // Because we've already been waiting for so long we can
1983 // wait a bit longer. Still nothing...
1984 expect(Scheduler).toFlushWithoutYielding();
1902 - expect(ReactNoop.getChildren()).toEqual([]);
1985 + expect(ReactNoop).toMatchRenderedOutput(null);
1986
1987 // Eventually we'll show the fallback.
1988 Scheduler.unstable_advanceTime(500);
@@ -1908,9 +1991,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
1991 expect(Scheduler).toFlushWithoutYielding();
1992 if (gate(flags => flags.enableSyncDefaultUpdates)) {
1993 // Since this is a transition, we never fallback.
1911 - expect(ReactNoop.getChildren()).toEqual([]);
1994 + expect(ReactNoop).toMatchRenderedOutput(null);
1995 } else {
1913 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1996 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
1997 }
1998
1999 // Flush the promise completely
@@ -1922,7 +2005,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2005 } else {
2006 expect(Scheduler).toFlushAndYield(['A']);
2007 }
1925 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2008 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2009 });
2010
2011 // @gate enableLegacyCache
@@ -1950,7 +2033,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2033 'Loading more...',
2034 'Loading...',
2035 ]);
1953 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2036 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2037
2038 await resolveText('A');
2039 // Wait a long time.
@@ -1967,16 +2050,23 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2050
2051 // Because we've already been waiting for so long we've exceeded
2052 // our threshold and we show the next level immediately.
1970 - expect(ReactNoop.getChildren()).toEqual([
1971 - span('A'),
1972 - span('Loading more...'),
1973 - ]);
2053 + expect(ReactNoop).toMatchRenderedOutput(
2054 + <>
2055 + <span prop="A" />
2056 + <span prop="Loading more..." />
2057 + </>,
2058 + );
2059
2060 // Flush the last promise completely
2061 await resolveText('B');
2062 // Renders successfully
2063 expect(Scheduler).toFlushAndYield(['B']);
1979 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
2064 + expect(ReactNoop).toMatchRenderedOutput(
2065 + <>
2066 + <span prop="A" />
2067 + <span prop="B" />
2068 + </>,
2069 + );
2070 });
2071
2072 // @gate enableLegacyCache
@@ -2004,7 +2094,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2094 'Loading more...',
2095 'Loading...',
2096 ]);
2007 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2097 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2098
2099 await resolveText('A');
2100
@@ -2017,16 +2107,21 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2107 ]);
2108 // Because we've already been waiting for so long we can
2109 // wait a bit longer. Still nothing...
2020 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2110 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2111
2112 await resolveText('B');
2113
2114 // Before we commit another Promise resolves.
2115 // We're still showing the first loading state.
2026 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2116 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2117 // Restart and render the complete content.
2118 expect(Scheduler).toFlushAndYield(['A', 'B']);
2029 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
2119 + expect(ReactNoop).toMatchRenderedOutput(
2120 + <>
2121 + <span prop="A" />
2122 + <span prop="B" />
2123 + </>,
2124 + );
2125 });
2126
2127 // @gate enableLegacyCache
@@ -2063,7 +2158,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2158 ]);
2159
2160 // We're now suspended and we haven't shown anything yet.
2066 - expect(ReactNoop.getChildren()).toEqual([]);
2161 + expect(ReactNoop).toMatchRenderedOutput(null);
2162
2163 // Flush some of the time
2164 Scheduler.unstable_advanceTime(500);
@@ -2076,9 +2171,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2171 expect(Scheduler).toFlushWithoutYielding();
2172 if (gate(flags => flags.enableSyncDefaultUpdates)) {
2173 // Transitions never fallback.
2079 - expect(ReactNoop.getChildren()).toEqual([]);
2174 + expect(ReactNoop).toMatchRenderedOutput(null);
2175 } else {
2081 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2176 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2177 }
2178 });
2179
@@ -2273,12 +2368,17 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2368 'B',
2369 'Initial load...',
2370 ]);
2276 - expect(ReactNoop.getChildren()).toEqual([span('Initial load...')]);
2371 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Initial load..." />);
2372
2373 // Eventually we resolve and show the data.
2374 await resolveText('A');
2375 expect(Scheduler).toFlushAndYield(['A', 'B']);
2281 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
2376 + expect(ReactNoop).toMatchRenderedOutput(
2377 + <>
2378 + <span prop="A" />
2379 + <span prop="B" />
2380 + </>,
2381 + );
2382
2383 // Update to show C
2384 ReactNoop.render(<Foo showC={true} />);
@@ -2294,16 +2394,24 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2394 await advanceTimers(600);
2395 // Since the optional suspense boundary is already showing its content,
2396 // we have to use the inner fallback instead.
2297 - expect(ReactNoop.getChildren()).toEqual([
2298 - hiddenSpan('A'),
2299 - span('Updating...'),
2300 - span('B'),
2301 - ]);
2397 + expect(ReactNoop).toMatchRenderedOutput(
2398 + <>
2399 + <span prop="A" hidden={true} />
2400 + <span prop="Updating..." />
2401 + <span prop="B" />
2402 + </>,
2403 + );
2404
2405 // Later we load the data.
2406 await resolveText('C');
2407 expect(Scheduler).toFlushAndYield(['A', 'C']);
2306 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('C'), span('B')]);
2408 + expect(ReactNoop).toMatchRenderedOutput(
2409 + <>
2410 + <span prop="A" />
2411 + <span prop="C" />
2412 + <span prop="B" />
2413 + </>,
2414 + );
2415 });
2416
2417 // @gate enableLegacyCache
@@ -2328,12 +2436,17 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2436 'B',
2437 // null
2438 ]);
2331 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2439 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2440
2441 // Eventually we resolve and show the data.
2442 await resolveText('A');
2443 expect(Scheduler).toFlushAndYield(['A']);
2336 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('B')]);
2444 + expect(ReactNoop).toMatchRenderedOutput(
2445 + <>
2446 + <span prop="A" />
2447 + <span prop="B" />
2448 + </>,
2449 + );
2450
2451 // Update to show C
2452 ReactNoop.render(<Foo showC={true} />);
@@ -2347,12 +2460,23 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2460 // Flush to skip suspended time.
2461 Scheduler.unstable_advanceTime(600);
2462 await advanceTimers(600);
2350 - expect(ReactNoop.getChildren()).toEqual([hiddenSpan('A'), span('B')]);
2463 + expect(ReactNoop).toMatchRenderedOutput(
2464 + <>
2465 + <span prop="A" hidden={true} />
2466 + <span prop="B" />
2467 + </>,
2468 + );
2469
2470 // Later we load the data.
2471 await resolveText('C');
2472 expect(Scheduler).toFlushAndYield(['A', 'C']);
2355 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('C'), span('B')]);
2473 + expect(ReactNoop).toMatchRenderedOutput(
2474 + <>
2475 + <span prop="A" />
2476 + <span prop="C" />
2477 + <span prop="B" />
2478 + </>,
2479 + );
2480 });
2481
2482 // @gate enableLegacyCache
@@ -2384,7 +2508,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2508 Scheduler.unstable_advanceTime(600);
2509 await advanceTimers(600);
2510
2387 - expect(ReactNoop.getChildren()).toEqual([span('A'), span('Loading B...')]);
2511 + expect(ReactNoop).toMatchRenderedOutput(
2512 + <>
2513 + <span prop="A" />
2514 + <span prop="Loading B..." />
2515 + </>,
2516 + );
2517 });
2518
2519 // @gate enableLegacyCache && enableSuspenseAvoidThisFallback
@@ -2411,7 +2540,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2540
2541 ReactNoop.render(<Foo />);
2542 expect(Scheduler).toFlushAndYield(['Foo', 'A']);
2414 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2543 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2544
2545 if (gate(flags => flags.enableSyncDefaultUpdates)) {
2546 React.startTransition(() => {
@@ -2428,7 +2557,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2557 'Loading B...',
2558 ]);
2559 // Still suspended.
2431 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2560 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2561
2562 // Flush to skip suspended time.
2563 Scheduler.unstable_advanceTime(600);
@@ -2436,12 +2565,14 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2565
2566 if (gate(flags => flags.enableSyncDefaultUpdates)) {
2567 // Transitions never fall back.
2439 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2568 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2569 } else {
2441 - expect(ReactNoop.getChildren()).toEqual([
2442 - span('A'),
2443 - span('Loading B...'),
2444 - ]);
2570 + expect(ReactNoop).toMatchRenderedOutput(
2571 + <>
2572 + <span prop="A" />
2573 + <span prop="Loading B..." />
2574 + </>,
2575 + );
2576 }
2577 });
2578
@@ -2465,7 +2596,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2596
2597 ReactNoop.render(<Foo />);
2598 expect(Scheduler).toFlushAndYield(['Foo', 'A']);
2468 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2599 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2600
2601 if (gate(flags => flags.enableSyncDefaultUpdates)) {
2602 React.startTransition(() => {
@@ -2482,7 +2613,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2613 // Null
2614 ]);
2615 // Still suspended.
2485 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2616 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2617
2618 // Flush to skip suspended time.
2619 Scheduler.unstable_advanceTime(600);
@@ -2490,9 +2621,9 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2621
2622 if (gate(flags => flags.enableSyncDefaultUpdates)) {
2623 // Transitions never fall back.
2493 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2624 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2625 } else {
2495 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2626 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2627 }
2628 });
2629
@@ -2520,7 +2651,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2651
2652 expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading A...']);
2653 // We're still suspended.
2523 - expect(ReactNoop.getChildren()).toEqual([]);
2654 + expect(ReactNoop).toMatchRenderedOutput(null);
2655
2656 // Schedule an update at idle pri.
2657 ReactNoop.idleUpdates(() => ReactNoop.render(<Foo renderContent={2} />));
@@ -2528,20 +2659,20 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2659 expect(Scheduler).toFlushAndYield([]);
2660
2661 // We're still suspended.
2531 - expect(ReactNoop.getChildren()).toEqual([]);
2662 + expect(ReactNoop).toMatchRenderedOutput(null);
2663
2664 // Advance time a little bit.
2665 Scheduler.unstable_advanceTime(150);
2666 await advanceTimers(150);
2667
2668 // We should not have committed yet because we had a long suspense time.
2538 - expect(ReactNoop.getChildren()).toEqual([]);
2669 + expect(ReactNoop).toMatchRenderedOutput(null);
2670
2671 // Flush to skip suspended time.
2672 Scheduler.unstable_advanceTime(600);
2673 await advanceTimers(600);
2674
2544 - expect(ReactNoop.getChildren()).toEqual([span('Loading A...')]);
2675 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading A..." />);
2676 });
2677
2678 describe('startTransition', () => {
@@ -2562,12 +2693,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2693 // Only a short time is needed to unsuspend the initial loading state.
2694 Scheduler.unstable_advanceTime(400);
2695 await advanceTimers(400);
2565 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2696 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2697
2698 // Later we load the data.
2699 await resolveText('A');
2700 expect(Scheduler).toFlushAndYield(['A']);
2570 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2701 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2702
2703 // Start transition.
2704 React.startTransition(() => ReactNoop.render(<App page="B" />));
@@ -2577,11 +2708,11 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2708 await advanceTimers(100000);
2709 // Even after lots of time has passed, we have still not yet flushed the
2710 // loading state.
2580 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2711 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2712 // Later we load the data.
2713 await resolveText('B');
2714 expect(Scheduler).toFlushAndYield(['B']);
2584 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2715 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2716 });
2717
2718 // @gate enableLegacyCache
@@ -2611,13 +2742,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2742 // Only a short time is needed to unsuspend the initial loading state.
2743 Scheduler.unstable_advanceTime(400);
2744 await advanceTimers(400);
2614 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2745 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2746 });
2747
2748 // Later we load the data.
2749 await resolveText('A');
2750 expect(Scheduler).toFlushAndYield(['A']);
2620 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2751 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2752
2753 // Start transition.
2754 await act(async () => {
@@ -2628,12 +2759,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2759 await advanceTimers(100000);
2760 // Even after lots of time has passed, we have still not yet flushed the
2761 // loading state.
2631 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2762 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2763 });
2764 // Later we load the data.
2765 await resolveText('B');
2766 expect(Scheduler).toFlushAndYield(['B']);
2636 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2767 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2768 });
2769
2770 // @gate enableLegacyCache
@@ -2666,13 +2797,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2797 // Only a short time is needed to unsuspend the initial loading state.
2798 Scheduler.unstable_advanceTime(400);
2799 await advanceTimers(400);
2669 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2800 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2801 });
2802
2803 // Later we load the data.
2804 await resolveText('A');
2805 expect(Scheduler).toFlushAndYield(['A']);
2675 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2806 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2807
2808 // Start transition.
2809 await act(async () => {
@@ -2683,12 +2814,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2814 await advanceTimers(100000);
2815 // Even after lots of time has passed, we have still not yet flushed the
2816 // loading state.
2686 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2817 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2818 });
2819 // Later we load the data.
2820 await resolveText('B');
2821 expect(Scheduler).toFlushAndYield(['B']);
2691 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2822 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2823 });
2824 });
2825
@@ -2710,12 +2841,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2841 // Only a short time is needed to unsuspend the initial loading state.
2842 Scheduler.unstable_advanceTime(400);
2843 await advanceTimers(400);
2713 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2844 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2845
2846 // Later we load the data.
2847 await resolveText('A');
2848 expect(Scheduler).toFlushAndYield(['A']);
2718 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2849 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2850
2851 // Start transition.
2852 React.startTransition(() => ReactNoop.render(<App page="B" />));
@@ -2725,12 +2856,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2856 await advanceTimers(2999);
2857 // Since the timeout is infinite (or effectively infinite),
2858 // we have still not yet flushed the loading state.
2728 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2859 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2860
2861 // Later we load the data.
2862 await resolveText('B');
2863 expect(Scheduler).toFlushAndYield(['B']);
2733 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2864 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2865
2866 // Start a long (infinite) transition.
2867 React.startTransition(() => ReactNoop.render(<App page="C" />));
@@ -2740,7 +2871,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2871 // loading state.
2872 Scheduler.unstable_advanceTime(100000);
2873 await advanceTimers(100000);
2743 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2874 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2875 });
2876
2877 // @gate enableLegacyCache
@@ -2770,13 +2901,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2901 // Only a short time is needed to unsuspend the initial loading state.
2902 Scheduler.unstable_advanceTime(400);
2903 await advanceTimers(400);
2773 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2904 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2905 });
2906
2907 // Later we load the data.
2908 await resolveText('A');
2909 expect(Scheduler).toFlushAndYield(['A']);
2779 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2910 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2911
2912 // Start transition.
2913 await act(async () => {
@@ -2788,13 +2919,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2919 await advanceTimers(2999);
2920 // Since the timeout is infinite (or effectively infinite),
2921 // we have still not yet flushed the loading state.
2791 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2922 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2923 });
2924
2925 // Later we load the data.
2926 await resolveText('B');
2927 expect(Scheduler).toFlushAndYield(['B']);
2797 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2928 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2929
2930 // Start a long (infinite) transition.
2931 await act(async () => {
@@ -2806,7 +2937,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2937 // loading state.
2938 Scheduler.unstable_advanceTime(100000);
2939 await advanceTimers(100000);
2809 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2940 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2941 });
2942 });
2943
@@ -2840,13 +2971,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2971 // Only a short time is needed to unsuspend the initial loading state.
2972 Scheduler.unstable_advanceTime(400);
2973 await advanceTimers(400);
2843 - expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
2974 + expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
2975 });
2976
2977 // Later we load the data.
2978 await resolveText('A');
2979 expect(Scheduler).toFlushAndYield(['A']);
2849 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2980 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2981
2982 // Start transition.
2983 await act(async () => {
@@ -2857,13 +2988,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
2988 await advanceTimers(2999);
2989 // Since the timeout is infinite (or effectively infinite),
2990 // we have still not yet flushed the loading state.
2860 - expect(ReactNoop.getChildren()).toEqual([span('A')]);
2991 + expect(ReactNoop).toMatchRenderedOutput(<span prop="A" />);
2992 });
2993
2994 // Later we load the data.
2995 await resolveText('B');
2996 expect(Scheduler).toFlushAndYield(['B']);
2866 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
2997 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
2998
2999 // Start a long (infinite) transition.
3000 await act(async () => {
@@ -2875,7 +3006,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3006 // loading state.
3007 Scheduler.unstable_advanceTime(100000);
3008 await advanceTimers(100000);
2878 - expect(ReactNoop.getChildren()).toEqual([span('B')]);
3009 + expect(ReactNoop).toMatchRenderedOutput(<span prop="B" />);
3010 });
3011 });
3012 });
@@ -2900,7 +3031,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3031 expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [A]', 'Loading...']);
3032 await resolveText('A');
3033 expect(Scheduler).toFlushAndYield(['Hi!', 'A']);
2903 - expect(ReactNoop.getChildren()).toEqual([span('Hi!'), span('A')]);
3034 + expect(ReactNoop).toMatchRenderedOutput(
3035 + <>
3036 + <span prop="Hi!" />
3037 + <span prop="A" />
3038 + </>,
3039 + );
3040
3041 // Start transition.
3042 React.startTransition(() => ReactNoop.render(<App page="B" />));
@@ -2908,12 +3044,22 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3044 expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [B]', 'Loading B...']);
3045
3046 // Suspended
2911 - expect(ReactNoop.getChildren()).toEqual([span('Hi!'), span('A')]);
3047 + expect(ReactNoop).toMatchRenderedOutput(
3048 + <>
3049 + <span prop="Hi!" />
3050 + <span prop="A" />
3051 + </>,
3052 + );
3053 Scheduler.unstable_advanceTime(1800);
3054 await advanceTimers(1800);
3055 expect(Scheduler).toFlushAndYield([]);
3056 // We should still be suspended here because this loading state should be avoided.
2916 - expect(ReactNoop.getChildren()).toEqual([span('Hi!'), span('A')]);
3057 + expect(ReactNoop).toMatchRenderedOutput(
3058 + <>
3059 + <span prop="Hi!" />
3060 + <span prop="A" />
3061 + </>,
3062 + );
3063 await resolveText('B');
3064 expect(Scheduler).toFlushAndYield(['Hi!', 'B']);
3065 expect(ReactNoop).toMatchRenderedOutput(
@@ -2946,7 +3092,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3092 // Initial render.
3093 ReactNoop.render(<App page="A" />);
3094 expect(Scheduler).toFlushAndYield(['Hi!', 'A']);
2949 - expect(ReactNoop.getChildren()).toEqual([span('Hi!'), span('A')]);
3095 + expect(ReactNoop).toMatchRenderedOutput(
3096 + <>
3097 + <span prop="Hi!" />
3098 + <span prop="A" />
3099 + </>,
3100 + );
3101
3102 // Start transition.
3103 React.startTransition(() => ReactNoop.render(<App page="B" />));
@@ -2954,7 +3105,12 @@ describe('ReactSuspenseWithNoopRenderer', () => {
3105 expect(Scheduler).toFlushAndYield(['Hi!', 'Suspend! [B]', 'Loading B...']);
3106
3107 // Suspended
2957 - expect(ReactNoop.getChildren()).toEqual([span('Hi!'), span('A')]);
3108 + expect(ReactNoop).toMatchRenderedOutput(
3109 + <>
3110 + <span prop="Hi!" />
3111 + <span prop="A" />
3112 + </>,
3113 + );
3114 Scheduler.unstable_advanceTime(1800);
3115 await advanceTimers(1800);
3116 expect(Scheduler).toFlushAndYield([]);
packages/react-reconciler/src/__tests__/useEffectEvent-test.js
+162 -116
@@ -42,10 +42,6 @@ describe('useEffectEvent', () => {
42 useMemo = React.useMemo;
43 });
44
45 - function span(prop) {
46 - return {type: 'span', hidden: false, children: [], prop};
47 - }
48 -
45 function Text(props) {
46 Scheduler.unstable_yieldValue(props.text);
47 return <span prop={props.text} />;
@@ -77,17 +73,21 @@ describe('useEffectEvent', () => {
73 const button = React.createRef(null);
74 ReactNoop.render(<Counter incrementBy={1} />);
75 expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
80 - expect(ReactNoop.getChildren()).toEqual([
81 - span('Increment'),
82 - span('Count: 0'),
83 - ]);
76 + expect(ReactNoop).toMatchRenderedOutput(
77 + <>
78 + <span prop="Increment" />
79 + <span prop="Count: 0" />
80 + </>,
81 + );
82
83 act(button.current.increment);
84 expect(Scheduler).toHaveYielded(['Increment', 'Count: 1']);
87 - expect(ReactNoop.getChildren()).toEqual([
88 - span('Increment'),
89 - span('Count: 1'),
90 - ]);
85 + expect(ReactNoop).toMatchRenderedOutput(
86 + <>
87 + <span prop="Increment" />
88 + <span prop="Count: 1" />
89 + </>,
90 + );
91
92 act(button.current.increment);
93 expect(Scheduler).toHaveYielded([
@@ -95,26 +95,32 @@ describe('useEffectEvent', () => {
95 // Event should use the updated callback function closed over the new value.
96 'Count: 2',
97 ]);
98 - expect(ReactNoop.getChildren()).toEqual([
99 - span('Increment'),
100 - span('Count: 2'),
101 - ]);
98 + expect(ReactNoop).toMatchRenderedOutput(
99 + <>
100 + <span prop="Increment" />
101 + <span prop="Count: 2" />
102 + </>,
103 + );
104
105 // Increase the increment prop amount
106 ReactNoop.render(<Counter incrementBy={10} />);
107 expect(Scheduler).toFlushAndYield(['Increment', 'Count: 2']);
106 - expect(ReactNoop.getChildren()).toEqual([
107 - span('Increment'),
108 - span('Count: 2'),
109 - ]);
108 + expect(ReactNoop).toMatchRenderedOutput(
109 + <>
110 + <span prop="Increment" />
111 + <span prop="Count: 2" />
112 + </>,
113 + );
114
115 // Event uses the new prop
116 act(button.current.increment);
117 expect(Scheduler).toHaveYielded(['Increment', 'Count: 12']);
114 - expect(ReactNoop.getChildren()).toEqual([
115 - span('Increment'),
116 - span('Count: 12'),
117 - ]);
118 + expect(ReactNoop).toMatchRenderedOutput(
119 + <>
120 + <span prop="Increment" />
121 + <span prop="Count: 12" />
122 + </>,
123 + );
124 });
125
126 // @gate enableUseEffectEventHook
@@ -153,24 +159,30 @@ describe('useEffectEvent', () => {
159 const button = React.createRef(null);
160 ReactNoop.render(<Counter incrementBy={5} />);
161 expect(Scheduler).toFlushAndYield(['Increment', 'Count: 0']);
156 - expect(ReactNoop.getChildren()).toEqual([
157 - span('Increment'),
158 - span('Count: 0'),
159 - ]);
162 + expect(ReactNoop).toMatchRenderedOutput(
163 + <>
164 + <span prop="Increment" />
165 + <span prop="Count: 0" />
166 + </>,
167 + );
168
169 act(button.current.increment);
170 expect(Scheduler).toHaveYielded(['Increment', 'Count: 5']);
163 - expect(ReactNoop.getChildren()).toEqual([
164 - span('Increment'),
165 - span('Count: 5'),
166 - ]);
171 + expect(ReactNoop).toMatchRenderedOutput(
172 + <>
173 + <span prop="Increment" />
174 + <span prop="Count: 5" />
175 + </>,
176 + );
177
178 act(button.current.multiply);
179 expect(Scheduler).toHaveYielded(['Increment', 'Count: 25']);
170 - expect(ReactNoop.getChildren()).toEqual([
171 - span('Increment'),
172 - span('Count: 25'),
173 - ]);
180 + expect(ReactNoop).toMatchRenderedOutput(
181 + <>
182 + <span prop="Increment" />
183 + <span prop="Count: 25" />
184 + </>,
185 + );
186 });
187
188 // @gate enableUseEffectEventHook
@@ -206,20 +218,24 @@ describe('useEffectEvent', () => {
218 const button = React.createRef(null);
219 ReactNoop.render(<Greeter hello={'hej'} />);
220 expect(Scheduler).toFlushAndYield(['Say hej', 'Greeting: Seb says hej']);
209 - expect(ReactNoop.getChildren()).toEqual([
210 - span('Say hej'),
211 - span('Greeting: Seb says hej'),
212 - ]);
221 + expect(ReactNoop).toMatchRenderedOutput(
222 + <>
223 + <span prop="Say hej" />
224 + <span prop="Greeting: Seb says hej" />
225 + </>,
226 + );
227
228 act(button.current.greet);
229 expect(Scheduler).toHaveYielded([
230 'Say hej',
231 'Greeting: undefined says hej',
232 ]);
219 - expect(ReactNoop.getChildren()).toEqual([
220 - span('Say hej'),
221 - span('Greeting: undefined says hej'),
222 - ]);
233 + expect(ReactNoop).toMatchRenderedOutput(
234 + <>
235 + <span prop="Say hej" />
236 + <span prop="Greeting: undefined says hej" />
237 + </>,
238 + );
239 });
240
241 // @gate enableUseEffectEventHook
@@ -299,10 +315,12 @@ describe('useEffectEvent', () => {
315 'Increment',
316 'Count: 2',
317 ]);
302 - expect(ReactNoop.getChildren()).toEqual([
303 - span('Increment'),
304 - span('Count: 2'),
305 - ]);
318 + expect(ReactNoop).toMatchRenderedOutput(
319 + <>
320 + <span prop="Increment" />
321 + <span prop="Count: 2" />
322 + </>,
323 + );
324
325 act(button.current.increment);
326 expect(Scheduler).toHaveYielded([
@@ -310,10 +328,12 @@ describe('useEffectEvent', () => {
328 // Effect should not re-run because the dependency hasn't changed.
329 'Count: 3',
330 ]);
313 - expect(ReactNoop.getChildren()).toEqual([
314 - span('Increment'),
315 - span('Count: 3'),
316 - ]);
331 + expect(ReactNoop).toMatchRenderedOutput(
332 + <>
333 + <span prop="Increment" />
334 + <span prop="Count: 3" />
335 + </>,
336 + );
337
338 act(button.current.increment);
339 expect(Scheduler).toHaveYielded([
@@ -321,10 +341,12 @@ describe('useEffectEvent', () => {
341 // Event should use the updated callback function closed over the new value.
342 'Count: 4',
343 ]);
324 - expect(ReactNoop.getChildren()).toEqual([
325 - span('Increment'),
326 - span('Count: 4'),
327 - ]);
344 + expect(ReactNoop).toMatchRenderedOutput(
345 + <>
346 + <span prop="Increment" />
347 + <span prop="Count: 4" />
348 + </>,
349 + );
350
351 // Increase the increment prop amount
352 ReactNoop.render(<Counter incrementBy={10} />);
@@ -335,18 +357,22 @@ describe('useEffectEvent', () => {
357 'Increment',
358 'Count: 24',
359 ]);
338 - expect(ReactNoop.getChildren()).toEqual([
339 - span('Increment'),
340 - span('Count: 24'),
341 - ]);
360 + expect(ReactNoop).toMatchRenderedOutput(
361 + <>
362 + <span prop="Increment" />
363 + <span prop="Count: 24" />
364 + </>,
365 + );
366
367 // Event uses the new prop
368 act(button.current.increment);
369 expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
346 - expect(ReactNoop.getChildren()).toEqual([
347 - span('Increment'),
348 - span('Count: 34'),
349 - ]);
370 + expect(ReactNoop).toMatchRenderedOutput(
371 + <>
372 + <span prop="Increment" />
373 + <span prop="Count: 34" />
374 + </>,
375 + );
376 });
377
378 // @gate enableUseEffectEventHook
@@ -388,10 +414,12 @@ describe('useEffectEvent', () => {
414 'Increment',
415 'Count: 2',
416 ]);
391 - expect(ReactNoop.getChildren()).toEqual([
392 - span('Increment'),
393 - span('Count: 2'),
394 - ]);
417 + expect(ReactNoop).toMatchRenderedOutput(
418 + <>
419 + <span prop="Increment" />
420 + <span prop="Count: 2" />
421 + </>,
422 + );
423
424 act(button.current.increment);
425 expect(Scheduler).toHaveYielded([
@@ -399,10 +427,12 @@ describe('useEffectEvent', () => {
427 // Effect should not re-run because the dependency hasn't changed.
428 'Count: 3',
429 ]);
402 - expect(ReactNoop.getChildren()).toEqual([
403 - span('Increment'),
404 - span('Count: 3'),
405 - ]);
430 + expect(ReactNoop).toMatchRenderedOutput(
431 + <>
432 + <span prop="Increment" />
433 + <span prop="Count: 3" />
434 + </>,
435 + );
436
437 act(button.current.increment);
438 expect(Scheduler).toHaveYielded([
@@ -410,10 +440,12 @@ describe('useEffectEvent', () => {
440 // Event should use the updated callback function closed over the new value.
441 'Count: 4',
442 ]);
413 - expect(ReactNoop.getChildren()).toEqual([
414 - span('Increment'),
415 - span('Count: 4'),
416 - ]);
443 + expect(ReactNoop).toMatchRenderedOutput(
444 + <>
445 + <span prop="Increment" />
446 + <span prop="Count: 4" />
447 + </>,
448 + );
449
450 // Increase the increment prop amount
451 ReactNoop.render(<Counter incrementBy={10} />);
@@ -424,18 +456,22 @@ describe('useEffectEvent', () => {
456 'Increment',
457 'Count: 24',
458 ]);
427 - expect(ReactNoop.getChildren()).toEqual([
428 - span('Increment'),
429 - span('Count: 24'),
430 - ]);
459 + expect(ReactNoop).toMatchRenderedOutput(
460 + <>
461 + <span prop="Increment" />
462 + <span prop="Count: 24" />
463 + </>,
464 + );
465
466 // Event uses the new prop
467 act(button.current.increment);
468 expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
435 - expect(ReactNoop.getChildren()).toEqual([
436 - span('Increment'),
437 - span('Count: 34'),
438 - ]);
469 + expect(ReactNoop).toMatchRenderedOutput(
470 + <>
471 + <span prop="Increment" />
472 + <span prop="Count: 34" />
473 + </>,
474 + );
475 });
476
477 // @gate enableUseEffectEventHook
@@ -483,10 +519,12 @@ describe('useEffectEvent', () => {
519 'Increment',
520 'Count: 2',
521 ]);
486 - expect(ReactNoop.getChildren()).toEqual([
487 - span('Increment'),
488 - span('Count: 2'),
489 - ]);
522 + expect(ReactNoop).toMatchRenderedOutput(
523 + <>
524 + <span prop="Increment" />
525 + <span prop="Count: 2" />
526 + </>,
527 + );
528
529 act(button.current.increment);
530 expect(Scheduler).toHaveYielded([
@@ -494,10 +532,12 @@ describe('useEffectEvent', () => {
532 // Effect should not re-run because the dependency hasn't changed.
533 'Count: 3',
534 ]);
497 - expect(ReactNoop.getChildren()).toEqual([
498 - span('Increment'),
499 - span('Count: 3'),
500 - ]);
535 + expect(ReactNoop).toMatchRenderedOutput(
536 + <>
537 + <span prop="Increment" />
538 + <span prop="Count: 3" />
539 + </>,
540 + );
541
542 act(button.current.increment);
543 expect(Scheduler).toHaveYielded([
@@ -505,10 +545,12 @@ describe('useEffectEvent', () => {
545 // Event should use the updated callback function closed over the new value.
546 'Count: 4',
547 ]);
508 - expect(ReactNoop.getChildren()).toEqual([
509 - span('Increment'),
510 - span('Count: 4'),
511 - ]);
548 + expect(ReactNoop).toMatchRenderedOutput(
549 + <>
550 + <span prop="Increment" />
551 + <span prop="Count: 4" />
552 + </>,
553 + );
554
555 // Increase the increment prop amount
556 ReactNoop.render(<Counter incrementBy={10} />);
@@ -519,18 +561,22 @@ describe('useEffectEvent', () => {
561 'Increment',
562 'Count: 24',
563 ]);
522 - expect(ReactNoop.getChildren()).toEqual([
523 - span('Increment'),
524 - span('Count: 24'),
525 - ]);
564 + expect(ReactNoop).toMatchRenderedOutput(
565 + <>
566 + <span prop="Increment" />
567 + <span prop="Count: 24" />
568 + </>,
569 + );
570
571 // Event uses the new prop
572 act(button.current.increment);
573 expect(Scheduler).toHaveYielded(['Increment', 'Count: 34']);
530 - expect(ReactNoop.getChildren()).toEqual([
531 - span('Increment'),
532 - span('Count: 34'),
533 - ]);
574 + expect(ReactNoop).toMatchRenderedOutput(
575 + <>
576 + <span prop="Increment" />
577 + <span prop="Count: 34" />
578 + </>,
579 + );
580 });
581
582 // @gate enableUseEffectEventHook
@@ -693,9 +739,9 @@ describe('useEffectEvent', () => {
739
740 act(() => ReactNoop.render(<ChatRoom roomId="general" theme="light" />));
741 expect(Scheduler).toHaveYielded(['Welcome to the general room!']);
696 - expect(ReactNoop.getChildren()).toEqual([
697 - span('Welcome to the general room!'),
698 - ]);
742 + expect(ReactNoop).toMatchRenderedOutput(
743 + <span prop="Welcome to the general room!" />,
744 + );
745
746 jest.advanceTimersByTime(100);
747 Scheduler.unstable_advanceTime(100);
@@ -704,9 +750,9 @@ describe('useEffectEvent', () => {
750 // change roomId only
751 act(() => ReactNoop.render(<ChatRoom roomId="music" theme="light" />));
752 expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
707 - expect(ReactNoop.getChildren()).toEqual([
708 - span('Welcome to the music room!'),
709 - ]);
753 + expect(ReactNoop).toMatchRenderedOutput(
754 + <span prop="Welcome to the music room!" />,
755 + );
756 jest.advanceTimersByTime(100);
757 Scheduler.unstable_advanceTime(100);
758 // should trigger a reconnect
@@ -715,9 +761,9 @@ describe('useEffectEvent', () => {
761 // change theme only
762 act(() => ReactNoop.render(<ChatRoom roomId="music" theme="dark" />));
763 expect(Scheduler).toHaveYielded(['Welcome to the music room!']);
718 - expect(ReactNoop.getChildren()).toEqual([
719 - span('Welcome to the music room!'),
720 - ]);
764 + expect(ReactNoop).toMatchRenderedOutput(
765 + <span prop="Welcome to the music room!" />,
766 + );
767 jest.advanceTimersByTime(100);
768 Scheduler.unstable_advanceTime(100);
769 // should not trigger a reconnect
@@ -726,9 +772,9 @@ describe('useEffectEvent', () => {
772 // change roomId only
773 act(() => ReactNoop.render(<ChatRoom roomId="travel" theme="dark" />));
774 expect(Scheduler).toHaveYielded(['Welcome to the travel room!']);
729 - expect(ReactNoop.getChildren()).toEqual([
730 - span('Welcome to the travel room!'),
731 - ]);
775 + expect(ReactNoop).toMatchRenderedOutput(
776 + <span prop="Welcome to the travel room!" />,
777 + );
778 jest.advanceTimersByTime(100);
779 Scheduler.unstable_advanceTime(100);
780 // should trigger a reconnect