@samitouri / QOS-React-2 / commits / 4ce89a58da

Test bad useEffect return value with noop-renderer (#22258)

* Test bad useEffect return value with noop-renderer * Use previous "root"-approach Tests should now be invariant under variants * Add same test for layout effects

Sebastian Silbermann committed Sep 9, 2021 at 00:58 UTC 4ce89a58dac5287a2c7fc454e86562297e97a6d9
2 files changed +96 -36
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
-36
@@ -727,42 +727,6 @@ describe('ReactHooks', () => {
727 ReactTestRenderer.create(<App deps={undefined} />);
728 });
729
730 - it('assumes useEffect clean-up function is either a function or undefined', () => {
731 - const {useLayoutEffect} = React;
732 -
733 - function App(props) {
734 - useLayoutEffect(() => {
735 - return props.return;
736 - });
737 - return null;
738 - }
739 -
740 - const root1 = ReactTestRenderer.create(null);
741 - expect(() => root1.update(<App return={17} />)).toErrorDev([
742 - 'Warning: An effect function must not return anything besides a ' +
743 - 'function, which is used for clean-up. You returned: 17',
744 - ]);
745 -
746 - const root2 = ReactTestRenderer.create(null);
747 - expect(() => root2.update(<App return={null} />)).toErrorDev([
748 - 'Warning: An effect function must not return anything besides a ' +
749 - 'function, which is used for clean-up. You returned null. If your ' +
750 - 'effect does not require clean up, return undefined (or nothing).',
751 - ]);
752 -
753 - const root3 = ReactTestRenderer.create(null);
754 - expect(() => root3.update(<App return={Promise.resolve()} />)).toErrorDev([
755 - 'Warning: An effect function must not return anything besides a ' +
756 - 'function, which is used for clean-up.\n\n' +
757 - 'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
758 - ]);
759 -
760 - // Error on unmount because React assumes the value is a function
761 - expect(() => {
762 - root3.update(null);
763 - }).toThrow('is not a function');
764 - });
765 -
730 it('does not forget render phase useState updates inside an effect', () => {
731 const {useState, useEffect} = React;
732
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+96
@@ -2632,6 +2632,54 @@ describe('ReactHooksWithNoopRenderer', () => {
2632 });
2633 expect(Scheduler).toHaveYielded(['layout destroy', 'passive destroy']);
2634 });
2635 +
2636 + it('assumes passive effect destroy function is either a function or undefined', () => {
2637 + function App(props) {
2638 + useEffect(() => {
2639 + return props.return;
2640 + });
2641 + return null;
2642 + }
2643 +
2644 + const root1 = ReactNoop.createRoot();
2645 + expect(() =>
2646 + act(() => {
2647 + root1.render(<App return={17} />);
2648 + }),
2649 + ).toErrorDev([
2650 + 'Warning: An effect function must not return anything besides a ' +
2651 + 'function, which is used for clean-up. You returned: 17',
2652 + ]);
2653 +
2654 + const root2 = ReactNoop.createRoot();
2655 + expect(() =>
2656 + act(() => {
2657 + root2.render(<App return={null} />);
2658 + }),
2659 + ).toErrorDev([
2660 + 'Warning: An effect function must not return anything besides a ' +
2661 + 'function, which is used for clean-up. You returned null. If your ' +
2662 + 'effect does not require clean up, return undefined (or nothing).',
2663 + ]);
2664 +
2665 + const root3 = ReactNoop.createRoot();
2666 + expect(() =>
2667 + act(() => {
2668 + root3.render(<App return={Promise.resolve()} />);
2669 + }),
2670 + ).toErrorDev([
2671 + 'Warning: An effect function must not return anything besides a ' +
2672 + 'function, which is used for clean-up.\n\n' +
2673 + 'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
2674 + ]);
2675 +
2676 + // Error on unmount because React assumes the value is a function
2677 + expect(() =>
2678 + act(() => {
2679 + root3.unmount();
2680 + }),
2681 + ).toThrow('is not a function');
2682 + });
2683 });
2684
2685 describe('useLayoutEffect', () => {
@@ -2810,6 +2858,54 @@ describe('ReactHooksWithNoopRenderer', () => {
2858 ]);
2859 expect(ReactNoop.getChildren()).toEqual([span('OuterFallback')]);
2860 });
2861 +
2862 + it('assumes layout effect destroy function is either a function or undefined', () => {
2863 + function App(props) {
2864 + useLayoutEffect(() => {
2865 + return props.return;
2866 + });
2867 + return null;
2868 + }
2869 +
2870 + const root1 = ReactNoop.createRoot();
2871 + expect(() =>
2872 + act(() => {
2873 + root1.render(<App return={17} />);
2874 + }),
2875 + ).toErrorDev([
2876 + 'Warning: An effect function must not return anything besides a ' +
2877 + 'function, which is used for clean-up. You returned: 17',
2878 + ]);
2879 +
2880 + const root2 = ReactNoop.createRoot();
2881 + expect(() =>
2882 + act(() => {
2883 + root2.render(<App return={null} />);
2884 + }),
2885 + ).toErrorDev([
2886 + 'Warning: An effect function must not return anything besides a ' +
2887 + 'function, which is used for clean-up. You returned null. If your ' +
2888 + 'effect does not require clean up, return undefined (or nothing).',
2889 + ]);
2890 +
2891 + const root3 = ReactNoop.createRoot();
2892 + expect(() =>
2893 + act(() => {
2894 + root3.render(<App return={Promise.resolve()} />);
2895 + }),
2896 + ).toErrorDev([
2897 + 'Warning: An effect function must not return anything besides a ' +
2898 + 'function, which is used for clean-up.\n\n' +
2899 + 'It looks like you wrote useEffect(async () => ...) or returned a Promise.',
2900 + ]);
2901 +
2902 + // Error on unmount because React assumes the value is a function
2903 + expect(() =>
2904 + act(() => {
2905 + root3.unmount();
2906 + }),
2907 + ).toThrow('is not a function');
2908 + });
2909 });
2910
2911 describe('useCallback', () => {