1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type Store from 'react-devtools-shared/src/devtools/store';
    
  11. 
    
  12. describe('profiling charts', () => {
    
  13.   let React;
    
  14.   let Scheduler;
    
  15.   let legacyRender;
    
  16.   let store: Store;
    
  17.   let utils;
    
  18. 
    
  19.   beforeEach(() => {
    
  20.     utils = require('./utils');
    
  21.     utils.beforeEachProfiling();
    
  22. 
    
  23.     legacyRender = utils.legacyRender;
    
  24. 
    
  25.     store = global.store;
    
  26.     store.collapseNodesByDefault = false;
    
  27.     store.recordChangeDescriptions = true;
    
  28. 
    
  29.     React = require('react');
    
  30.     Scheduler = require('scheduler');
    
  31.   });
    
  32. 
    
  33.   function getFlamegraphChartData(rootID, commitIndex) {
    
  34.     const commitTree = store.profilerStore.profilingCache.getCommitTree({
    
  35.       commitIndex,
    
  36.       rootID,
    
  37.     });
    
  38.     const chartData = store.profilerStore.profilingCache.getFlamegraphChartData(
    
  39.       {
    
  40.         commitIndex,
    
  41.         commitTree,
    
  42.         rootID,
    
  43.       },
    
  44.     );
    
  45.     return {commitTree, chartData};
    
  46.   }
    
  47. 
    
  48.   function getRankedChartData(rootID, commitIndex) {
    
  49.     const commitTree = store.profilerStore.profilingCache.getCommitTree({
    
  50.       commitIndex,
    
  51.       rootID,
    
  52.     });
    
  53.     const chartData = store.profilerStore.profilingCache.getRankedChartData({
    
  54.       commitIndex,
    
  55.       commitTree,
    
  56.       rootID,
    
  57.     });
    
  58.     return {commitTree, chartData};
    
  59.   }
    
  60. 
    
  61.   describe('flamegraph chart', () => {
    
  62.     // @reactVersion >= 16.9
    
  63.     it('should contain valid data', () => {
    
  64.       const Parent = (_: {}) => {
    
  65.         Scheduler.unstable_advanceTime(10);
    
  66.         return (
    
  67.           <React.Fragment>
    
  68.             <Child key="first" duration={3} />
    
  69.             <Child key="second" duration={2} />
    
  70.             <Child key="third" duration={0} />
    
  71.           </React.Fragment>
    
  72.         );
    
  73.       };
    
  74. 
    
  75.       // Memoize children to verify that chart doesn't include in the update.
    
  76.       const Child = React.memo(function Child({duration}) {
    
  77.         Scheduler.unstable_advanceTime(duration);
    
  78.         return null;
    
  79.       });
    
  80. 
    
  81.       const container = document.createElement('div');
    
  82. 
    
  83.       utils.act(() => store.profilerStore.startProfiling());
    
  84. 
    
  85.       utils.act(() => legacyRender(<Parent />, container));
    
  86.       expect(store).toMatchInlineSnapshot(`
    
  87.         [root]
    
  88.           ▾ <Parent>
    
  89.               <Child key="first"> [Memo]
    
  90.               <Child key="second"> [Memo]
    
  91.               <Child key="third"> [Memo]
    
  92.       `);
    
  93. 
    
  94.       utils.act(() => legacyRender(<Parent />, container));
    
  95.       expect(store).toMatchInlineSnapshot(`
    
  96.         [root]
    
  97.           ▾ <Parent>
    
  98.               <Child key="first"> [Memo]
    
  99.               <Child key="second"> [Memo]
    
  100.               <Child key="third"> [Memo]
    
  101.       `);
    
  102. 
    
  103.       utils.act(() => store.profilerStore.stopProfiling());
    
  104. 
    
  105.       const rootID = store.roots[0];
    
  106. 
    
  107.       const firstCommitData = getFlamegraphChartData(rootID, 0);
    
  108.       expect(firstCommitData.commitTree.nodes.size).toBe(5);
    
  109.       expect(firstCommitData.chartData.rows).toMatchInlineSnapshot(`
    
  110.         [
    
  111.           [
    
  112.             {
    
  113.               "actualDuration": 15,
    
  114.               "didRender": true,
    
  115.               "id": 2,
    
  116.               "label": "Parent (10ms of 15ms)",
    
  117.               "name": "Parent",
    
  118.               "offset": 0,
    
  119.               "selfDuration": 10,
    
  120.               "treeBaseDuration": 15,
    
  121.             },
    
  122.           ],
    
  123.           [
    
  124.             {
    
  125.               "actualDuration": 0,
    
  126.               "didRender": true,
    
  127.               "id": 5,
    
  128.               "label": "Memo(Child) key="third" (<0.1ms of <0.1ms)",
    
  129.               "name": "Memo(Child)",
    
  130.               "offset": 15,
    
  131.               "selfDuration": 0,
    
  132.               "treeBaseDuration": 0,
    
  133.             },
    
  134.             {
    
  135.               "actualDuration": 2,
    
  136.               "didRender": true,
    
  137.               "id": 4,
    
  138.               "label": "Memo(Child) key="second" (2ms of 2ms)",
    
  139.               "name": "Memo(Child)",
    
  140.               "offset": 13,
    
  141.               "selfDuration": 2,
    
  142.               "treeBaseDuration": 2,
    
  143.             },
    
  144.             {
    
  145.               "actualDuration": 3,
    
  146.               "didRender": true,
    
  147.               "id": 3,
    
  148.               "label": "Memo(Child) key="first" (3ms of 3ms)",
    
  149.               "name": "Memo(Child)",
    
  150.               "offset": 10,
    
  151.               "selfDuration": 3,
    
  152.               "treeBaseDuration": 3,
    
  153.             },
    
  154.           ],
    
  155.         ]
    
  156.       `);
    
  157. 
    
  158.       const secondCommitData = getFlamegraphChartData(rootID, 1);
    
  159.       expect(secondCommitData.commitTree.nodes.size).toBe(5);
    
  160.       expect(secondCommitData.chartData.rows).toMatchInlineSnapshot(`
    
  161.         [
    
  162.           [
    
  163.             {
    
  164.               "actualDuration": 10,
    
  165.               "didRender": true,
    
  166.               "id": 2,
    
  167.               "label": "Parent (10ms of 10ms)",
    
  168.               "name": "Parent",
    
  169.               "offset": 0,
    
  170.               "selfDuration": 10,
    
  171.               "treeBaseDuration": 15,
    
  172.             },
    
  173.           ],
    
  174.           [
    
  175.             {
    
  176.               "actualDuration": 0,
    
  177.               "didRender": false,
    
  178.               "id": 5,
    
  179.               "label": "Memo(Child) key="third"",
    
  180.               "name": "Memo(Child)",
    
  181.               "offset": 15,
    
  182.               "selfDuration": 0,
    
  183.               "treeBaseDuration": 0,
    
  184.             },
    
  185.             {
    
  186.               "actualDuration": 0,
    
  187.               "didRender": false,
    
  188.               "id": 4,
    
  189.               "label": "Memo(Child) key="second"",
    
  190.               "name": "Memo(Child)",
    
  191.               "offset": 13,
    
  192.               "selfDuration": 0,
    
  193.               "treeBaseDuration": 2,
    
  194.             },
    
  195.             {
    
  196.               "actualDuration": 0,
    
  197.               "didRender": false,
    
  198.               "id": 3,
    
  199.               "label": "Memo(Child) key="first"",
    
  200.               "name": "Memo(Child)",
    
  201.               "offset": 10,
    
  202.               "selfDuration": 0,
    
  203.               "treeBaseDuration": 3,
    
  204.             },
    
  205.           ],
    
  206.         ]
    
  207.       `);
    
  208.     });
    
  209.   });
    
  210. 
    
  211.   describe('ranked chart', () => {
    
  212.     // @reactVersion >= 16.9
    
  213.     it('should contain valid data', () => {
    
  214.       const Parent = (_: {}) => {
    
  215.         Scheduler.unstable_advanceTime(10);
    
  216.         return (
    
  217.           <React.Fragment>
    
  218.             <Child key="first" duration={3} />
    
  219.             <Child key="second" duration={2} />
    
  220.             <Child key="third" duration={0} />
    
  221.           </React.Fragment>
    
  222.         );
    
  223.       };
    
  224. 
    
  225.       // Memoize children to verify that chart doesn't include in the update.
    
  226.       const Child = React.memo(function Child({duration}) {
    
  227.         Scheduler.unstable_advanceTime(duration);
    
  228.         return null;
    
  229.       });
    
  230. 
    
  231.       const container = document.createElement('div');
    
  232. 
    
  233.       utils.act(() => store.profilerStore.startProfiling());
    
  234. 
    
  235.       utils.act(() => legacyRender(<Parent />, container));
    
  236.       expect(store).toMatchInlineSnapshot(`
    
  237.         [root]
    
  238.           ▾ <Parent>
    
  239.               <Child key="first"> [Memo]
    
  240.               <Child key="second"> [Memo]
    
  241.               <Child key="third"> [Memo]
    
  242.       `);
    
  243. 
    
  244.       utils.act(() => legacyRender(<Parent />, container));
    
  245.       expect(store).toMatchInlineSnapshot(`
    
  246.         [root]
    
  247.           ▾ <Parent>
    
  248.               <Child key="first"> [Memo]
    
  249.               <Child key="second"> [Memo]
    
  250.               <Child key="third"> [Memo]
    
  251.       `);
    
  252. 
    
  253.       utils.act(() => store.profilerStore.stopProfiling());
    
  254. 
    
  255.       const rootID = store.roots[0];
    
  256. 
    
  257.       // Everything should render the first time.
    
  258.       const firstCommitData = getRankedChartData(rootID, 0);
    
  259.       expect(firstCommitData.commitTree.nodes.size).toBe(5);
    
  260.       expect(firstCommitData.chartData.nodes).toMatchInlineSnapshot(`
    
  261.         [
    
  262.           {
    
  263.             "id": 2,
    
  264.             "label": "Parent (10ms)",
    
  265.             "name": "Parent",
    
  266.             "value": 10,
    
  267.           },
    
  268.           {
    
  269.             "id": 3,
    
  270.             "label": "Memo(Child) (Memo) key="first" (3ms)",
    
  271.             "name": "Memo(Child)",
    
  272.             "value": 3,
    
  273.           },
    
  274.           {
    
  275.             "id": 4,
    
  276.             "label": "Memo(Child) (Memo) key="second" (2ms)",
    
  277.             "name": "Memo(Child)",
    
  278.             "value": 2,
    
  279.           },
    
  280.           {
    
  281.             "id": 5,
    
  282.             "label": "Memo(Child) (Memo) key="third" (<0.1ms)",
    
  283.             "name": "Memo(Child)",
    
  284.             "value": 0,
    
  285.           },
    
  286.         ]
    
  287.       `);
    
  288. 
    
  289.       // Only parent should render the second time, since child props have not changed.
    
  290.       const secondCommitData = getRankedChartData(rootID, 1);
    
  291.       expect(secondCommitData.commitTree.nodes.size).toBe(5);
    
  292.       expect(secondCommitData.chartData.nodes).toMatchInlineSnapshot(`
    
  293.         [
    
  294.           {
    
  295.             "id": 2,
    
  296.             "label": "Parent (10ms)",
    
  297.             "name": "Parent",
    
  298.             "value": 10,
    
  299.           },
    
  300.         ]
    
  301.       `);
    
  302.     });
    
  303.   });
    
  304. });