@samitouri / QOS-React-1 / commits / 75de4ff72a

fix[devtools/ci]: split profiling cache test for different react versions and toEqual checker (#28628)

This should fix the failing backwards-compatibility tests on CI: - https://app.circleci.com/pipelines/github/facebook/react/51347/workflows/9d319db5-7a29-4e9a-a3a0-8d49a24ee9bd/jobs/809381 - https://app.circleci.com/pipelines/github/facebook/react/51347/workflows/9d319db5-7a29-4e9a-a3a0-8d49a24ee9bd/jobs/809386 Started failing after https://github.com/facebook/react/pull/27991. Brief summary: 1. Revert changes to `profilingCache-test` in https://github.com/facebook/react/pull/27991. 2. Scope previous test to react versions 16.9 - 18.2 3. Add a new test for react versions > 18.2 (includes testing react from souce), which is also gated with `!disableLegacyContext`. > [!IMPORTANT] > `@gate` pragma expects the test to throw. Jest doesn't throw any exceptions when snapshots are mismatched, this is why I've migrated test from `toMatchInlineSnapshot` checker to `toEqual`. > If the test doesn't throw, we will fail it manually: > https://github.com/facebook/react/blob/5a75f9e78544fa6d052aff7fe99607e48f35b979/scripts/jest/setupTests.js#L291-L295

Ruslan Lesiutin committed Mar 26, 2024 at 10:55 UTC 75de4ff72aa8b53a0af1f6b1f23e338329069c74
1 file changed +386 -21
packages/react-devtools-shared/src/__tests__/profilingCache-test.js
+386 -21
@@ -310,7 +310,8 @@ describe('ProfilingCache', () => {
310 });
311
312 // @reactVersion >= 16.9
313 - it('should record changed props/state/context/hooks', () => {
313 + // @reactVersion <= 18.2
314 + it('should record changed props/state/context/hooks for React version [16.9; 18.2] with legacy context', () => {
315 let instance = null;
316
317 const ModernContext = React.createContext(0);
@@ -411,21 +412,6 @@ describe('ProfilingCache', () => {
412 },
413 }
414 `);
414 -
415 - if (gate(flags => !flags.disableLegacyContext)) {
416 - expect(changeDescriptions[1].get(6).context).toEqual(['count']);
417 - expect(changeDescriptions[1].get(7).props).toEqual(['count']);
418 - expect(changeDescriptions[2].get(6).context).toEqual([]);
419 - expect(changeDescriptions[3].get(6).context).toEqual([]);
420 - expect(changeDescriptions[4].get(6).context).toEqual([]);
421 -
422 - changeDescriptions[1].get(6).context = null;
423 - changeDescriptions[1].get(7).props = [];
424 - changeDescriptions[2].get(6).context = null;
425 - changeDescriptions[3].get(6).context = null;
426 - changeDescriptions[4].get(6).context = null;
427 - }
428 -
415 expect(changeDescriptions[1]).toMatchInlineSnapshot(`
416 Map {
417 5 => {
@@ -451,11 +437,15 @@ describe('ProfilingCache', () => {
437 "didHooksChange": false,
438 "hooks": [],
439 "isFirstMount": false,
454 - "props": [],
440 + "props": [
441 + "count",
442 + ],
443 "state": null,
444 },
445 6 => {
458 - "context": null,
446 + "context": [
447 + "count",
448 + ],
449 "didHooksChange": false,
450 "hooks": null,
451 "isFirstMount": false,
@@ -501,7 +491,7 @@ describe('ProfilingCache', () => {
491 "state": null,
492 },
493 6 => {
504 - "context": null,
494 + "context": [],
495 "didHooksChange": false,
496 "hooks": null,
497 "isFirstMount": false,
@@ -547,7 +537,7 @@ describe('ProfilingCache', () => {
537 "state": null,
538 },
539 6 => {
550 - "context": null,
540 + "context": [],
541 "didHooksChange": false,
542 "hooks": null,
543 "isFirstMount": false,
@@ -594,7 +584,7 @@ describe('ProfilingCache', () => {
584 "state": null,
585 },
586 6 => {
597 - "context": null,
587 + "context": [],
588 "didHooksChange": false,
589 "hooks": null,
590 "isFirstMount": false,
@@ -630,6 +620,381 @@ describe('ProfilingCache', () => {
620 }
621 });
622
623 + // @reactVersion > 18.2
624 + // @gate !disableLegacyContext
625 + it('should record changed props/state/context/hooks for React version (18.2; ∞) with legacy context enabled', () => {
626 + let instance = null;
627 +
628 + const ModernContext = React.createContext(0);
629 +
630 + class LegacyContextProvider extends React.Component<any, {count: number}> {
631 + static childContextTypes = {
632 + count: PropTypes.number,
633 + };
634 + state = {count: 0};
635 + getChildContext() {
636 + return this.state;
637 + }
638 + render() {
639 + instance = this;
640 + return (
641 + <ModernContext.Provider value={this.state.count}>
642 + <React.Fragment>
643 + <ModernContextConsumer />
644 + <LegacyContextConsumer />
645 + </React.Fragment>
646 + </ModernContext.Provider>
647 + );
648 + }
649 + }
650 +
651 + const FunctionComponentWithHooks = ({count}) => {
652 + React.useMemo(() => count, [count]);
653 + return null;
654 + };
655 +
656 + class ModernContextConsumer extends React.Component<any> {
657 + static contextType = ModernContext;
658 + render() {
659 + return <FunctionComponentWithHooks count={this.context} />;
660 + }
661 + }
662 +
663 + class LegacyContextConsumer extends React.Component<any> {
664 + static contextTypes = {
665 + count: PropTypes.number,
666 + };
667 + render() {
668 + return <FunctionComponentWithHooks count={this.context.count} />;
669 + }
670 + }
671 +
672 + utils.act(() => store.profilerStore.startProfiling());
673 + utils.act(() => render(<LegacyContextProvider />));
674 + expect(instance).not.toBeNull();
675 + utils.act(() => (instance: any).setState({count: 1}));
676 + utils.act(() => render(<LegacyContextProvider foo={123} />));
677 + utils.act(() => render(<LegacyContextProvider bar="abc" />));
678 + utils.act(() => render(<LegacyContextProvider />));
679 + utils.act(() => store.profilerStore.stopProfiling());
680 +
681 + const rootID = store.roots[0];
682 +
683 + let changeDescriptions = store.profilerStore
684 + .getDataForRoot(rootID)
685 + .commitData.map(commitData => commitData.changeDescriptions);
686 + expect(changeDescriptions).toHaveLength(5);
687 + expect(changeDescriptions[0]).toEqual(
688 + new Map([
689 + [
690 + 2,
691 + {
692 + context: null,
693 + didHooksChange: false,
694 + isFirstMount: true,
695 + props: null,
696 + state: null,
697 + },
698 + ],
699 + [
700 + 4,
701 + {
702 + context: null,
703 + didHooksChange: false,
704 + isFirstMount: true,
705 + props: null,
706 + state: null,
707 + },
708 + ],
709 + [
710 + 5,
711 + {
712 + context: null,
713 + didHooksChange: false,
714 + isFirstMount: true,
715 + props: null,
716 + state: null,
717 + },
718 + ],
719 + [
720 + 6,
721 + {
722 + context: null,
723 + didHooksChange: false,
724 + isFirstMount: true,
725 + props: null,
726 + state: null,
727 + },
728 + ],
729 + [
730 + 7,
731 + {
732 + context: null,
733 + didHooksChange: false,
734 + isFirstMount: true,
735 + props: null,
736 + state: null,
737 + },
738 + ],
739 + ]),
740 + );
741 +
742 + expect(changeDescriptions[1]).toEqual(
743 + new Map([
744 + [
745 + 5,
746 + {
747 + context: null,
748 + didHooksChange: false,
749 + hooks: [],
750 + isFirstMount: false,
751 + props: ['count'],
752 + state: null,
753 + },
754 + ],
755 + [
756 + 4,
757 + {
758 + context: true,
759 + didHooksChange: false,
760 + hooks: null,
761 + isFirstMount: false,
762 + props: [],
763 + state: null,
764 + },
765 + ],
766 + [
767 + 7,
768 + {
769 + context: null,
770 + didHooksChange: false,
771 + hooks: [],
772 + isFirstMount: false,
773 + props: ['count'],
774 + state: null,
775 + },
776 + ],
777 + [
778 + 6,
779 + {
780 + context: ['count'],
781 + didHooksChange: false,
782 + hooks: null,
783 + isFirstMount: false,
784 + props: [],
785 + state: null,
786 + },
787 + ],
788 + [
789 + 2,
790 + {
791 + context: null,
792 + didHooksChange: false,
793 + hooks: [],
794 + isFirstMount: false,
795 + props: [],
796 + state: ['count'],
797 + },
798 + ],
799 + ]),
800 + );
801 +
802 + expect(changeDescriptions[2]).toEqual(
803 + new Map([
804 + [
805 + 5,
806 + {
807 + context: null,
808 + didHooksChange: false,
809 + hooks: [],
810 + isFirstMount: false,
811 + props: [],
812 + state: null,
813 + },
814 + ],
815 + [
816 + 4,
817 + {
818 + context: false,
819 + didHooksChange: false,
820 + hooks: null,
821 + isFirstMount: false,
822 + props: [],
823 + state: null,
824 + },
825 + ],
826 + [
827 + 7,
828 + {
829 + context: null,
830 + didHooksChange: false,
831 + hooks: [],
832 + isFirstMount: false,
833 + props: [],
834 + state: null,
835 + },
836 + ],
837 + [
838 + 6,
839 + {
840 + context: [],
841 + didHooksChange: false,
842 + hooks: null,
843 + isFirstMount: false,
844 + props: [],
845 + state: null,
846 + },
847 + ],
848 + [
849 + 2,
850 + {
851 + context: null,
852 + didHooksChange: false,
853 + hooks: [],
854 + isFirstMount: false,
855 + props: ['foo'],
856 + state: [],
857 + },
858 + ],
859 + ]),
860 + );
861 +
862 + expect(changeDescriptions[3]).toEqual(
863 + new Map([
864 + [
865 + 5,
866 + {
867 + context: null,
868 + didHooksChange: false,
869 + hooks: [],
870 + isFirstMount: false,
871 + props: [],
872 + state: null,
873 + },
874 + ],
875 + [
876 + 4,
877 + {
878 + context: false,
879 + didHooksChange: false,
880 + hooks: null,
881 + isFirstMount: false,
882 + props: [],
883 + state: null,
884 + },
885 + ],
886 + [
887 + 7,
888 + {
889 + context: null,
890 + didHooksChange: false,
891 + hooks: [],
892 + isFirstMount: false,
893 + props: [],
894 + state: null,
895 + },
896 + ],
897 + [
898 + 6,
899 + {
900 + context: [],
901 + didHooksChange: false,
902 + hooks: null,
903 + isFirstMount: false,
904 + props: [],
905 + state: null,
906 + },
907 + ],
908 + [
909 + 2,
910 + {
911 + context: null,
912 + didHooksChange: false,
913 + hooks: [],
914 + isFirstMount: false,
915 + props: ['foo', 'bar'],
916 + state: [],
917 + },
918 + ],
919 + ]),
920 + );
921 +
922 + expect(changeDescriptions[4]).toEqual(
923 + new Map([
924 + [
925 + 5,
926 + {
927 + context: null,
928 + didHooksChange: false,
929 + hooks: [],
930 + isFirstMount: false,
931 + props: [],
932 + state: null,
933 + },
934 + ],
935 + [
936 + 4,
937 + {
938 + context: false,
939 + didHooksChange: false,
940 + hooks: null,
941 + isFirstMount: false,
942 + props: [],
943 + state: null,
944 + },
945 + ],
946 + [
947 + 7,
948 + {
949 + context: null,
950 + didHooksChange: false,
951 + hooks: [],
952 + isFirstMount: false,
953 + props: [],
954 + state: null,
955 + },
956 + ],
957 + [
958 + 6,
959 + {
960 + context: [],
961 + didHooksChange: false,
962 + hooks: null,
963 + isFirstMount: false,
964 + props: [],
965 + state: null,
966 + },
967 + ],
968 + [
969 + 2,
970 + {
971 + context: null,
972 + didHooksChange: false,
973 + hooks: [],
974 + isFirstMount: false,
975 + props: ['bar'],
976 + state: [],
977 + },
978 + ],
979 + ]),
980 + );
981 +
982 + utils.exportImportHelper(bridge, store);
983 +
984 + const prevChangeDescriptions = [...changeDescriptions];
985 +
986 + changeDescriptions = store.profilerStore
987 + .getDataForRoot(rootID)
988 + .commitData.map(commitData => commitData.changeDescriptions);
989 + expect(changeDescriptions).toHaveLength(5);
990 +
991 + for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
992 + expect(changeDescriptions[commitIndex]).toEqual(
993 + prevChangeDescriptions[commitIndex],
994 + );
995 + }
996 + });
997 +
998 // @reactVersion >= 18.0
999 it('should properly detect changed hooks', () => {
1000 const Context = React.createContext(0);