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. 'use strict';
    
  11. 
    
  12. import {normalizeCodeLocInfo} from './utils';
    
  13. 
    
  14. describe('Timeline profiler', () => {
    
  15.   let React;
    
  16.   let ReactDOMClient;
    
  17.   let Scheduler;
    
  18.   let renderHelper;
    
  19.   let renderRootHelper;
    
  20.   let store;
    
  21.   let unmountFns;
    
  22.   let utils;
    
  23.   let waitFor;
    
  24.   let waitForAll;
    
  25.   let waitForPaint;
    
  26.   let assertLog;
    
  27. 
    
  28.   beforeEach(() => {
    
  29.     utils = require('./utils');
    
  30.     utils.beforeEachProfiling();
    
  31. 
    
  32.     unmountFns = [];
    
  33.     renderHelper = element => {
    
  34.       const unmountFn = utils.legacyRender(element);
    
  35.       unmountFns.push(unmountFn);
    
  36.       return unmountFn;
    
  37.     };
    
  38.     renderRootHelper = element => {
    
  39.       const container = document.createElement('div');
    
  40.       const root = ReactDOMClient.createRoot(container);
    
  41.       root.render(element);
    
  42.       const unmountFn = () => root.unmount();
    
  43.       unmountFns.push(unmountFn);
    
  44.       return unmountFn;
    
  45.     };
    
  46. 
    
  47.     React = require('react');
    
  48.     ReactDOMClient = require('react-dom/client');
    
  49.     Scheduler = require('scheduler');
    
  50. 
    
  51.     const InternalTestUtils = require('internal-test-utils');
    
  52.     waitFor = InternalTestUtils.waitFor;
    
  53.     waitForAll = InternalTestUtils.waitForAll;
    
  54.     waitForPaint = InternalTestUtils.waitForPaint;
    
  55.     assertLog = InternalTestUtils.assertLog;
    
  56. 
    
  57.     store = global.store;
    
  58.   });
    
  59. 
    
  60.   afterEach(() => {
    
  61.     jest.restoreAllMocks();
    
  62.   });
    
  63. 
    
  64.   describe('User Timing API', () => {
    
  65.     let clearedMarks;
    
  66.     let featureDetectionMarkName = null;
    
  67.     let marks;
    
  68.     let setPerformanceMock;
    
  69. 
    
  70.     function createUserTimingPolyfill() {
    
  71.       featureDetectionMarkName = null;
    
  72. 
    
  73.       clearedMarks = [];
    
  74.       marks = [];
    
  75. 
    
  76.       // Remove file-system specific bits or version-specific bits of information from the module range marks.
    
  77.       function filterMarkData(markName) {
    
  78.         if (markName.startsWith('--react-internal-module-start')) {
    
  79.           return '--react-internal-module-start-  at filtered (<anonymous>:0:0)';
    
  80.         } else if (markName.startsWith('--react-internal-module-stop')) {
    
  81.           return '--react-internal-module-stop-  at filtered (<anonymous>:1:1)';
    
  82.         } else if (markName.startsWith('--react-version')) {
    
  83.           return '--react-version-<filtered-version>';
    
  84.         } else {
    
  85.           return markName;
    
  86.         }
    
  87.       }
    
  88. 
    
  89.       // This is not a true polyfill, but it gives us enough to capture marks.
    
  90.       // Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
    
  91.       return {
    
  92.         clearMarks(markName) {
    
  93.           markName = filterMarkData(markName);
    
  94. 
    
  95.           clearedMarks.push(markName);
    
  96.           marks = marks.filter(mark => mark !== markName);
    
  97.         },
    
  98.         mark(markName, markOptions) {
    
  99.           markName = filterMarkData(markName);
    
  100. 
    
  101.           if (featureDetectionMarkName === null) {
    
  102.             featureDetectionMarkName = markName;
    
  103.           }
    
  104. 
    
  105.           marks.push(markName);
    
  106. 
    
  107.           if (markOptions != null) {
    
  108.             // This is triggers the feature detection.
    
  109.             markOptions.startTime++;
    
  110.           }
    
  111.         },
    
  112.       };
    
  113.     }
    
  114. 
    
  115.     function clearPendingMarks() {
    
  116.       clearedMarks.splice(0);
    
  117.     }
    
  118. 
    
  119.     function dispatchAndSetCurrentEvent(element, event) {
    
  120.       try {
    
  121.         window.event = event;
    
  122.         element.dispatchEvent(event);
    
  123.       } finally {
    
  124.         window.event = undefined;
    
  125.       }
    
  126.     }
    
  127. 
    
  128.     beforeEach(() => {
    
  129.       setPerformanceMock =
    
  130.         require('react-devtools-shared/src/backend/profilingHooks').setPerformanceMock_ONLY_FOR_TESTING;
    
  131.       setPerformanceMock(createUserTimingPolyfill());
    
  132.     });
    
  133. 
    
  134.     afterEach(() => {
    
  135.       // Verify all logged marks also get cleared.
    
  136.       expect(marks).toHaveLength(0);
    
  137. 
    
  138.       unmountFns.forEach(unmountFn => unmountFn());
    
  139. 
    
  140.       setPerformanceMock(null);
    
  141.     });
    
  142. 
    
  143.     it('should mark sync render without suspends or state updates', () => {
    
  144.       renderHelper(<div />);
    
  145. 
    
  146.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  147.         [
    
  148.           "--schedule-render-2",
    
  149.           "--render-start-2",
    
  150.           "--render-stop",
    
  151.           "--commit-start-2",
    
  152.           "--react-version-<filtered-version>",
    
  153.           "--profiler-version-1",
    
  154.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  155.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  156.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  157.           "--layout-effects-start-2",
    
  158.           "--layout-effects-stop",
    
  159.           "--commit-stop",
    
  160.         ]
    
  161.       `);
    
  162.     });
    
  163. 
    
  164.     it('should mark concurrent render without suspends or state updates', async () => {
    
  165.       renderRootHelper(<div />);
    
  166. 
    
  167.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  168.         [
    
  169.           "--schedule-render-32",
    
  170.         ]
    
  171.       `);
    
  172. 
    
  173.       clearPendingMarks();
    
  174. 
    
  175.       await waitForPaint([]);
    
  176. 
    
  177.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  178.         [
    
  179.           "--render-start-32",
    
  180.           "--render-stop",
    
  181.           "--commit-start-32",
    
  182.           "--react-version-<filtered-version>",
    
  183.           "--profiler-version-1",
    
  184.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  185.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  186.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  187.           "--layout-effects-start-32",
    
  188.           "--layout-effects-stop",
    
  189.           "--commit-stop",
    
  190.         ]
    
  191.       `);
    
  192.     });
    
  193. 
    
  194.     it('should mark render yields', async () => {
    
  195.       function Bar() {
    
  196.         Scheduler.log('Bar');
    
  197.         return null;
    
  198.       }
    
  199. 
    
  200.       function Foo() {
    
  201.         Scheduler.log('Foo');
    
  202.         return <Bar />;
    
  203.       }
    
  204. 
    
  205.       React.startTransition(() => {
    
  206.         renderRootHelper(<Foo />);
    
  207.       });
    
  208. 
    
  209.       await waitFor(['Foo']);
    
  210. 
    
  211.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  212.         [
    
  213.           "--schedule-render-128",
    
  214.           "--render-start-128",
    
  215.           "--component-render-start-Foo",
    
  216.           "--component-render-stop",
    
  217.           "--render-yield",
    
  218.         ]
    
  219.       `);
    
  220.     });
    
  221. 
    
  222.     it('should mark sync render with suspense that resolves', async () => {
    
  223.       const fakeSuspensePromise = Promise.resolve(true);
    
  224.       function Example() {
    
  225.         throw fakeSuspensePromise;
    
  226.       }
    
  227. 
    
  228.       renderHelper(
    
  229.         <React.Suspense fallback={null}>
    
  230.           <Example />
    
  231.         </React.Suspense>,
    
  232.       );
    
  233. 
    
  234.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  235.         [
    
  236.           "--schedule-render-2",
    
  237.           "--render-start-2",
    
  238.           "--component-render-start-Example",
    
  239.           "--component-render-stop",
    
  240.           "--suspense-suspend-0-Example-mount-2-",
    
  241.           "--render-stop",
    
  242.           "--commit-start-2",
    
  243.           "--react-version-<filtered-version>",
    
  244.           "--profiler-version-1",
    
  245.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  246.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  247.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  248.           "--layout-effects-start-2",
    
  249.           "--layout-effects-stop",
    
  250.           "--commit-stop",
    
  251.         ]
    
  252.       `);
    
  253. 
    
  254.       clearPendingMarks();
    
  255. 
    
  256.       await fakeSuspensePromise;
    
  257.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  258.                 [
    
  259.                   "--suspense-resolved-0-Example",
    
  260.                 ]
    
  261.           `);
    
  262.     });
    
  263. 
    
  264.     it('should mark sync render with suspense that rejects', async () => {
    
  265.       const fakeSuspensePromise = Promise.reject(new Error('error'));
    
  266.       function Example() {
    
  267.         throw fakeSuspensePromise;
    
  268.       }
    
  269. 
    
  270.       renderHelper(
    
  271.         <React.Suspense fallback={null}>
    
  272.           <Example />
    
  273.         </React.Suspense>,
    
  274.       );
    
  275. 
    
  276.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  277.         [
    
  278.           "--schedule-render-2",
    
  279.           "--render-start-2",
    
  280.           "--component-render-start-Example",
    
  281.           "--component-render-stop",
    
  282.           "--suspense-suspend-0-Example-mount-2-",
    
  283.           "--render-stop",
    
  284.           "--commit-start-2",
    
  285.           "--react-version-<filtered-version>",
    
  286.           "--profiler-version-1",
    
  287.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  288.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  289.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  290.           "--layout-effects-start-2",
    
  291.           "--layout-effects-stop",
    
  292.           "--commit-stop",
    
  293.         ]
    
  294.       `);
    
  295. 
    
  296.       clearPendingMarks();
    
  297. 
    
  298.       await expect(fakeSuspensePromise).rejects.toThrow();
    
  299.       expect(clearedMarks).toContain(`--suspense-rejected-0-Example`);
    
  300.     });
    
  301. 
    
  302.     it('should mark concurrent render with suspense that resolves', async () => {
    
  303.       let resolveFakePromise;
    
  304.       const fakeSuspensePromise = new Promise(
    
  305.         resolve => (resolveFakePromise = resolve),
    
  306.       );
    
  307. 
    
  308.       function Example() {
    
  309.         throw fakeSuspensePromise;
    
  310.       }
    
  311. 
    
  312.       renderRootHelper(
    
  313.         <React.Suspense fallback={null}>
    
  314.           <Example />
    
  315.         </React.Suspense>,
    
  316.       );
    
  317. 
    
  318.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  319.         [
    
  320.           "--schedule-render-32",
    
  321.         ]
    
  322.       `);
    
  323. 
    
  324.       clearPendingMarks();
    
  325. 
    
  326.       await waitForPaint([]);
    
  327. 
    
  328.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  329.         [
    
  330.           "--render-start-32",
    
  331.           "--component-render-start-Example",
    
  332.           "--component-render-stop",
    
  333.           "--suspense-suspend-0-Example-mount-32-",
    
  334.           "--render-stop",
    
  335.           "--commit-start-32",
    
  336.           "--react-version-<filtered-version>",
    
  337.           "--profiler-version-1",
    
  338.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  339.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  340.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  341.           "--layout-effects-start-32",
    
  342.           "--layout-effects-stop",
    
  343.           "--commit-stop",
    
  344.         ]
    
  345.       `);
    
  346. 
    
  347.       clearPendingMarks();
    
  348. 
    
  349.       await resolveFakePromise();
    
  350.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  351.             [
    
  352.               "--suspense-resolved-0-Example",
    
  353.             ]
    
  354.         `);
    
  355.     });
    
  356. 
    
  357.     it('should mark concurrent render with suspense that rejects', async () => {
    
  358.       let rejectFakePromise;
    
  359.       const fakeSuspensePromise = new Promise(
    
  360.         (_, reject) => (rejectFakePromise = reject),
    
  361.       );
    
  362. 
    
  363.       function Example() {
    
  364.         throw fakeSuspensePromise;
    
  365.       }
    
  366. 
    
  367.       renderRootHelper(
    
  368.         <React.Suspense fallback={null}>
    
  369.           <Example />
    
  370.         </React.Suspense>,
    
  371.       );
    
  372. 
    
  373.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  374.         [
    
  375.           "--schedule-render-32",
    
  376.         ]
    
  377.       `);
    
  378. 
    
  379.       clearPendingMarks();
    
  380. 
    
  381.       await waitForPaint([]);
    
  382. 
    
  383.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  384.         [
    
  385.           "--render-start-32",
    
  386.           "--component-render-start-Example",
    
  387.           "--component-render-stop",
    
  388.           "--suspense-suspend-0-Example-mount-32-",
    
  389.           "--render-stop",
    
  390.           "--commit-start-32",
    
  391.           "--react-version-<filtered-version>",
    
  392.           "--profiler-version-1",
    
  393.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  394.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  395.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  396.           "--layout-effects-start-32",
    
  397.           "--layout-effects-stop",
    
  398.           "--commit-stop",
    
  399.         ]
    
  400.       `);
    
  401. 
    
  402.       clearPendingMarks();
    
  403. 
    
  404.       await expect(() => {
    
  405.         rejectFakePromise(new Error('error'));
    
  406.         return fakeSuspensePromise;
    
  407.       }).rejects.toThrow();
    
  408.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  409.                 [
    
  410.                   "--suspense-rejected-0-Example",
    
  411.                 ]
    
  412.           `);
    
  413.     });
    
  414. 
    
  415.     it('should mark cascading class component state updates', async () => {
    
  416.       class Example extends React.Component {
    
  417.         state = {didMount: false};
    
  418.         componentDidMount() {
    
  419.           this.setState({didMount: true});
    
  420.         }
    
  421.         render() {
    
  422.           return null;
    
  423.         }
    
  424.       }
    
  425. 
    
  426.       renderRootHelper(<Example />);
    
  427. 
    
  428.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  429.         [
    
  430.           "--schedule-render-32",
    
  431.         ]
    
  432.       `);
    
  433. 
    
  434.       clearPendingMarks();
    
  435. 
    
  436.       await waitForPaint([]);
    
  437. 
    
  438.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  439.         [
    
  440.           "--render-start-32",
    
  441.           "--component-render-start-Example",
    
  442.           "--component-render-stop",
    
  443.           "--render-stop",
    
  444.           "--commit-start-32",
    
  445.           "--react-version-<filtered-version>",
    
  446.           "--profiler-version-1",
    
  447.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  448.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  449.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  450.           "--layout-effects-start-32",
    
  451.           "--schedule-state-update-2-Example",
    
  452.           "--layout-effects-stop",
    
  453.           "--render-start-2",
    
  454.           "--component-render-start-Example",
    
  455.           "--component-render-stop",
    
  456.           "--render-stop",
    
  457.           "--commit-start-2",
    
  458.           "--react-version-<filtered-version>",
    
  459.           "--profiler-version-1",
    
  460.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  461.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  462.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  463.           "--commit-stop",
    
  464.           "--commit-stop",
    
  465.         ]
    
  466.       `);
    
  467.     });
    
  468. 
    
  469.     it('should mark cascading class component force updates', async () => {
    
  470.       class Example extends React.Component {
    
  471.         componentDidMount() {
    
  472.           this.forceUpdate();
    
  473.         }
    
  474.         render() {
    
  475.           return null;
    
  476.         }
    
  477.       }
    
  478. 
    
  479.       renderRootHelper(<Example />);
    
  480. 
    
  481.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  482.         [
    
  483.           "--schedule-render-32",
    
  484.         ]
    
  485.       `);
    
  486. 
    
  487.       clearPendingMarks();
    
  488. 
    
  489.       await waitForPaint([]);
    
  490. 
    
  491.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  492.         [
    
  493.           "--render-start-32",
    
  494.           "--component-render-start-Example",
    
  495.           "--component-render-stop",
    
  496.           "--render-stop",
    
  497.           "--commit-start-32",
    
  498.           "--react-version-<filtered-version>",
    
  499.           "--profiler-version-1",
    
  500.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  501.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  502.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  503.           "--layout-effects-start-32",
    
  504.           "--schedule-forced-update-2-Example",
    
  505.           "--layout-effects-stop",
    
  506.           "--render-start-2",
    
  507.           "--component-render-start-Example",
    
  508.           "--component-render-stop",
    
  509.           "--render-stop",
    
  510.           "--commit-start-2",
    
  511.           "--react-version-<filtered-version>",
    
  512.           "--profiler-version-1",
    
  513.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  514.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  515.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  516.           "--commit-stop",
    
  517.           "--commit-stop",
    
  518.         ]
    
  519.       `);
    
  520.     });
    
  521. 
    
  522.     it('should mark render phase state updates for class component', async () => {
    
  523.       class Example extends React.Component {
    
  524.         state = {didRender: false};
    
  525.         render() {
    
  526.           if (this.state.didRender === false) {
    
  527.             this.setState({didRender: true});
    
  528.           }
    
  529.           return null;
    
  530.         }
    
  531.       }
    
  532. 
    
  533.       renderRootHelper(<Example />);
    
  534. 
    
  535.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  536.         [
    
  537.           "--schedule-render-32",
    
  538.         ]
    
  539.       `);
    
  540. 
    
  541.       clearPendingMarks();
    
  542. 
    
  543.       let errorMessage;
    
  544.       jest.spyOn(console, 'error').mockImplementation(message => {
    
  545.         errorMessage = message;
    
  546.       });
    
  547. 
    
  548.       await waitForPaint([]);
    
  549. 
    
  550.       expect(console.error).toHaveBeenCalledTimes(1);
    
  551.       expect(errorMessage).toContain(
    
  552.         'Cannot update during an existing state transition',
    
  553.       );
    
  554. 
    
  555.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  556.         [
    
  557.           "--render-start-32",
    
  558.           "--component-render-start-Example",
    
  559.           "--schedule-state-update-32-Example",
    
  560.           "--component-render-stop",
    
  561.           "--render-stop",
    
  562.           "--commit-start-32",
    
  563.           "--react-version-<filtered-version>",
    
  564.           "--profiler-version-1",
    
  565.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  566.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  567.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  568.           "--layout-effects-start-32",
    
  569.           "--layout-effects-stop",
    
  570.           "--commit-stop",
    
  571.         ]
    
  572.       `);
    
  573.     });
    
  574. 
    
  575.     it('should mark render phase force updates for class component', async () => {
    
  576.       let forced = false;
    
  577.       class Example extends React.Component {
    
  578.         render() {
    
  579.           if (!forced) {
    
  580.             forced = true;
    
  581.             this.forceUpdate();
    
  582.           }
    
  583.           return null;
    
  584.         }
    
  585.       }
    
  586. 
    
  587.       renderRootHelper(<Example />);
    
  588. 
    
  589.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  590.         [
    
  591.           "--schedule-render-32",
    
  592.         ]
    
  593.       `);
    
  594. 
    
  595.       clearPendingMarks();
    
  596. 
    
  597.       let errorMessage;
    
  598.       jest.spyOn(console, 'error').mockImplementation(message => {
    
  599.         errorMessage = message;
    
  600.       });
    
  601. 
    
  602.       await waitForPaint([]);
    
  603. 
    
  604.       expect(console.error).toHaveBeenCalledTimes(1);
    
  605.       expect(errorMessage).toContain(
    
  606.         'Cannot update during an existing state transition',
    
  607.       );
    
  608. 
    
  609.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  610.         [
    
  611.           "--render-start-32",
    
  612.           "--component-render-start-Example",
    
  613.           "--schedule-forced-update-32-Example",
    
  614.           "--component-render-stop",
    
  615.           "--render-stop",
    
  616.           "--commit-start-32",
    
  617.           "--react-version-<filtered-version>",
    
  618.           "--profiler-version-1",
    
  619.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  620.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  621.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  622.           "--layout-effects-start-32",
    
  623.           "--layout-effects-stop",
    
  624.           "--commit-stop",
    
  625.         ]
    
  626.       `);
    
  627.     });
    
  628. 
    
  629.     it('should mark cascading layout updates', async () => {
    
  630.       function Example() {
    
  631.         const [didMount, setDidMount] = React.useState(false);
    
  632.         React.useLayoutEffect(() => {
    
  633.           setDidMount(true);
    
  634.         }, []);
    
  635.         return didMount;
    
  636.       }
    
  637. 
    
  638.       renderRootHelper(<Example />);
    
  639. 
    
  640.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  641.         [
    
  642.           "--schedule-render-32",
    
  643.         ]
    
  644.       `);
    
  645. 
    
  646.       clearPendingMarks();
    
  647. 
    
  648.       await waitForPaint([]);
    
  649. 
    
  650.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  651.         [
    
  652.           "--render-start-32",
    
  653.           "--component-render-start-Example",
    
  654.           "--component-render-stop",
    
  655.           "--render-stop",
    
  656.           "--commit-start-32",
    
  657.           "--react-version-<filtered-version>",
    
  658.           "--profiler-version-1",
    
  659.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  660.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  661.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  662.           "--layout-effects-start-32",
    
  663.           "--component-layout-effect-mount-start-Example",
    
  664.           "--schedule-state-update-2-Example",
    
  665.           "--component-layout-effect-mount-stop",
    
  666.           "--layout-effects-stop",
    
  667.           "--render-start-2",
    
  668.           "--component-render-start-Example",
    
  669.           "--component-render-stop",
    
  670.           "--render-stop",
    
  671.           "--commit-start-2",
    
  672.           "--react-version-<filtered-version>",
    
  673.           "--profiler-version-1",
    
  674.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  675.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  676.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  677.           "--commit-stop",
    
  678.           "--commit-stop",
    
  679.         ]
    
  680.       `);
    
  681.     });
    
  682. 
    
  683.     it('should mark cascading passive updates', async () => {
    
  684.       function Example() {
    
  685.         const [didMount, setDidMount] = React.useState(false);
    
  686.         React.useEffect(() => {
    
  687.           setDidMount(true);
    
  688.         }, []);
    
  689.         return didMount;
    
  690.       }
    
  691. 
    
  692.       renderRootHelper(<Example />);
    
  693. 
    
  694.       await waitForAll([]);
    
  695. 
    
  696.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  697.         [
    
  698.           "--schedule-render-32",
    
  699.           "--render-start-32",
    
  700.           "--component-render-start-Example",
    
  701.           "--component-render-stop",
    
  702.           "--render-stop",
    
  703.           "--commit-start-32",
    
  704.           "--react-version-<filtered-version>",
    
  705.           "--profiler-version-1",
    
  706.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  707.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  708.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  709.           "--layout-effects-start-32",
    
  710.           "--layout-effects-stop",
    
  711.           "--commit-stop",
    
  712.           "--passive-effects-start-32",
    
  713.           "--component-passive-effect-mount-start-Example",
    
  714.           "--schedule-state-update-32-Example",
    
  715.           "--component-passive-effect-mount-stop",
    
  716.           "--passive-effects-stop",
    
  717.           "--render-start-32",
    
  718.           "--component-render-start-Example",
    
  719.           "--component-render-stop",
    
  720.           "--render-stop",
    
  721.           "--commit-start-32",
    
  722.           "--react-version-<filtered-version>",
    
  723.           "--profiler-version-1",
    
  724.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  725.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  726.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  727.           "--commit-stop",
    
  728.         ]
    
  729.       `);
    
  730.     });
    
  731. 
    
  732.     it('should mark render phase updates', async () => {
    
  733.       function Example() {
    
  734.         const [didRender, setDidRender] = React.useState(false);
    
  735.         if (!didRender) {
    
  736.           setDidRender(true);
    
  737.         }
    
  738.         return didRender;
    
  739.       }
    
  740. 
    
  741.       renderRootHelper(<Example />);
    
  742. 
    
  743.       await waitForAll([]);
    
  744. 
    
  745.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  746.         [
    
  747.           "--schedule-render-32",
    
  748.           "--render-start-32",
    
  749.           "--component-render-start-Example",
    
  750.           "--schedule-state-update-32-Example",
    
  751.           "--component-render-stop",
    
  752.           "--render-stop",
    
  753.           "--commit-start-32",
    
  754.           "--react-version-<filtered-version>",
    
  755.           "--profiler-version-1",
    
  756.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  757.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  758.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  759.           "--layout-effects-start-32",
    
  760.           "--layout-effects-stop",
    
  761.           "--commit-stop",
    
  762.         ]
    
  763.       `);
    
  764.     });
    
  765. 
    
  766.     it('should mark sync render that throws', async () => {
    
  767.       jest.spyOn(console, 'error').mockImplementation(() => {});
    
  768. 
    
  769.       class ErrorBoundary extends React.Component {
    
  770.         state = {error: null};
    
  771.         componentDidCatch(error) {
    
  772.           this.setState({error});
    
  773.         }
    
  774.         render() {
    
  775.           if (this.state.error) {
    
  776.             return null;
    
  777.           }
    
  778.           return this.props.children;
    
  779.         }
    
  780.       }
    
  781. 
    
  782.       function ExampleThatThrows() {
    
  783.         throw Error('Expected error');
    
  784.       }
    
  785. 
    
  786.       renderHelper(
    
  787.         <ErrorBoundary>
    
  788.           <ExampleThatThrows />
    
  789.         </ErrorBoundary>,
    
  790.       );
    
  791. 
    
  792.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  793.         [
    
  794.           "--schedule-render-2",
    
  795.           "--render-start-2",
    
  796.           "--component-render-start-ErrorBoundary",
    
  797.           "--component-render-stop",
    
  798.           "--component-render-start-ExampleThatThrows",
    
  799.           "--component-render-start-ExampleThatThrows",
    
  800.           "--component-render-stop",
    
  801.           "--error-ExampleThatThrows-mount-Expected error",
    
  802.           "--render-stop",
    
  803.           "--commit-start-2",
    
  804.           "--react-version-<filtered-version>",
    
  805.           "--profiler-version-1",
    
  806.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  807.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  808.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  809.           "--layout-effects-start-2",
    
  810.           "--schedule-state-update-2-ErrorBoundary",
    
  811.           "--layout-effects-stop",
    
  812.           "--commit-stop",
    
  813.           "--render-start-2",
    
  814.           "--component-render-start-ErrorBoundary",
    
  815.           "--component-render-stop",
    
  816.           "--render-stop",
    
  817.           "--commit-start-2",
    
  818.           "--react-version-<filtered-version>",
    
  819.           "--profiler-version-1",
    
  820.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  821.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  822.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  823.           "--commit-stop",
    
  824.         ]
    
  825.       `);
    
  826.     });
    
  827. 
    
  828.     it('should mark concurrent render that throws', async () => {
    
  829.       jest.spyOn(console, 'error').mockImplementation(() => {});
    
  830. 
    
  831.       class ErrorBoundary extends React.Component {
    
  832.         state = {error: null};
    
  833.         componentDidCatch(error) {
    
  834.           this.setState({error});
    
  835.         }
    
  836.         render() {
    
  837.           if (this.state.error) {
    
  838.             return null;
    
  839.           }
    
  840.           return this.props.children;
    
  841.         }
    
  842.       }
    
  843. 
    
  844.       function ExampleThatThrows() {
    
  845.         // eslint-disable-next-line no-throw-literal
    
  846.         throw 'Expected error';
    
  847.       }
    
  848. 
    
  849.       renderRootHelper(
    
  850.         <ErrorBoundary>
    
  851.           <ExampleThatThrows />
    
  852.         </ErrorBoundary>,
    
  853.       );
    
  854. 
    
  855.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  856.         [
    
  857.           "--schedule-render-32",
    
  858.         ]
    
  859.       `);
    
  860. 
    
  861.       clearPendingMarks();
    
  862. 
    
  863.       await waitForPaint([]);
    
  864. 
    
  865.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  866.         [
    
  867.           "--render-start-32",
    
  868.           "--component-render-start-ErrorBoundary",
    
  869.           "--component-render-stop",
    
  870.           "--component-render-start-ExampleThatThrows",
    
  871.           "--component-render-start-ExampleThatThrows",
    
  872.           "--component-render-stop",
    
  873.           "--error-ExampleThatThrows-mount-Expected error",
    
  874.           "--render-stop",
    
  875.           "--render-start-32",
    
  876.           "--component-render-start-ErrorBoundary",
    
  877.           "--component-render-stop",
    
  878.           "--component-render-start-ExampleThatThrows",
    
  879.           "--component-render-start-ExampleThatThrows",
    
  880.           "--component-render-stop",
    
  881.           "--error-ExampleThatThrows-mount-Expected error",
    
  882.           "--render-stop",
    
  883.           "--commit-start-32",
    
  884.           "--react-version-<filtered-version>",
    
  885.           "--profiler-version-1",
    
  886.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  887.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  888.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  889.           "--layout-effects-start-32",
    
  890.           "--schedule-state-update-2-ErrorBoundary",
    
  891.           "--layout-effects-stop",
    
  892.           "--render-start-2",
    
  893.           "--component-render-start-ErrorBoundary",
    
  894.           "--component-render-stop",
    
  895.           "--render-stop",
    
  896.           "--commit-start-2",
    
  897.           "--react-version-<filtered-version>",
    
  898.           "--profiler-version-1",
    
  899.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  900.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  901.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  902.           "--commit-stop",
    
  903.           "--commit-stop",
    
  904.         ]
    
  905.       `);
    
  906.     });
    
  907. 
    
  908.     it('should mark passive and layout effects', async () => {
    
  909.       function ComponentWithEffects() {
    
  910.         React.useLayoutEffect(() => {
    
  911.           Scheduler.log('layout 1 mount');
    
  912.           return () => {
    
  913.             Scheduler.log('layout 1 unmount');
    
  914.           };
    
  915.         }, []);
    
  916. 
    
  917.         React.useEffect(() => {
    
  918.           Scheduler.log('passive 1 mount');
    
  919.           return () => {
    
  920.             Scheduler.log('passive 1 unmount');
    
  921.           };
    
  922.         }, []);
    
  923. 
    
  924.         React.useLayoutEffect(() => {
    
  925.           Scheduler.log('layout 2 mount');
    
  926.           return () => {
    
  927.             Scheduler.log('layout 2 unmount');
    
  928.           };
    
  929.         }, []);
    
  930. 
    
  931.         React.useEffect(() => {
    
  932.           Scheduler.log('passive 2 mount');
    
  933.           return () => {
    
  934.             Scheduler.log('passive 2 unmount');
    
  935.           };
    
  936.         }, []);
    
  937. 
    
  938.         React.useEffect(() => {
    
  939.           Scheduler.log('passive 3 mount');
    
  940.           return () => {
    
  941.             Scheduler.log('passive 3 unmount');
    
  942.           };
    
  943.         }, []);
    
  944. 
    
  945.         return null;
    
  946.       }
    
  947. 
    
  948.       const unmount = renderRootHelper(<ComponentWithEffects />);
    
  949. 
    
  950.       await waitForPaint(['layout 1 mount', 'layout 2 mount']);
    
  951. 
    
  952.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  953.         [
    
  954.           "--schedule-render-32",
    
  955.           "--render-start-32",
    
  956.           "--component-render-start-ComponentWithEffects",
    
  957.           "--component-render-stop",
    
  958.           "--render-stop",
    
  959.           "--commit-start-32",
    
  960.           "--react-version-<filtered-version>",
    
  961.           "--profiler-version-1",
    
  962.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  963.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  964.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  965.           "--layout-effects-start-32",
    
  966.           "--component-layout-effect-mount-start-ComponentWithEffects",
    
  967.           "--component-layout-effect-mount-stop",
    
  968.           "--component-layout-effect-mount-start-ComponentWithEffects",
    
  969.           "--component-layout-effect-mount-stop",
    
  970.           "--layout-effects-stop",
    
  971.           "--commit-stop",
    
  972.         ]
    
  973.       `);
    
  974. 
    
  975.       clearPendingMarks();
    
  976. 
    
  977.       await waitForAll([
    
  978.         'passive 1 mount',
    
  979.         'passive 2 mount',
    
  980.         'passive 3 mount',
    
  981.       ]);
    
  982. 
    
  983.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  984.         [
    
  985.           "--passive-effects-start-32",
    
  986.           "--component-passive-effect-mount-start-ComponentWithEffects",
    
  987.           "--component-passive-effect-mount-stop",
    
  988.           "--component-passive-effect-mount-start-ComponentWithEffects",
    
  989.           "--component-passive-effect-mount-stop",
    
  990.           "--component-passive-effect-mount-start-ComponentWithEffects",
    
  991.           "--component-passive-effect-mount-stop",
    
  992.           "--passive-effects-stop",
    
  993.         ]
    
  994.       `);
    
  995. 
    
  996.       clearPendingMarks();
    
  997. 
    
  998.       await waitForAll([]);
    
  999. 
    
  1000.       unmount();
    
  1001. 
    
  1002.       assertLog([
    
  1003.         'layout 1 unmount',
    
  1004.         'layout 2 unmount',
    
  1005.         'passive 1 unmount',
    
  1006.         'passive 2 unmount',
    
  1007.         'passive 3 unmount',
    
  1008.       ]);
    
  1009. 
    
  1010.       expect(clearedMarks).toMatchInlineSnapshot(`
    
  1011.         [
    
  1012.           "--schedule-render-2",
    
  1013.           "--render-start-2",
    
  1014.           "--render-stop",
    
  1015.           "--commit-start-2",
    
  1016.           "--react-version-<filtered-version>",
    
  1017.           "--profiler-version-1",
    
  1018.           "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  1019.           "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  1020.           "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  1021.           "--component-layout-effect-unmount-start-ComponentWithEffects",
    
  1022.           "--component-layout-effect-unmount-stop",
    
  1023.           "--component-layout-effect-unmount-start-ComponentWithEffects",
    
  1024.           "--component-layout-effect-unmount-stop",
    
  1025.           "--layout-effects-start-2",
    
  1026.           "--layout-effects-stop",
    
  1027.           "--passive-effects-start-2",
    
  1028.           "--component-passive-effect-unmount-start-ComponentWithEffects",
    
  1029.           "--component-passive-effect-unmount-stop",
    
  1030.           "--component-passive-effect-unmount-start-ComponentWithEffects",
    
  1031.           "--component-passive-effect-unmount-stop",
    
  1032.           "--component-passive-effect-unmount-start-ComponentWithEffects",
    
  1033.           "--component-passive-effect-unmount-stop",
    
  1034.           "--passive-effects-stop",
    
  1035.           "--commit-stop",
    
  1036.         ]
    
  1037.       `);
    
  1038.     });
    
  1039. 
    
  1040.     describe('lane labels', () => {
    
  1041.       it('regression test SyncLane', () => {
    
  1042.         renderHelper(<div />);
    
  1043. 
    
  1044.         expect(clearedMarks).toMatchInlineSnapshot(`
    
  1045.           [
    
  1046.             "--schedule-render-2",
    
  1047.             "--render-start-2",
    
  1048.             "--render-stop",
    
  1049.             "--commit-start-2",
    
  1050.             "--react-version-<filtered-version>",
    
  1051.             "--profiler-version-1",
    
  1052.             "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  1053.             "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  1054.             "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  1055.             "--layout-effects-start-2",
    
  1056.             "--layout-effects-stop",
    
  1057.             "--commit-stop",
    
  1058.           ]
    
  1059.         `);
    
  1060.       });
    
  1061. 
    
  1062.       it('regression test DefaultLane', () => {
    
  1063.         renderRootHelper(<div />);
    
  1064.         expect(clearedMarks).toMatchInlineSnapshot(`
    
  1065.           [
    
  1066.             "--schedule-render-32",
    
  1067.           ]
    
  1068.         `);
    
  1069.       });
    
  1070. 
    
  1071.       it('regression test InputDiscreteLane', async () => {
    
  1072.         const targetRef = React.createRef(null);
    
  1073. 
    
  1074.         function App() {
    
  1075.           const [count, setCount] = React.useState(0);
    
  1076.           const handleClick = () => {
    
  1077.             setCount(count + 1);
    
  1078.           };
    
  1079.           return <button ref={targetRef} onClick={handleClick} />;
    
  1080.         }
    
  1081. 
    
  1082.         renderRootHelper(<App />);
    
  1083.         await waitForAll([]);
    
  1084. 
    
  1085.         clearedMarks.splice(0);
    
  1086. 
    
  1087.         targetRef.current.click();
    
  1088. 
    
  1089.         // Wait a frame, for React to process the "click" update.
    
  1090.         await Promise.resolve();
    
  1091. 
    
  1092.         expect(clearedMarks).toMatchInlineSnapshot(`
    
  1093.           [
    
  1094.             "--schedule-state-update-2-App",
    
  1095.             "--render-start-2",
    
  1096.             "--component-render-start-App",
    
  1097.             "--component-render-stop",
    
  1098.             "--render-stop",
    
  1099.             "--commit-start-2",
    
  1100.             "--react-version-<filtered-version>",
    
  1101.             "--profiler-version-1",
    
  1102.             "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  1103.             "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  1104.             "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  1105.             "--layout-effects-start-2",
    
  1106.             "--layout-effects-stop",
    
  1107.             "--commit-stop",
    
  1108.           ]
    
  1109.         `);
    
  1110.       });
    
  1111. 
    
  1112.       it('regression test InputContinuousLane', async () => {
    
  1113.         const targetRef = React.createRef(null);
    
  1114. 
    
  1115.         function App() {
    
  1116.           const [count, setCount] = React.useState(0);
    
  1117.           const handleMouseOver = () => setCount(count + 1);
    
  1118.           return <div ref={targetRef} onMouseOver={handleMouseOver} />;
    
  1119.         }
    
  1120. 
    
  1121.         renderRootHelper(<App />);
    
  1122.         await waitForAll([]);
    
  1123. 
    
  1124.         clearedMarks.splice(0);
    
  1125. 
    
  1126.         const event = document.createEvent('MouseEvents');
    
  1127.         event.initEvent('mouseover', true, true);
    
  1128.         dispatchAndSetCurrentEvent(targetRef.current, event);
    
  1129. 
    
  1130.         await waitForAll([]);
    
  1131. 
    
  1132.         expect(clearedMarks).toMatchInlineSnapshot(`
    
  1133.           [
    
  1134.             "--schedule-state-update-8-App",
    
  1135.             "--render-start-8",
    
  1136.             "--component-render-start-App",
    
  1137.             "--component-render-stop",
    
  1138.             "--render-stop",
    
  1139.             "--commit-start-8",
    
  1140.             "--react-version-<filtered-version>",
    
  1141.             "--profiler-version-1",
    
  1142.             "--react-internal-module-start-  at filtered (<anonymous>:0:0)",
    
  1143.             "--react-internal-module-stop-  at filtered (<anonymous>:1:1)",
    
  1144.             "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred",
    
  1145.             "--layout-effects-start-8",
    
  1146.             "--layout-effects-stop",
    
  1147.             "--commit-stop",
    
  1148.           ]
    
  1149.         `);
    
  1150.       });
    
  1151.     });
    
  1152.   });
    
  1153. 
    
  1154.   describe('DevTools hook (in memory)', () => {
    
  1155.     let getBatchOfWork;
    
  1156.     let stopProfilingAndGetTimelineData;
    
  1157. 
    
  1158.     beforeEach(() => {
    
  1159.       getBatchOfWork = index => {
    
  1160.         const timelineData = stopProfilingAndGetTimelineData();
    
  1161.         if (timelineData) {
    
  1162.           if (timelineData.batchUIDToMeasuresMap.size > index) {
    
  1163.             return Array.from(timelineData.batchUIDToMeasuresMap.values())[
    
  1164.               index
    
  1165.             ];
    
  1166.           }
    
  1167.         }
    
  1168. 
    
  1169.         return null;
    
  1170.       };
    
  1171. 
    
  1172.       stopProfilingAndGetTimelineData = () => {
    
  1173.         utils.act(() => store.profilerStore.stopProfiling());
    
  1174. 
    
  1175.         const timelineData = store.profilerStore.profilingData?.timelineData;
    
  1176. 
    
  1177.         if (timelineData) {
    
  1178.           expect(timelineData).toHaveLength(1);
    
  1179. 
    
  1180.           // normalize the location for component stack source
    
  1181.           // for snapshot testing
    
  1182.           timelineData.forEach(data => {
    
  1183.             data.schedulingEvents.forEach(event => {
    
  1184.               if (event.componentStack) {
    
  1185.                 event.componentStack = normalizeCodeLocInfo(
    
  1186.                   event.componentStack,
    
  1187.                 );
    
  1188.               }
    
  1189.             });
    
  1190.           });
    
  1191. 
    
  1192.           return timelineData[0];
    
  1193.         } else {
    
  1194.           return null;
    
  1195.         }
    
  1196.       };
    
  1197.     });
    
  1198. 
    
  1199.     afterEach(() => {
    
  1200.       unmountFns.forEach(unmountFn => unmountFn());
    
  1201.     });
    
  1202. 
    
  1203.     describe('when profiling', () => {
    
  1204.       beforeEach(() => {
    
  1205.         utils.act(() => store.profilerStore.startProfiling());
    
  1206.       });
    
  1207. 
    
  1208.       it('should mark sync render without suspends or state updates', () => {
    
  1209.         renderHelper(<div />);
    
  1210. 
    
  1211.         const timelineData = stopProfilingAndGetTimelineData();
    
  1212.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1213.           [
    
  1214.             {
    
  1215.               "lanes": "0b0000000000000000000000000000010",
    
  1216.               "timestamp": 10,
    
  1217.               "type": "schedule-render",
    
  1218.               "warning": null,
    
  1219.             },
    
  1220.           ]
    
  1221.         `);
    
  1222.       });
    
  1223. 
    
  1224.       it('should mark concurrent render without suspends or state updates', () => {
    
  1225.         utils.act(() => renderRootHelper(<div />));
    
  1226. 
    
  1227.         const timelineData = stopProfilingAndGetTimelineData();
    
  1228.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1229.           [
    
  1230.             {
    
  1231.               "lanes": "0b0000000000000000000000000100000",
    
  1232.               "timestamp": 10,
    
  1233.               "type": "schedule-render",
    
  1234.               "warning": null,
    
  1235.             },
    
  1236.           ]
    
  1237.         `);
    
  1238.       });
    
  1239. 
    
  1240.       it('should mark concurrent render without suspends or state updates', () => {
    
  1241.         let updaterFn;
    
  1242. 
    
  1243.         function Example() {
    
  1244.           const setHigh = React.useState(0)[1];
    
  1245.           const setLow = React.useState(0)[1];
    
  1246. 
    
  1247.           updaterFn = () => {
    
  1248.             React.startTransition(() => {
    
  1249.               setLow(prevLow => prevLow + 1);
    
  1250.             });
    
  1251.             setHigh(prevHigh => prevHigh + 1);
    
  1252.           };
    
  1253. 
    
  1254.           Scheduler.unstable_advanceTime(10);
    
  1255. 
    
  1256.           return null;
    
  1257.         }
    
  1258. 
    
  1259.         utils.act(() => renderRootHelper(<Example />));
    
  1260.         utils.act(() => store.profilerStore.stopProfiling());
    
  1261.         utils.act(() => store.profilerStore.startProfiling());
    
  1262.         utils.act(updaterFn);
    
  1263. 
    
  1264.         const timelineData = stopProfilingAndGetTimelineData();
    
  1265.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1266.           [
    
  1267.             {
    
  1268.               "componentName": "Example",
    
  1269.               "componentStack": "
    
  1270.               in Example (at **)",
    
  1271.               "lanes": "0b0000000000000000000000010000000",
    
  1272.               "timestamp": 10,
    
  1273.               "type": "schedule-state-update",
    
  1274.               "warning": null,
    
  1275.             },
    
  1276.             {
    
  1277.               "componentName": "Example",
    
  1278.               "componentStack": "
    
  1279.               in Example (at **)",
    
  1280.               "lanes": "0b0000000000000000000000000100000",
    
  1281.               "timestamp": 10,
    
  1282.               "type": "schedule-state-update",
    
  1283.               "warning": null,
    
  1284.             },
    
  1285.           ]
    
  1286.         `);
    
  1287.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1288.           [
    
  1289.             {
    
  1290.               "componentName": "Example",
    
  1291.               "duration": 0,
    
  1292.               "timestamp": 10,
    
  1293.               "type": "render",
    
  1294.               "warning": null,
    
  1295.             },
    
  1296.             {
    
  1297.               "componentName": "Example",
    
  1298.               "duration": 10,
    
  1299.               "timestamp": 10,
    
  1300.               "type": "render",
    
  1301.               "warning": null,
    
  1302.             },
    
  1303.           ]
    
  1304.         `);
    
  1305.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1306.       });
    
  1307. 
    
  1308.       // @reactVersion >=18.2
    
  1309.       it('should mark render yields', async () => {
    
  1310.         function Bar() {
    
  1311.           Scheduler.log('Bar');
    
  1312.           return null;
    
  1313.         }
    
  1314. 
    
  1315.         function Foo() {
    
  1316.           Scheduler.log('Foo');
    
  1317.           return <Bar />;
    
  1318.         }
    
  1319. 
    
  1320.         React.startTransition(() => {
    
  1321.           renderRootHelper(<Foo />);
    
  1322.         });
    
  1323. 
    
  1324.         // Do one step of work.
    
  1325.         await waitFor(['Foo']);
    
  1326. 
    
  1327.         // Finish flushing so React commits;
    
  1328.         // Unless we do this, the ProfilerStore won't collect Profiling data.
    
  1329.         await waitForAll(['Bar']);
    
  1330. 
    
  1331.         // Since we yielded, the batch should report two separate "render" chunks.
    
  1332.         const batch = getBatchOfWork(0);
    
  1333.         expect(batch.filter(({type}) => type === 'render')).toHaveLength(2);
    
  1334.       });
    
  1335. 
    
  1336.       it('should mark sync render with suspense that resolves', async () => {
    
  1337.         let resolveFn;
    
  1338.         let resolved = false;
    
  1339.         const suspensePromise = new Promise(resolve => {
    
  1340.           resolveFn = () => {
    
  1341.             resolved = true;
    
  1342.             resolve();
    
  1343.           };
    
  1344.         });
    
  1345. 
    
  1346.         function Example() {
    
  1347.           Scheduler.log(resolved ? 'resolved' : 'suspended');
    
  1348.           if (!resolved) {
    
  1349.             throw suspensePromise;
    
  1350.           }
    
  1351.           return null;
    
  1352.         }
    
  1353. 
    
  1354.         renderHelper(
    
  1355.           <React.Suspense fallback={null}>
    
  1356.             <Example />
    
  1357.           </React.Suspense>,
    
  1358.         );
    
  1359. 
    
  1360.         assertLog(['suspended']);
    
  1361. 
    
  1362.         Scheduler.unstable_advanceTime(10);
    
  1363.         resolveFn();
    
  1364.         await suspensePromise;
    
  1365. 
    
  1366.         await waitForAll(['resolved']);
    
  1367. 
    
  1368.         const timelineData = stopProfilingAndGetTimelineData();
    
  1369. 
    
  1370.         // Verify the Suspense event and duration was recorded.
    
  1371.         expect(timelineData.suspenseEvents).toHaveLength(1);
    
  1372.         const suspenseEvent = timelineData.suspenseEvents[0];
    
  1373.         expect(suspenseEvent).toMatchInlineSnapshot(`
    
  1374.           {
    
  1375.             "componentName": "Example",
    
  1376.             "depth": 0,
    
  1377.             "duration": 10,
    
  1378.             "id": "0",
    
  1379.             "phase": "mount",
    
  1380.             "promiseName": "",
    
  1381.             "resolution": "resolved",
    
  1382.             "timestamp": 10,
    
  1383.             "type": "suspense",
    
  1384.             "warning": null,
    
  1385.           }
    
  1386.         `);
    
  1387. 
    
  1388.         // There should be two batches of renders: Suspeneded and resolved.
    
  1389.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1390.         expect(timelineData.componentMeasures).toHaveLength(2);
    
  1391.       });
    
  1392. 
    
  1393.       // @reactVersion >=18.2
    
  1394.       it('should mark sync render with suspense that rejects', async () => {
    
  1395.         let rejectFn;
    
  1396.         let rejected = false;
    
  1397.         const suspensePromise = new Promise((resolve, reject) => {
    
  1398.           rejectFn = () => {
    
  1399.             rejected = true;
    
  1400.             reject(new Error('error'));
    
  1401.           };
    
  1402.         });
    
  1403. 
    
  1404.         function Example() {
    
  1405.           Scheduler.log(rejected ? 'rejected' : 'suspended');
    
  1406.           if (!rejected) {
    
  1407.             throw suspensePromise;
    
  1408.           }
    
  1409.           return null;
    
  1410.         }
    
  1411. 
    
  1412.         renderHelper(
    
  1413.           <React.Suspense fallback={null}>
    
  1414.             <Example />
    
  1415.           </React.Suspense>,
    
  1416.         );
    
  1417. 
    
  1418.         assertLog(['suspended']);
    
  1419. 
    
  1420.         Scheduler.unstable_advanceTime(10);
    
  1421.         rejectFn();
    
  1422.         await expect(suspensePromise).rejects.toThrow();
    
  1423. 
    
  1424.         assertLog(['rejected']);
    
  1425. 
    
  1426.         const timelineData = stopProfilingAndGetTimelineData();
    
  1427. 
    
  1428.         // Verify the Suspense event and duration was recorded.
    
  1429.         expect(timelineData.suspenseEvents).toHaveLength(1);
    
  1430.         const suspenseEvent = timelineData.suspenseEvents[0];
    
  1431.         expect(suspenseEvent).toMatchInlineSnapshot(`
    
  1432.           {
    
  1433.             "componentName": "Example",
    
  1434.             "depth": 0,
    
  1435.             "duration": 10,
    
  1436.             "id": "0",
    
  1437.             "phase": "mount",
    
  1438.             "promiseName": "",
    
  1439.             "resolution": "rejected",
    
  1440.             "timestamp": 10,
    
  1441.             "type": "suspense",
    
  1442.             "warning": null,
    
  1443.           }
    
  1444.         `);
    
  1445. 
    
  1446.         // There should be two batches of renders: Suspeneded and resolved.
    
  1447.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1448.         expect(timelineData.componentMeasures).toHaveLength(2);
    
  1449.       });
    
  1450. 
    
  1451.       // @reactVersion >=18.2
    
  1452.       it('should mark concurrent render with suspense that resolves', async () => {
    
  1453.         let resolveFn;
    
  1454.         let resolved = false;
    
  1455.         const suspensePromise = new Promise(resolve => {
    
  1456.           resolveFn = () => {
    
  1457.             resolved = true;
    
  1458.             resolve();
    
  1459.           };
    
  1460.         });
    
  1461. 
    
  1462.         function Example() {
    
  1463.           Scheduler.log(resolved ? 'resolved' : 'suspended');
    
  1464.           if (!resolved) {
    
  1465.             throw suspensePromise;
    
  1466.           }
    
  1467.           return null;
    
  1468.         }
    
  1469. 
    
  1470.         renderRootHelper(
    
  1471.           <React.Suspense fallback={null}>
    
  1472.             <Example />
    
  1473.           </React.Suspense>,
    
  1474.         );
    
  1475. 
    
  1476.         await waitForAll(['suspended']);
    
  1477. 
    
  1478.         Scheduler.unstable_advanceTime(10);
    
  1479.         resolveFn();
    
  1480.         await suspensePromise;
    
  1481. 
    
  1482.         await waitForAll(['resolved']);
    
  1483. 
    
  1484.         const timelineData = stopProfilingAndGetTimelineData();
    
  1485. 
    
  1486.         // Verify the Suspense event and duration was recorded.
    
  1487.         expect(timelineData.suspenseEvents).toHaveLength(1);
    
  1488.         const suspenseEvent = timelineData.suspenseEvents[0];
    
  1489.         expect(suspenseEvent).toMatchInlineSnapshot(`
    
  1490.           {
    
  1491.             "componentName": "Example",
    
  1492.             "depth": 0,
    
  1493.             "duration": 10,
    
  1494.             "id": "0",
    
  1495.             "phase": "mount",
    
  1496.             "promiseName": "",
    
  1497.             "resolution": "resolved",
    
  1498.             "timestamp": 10,
    
  1499.             "type": "suspense",
    
  1500.             "warning": null,
    
  1501.           }
    
  1502.         `);
    
  1503. 
    
  1504.         // There should be two batches of renders: Suspeneded and resolved.
    
  1505.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1506.         expect(timelineData.componentMeasures).toHaveLength(2);
    
  1507.       });
    
  1508. 
    
  1509.       // @reactVersion >=18.2
    
  1510.       it('should mark concurrent render with suspense that rejects', async () => {
    
  1511.         let rejectFn;
    
  1512.         let rejected = false;
    
  1513.         const suspensePromise = new Promise((resolve, reject) => {
    
  1514.           rejectFn = () => {
    
  1515.             rejected = true;
    
  1516.             reject(new Error('error'));
    
  1517.           };
    
  1518.         });
    
  1519. 
    
  1520.         function Example() {
    
  1521.           Scheduler.log(rejected ? 'rejected' : 'suspended');
    
  1522.           if (!rejected) {
    
  1523.             throw suspensePromise;
    
  1524.           }
    
  1525.           return null;
    
  1526.         }
    
  1527. 
    
  1528.         renderRootHelper(
    
  1529.           <React.Suspense fallback={null}>
    
  1530.             <Example />
    
  1531.           </React.Suspense>,
    
  1532.         );
    
  1533. 
    
  1534.         await waitForAll(['suspended']);
    
  1535. 
    
  1536.         Scheduler.unstable_advanceTime(10);
    
  1537.         rejectFn();
    
  1538.         await expect(suspensePromise).rejects.toThrow();
    
  1539. 
    
  1540.         await waitForAll(['rejected']);
    
  1541. 
    
  1542.         const timelineData = stopProfilingAndGetTimelineData();
    
  1543. 
    
  1544.         // Verify the Suspense event and duration was recorded.
    
  1545.         expect(timelineData.suspenseEvents).toHaveLength(1);
    
  1546.         const suspenseEvent = timelineData.suspenseEvents[0];
    
  1547.         expect(suspenseEvent).toMatchInlineSnapshot(`
    
  1548.           {
    
  1549.             "componentName": "Example",
    
  1550.             "depth": 0,
    
  1551.             "duration": 10,
    
  1552.             "id": "0",
    
  1553.             "phase": "mount",
    
  1554.             "promiseName": "",
    
  1555.             "resolution": "rejected",
    
  1556.             "timestamp": 10,
    
  1557.             "type": "suspense",
    
  1558.             "warning": null,
    
  1559.           }
    
  1560.         `);
    
  1561. 
    
  1562.         // There should be two batches of renders: Suspeneded and resolved.
    
  1563.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1564.         expect(timelineData.componentMeasures).toHaveLength(2);
    
  1565.       });
    
  1566. 
    
  1567.       it('should mark cascading class component state updates', async () => {
    
  1568.         class Example extends React.Component {
    
  1569.           state = {didMount: false};
    
  1570.           componentDidMount() {
    
  1571.             this.setState({didMount: true});
    
  1572.           }
    
  1573.           render() {
    
  1574.             Scheduler.unstable_advanceTime(10);
    
  1575.             Scheduler.log(this.state.didMount ? 'update' : 'mount');
    
  1576.             return null;
    
  1577.           }
    
  1578.         }
    
  1579. 
    
  1580.         renderRootHelper(<Example />);
    
  1581. 
    
  1582.         await waitForPaint(['mount', 'update']);
    
  1583. 
    
  1584.         const timelineData = stopProfilingAndGetTimelineData();
    
  1585.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1586.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1587.           [
    
  1588.             {
    
  1589.               "componentName": "Example",
    
  1590.               "duration": 10,
    
  1591.               "timestamp": 10,
    
  1592.               "type": "render",
    
  1593.               "warning": null,
    
  1594.             },
    
  1595.             {
    
  1596.               "componentName": "Example",
    
  1597.               "duration": 10,
    
  1598.               "timestamp": 20,
    
  1599.               "type": "render",
    
  1600.               "warning": null,
    
  1601.             },
    
  1602.           ]
    
  1603.         `);
    
  1604.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1605.           [
    
  1606.             {
    
  1607.               "lanes": "0b0000000000000000000000000100000",
    
  1608.               "timestamp": 10,
    
  1609.               "type": "schedule-render",
    
  1610.               "warning": null,
    
  1611.             },
    
  1612.             {
    
  1613.               "componentName": "Example",
    
  1614.               "componentStack": "
    
  1615.               in Example (at **)",
    
  1616.               "lanes": "0b0000000000000000000000000000010",
    
  1617.               "timestamp": 20,
    
  1618.               "type": "schedule-state-update",
    
  1619.               "warning": null,
    
  1620.             },
    
  1621.           ]
    
  1622.         `);
    
  1623.       });
    
  1624. 
    
  1625.       it('should mark cascading class component force updates', async () => {
    
  1626.         let forced = false;
    
  1627.         class Example extends React.Component {
    
  1628.           componentDidMount() {
    
  1629.             forced = true;
    
  1630.             this.forceUpdate();
    
  1631.           }
    
  1632.           render() {
    
  1633.             Scheduler.unstable_advanceTime(10);
    
  1634.             Scheduler.log(forced ? 'force update' : 'mount');
    
  1635.             return null;
    
  1636.           }
    
  1637.         }
    
  1638. 
    
  1639.         renderRootHelper(<Example />);
    
  1640. 
    
  1641.         await waitForPaint(['mount', 'force update']);
    
  1642. 
    
  1643.         const timelineData = stopProfilingAndGetTimelineData();
    
  1644.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1645.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1646.           [
    
  1647.             {
    
  1648.               "componentName": "Example",
    
  1649.               "duration": 10,
    
  1650.               "timestamp": 10,
    
  1651.               "type": "render",
    
  1652.               "warning": null,
    
  1653.             },
    
  1654.             {
    
  1655.               "componentName": "Example",
    
  1656.               "duration": 10,
    
  1657.               "timestamp": 20,
    
  1658.               "type": "render",
    
  1659.               "warning": null,
    
  1660.             },
    
  1661.           ]
    
  1662.         `);
    
  1663.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1664.           [
    
  1665.             {
    
  1666.               "lanes": "0b0000000000000000000000000100000",
    
  1667.               "timestamp": 10,
    
  1668.               "type": "schedule-render",
    
  1669.               "warning": null,
    
  1670.             },
    
  1671.             {
    
  1672.               "componentName": "Example",
    
  1673.               "lanes": "0b0000000000000000000000000000010",
    
  1674.               "timestamp": 20,
    
  1675.               "type": "schedule-force-update",
    
  1676.               "warning": null,
    
  1677.             },
    
  1678.           ]
    
  1679.         `);
    
  1680.       });
    
  1681. 
    
  1682.       it('should mark render phase state updates for class component', async () => {
    
  1683.         class Example extends React.Component {
    
  1684.           state = {didRender: false};
    
  1685.           render() {
    
  1686.             if (this.state.didRender === false) {
    
  1687.               this.setState({didRender: true});
    
  1688.             }
    
  1689.             Scheduler.unstable_advanceTime(10);
    
  1690.             Scheduler.log(
    
  1691.               this.state.didRender ? 'second render' : 'first render',
    
  1692.             );
    
  1693.             return null;
    
  1694.           }
    
  1695.         }
    
  1696. 
    
  1697.         renderRootHelper(<Example />);
    
  1698. 
    
  1699.         let errorMessage;
    
  1700.         jest.spyOn(console, 'error').mockImplementation(message => {
    
  1701.           errorMessage = message;
    
  1702.         });
    
  1703. 
    
  1704.         await waitForAll(['first render', 'second render']);
    
  1705. 
    
  1706.         expect(console.error).toHaveBeenCalledTimes(1);
    
  1707.         expect(errorMessage).toContain(
    
  1708.           'Cannot update during an existing state transition',
    
  1709.         );
    
  1710. 
    
  1711.         const timelineData = stopProfilingAndGetTimelineData();
    
  1712.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1713.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1714.           [
    
  1715.             {
    
  1716.               "componentName": "Example",
    
  1717.               "duration": 10,
    
  1718.               "timestamp": 10,
    
  1719.               "type": "render",
    
  1720.               "warning": null,
    
  1721.             },
    
  1722.             {
    
  1723.               "componentName": "Example",
    
  1724.               "duration": 10,
    
  1725.               "timestamp": 20,
    
  1726.               "type": "render",
    
  1727.               "warning": null,
    
  1728.             },
    
  1729.           ]
    
  1730.         `);
    
  1731.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1732.           [
    
  1733.             {
    
  1734.               "lanes": "0b0000000000000000000000000100000",
    
  1735.               "timestamp": 10,
    
  1736.               "type": "schedule-render",
    
  1737.               "warning": null,
    
  1738.             },
    
  1739.             {
    
  1740.               "componentName": "Example",
    
  1741.               "componentStack": "
    
  1742.               in Example (at **)",
    
  1743.               "lanes": "0b0000000000000000000000000100000",
    
  1744.               "timestamp": 10,
    
  1745.               "type": "schedule-state-update",
    
  1746.               "warning": null,
    
  1747.             },
    
  1748.           ]
    
  1749.         `);
    
  1750.       });
    
  1751. 
    
  1752.       it('should mark render phase force updates for class component', async () => {
    
  1753.         let forced = false;
    
  1754.         class Example extends React.Component {
    
  1755.           render() {
    
  1756.             Scheduler.unstable_advanceTime(10);
    
  1757.             Scheduler.log(forced ? 'force update' : 'render');
    
  1758.             if (!forced) {
    
  1759.               forced = true;
    
  1760.               this.forceUpdate();
    
  1761.             }
    
  1762.             return null;
    
  1763.           }
    
  1764.         }
    
  1765. 
    
  1766.         renderRootHelper(<Example />);
    
  1767. 
    
  1768.         let errorMessage;
    
  1769.         jest.spyOn(console, 'error').mockImplementation(message => {
    
  1770.           errorMessage = message;
    
  1771.         });
    
  1772. 
    
  1773.         await waitForAll(['render', 'force update']);
    
  1774. 
    
  1775.         expect(console.error).toHaveBeenCalledTimes(1);
    
  1776.         expect(errorMessage).toContain(
    
  1777.           'Cannot update during an existing state transition',
    
  1778.         );
    
  1779. 
    
  1780.         const timelineData = stopProfilingAndGetTimelineData();
    
  1781.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1782.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1783.           [
    
  1784.             {
    
  1785.               "componentName": "Example",
    
  1786.               "duration": 10,
    
  1787.               "timestamp": 10,
    
  1788.               "type": "render",
    
  1789.               "warning": null,
    
  1790.             },
    
  1791.             {
    
  1792.               "componentName": "Example",
    
  1793.               "duration": 10,
    
  1794.               "timestamp": 20,
    
  1795.               "type": "render",
    
  1796.               "warning": null,
    
  1797.             },
    
  1798.           ]
    
  1799.         `);
    
  1800.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1801.           [
    
  1802.             {
    
  1803.               "lanes": "0b0000000000000000000000000100000",
    
  1804.               "timestamp": 10,
    
  1805.               "type": "schedule-render",
    
  1806.               "warning": null,
    
  1807.             },
    
  1808.             {
    
  1809.               "componentName": "Example",
    
  1810.               "lanes": "0b0000000000000000000000000100000",
    
  1811.               "timestamp": 20,
    
  1812.               "type": "schedule-force-update",
    
  1813.               "warning": null,
    
  1814.             },
    
  1815.           ]
    
  1816.         `);
    
  1817.       });
    
  1818. 
    
  1819.       it('should mark cascading layout updates', async () => {
    
  1820.         function Example() {
    
  1821.           const [didMount, setDidMount] = React.useState(false);
    
  1822.           React.useLayoutEffect(() => {
    
  1823.             Scheduler.unstable_advanceTime(1);
    
  1824.             setDidMount(true);
    
  1825.           }, []);
    
  1826.           Scheduler.unstable_advanceTime(10);
    
  1827.           Scheduler.log(didMount ? 'update' : 'mount');
    
  1828.           return didMount;
    
  1829.         }
    
  1830. 
    
  1831.         renderRootHelper(<Example />);
    
  1832. 
    
  1833.         await waitForAll(['mount', 'update']);
    
  1834. 
    
  1835.         const timelineData = stopProfilingAndGetTimelineData();
    
  1836.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1837.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1838.           [
    
  1839.             {
    
  1840.               "componentName": "Example",
    
  1841.               "duration": 10,
    
  1842.               "timestamp": 10,
    
  1843.               "type": "render",
    
  1844.               "warning": null,
    
  1845.             },
    
  1846.             {
    
  1847.               "componentName": "Example",
    
  1848.               "duration": 1,
    
  1849.               "timestamp": 20,
    
  1850.               "type": "layout-effect-mount",
    
  1851.               "warning": null,
    
  1852.             },
    
  1853.             {
    
  1854.               "componentName": "Example",
    
  1855.               "duration": 10,
    
  1856.               "timestamp": 21,
    
  1857.               "type": "render",
    
  1858.               "warning": null,
    
  1859.             },
    
  1860.           ]
    
  1861.         `);
    
  1862.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1863.           [
    
  1864.             {
    
  1865.               "lanes": "0b0000000000000000000000000100000",
    
  1866.               "timestamp": 10,
    
  1867.               "type": "schedule-render",
    
  1868.               "warning": null,
    
  1869.             },
    
  1870.             {
    
  1871.               "componentName": "Example",
    
  1872.               "componentStack": "
    
  1873.               in Example (at **)",
    
  1874.               "lanes": "0b0000000000000000000000000000010",
    
  1875.               "timestamp": 21,
    
  1876.               "type": "schedule-state-update",
    
  1877.               "warning": null,
    
  1878.             },
    
  1879.           ]
    
  1880.         `);
    
  1881.       });
    
  1882. 
    
  1883.       it('should mark cascading passive updates', async () => {
    
  1884.         function Example() {
    
  1885.           const [didMount, setDidMount] = React.useState(false);
    
  1886.           React.useEffect(() => {
    
  1887.             Scheduler.unstable_advanceTime(1);
    
  1888.             setDidMount(true);
    
  1889.           }, []);
    
  1890.           Scheduler.unstable_advanceTime(10);
    
  1891.           Scheduler.log(didMount ? 'update' : 'mount');
    
  1892.           return didMount;
    
  1893.         }
    
  1894. 
    
  1895.         renderRootHelper(<Example />);
    
  1896.         await waitForAll(['mount', 'update']);
    
  1897. 
    
  1898.         const timelineData = stopProfilingAndGetTimelineData();
    
  1899.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(2);
    
  1900.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1901.           [
    
  1902.             {
    
  1903.               "componentName": "Example",
    
  1904.               "duration": 10,
    
  1905.               "timestamp": 10,
    
  1906.               "type": "render",
    
  1907.               "warning": null,
    
  1908.             },
    
  1909.             {
    
  1910.               "componentName": "Example",
    
  1911.               "duration": 1,
    
  1912.               "timestamp": 20,
    
  1913.               "type": "passive-effect-mount",
    
  1914.               "warning": null,
    
  1915.             },
    
  1916.             {
    
  1917.               "componentName": "Example",
    
  1918.               "duration": 10,
    
  1919.               "timestamp": 21,
    
  1920.               "type": "render",
    
  1921.               "warning": null,
    
  1922.             },
    
  1923.           ]
    
  1924.         `);
    
  1925.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1926.           [
    
  1927.             {
    
  1928.               "lanes": "0b0000000000000000000000000100000",
    
  1929.               "timestamp": 10,
    
  1930.               "type": "schedule-render",
    
  1931.               "warning": null,
    
  1932.             },
    
  1933.             {
    
  1934.               "componentName": "Example",
    
  1935.               "componentStack": "
    
  1936.               in Example (at **)",
    
  1937.               "lanes": "0b0000000000000000000000000100000",
    
  1938.               "timestamp": 21,
    
  1939.               "type": "schedule-state-update",
    
  1940.               "warning": null,
    
  1941.             },
    
  1942.           ]
    
  1943.         `);
    
  1944.       });
    
  1945. 
    
  1946.       it('should mark render phase updates', async () => {
    
  1947.         function Example() {
    
  1948.           const [didRender, setDidRender] = React.useState(false);
    
  1949.           Scheduler.unstable_advanceTime(10);
    
  1950.           if (!didRender) {
    
  1951.             setDidRender(true);
    
  1952.           }
    
  1953.           Scheduler.log(didRender ? 'update' : 'mount');
    
  1954.           return didRender;
    
  1955.         }
    
  1956. 
    
  1957.         renderRootHelper(<Example />);
    
  1958.         await waitForAll(['mount', 'update']);
    
  1959. 
    
  1960.         const timelineData = stopProfilingAndGetTimelineData();
    
  1961.         // Render phase updates should be retried as part of the same batch.
    
  1962.         expect(timelineData.batchUIDToMeasuresMap.size).toBe(1);
    
  1963.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  1964.           [
    
  1965.             {
    
  1966.               "componentName": "Example",
    
  1967.               "duration": 20,
    
  1968.               "timestamp": 10,
    
  1969.               "type": "render",
    
  1970.               "warning": null,
    
  1971.             },
    
  1972.           ]
    
  1973.         `);
    
  1974.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  1975.           [
    
  1976.             {
    
  1977.               "lanes": "0b0000000000000000000000000100000",
    
  1978.               "timestamp": 10,
    
  1979.               "type": "schedule-render",
    
  1980.               "warning": null,
    
  1981.             },
    
  1982.             {
    
  1983.               "componentName": "Example",
    
  1984.               "componentStack": "
    
  1985.               in Example (at **)",
    
  1986.               "lanes": "0b0000000000000000000000000100000",
    
  1987.               "timestamp": 20,
    
  1988.               "type": "schedule-state-update",
    
  1989.               "warning": null,
    
  1990.             },
    
  1991.           ]
    
  1992.         `);
    
  1993.       });
    
  1994. 
    
  1995.       it('should mark sync render that throws', async () => {
    
  1996.         jest.spyOn(console, 'error').mockImplementation(() => {});
    
  1997. 
    
  1998.         class ErrorBoundary extends React.Component {
    
  1999.           state = {error: null};
    
  2000.           componentDidCatch(error) {
    
  2001.             this.setState({error});
    
  2002.           }
    
  2003.           render() {
    
  2004.             Scheduler.unstable_advanceTime(10);
    
  2005.             if (this.state.error) {
    
  2006.               Scheduler.log('ErrorBoundary fallback');
    
  2007.               return null;
    
  2008.             }
    
  2009.             Scheduler.log('ErrorBoundary render');
    
  2010.             return this.props.children;
    
  2011.           }
    
  2012.         }
    
  2013. 
    
  2014.         function ExampleThatThrows() {
    
  2015.           Scheduler.log('ExampleThatThrows');
    
  2016.           throw Error('Expected error');
    
  2017.         }
    
  2018. 
    
  2019.         renderHelper(
    
  2020.           <ErrorBoundary>
    
  2021.             <ExampleThatThrows />
    
  2022.           </ErrorBoundary>,
    
  2023.         );
    
  2024. 
    
  2025.         assertLog([
    
  2026.           'ErrorBoundary render',
    
  2027.           'ExampleThatThrows',
    
  2028.           'ExampleThatThrows',
    
  2029.           'ErrorBoundary fallback',
    
  2030.         ]);
    
  2031. 
    
  2032.         const timelineData = stopProfilingAndGetTimelineData();
    
  2033.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  2034.           [
    
  2035.             {
    
  2036.               "componentName": "ErrorBoundary",
    
  2037.               "duration": 10,
    
  2038.               "timestamp": 10,
    
  2039.               "type": "render",
    
  2040.               "warning": null,
    
  2041.             },
    
  2042.             {
    
  2043.               "componentName": "ExampleThatThrows",
    
  2044.               "duration": 0,
    
  2045.               "timestamp": 20,
    
  2046.               "type": "render",
    
  2047.               "warning": null,
    
  2048.             },
    
  2049.             {
    
  2050.               "componentName": "ErrorBoundary",
    
  2051.               "duration": 10,
    
  2052.               "timestamp": 20,
    
  2053.               "type": "render",
    
  2054.               "warning": null,
    
  2055.             },
    
  2056.           ]
    
  2057.         `);
    
  2058.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  2059.           [
    
  2060.             {
    
  2061.               "lanes": "0b0000000000000000000000000000010",
    
  2062.               "timestamp": 10,
    
  2063.               "type": "schedule-render",
    
  2064.               "warning": null,
    
  2065.             },
    
  2066.             {
    
  2067.               "componentName": "ErrorBoundary",
    
  2068.               "componentStack": "
    
  2069.               in ErrorBoundary (at **)",
    
  2070.               "lanes": "0b0000000000000000000000000000010",
    
  2071.               "timestamp": 20,
    
  2072.               "type": "schedule-state-update",
    
  2073.               "warning": null,
    
  2074.             },
    
  2075.           ]
    
  2076.         `);
    
  2077.         expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
    
  2078.           [
    
  2079.             {
    
  2080.               "componentName": "ExampleThatThrows",
    
  2081.               "message": "Expected error",
    
  2082.               "phase": "mount",
    
  2083.               "timestamp": 20,
    
  2084.               "type": "thrown-error",
    
  2085.             },
    
  2086.           ]
    
  2087.         `);
    
  2088.       });
    
  2089. 
    
  2090.       it('should mark concurrent render that throws', async () => {
    
  2091.         jest.spyOn(console, 'error').mockImplementation(() => {});
    
  2092. 
    
  2093.         class ErrorBoundary extends React.Component {
    
  2094.           state = {error: null};
    
  2095.           componentDidCatch(error) {
    
  2096.             this.setState({error});
    
  2097.           }
    
  2098.           render() {
    
  2099.             Scheduler.unstable_advanceTime(10);
    
  2100.             if (this.state.error) {
    
  2101.               Scheduler.log('ErrorBoundary fallback');
    
  2102.               return null;
    
  2103.             }
    
  2104.             Scheduler.log('ErrorBoundary render');
    
  2105.             return this.props.children;
    
  2106.           }
    
  2107.         }
    
  2108. 
    
  2109.         function ExampleThatThrows() {
    
  2110.           Scheduler.log('ExampleThatThrows');
    
  2111.           // eslint-disable-next-line no-throw-literal
    
  2112.           throw 'Expected error';
    
  2113.         }
    
  2114. 
    
  2115.         renderRootHelper(
    
  2116.           <ErrorBoundary>
    
  2117.             <ExampleThatThrows />
    
  2118.           </ErrorBoundary>,
    
  2119.         );
    
  2120. 
    
  2121.         await waitForAll([
    
  2122.           'ErrorBoundary render',
    
  2123.           'ExampleThatThrows',
    
  2124.           'ExampleThatThrows',
    
  2125.           'ErrorBoundary render',
    
  2126.           'ExampleThatThrows',
    
  2127.           'ExampleThatThrows',
    
  2128.           'ErrorBoundary fallback',
    
  2129.         ]);
    
  2130. 
    
  2131.         const timelineData = stopProfilingAndGetTimelineData();
    
  2132.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  2133.           [
    
  2134.             {
    
  2135.               "componentName": "ErrorBoundary",
    
  2136.               "duration": 10,
    
  2137.               "timestamp": 10,
    
  2138.               "type": "render",
    
  2139.               "warning": null,
    
  2140.             },
    
  2141.             {
    
  2142.               "componentName": "ExampleThatThrows",
    
  2143.               "duration": 0,
    
  2144.               "timestamp": 20,
    
  2145.               "type": "render",
    
  2146.               "warning": null,
    
  2147.             },
    
  2148.             {
    
  2149.               "componentName": "ErrorBoundary",
    
  2150.               "duration": 10,
    
  2151.               "timestamp": 20,
    
  2152.               "type": "render",
    
  2153.               "warning": null,
    
  2154.             },
    
  2155.             {
    
  2156.               "componentName": "ExampleThatThrows",
    
  2157.               "duration": 0,
    
  2158.               "timestamp": 30,
    
  2159.               "type": "render",
    
  2160.               "warning": null,
    
  2161.             },
    
  2162.             {
    
  2163.               "componentName": "ErrorBoundary",
    
  2164.               "duration": 10,
    
  2165.               "timestamp": 30,
    
  2166.               "type": "render",
    
  2167.               "warning": null,
    
  2168.             },
    
  2169.           ]
    
  2170.         `);
    
  2171.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  2172.           [
    
  2173.             {
    
  2174.               "lanes": "0b0000000000000000000000000100000",
    
  2175.               "timestamp": 10,
    
  2176.               "type": "schedule-render",
    
  2177.               "warning": null,
    
  2178.             },
    
  2179.             {
    
  2180.               "componentName": "ErrorBoundary",
    
  2181.               "componentStack": "
    
  2182.               in ErrorBoundary (at **)",
    
  2183.               "lanes": "0b0000000000000000000000000000010",
    
  2184.               "timestamp": 30,
    
  2185.               "type": "schedule-state-update",
    
  2186.               "warning": null,
    
  2187.             },
    
  2188.           ]
    
  2189.         `);
    
  2190.         expect(timelineData.thrownErrors).toMatchInlineSnapshot(`
    
  2191.           [
    
  2192.             {
    
  2193.               "componentName": "ExampleThatThrows",
    
  2194.               "message": "Expected error",
    
  2195.               "phase": "mount",
    
  2196.               "timestamp": 20,
    
  2197.               "type": "thrown-error",
    
  2198.             },
    
  2199.             {
    
  2200.               "componentName": "ExampleThatThrows",
    
  2201.               "message": "Expected error",
    
  2202.               "phase": "mount",
    
  2203.               "timestamp": 30,
    
  2204.               "type": "thrown-error",
    
  2205.             },
    
  2206.           ]
    
  2207.         `);
    
  2208.       });
    
  2209. 
    
  2210.       it('should mark passive and layout effects', async () => {
    
  2211.         function ComponentWithEffects() {
    
  2212.           React.useLayoutEffect(() => {
    
  2213.             Scheduler.log('layout 1 mount');
    
  2214.             return () => {
    
  2215.               Scheduler.log('layout 1 unmount');
    
  2216.             };
    
  2217.           }, []);
    
  2218. 
    
  2219.           React.useEffect(() => {
    
  2220.             Scheduler.log('passive 1 mount');
    
  2221.             return () => {
    
  2222.               Scheduler.log('passive 1 unmount');
    
  2223.             };
    
  2224.           }, []);
    
  2225. 
    
  2226.           React.useLayoutEffect(() => {
    
  2227.             Scheduler.log('layout 2 mount');
    
  2228.             return () => {
    
  2229.               Scheduler.log('layout 2 unmount');
    
  2230.             };
    
  2231.           }, []);
    
  2232. 
    
  2233.           React.useEffect(() => {
    
  2234.             Scheduler.log('passive 2 mount');
    
  2235.             return () => {
    
  2236.               Scheduler.log('passive 2 unmount');
    
  2237.             };
    
  2238.           }, []);
    
  2239. 
    
  2240.           React.useEffect(() => {
    
  2241.             Scheduler.log('passive 3 mount');
    
  2242.             return () => {
    
  2243.               Scheduler.log('passive 3 unmount');
    
  2244.             };
    
  2245.           }, []);
    
  2246. 
    
  2247.           return null;
    
  2248.         }
    
  2249. 
    
  2250.         const unmount = renderRootHelper(<ComponentWithEffects />);
    
  2251. 
    
  2252.         await waitForPaint(['layout 1 mount', 'layout 2 mount']);
    
  2253. 
    
  2254.         await waitForAll([
    
  2255.           'passive 1 mount',
    
  2256.           'passive 2 mount',
    
  2257.           'passive 3 mount',
    
  2258.         ]);
    
  2259. 
    
  2260.         await waitForAll([]);
    
  2261. 
    
  2262.         unmount();
    
  2263. 
    
  2264.         assertLog([
    
  2265.           'layout 1 unmount',
    
  2266.           'layout 2 unmount',
    
  2267.           'passive 1 unmount',
    
  2268.           'passive 2 unmount',
    
  2269.           'passive 3 unmount',
    
  2270.         ]);
    
  2271. 
    
  2272.         const timelineData = stopProfilingAndGetTimelineData();
    
  2273.         expect(timelineData.componentMeasures).toMatchInlineSnapshot(`
    
  2274.           [
    
  2275.             {
    
  2276.               "componentName": "ComponentWithEffects",
    
  2277.               "duration": 0,
    
  2278.               "timestamp": 10,
    
  2279.               "type": "render",
    
  2280.               "warning": null,
    
  2281.             },
    
  2282.             {
    
  2283.               "componentName": "ComponentWithEffects",
    
  2284.               "duration": 0,
    
  2285.               "timestamp": 10,
    
  2286.               "type": "layout-effect-mount",
    
  2287.               "warning": null,
    
  2288.             },
    
  2289.             {
    
  2290.               "componentName": "ComponentWithEffects",
    
  2291.               "duration": 0,
    
  2292.               "timestamp": 10,
    
  2293.               "type": "layout-effect-mount",
    
  2294.               "warning": null,
    
  2295.             },
    
  2296.             {
    
  2297.               "componentName": "ComponentWithEffects",
    
  2298.               "duration": 0,
    
  2299.               "timestamp": 10,
    
  2300.               "type": "passive-effect-mount",
    
  2301.               "warning": null,
    
  2302.             },
    
  2303.             {
    
  2304.               "componentName": "ComponentWithEffects",
    
  2305.               "duration": 0,
    
  2306.               "timestamp": 10,
    
  2307.               "type": "passive-effect-mount",
    
  2308.               "warning": null,
    
  2309.             },
    
  2310.             {
    
  2311.               "componentName": "ComponentWithEffects",
    
  2312.               "duration": 0,
    
  2313.               "timestamp": 10,
    
  2314.               "type": "passive-effect-mount",
    
  2315.               "warning": null,
    
  2316.             },
    
  2317.             {
    
  2318.               "componentName": "ComponentWithEffects",
    
  2319.               "duration": 0,
    
  2320.               "timestamp": 10,
    
  2321.               "type": "layout-effect-unmount",
    
  2322.               "warning": null,
    
  2323.             },
    
  2324.             {
    
  2325.               "componentName": "ComponentWithEffects",
    
  2326.               "duration": 0,
    
  2327.               "timestamp": 10,
    
  2328.               "type": "layout-effect-unmount",
    
  2329.               "warning": null,
    
  2330.             },
    
  2331.             {
    
  2332.               "componentName": "ComponentWithEffects",
    
  2333.               "duration": 0,
    
  2334.               "timestamp": 10,
    
  2335.               "type": "passive-effect-unmount",
    
  2336.               "warning": null,
    
  2337.             },
    
  2338.             {
    
  2339.               "componentName": "ComponentWithEffects",
    
  2340.               "duration": 0,
    
  2341.               "timestamp": 10,
    
  2342.               "type": "passive-effect-unmount",
    
  2343.               "warning": null,
    
  2344.             },
    
  2345.             {
    
  2346.               "componentName": "ComponentWithEffects",
    
  2347.               "duration": 0,
    
  2348.               "timestamp": 10,
    
  2349.               "type": "passive-effect-unmount",
    
  2350.               "warning": null,
    
  2351.             },
    
  2352.           ]
    
  2353.         `);
    
  2354.         expect(timelineData.batchUIDToMeasuresMap).toMatchInlineSnapshot(`
    
  2355.           Map {
    
  2356.             1 => [
    
  2357.               {
    
  2358.                 "batchUID": 1,
    
  2359.                 "depth": 0,
    
  2360.                 "duration": 0,
    
  2361.                 "lanes": "0b0000000000000000000000000100000",
    
  2362.                 "timestamp": 10,
    
  2363.                 "type": "render-idle",
    
  2364.               },
    
  2365.               {
    
  2366.                 "batchUID": 1,
    
  2367.                 "depth": 0,
    
  2368.                 "duration": 0,
    
  2369.                 "lanes": "0b0000000000000000000000000100000",
    
  2370.                 "timestamp": 10,
    
  2371.                 "type": "render",
    
  2372.               },
    
  2373.               {
    
  2374.                 "batchUID": 1,
    
  2375.                 "depth": 0,
    
  2376.                 "duration": 0,
    
  2377.                 "lanes": "0b0000000000000000000000000100000",
    
  2378.                 "timestamp": 10,
    
  2379.                 "type": "commit",
    
  2380.               },
    
  2381.               {
    
  2382.                 "batchUID": 1,
    
  2383.                 "depth": 1,
    
  2384.                 "duration": 0,
    
  2385.                 "lanes": "0b0000000000000000000000000100000",
    
  2386.                 "timestamp": 10,
    
  2387.                 "type": "layout-effects",
    
  2388.               },
    
  2389.               {
    
  2390.                 "batchUID": 1,
    
  2391.                 "depth": 0,
    
  2392.                 "duration": 0,
    
  2393.                 "lanes": "0b0000000000000000000000000100000",
    
  2394.                 "timestamp": 10,
    
  2395.                 "type": "passive-effects",
    
  2396.               },
    
  2397.             ],
    
  2398.             2 => [
    
  2399.               {
    
  2400.                 "batchUID": 2,
    
  2401.                 "depth": 0,
    
  2402.                 "duration": 0,
    
  2403.                 "lanes": "0b0000000000000000000000000000010",
    
  2404.                 "timestamp": 10,
    
  2405.                 "type": "render-idle",
    
  2406.               },
    
  2407.               {
    
  2408.                 "batchUID": 2,
    
  2409.                 "depth": 0,
    
  2410.                 "duration": 0,
    
  2411.                 "lanes": "0b0000000000000000000000000000010",
    
  2412.                 "timestamp": 10,
    
  2413.                 "type": "render",
    
  2414.               },
    
  2415.               {
    
  2416.                 "batchUID": 2,
    
  2417.                 "depth": 0,
    
  2418.                 "duration": 0,
    
  2419.                 "lanes": "0b0000000000000000000000000000010",
    
  2420.                 "timestamp": 10,
    
  2421.                 "type": "commit",
    
  2422.               },
    
  2423.               {
    
  2424.                 "batchUID": 2,
    
  2425.                 "depth": 1,
    
  2426.                 "duration": 0,
    
  2427.                 "lanes": "0b0000000000000000000000000000010",
    
  2428.                 "timestamp": 10,
    
  2429.                 "type": "layout-effects",
    
  2430.               },
    
  2431.               {
    
  2432.                 "batchUID": 2,
    
  2433.                 "depth": 1,
    
  2434.                 "duration": 0,
    
  2435.                 "lanes": "0b0000000000000000000000000000010",
    
  2436.                 "timestamp": 10,
    
  2437.                 "type": "passive-effects",
    
  2438.               },
    
  2439.             ],
    
  2440.           }
    
  2441.         `);
    
  2442.       });
    
  2443. 
    
  2444.       it('should generate component stacks for state update', async () => {
    
  2445.         function CommponentWithChildren({initialRender}) {
    
  2446.           Scheduler.log('Render ComponentWithChildren');
    
  2447.           return <Child initialRender={initialRender} />;
    
  2448.         }
    
  2449. 
    
  2450.         function Child({initialRender}) {
    
  2451.           const [didRender, setDidRender] = React.useState(initialRender);
    
  2452.           if (!didRender) {
    
  2453.             setDidRender(true);
    
  2454.           }
    
  2455.           Scheduler.log('Render Child');
    
  2456.           return null;
    
  2457.         }
    
  2458. 
    
  2459.         renderRootHelper(<CommponentWithChildren initialRender={false} />);
    
  2460. 
    
  2461.         await waitForAll([
    
  2462.           'Render ComponentWithChildren',
    
  2463.           'Render Child',
    
  2464.           'Render Child',
    
  2465.         ]);
    
  2466. 
    
  2467.         const timelineData = stopProfilingAndGetTimelineData();
    
  2468.         expect(timelineData.schedulingEvents).toMatchInlineSnapshot(`
    
  2469.           [
    
  2470.             {
    
  2471.               "lanes": "0b0000000000000000000000000100000",
    
  2472.               "timestamp": 10,
    
  2473.               "type": "schedule-render",
    
  2474.               "warning": null,
    
  2475.             },
    
  2476.             {
    
  2477.               "componentName": "Child",
    
  2478.               "componentStack": "
    
  2479.               in Child (at **)
    
  2480.               in CommponentWithChildren (at **)",
    
  2481.               "lanes": "0b0000000000000000000000000100000",
    
  2482.               "timestamp": 10,
    
  2483.               "type": "schedule-state-update",
    
  2484.               "warning": null,
    
  2485.             },
    
  2486.           ]
    
  2487.         `);
    
  2488.       });
    
  2489.     });
    
  2490. 
    
  2491.     describe('when not profiling', () => {
    
  2492.       // @reactVersion >=18.0
    
  2493.       it('should not log any marks', () => {
    
  2494.         renderHelper(<div />);
    
  2495. 
    
  2496.         const timelineData = stopProfilingAndGetTimelineData();
    
  2497.         expect(timelineData).toBeNull();
    
  2498.       });
    
  2499.     });
    
  2500.   });
    
  2501. });