diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index b7f1570823c14..b9e6b0c268061 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -1271,6 +1271,9 @@ describe('ReactLazy', () => { // @gate enableLazyElements it('mount and reorder lazy types', async () => { class Child extends React.Component { + componentWillUnmount() { + Scheduler.unstable_yieldValue('Did unmount: ' + this.props.label); + } componentDidMount() { Scheduler.unstable_yieldValue('Did mount: ' + this.props.label); } @@ -1348,6 +1351,12 @@ describe('ReactLazy', () => { expect(Scheduler).toFlushAndYield(['Init B2', 'Loading...']); jest.runAllTimers(); + gate(flags => { + if (flags.enableSuspenseLayoutEffectSemantics) { + expect(Scheduler).toHaveYielded(['Did unmount: A', 'Did unmount: B']); + } + }); + // The suspense boundary should've triggered now. expect(root).toMatchRenderedOutput('Loading...'); await resolveB2({default: ChildB}); @@ -1356,12 +1365,23 @@ describe('ReactLazy', () => { expect(Scheduler).toFlushAndYield(['Init A2']); await LazyChildA2; - expect(Scheduler).toFlushAndYield([ - 'b', - 'a', - 'Did update: b', - 'Did update: a', - ]); + gate(flags => { + if (flags.enableSuspenseLayoutEffectSemantics) { + expect(Scheduler).toFlushAndYield([ + 'b', + 'a', + 'Did mount: b', + 'Did mount: a', + ]); + } else { + expect(Scheduler).toFlushAndYield([ + 'b', + 'a', + 'Did update: b', + 'Did update: a', + ]); + } + }); expect(root).toMatchRenderedOutput('ba'); }); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js index c67edf8a15632..a0a0d08a2c42b 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseFuzz-test.internal.js @@ -100,7 +100,7 @@ describe('ReactSuspenseFuzz', () => { } }, [updates]); - const fullText = `${text}:${step}`; + const fullText = `[${text}:${step}]`; const shouldSuspend = useContext(ShouldSuspendContext); @@ -163,19 +163,26 @@ describe('ReactSuspenseFuzz', () => { resolveAllTasks(); const expectedOutput = expectedRoot.getChildrenAsJSX(); - resetCache(); - ReactNoop.renderLegacySyncRoot(children); - resolveAllTasks(); - const legacyOutput = ReactNoop.getChildrenAsJSX(); - expect(legacyOutput).toEqual(expectedOutput); - ReactNoop.renderLegacySyncRoot(null); - - resetCache(); - const concurrentRoot = ReactNoop.createRoot(); - concurrentRoot.render(children); - resolveAllTasks(); - const concurrentOutput = concurrentRoot.getChildrenAsJSX(); - expect(concurrentOutput).toEqual(expectedOutput); + gate(flags => { + resetCache(); + ReactNoop.renderLegacySyncRoot(children); + resolveAllTasks(); + const legacyOutput = ReactNoop.getChildrenAsJSX(); + expect(legacyOutput).toEqual(expectedOutput); + ReactNoop.renderLegacySyncRoot(null); + + // Observable behavior differs here in a way that's expected: + // If enableSuspenseLayoutEffectSemantics is enabled, layout effects are destroyed on re-suspend + // before larger 'beginAfter' timers have a chance to fire. + if (!flags.enableSuspenseLayoutEffectSemantics) { + resetCache(); + const concurrentRoot = ReactNoop.createRoot(); + concurrentRoot.render(children); + resolveAllTasks(); + const concurrentOutput = concurrentRoot.getChildrenAsJSX(); + expect(concurrentOutput).toEqual(expectedOutput); + } + }); } function pickRandomWeighted(rand, options) { @@ -410,5 +417,32 @@ Random seed is ${SEED} , ); }); + + it('4', () => { + const {Text, testResolvedOutput} = createFuzzer(); + testResolvedOutput( + + + + + + + + , + ); + }); }); }); diff --git a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js index 692b745f97085..9d6056e25d3db 100644 --- a/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.js @@ -468,19 +468,30 @@ describe('ReactSuspenseWithNoopRenderer', () => { await rejectText('Result', new Error('Failed to load: Result')); - expect(Scheduler).toFlushAndYield([ - 'Error! [Result]', + gate(flags => { + if (flags.enableSuspenseLayoutEffectSemantics) { + expect(Scheduler).toFlushAndYield([ + 'Error! [Result]', - // React retries one more time - 'Error! [Result]', + // React retries one more time + 'Error! [Result]', + ]); + expect(ReactNoop.getChildren()).toEqual([]); + } else { + expect(Scheduler).toFlushAndYield([ + 'Error! [Result]', - // Errored again on retry. Now handle it. + // React retries one more time + 'Error! [Result]', - 'Caught error: Failed to load: Result', - ]); - expect(ReactNoop.getChildren()).toEqual([ - span('Caught error: Failed to load: Result'), - ]); + // Errored again on retry. Now handle it. + 'Caught error: Failed to load: Result', + ]); + expect(ReactNoop.getChildren()).toEqual([ + span('Caught error: Failed to load: Result'), + ]); + } + }); }); // @gate enableCache